Code: Select all
[BITS 64]
global _start
section .text
_start:
;syscall(SYSCALL_PRINT, (uint64_t)msg, 0)
mov rax, 1 ; SYSCALL_PRINT = 1
lea rdi, [rel msg] ; First argument: pointer to message
xor rsi, rsi ; Second argument (not used)
syscall ; Perform syscall
; syscall(SYSCALL_EXIT, 0, 0)
mov rax, 3 ; SYSCALL_EXIT = 3
xor rdi, rdi ; First argument: exit code 0
xor rsi, rsi ; Second argument (unused)
syscall ; Exit syscall
.hang:
jmp .hang ; If syscall fails, loop forever
section .data
msg: db "Hello from user via syscall!", 0
Code: Select all
write_msr(MSR_LSTAR, (uint64_t)&syscall_entry);
Code: Select all
syscall_entry:
swapgs ; switch GS base with KernelGSBase
mov [gs:0x10], rsp ; Save old kernel RSP
mov rsp, [gs:8] ; Load per-core kernel stack
sub rsp, 8*8 ; make room for pushes (align if needed)
; Don't touch RCX or R11! They are used by sysretq.
push rcx
push r11
; Save other registers
push rdi
push rsi
push rdx
push rax
push r8
push r9
; Arguments to syscall_handler(syscall_num, arg1)
mov rsi, rdi ; user argument → RSI
mov rdi, rax ; syscall number → RDI
call syscall_handler
; Restore registers
pop r9
pop r8
pop rax
pop rdx
pop rsi
pop rdi
pop r11
pop rcx
swapgs ; switch GS base with KernelGSBase
; Return to user — RCX and R11 must still hold original values
sysretq
Code: Select all
void syscall_handler(uint64_t syscall_num, uint64_t arg1, uint64_t arg2) {
switch(syscall_num) {
case SYSCALL_PRINT:
const char* str = (const char*)arg1;
printf("%s\n", str);
break;
case SYSCALL_READ:
// Handle read syscall
char* user_buf = (char*)arg1;
uint64_t size = arg2;
// Implement read logic here
break;
case SYSCALL_EXIT:
printf("User requested shell exit.\n");
break;
default:
printf("Unknown syscall: %d\n", (int) syscall_num);
}
}
Code: Select all
Hello from user via syscall!
Page fault! ( Page not present, User-mode, Instruction fetch, ) at address 0x0
Halting the system due to page fault.