1use super::UserContext;
11use core::arch::global_asm;
12
13extern "sysv64" {
14 pub fn syscall_fn_entry();
25
26 fn syscall_fn_return(regs: &mut UserContext);
27}
28
29impl UserContext {
30 pub fn run_fncall(&mut self) {
35 unsafe {
36 syscall_fn_return(self);
37 }
38 self.trap_num = 0x100;
39 self.error_code = 0;
40 }
41}
42
43#[cfg(target_os = "linux")]
53global_asm!(
54 r#"
55.macro SWITCH_TO_KERNEL_STACK
56 mov rsp, fs:48 # rsp = kernel fsbase
57 mov rsp, [rsp + 64] # rsp = kernel stack
58.endm
59.macro SAVE_KERNEL_STACK
60 mov fs:64, rsp
61.endm
62.macro PUSH_USER_FSBASE
63 push fs:0
64.endm
65.macro SWITCH_TO_KERNEL_FSBASE
66 mov eax, 158 # SYS_arch_prctl
67 mov edi, 0x1002 # SET_FS
68 mov rsi, fs:48 # rsi = kernel fsbase
69 syscall
70.endm
71.macro POP_USER_FSBASE
72 mov rsi, [rsp + 18 * 8] # rsi = user fsbase
73 mov rdx, fs:0 # rdx = kernel fsbase
74 test rsi, rsi
75 jnz 1f # if not 0, goto set
760: lea rsi, [rdx + 72] # rsi = init user fsbase
77 mov [rsi], rsi # user_fs:0 = user fsbase
781: mov eax, 158 # SYS_arch_prctl
79 mov edi, 0x1002 # SET_FS
80 syscall # set fsbase
81 mov fs:48, rdx # user_fs:48 = kernel fsbase
82.endm
83
84.global syscall_fn_entry
85.global syscall_fn_return
86"#
87);
88
89#[cfg(target_os = "macos")]
105global_asm!(
106 r#"
107.macro SWITCH_TO_KERNEL_STACK
108 mov rsp, gs:48 # rsp = kernel gsbase
109 mov rsp, [rsp + 48] # rsp = kernel stack
110.endm
111.macro SAVE_KERNEL_STACK
112 mov gs:48, rsp
113.endm
114.macro PUSH_USER_FSBASE
115 push gs:0
116.endm
117.macro SWITCH_TO_KERNEL_FSBASE
118 mov rdi, gs:48 # rdi = kernel gsbase
119 mov eax, 0x3000003
120 syscall # set gsbase
121.endm
122.macro POP_USER_FSBASE
123 mov rdi, [rsp + 18 * 8] # rdi = user gsbase
124 mov rsi, gs:0
125 add rsi, 224 # rsi = kernel gsbase
126 test rdi, rdi
127 jnz 1f # if not 0, goto set
1280: lea rdi, [rsi + 30*8] # rdi = init user gsbase
129 # = pthread.tsd[30] (kernel gsbase + 30 * 8)
130 mov [rdi], rdi # user_gs:0 = user gsbase
1311: mov eax, 0x3000003
132 syscall # set gsbase
133 mov gs:48, rsi # user_gs:48 = kernel gsbase
134.endm
135
136.global _syscall_fn_entry
137.global syscall_fn_entry
138.global _syscall_fn_return
139.set _syscall_fn_entry, syscall_fn_entry
140.set _syscall_fn_return, syscall_fn_return
141"#
142);
143
144global_asm!(
145 r#"
146syscall_fn_entry:
147 # save rsp
148 lea r11, [rsp + 8] # save rsp to r11 (clobber)
149
150 SWITCH_TO_KERNEL_STACK
151 pop rsp
152 lea rsp, [rsp + 20*8] # rsp = top of trap frame
153
154 # push trap frame (struct GeneralRegs)
155 push 0 # ignore gs_base
156 PUSH_USER_FSBASE
157 pushfq # push rflags
158 push [r11 - 8] # push rip
159 push r15
160 push r14
161 push r13
162 push r12
163 push r11
164 push r10
165 push r9
166 push r8
167 push r11 # push rsp
168 push rbp
169 push rdi
170 push rsi
171 push rdx
172 push rcx
173 push rbx
174 push rax
175
176 # restore callee-saved registers
177 SWITCH_TO_KERNEL_STACK
178 pop rbx
179 pop rbx
180 pop rbp
181 pop r12
182 pop r13
183 pop r14
184 pop r15
185
186 SWITCH_TO_KERNEL_FSBASE
187
188 # go back to Rust
189 ret
190
191 # extern "sysv64" fn syscall_fn_return(&mut UserContext)
192syscall_fn_return:
193 # save callee-saved registers
194 push r15
195 push r14
196 push r13
197 push r12
198 push rbp
199 push rbx
200
201 push rdi
202 SAVE_KERNEL_STACK
203 mov rsp, rdi
204
205 POP_USER_FSBASE
206
207 # pop trap frame (struct GeneralRegs)
208 pop rax
209 pop rbx
210 pop rcx
211 pop rdx
212 pop rsi
213 pop rdi
214 pop rbp
215 pop r8 # skip rsp
216 pop r8
217 pop r9
218 pop r10
219 pop r11
220 pop r12
221 pop r13
222 pop r14
223 pop r15
224 pop r11 # r11 = rip. FIXME: don't overwrite r11!
225 popfq # pop rflags
226 mov rsp, [rsp - 8*11] # restore rsp
227 jmp r11 # restore rip
228"#
229);
230
231#[cfg(test)]
232mod tests {
233 use crate::*;
234 use core::arch::global_asm;
235
236 #[cfg(target_os = "macos")]
237 global_asm!(".set _dump_registers, dump_registers");
238
239 global_asm!(
241 r#"
242dump_registers:
243 push r15
244 push r14
245 push r13
246 push r12
247 push r11
248 push r10
249 push r9
250 push r8
251 push rsp
252 push rbp
253 push rdi
254 push rsi
255 push rdx
256 push rcx
257 push rbx
258 push rax
259
260 add rax, 10
261 add rbx, 10
262 add rcx, 10
263 add rdx, 10
264 add rsi, 10
265 add rdi, 10
266 add rbp, 10
267 add r8, 10
268 add r9, 10
269 add r10, 10
270 add r11, 10
271 add r12, 10
272 add r13, 10
273 add r14, 10
274 add r15, 10
275
276 call syscall_fn_entry
277"#
278 );
279
280 #[test]
281 fn run_fncall() {
282 extern "sysv64" {
283 fn dump_registers();
284 }
285 let mut stack = [0u8; 0x1000];
286 let mut cx = UserContext {
287 general: GeneralRegs {
288 rax: 0,
289 rbx: 1,
290 rcx: 2,
291 rdx: 3,
292 rsi: 4,
293 rdi: 5,
294 rbp: 6,
295 rsp: stack.as_mut_ptr() as usize + 0x1000,
296 r8: 8,
297 r9: 9,
298 r10: 10,
299 r11: 11,
300 r12: 12,
301 r13: 13,
302 r14: 14,
303 r15: 15,
304 rip: dump_registers as usize,
305 rflags: 0,
306 fsbase: 0, gsbase: 0,
308 },
309 trap_num: 0,
310 error_code: 0,
311 };
312 cx.run_fncall();
313 let general = unsafe { *(cx.general.rsp as *const GeneralRegs) };
315 assert_eq!(
316 general,
317 GeneralRegs {
318 rax: 0,
319 rbx: 1,
320 rcx: 2,
321 rdx: 3,
322 rsi: 4,
323 rdi: 5,
324 rbp: 6,
325 r8: 8,
327 r9: 9,
328 r10: 10,
329 r12: 12,
331 r13: 13,
332 r14: 14,
333 r15: 15,
334 ..general
335 }
336 );
337 assert_eq!(
339 cx.general,
340 GeneralRegs {
341 rax: 10,
342 rbx: 11,
343 rcx: 12,
344 rdx: 13,
345 rsi: 14,
346 rdi: 15,
347 rbp: 16,
348 r8: 18,
350 r9: 19,
351 r10: 20,
352 r12: 22,
354 r13: 23,
355 r14: 24,
356 r15: 25,
357 ..cx.general
358 }
359 );
360 assert_eq!(cx.trap_num, 0x100);
361 assert_eq!(cx.error_code, 0);
362 }
363}