3 * \brief System call entry point to the kernel and LRPC fast-path
7 * Copyright (c) 2007, 2008, 2009, 2010, ETH Zurich.
10 * This file is distributed under the terms in the attached LICENSE file.
11 * If you do not find this file, copies can be found by writing to:
12 * ETH Zurich D-INFK, Haldeneggsteig 4, CH-8092 Zurich. Attn: Systems Group.
15 #include <barrelfish_kpi/syscalls.h>
16 #include <barrelfish_kpi/capabilities.h>
17 #include <barrelfish_kpi/lmp.h>
18 #include <target/x86_64/offsets_target.h>
20 #include <asmoffsets.h>
26 /* is this an LRPC or a normal syscall? */
27 cmp $SYSCALL_LRPC, %rdi
28 jne syscall_path /* normal syscall, branch off */
30 /* Load pointer to current DCB */
31 mov dcb_current(%rip), %rdi
33 /* TODO: Check that caller is not disabled */
35 /* dcb_current->disabled=false */
36 movb $0, OFFSETOF_DCB_DISABLED(%rdi)
38 /* Save caller's registers */
39 mov OFFSETOF_DCB_DISP(%rdi), %rdi
40 lea OFFSETOF_DISP_X86_64_ENABLED_AREA(%rdi), %rdi
41 movq $SYS_ERR_OK, OFFSETOF_RAX_REG(%rdi)
42 mov %rcx, OFFSETOF_RIP_REG(%rdi)
43 mov %r11, OFFSETOF_EFLAGS_REG(%rdi)
44 mov %rsp, OFFSETOF_RSP_REG(%rdi)
45 mov %fs, OFFSETOF_FS_REG(%rdi)
46 mov %gs, OFFSETOF_GS_REG(%rdi)
48 /* Load pointer to root CNode cap */
49 mov dcb_current(%rip), %rdi
50 lea OFFSETOF_DCB_CSPACE_CAP(%rdi), %rdi
52 /* Check that slot number is within CNode */
53 movb OFFSETOF_CAP_CNODE_BITS(%rdi), %cl
59 /* Load pointer to endpoint cap */
60 shl $OBJBITS_CTE, %rsi
61 mov OFFSETOF_CAP_CNODE_CNODE(%rdi), %rcx
62 mov $0xfffffe0000000000, %rdi // phys_to_mem()
66 /* Check that it's an endpoint */
67 cmpl $OBJTYPE_ENDPOINT, OFFSETOF_CAP_TYPE(%rcx)
70 /* TODO: Check rights on the endpoint */
72 /* Set epoffset for receiver, load epbuflen */
73 mov OFFSETOF_CAP_ENDPOINT_EPOFFSET(%rcx), %rdi
74 mov OFFSETOF_CAP_ENDPOINT_EPBUFLEN(%rcx), %r13d /* r13d = epbuflen */
76 /* Load pointer to listener's DCB */
77 mov OFFSETOF_CAP_ENDPOINT_LISTENER(%rcx), %rsi
79 /* Check whether listener is runnable */
80 #if defined(CONFIG_SCHEDULER_RR)
81 cmpl $0, OFFSETOF_DCB_RR_PREV(%rsi)
83 #elif defined(CONFIG_SCHEDULER_RBED)
84 cmpl $0, OFFSETOF_DCB_RBED_NEXT(%rsi)
85 je lrpc_rbed_check_runnable
87 # error Must define a kernel scheduling policy!
90 lrpc_check_runnable_continue:
91 /* Check whether listener is disabled */
92 cmpb $0, OFFSETOF_DCB_DISABLED(%rsi)
95 /* RCX = target dispatcher */
96 mov OFFSETOF_DCB_DISP(%rsi), %rcx
98 /* Remember LRPC entry point on target (R15) */
99 mov OFFSETOF_DISP_LRPC(%rcx), %r15
101 /* check that the receiver has space in their buffer */
102 add %rdi, %rcx /* add epoffset to dispatcher: rcx = endpoint pointer */
103 mov OFFSETOF_LMP_ENDPOINT_DELIVERED(%rcx), %r11d /* r11d = delivered */
104 mov %r11d, %r12d /* r12d = delivered */
105 mov OFFSETOF_LMP_ENDPOINT_CONSUMED(%rcx), %r14d /* r14d = consumed */
108 * newpos = delivered + len;
109 * if (newpos >= consumed && consumed > delivered)
111 * if (newpos >= epbuflen) {
112 * newpos -= epbuflen;
113 * if (newpos >= consumed)
119 add $(LRPC_MSG_LENGTH + LMP_RECV_HEADER_LENGTH), %r11d /* r11d (newpos) = delivered + len */
122 jb 1f /* if newpos < consumed */
124 ja err_buflen /* if consumed > delivered */
128 jb 2f /* if newpos < epbuflen */
130 /* newpos >= epbuflen */
131 sub %r13d, %r11d /* newpos (r11d) -= epbuflen (r13d) */
132 cmp %r14d, %r11d /* if newpos >= consumed */
135 2: /* there's enough space, reserve it by updating delivered = newpos */
136 mov %r11d, OFFSETOF_LMP_ENDPOINT_DELIVERED(%rcx)
138 /* Set current domain to receiver */
139 mov %rsi, dcb_current(%rip)
141 /* Switch to listener address space */
142 mov OFFSETOF_DCB_VSPACE(%rsi), %rax
145 /* Zero registers to avoid the receiver getting hold of them
146 * FIXME: should zero all non-payload registers */
151 /* Get new dispatcher pointer */
152 mov OFFSETOF_DCB_DISP(%rsi), %rax
153 /* Disable target dispatcher -- gotta do it here for TLB hit reasons */
154 movl $1, OFFSETOF_DISP_DISABLED(%rax)
155 /* update dispatcher's global delivered count */
156 addl $(LRPC_MSG_LENGTH + LMP_RECV_HEADER_LENGTH), OFFSETOF_DISP_LMP_DELIVERED(%rax)
157 /* update systime field in dispatcher from kernel_now variable */
158 movq kernel_now(%rip), %r11
159 movq %r11, OFFSETOF_DISP_SYSTIME(%rax)
161 /* Check if it's necessary to load a new LDT */
162 mov OFFSETOF_DISP_X86_64_LDT_BASE(%rax), %r11
163 mov OFFSETOF_DISP_X86_64_LDT_NPAGES(%rax), %r14
164 cmp current_ldt_base(%rip), %r11
167 cmp current_ldt_npages(%rip), %r14
171 /* Enter at LRPC entry point */
172 mov %r12d, %esi /* bufpos of reserved space in EP buffer */
173 mov %r15, %rcx /* saved LRPC EP */
174 movq OFFSETOF_DISP_UDISP(%rax), %rax /* user-level dispatcher pointer */
175 mov $USER_RFLAGS, %r11 /* eflags */
178 load_ldt: /* Load a new LDT: r11 = base, r14 = npages, rcx = temp for descriptor */
180 /* If either base or npages is zero, load an invalid LDT */
186 /* Update segment descriptor for LDT */
188 movq %r11, current_ldt_base(%rip)
189 movq %r14, current_ldt_npages(%rip)
191 /* Format of high word of descriptor is:
192 * 32 bits of zero/reserved
198 // Store new descriptor (high half) to GDT
199 mov %rcx, (gdt + 8*LDT_HI_SEL)(%rip)
201 /* Format of low word of descriptor is:
202 * Base bits 31-24 (top 8 bits of 32-bit addr)
203 * 16 bits of flags/miscellany: 0x80e2
205 * operation_size = irrelevant
206 * long_mode = irrelevant
207 * available = irrelevant
208 * 4 high bits of limit address = 0 (assuming LDT is < 2**16 * 4k)
210 * privilege_level (2 bits wide) = 3 (user privilege)
211 * system descriptor = 0
212 * type (4 bits wide) = 2
213 * low 24 bits of base addr
214 * low 16 bits of limit
217 // bits 24:31 of base
225 // low 24 bits of base
231 // low 16 bits of limit
237 // Store new descriptor (low half) to GDT
238 mov %rcx, (gdt + 8*LDT_LO_SEL)(%rip)
240 // Construct segment selector and load it
241 mov $LDT_SELECTOR, %cx
243 jmp load_ldt_continue
245 load_ldt_invalid: /* Load an invalid LDT */
248 movq $0, current_ldt_base(%rip)
249 movq $0, current_ldt_npages(%rip)
250 jmp load_ldt_continue
252 err_slot: // Wrong slot
253 mov $SYS_ERR_LRPC_SLOT_INVALID, %rax
256 err_endpoint: // Not an endpoint
257 mov $SYS_ERR_LRPC_NOT_ENDPOINT, %rax
258 /* jmp err - fall through */
260 /* An error occured */
262 /* Restore user's state */
263 mov dcb_current(%rip), %rdi
264 mov OFFSETOF_DCB_DISP(%rdi), %rdi
265 lea OFFSETOF_DISP_X86_64_ENABLED_AREA(%rdi), %rdi
266 mov OFFSETOF_RIP_REG(%rdi), %rcx
267 mov OFFSETOF_EFLAGS_REG(%rdi), %r11
268 mov OFFSETOF_RSP_REG(%rdi), %rsp
271 err_disabled: // Target disabled
272 /* Return error to caller in their enabled save area */
273 mov dcb_current(%rip), %rdi
274 mov OFFSETOF_DCB_DISP(%rdi), %rdi
275 lea OFFSETOF_DISP_X86_64_ENABLED_AREA(%rdi), %rdi
276 movq $SYS_ERR_LMP_TARGET_DISABLED, OFFSETOF_RAX_REG(%rdi)
278 /* Yield to target (call dispatch(target) in C) */
279 mov %rsi, %rdi /* rdi = target DCB */
280 lea (x86_64_kernel_stack + X86_64_KERNEL_STACK_SIZE)(%rip), %rsp
281 jmp dispatch /* no return */
283 err_buflen: /* Target's endpoint buffer is full */
284 /* Return error to caller in their enabled save area */
285 mov dcb_current(%rip), %rdi
286 mov OFFSETOF_DCB_DISP(%rdi), %rdi
287 lea OFFSETOF_DISP_X86_64_ENABLED_AREA(%rdi), %rdi
288 movq $SYS_ERR_LMP_BUF_OVERFLOW, OFFSETOF_RAX_REG(%rdi)
290 /* Yield to target (call dispatch(target) in C) */
291 mov %rsi, %rdi /* rdi = target DCB */
292 lea (x86_64_kernel_stack + X86_64_KERNEL_STACK_SIZE)(%rip), %rsp
293 jmp dispatch /* no return */
295 #ifdef CONFIG_SCHEDULER_RBED
296 lrpc_rbed_check_runnable:
297 cmp queue_tail(%rip), %rsi
298 jne lrpc_make_runnable
299 jmp lrpc_check_runnable_continue
303 /* Save user stack */
304 movq %rsp, user_stack_save(%rip)
306 /* Get kernel stack */
307 lea (x86_64_kernel_stack + X86_64_KERNEL_STACK_SIZE)(%rip), %rsp
309 // Save complete register state
326 // Call make runnable in C
330 // Restore complete register state
347 /* Restore user stack */
348 movq user_stack_save(%rip), %rsp
351 jmp lrpc_check_runnable_continue
354 /* regular syscall path */
356 /* Save user stack */
357 movq %rsp, user_stack_save(%rip)
359 /* Get kernel stack */
360 lea (x86_64_kernel_stack + X86_64_KERNEL_STACK_SIZE)(%rip), %rsp
362 pushq %rcx /* Save user-space RIP */
363 pushq %r11 /* Save user-space RFLAGS */
365 pushq %rbx /* arg11 */
366 pushq %rbp /* arg10 */
367 pushq %rax /* arg9 */
368 pushq %r15 /* arg8 */
369 pushq %r14 /* arg7 */
370 pushq %r13 /* arg6 */
371 pushq %r12 /* arg5 */
374 pushq %r10 /* arg2 in r10, NOT rcx from syscall */
376 /* syscall number is in rdi (1st function argument) */
377 /* arg0 is in rsi (2nd function argument) */
378 /* arg1 is in rdx (3rd function argument) */
379 movq %r11, %r8 /* 5th function argument is user's flags */
380 movq %rcx, %r9 /* 6th function argument is user's IP */
381 movq %rsp, %rcx /* 4th function argument is pointer to arg buffer */
383 callq sys_syscall /* Process system call in C */
385 addq $0x50, %rsp /* Remove buffer from stack */
386 popq %r11 /* Restore RFLAGS */
387 popq %rcx /* Restore RIP */
388 movq user_stack_save(%rip), %rsp /* Restore user stack */
389 sysretq /* Return to user-space */
392 .comm user_stack_save, 8