syscall_entry.asm

Commit: 5ef818f0 Author: jokerz Raw Copy
1
; syscall_entry.asm - SYSCALL instruction handler + userspace entry/exit for Pactor64
2
;
3
; When the CPU executes SYSCALL:
4
;   RCX = return address (user RIP)
5
;   R11 = saved RFLAGS
6
;   RIP = MSR_LSTAR
7
;   CS = MSR_STAR[63:48]  (= 0x08)
8
;   SS = MSR_STAR[63:48] + 8  (= 0x10)
9
;
10
; SYSRET (with REX.W):
11
;   RIP = RCX
12
;   RFLAGS = R11
13
;   CS = MSR_STAR[47:32] | 3  (= 0x1B)
14
;   SS = (MSR_STAR[47:32] + 8) | 3  (= 0x23)
15
;
16
; IMPORTANT: SYSCALL only clobbers RCX and R11. All other GPRs must be
17
; preserved across the syscall handler for the user program to work correctly.
18
19
bits 64
20
section .text
21
22
extern syscall_dispatch
23
24
global syscall_entry
25
global enter_userspace
26
global return_to_kernel
27
28
; ============================================================================
29
; syscall_entry — SYSCALL handler entry point
30
; ============================================================================
31
syscall_entry:
32
    ; Save user RSP, RFLAGS (from R11), RIP (from RCX)
33
    mov [rel user_rsp], rsp
34
    mov [rel user_rflags], r11
35
    mov [rel user_rcx], rcx          ; User RIP (return address)
36
37
    ; Switch to kernel stack
38
    mov rsp, syscall_stack_top
39
40
    ; === Save ALL user registers ===
41
    ; SYSCALL clobbers RCX and R11, but all other GPRs must be preserved.
42
    ; We save everything so we can restore after dispatch.
43
    push rax                         ; syscall number / return value
44
    push rbx
45
    push rcx                         ; user RIP (already saved, but save for callee-saved)
46
    push rdx                         ; arg3
47
    push rsi                         ; arg2
48
    push rdi                         ; arg1
49
    push rbp
50
    push r8                          ; arg5
51
    push r9                          ; arg6
52
    push r10                         ; arg4
53
    push r12
54
    push r13
55
    push r14
56
    push r15
57
58
    ; === Map Linux syscall ABI → C calling convention ===
59
    ; Linux SYSCALL receives:
60
    ;   rax = syscall number
61
    ;   rdi = arg1, rsi = arg2, rdx = arg3
62
    ;   r10 = arg4, r8  = arg5, r9  = arg6
63
    ;
64
    ; C calling convention (for syscall_dispatch):
65
    ;   rdi = num, rsi = arg1, rdx = arg2, rcx = arg3
66
    ;   r8  = arg4, r9  = arg5
67
    ;
68
    ; Move in reverse order to avoid clobbering values we still need:
69
    mov r9, r8                       ; arg5 → C arg6 (r9)
70
    mov r8, r10                      ; arg4 → C arg5 (r8)
71
    mov rcx, rdx                     ; arg3 → C arg4 (rcx)
72
    mov rdx, rsi                     ; arg2 → C arg3 (rdx)
73
    mov rsi, rdi                     ; arg1 → C arg2 (rsi)
74
    mov rdi, rax                     ; num  → C arg1 (rdi)
75
76
    ; Check if this is exit(60) BEFORE calling dispatch — handle in asm
77
    cmp rdi, 60             ; SYS_EXIT?
78
    jne .not_exit
79
    ; Exit: print message and return to kernel
80
    ; rsi = exit status
81
    jmp .return_to_kernel
82
.not_exit:
83
    call syscall_dispatch
84
    ; Return value is in rax — save it
85
    mov [rel syscall_retval], rax
86
87
    ; === Restore ALL user registers ===
88
    ; Restore in reverse order (except rax — we'll set it to the return value)
89
    pop r15
90
    pop r14
91
    pop r13
92
    pop r12
93
    pop r10
94
    pop r9
95
    pop r8
96
    pop rbp
97
    pop rdi
98
    pop rsi
99
    pop rdx
100
    pop rcx                          ; original user RCX (will be overwritten by SYSRET)
101
    pop rbx
102
    ; Don't pop rax yet — we need the return value
103
104
    ; Set return value
105
    mov rax, [rel syscall_retval]
106
107
    ; Restore user RSP and RFLAGS for SYSRET
108
    mov rsp, [rel user_rsp]
109
    mov r11, [rel user_rflags]
110
    mov rcx, [rel user_rcx]
111
112
    ; Enable interrupts on return
113
    or r11, (1 << 9)                 ; Set IF bit
114
115
    ; SYSRET returns to RCX:RIP with R11:RFLAGS
116
    o64 sysret
117
118
; ============================================================================
119
; .return_to_kernel — exit syscall path
120
;   Clean up syscall stack and return to shell.
121
;   Called when syscall_dispatch returns -999 (exit).
122
; ============================================================================
123
.return_to_kernel:
124
    ; Clean up the 14 saved registers from the syscall stack
125
    add rsp, 14 * 8
126
    ; Jump to return_to_kernel which restores kernel RSP and rets
127
    jmp return_to_kernel
128
129
; ============================================================================
130
; enter_userspace(uint64_t user_rip, uint64_t user_rsp)
131
;   Jump from ring 0 to ring 3 using IRETQ.
132
;   rdi = user RIP
133
;   rsi = user RSP
134
; ============================================================================
135
enter_userspace:
136
    ; Save kernel state for return_to_kernel
137
    mov [rel saved_kernel_rsp], rsp
138
    mov [rel saved_kernel_rbp], rbp
139
140
    ; Disable interrupts during IRETQ frame setup
141
    cli
142
143
    ; Use SYSRET to enter ring 3 — set RSP manually before SYSRET
144
    mov rsp, rsi                     ; User RSP
145
    mov rcx, rdi                     ; User RIP (SYSRET sets RIP=RCX)
146
    mov r11, 0x202                   ; User RFLAGS (IF=1, bit1=1)
147
    o64 sysret
148
149
return_to_kernel:
150
    cli
151
    ; Restore kernel segments
152
    push rax
153
    mov ax, 0x10
154
    mov ds, ax
155
    mov es, ax
156
    mov fs, ax
157
    mov gs, ax
158
    pop rax
159
    ; Get return address and target RSP
160
    mov r8, [rel saved_kernel_rsp]
161
    mov r9, [r8]        ; return address
162
    add r8, 8           ; RSP past the return addr
163
    mov rbp, [rel saved_kernel_rbp]
164
    ; Build IRETQ on syscall stack (clean CS/SS transition)
165
    mov rsp, syscall_stack_top
166
    push qword 0x10     ; SS = kernel data
167
    push r8              ; RSP = shell stack
168
    pushfq
169
    or qword [rsp], 0x200 ; IF
170
    push qword 0x08     ; CS = kernel code
171
    push r9              ; RIP = return address
172
    iretq
173
174
175
176
    ; Restore kernel segment registers (user code set them to ring 3 values)
177
    push rax
178
    mov ax, 0x10
179
    mov ds, ax
180
    mov es, ax
181
    mov fs, ax
182
    mov gs, ax
183
    pop rax
184
    pop rax
185
186
    sti
187
    ret
188
189
; ============================================================================
190
; Data section — saved state
191
; ============================================================================
192
section .data
193
align 8
194
user_rsp:           dq 0
195
user_rflags:        dq 0
196
user_rcx:           dq 0
197
saved_kernel_rsp:   dq 0
198
saved_kernel_rbp:   dq 0
199
syscall_retval:     dq 0
200
201
; ============================================================================
202
; BSS — dedicated syscall stack (8KB)
203
; ============================================================================
204
section .bss
205
align 16
206
    resb 8192                        ; 8KB syscall stack
207
syscall_stack_top:
208