added entries to gitignore
[barrelfish] / kernel / arch / x86_64 / entry.S
1 /**
2  * \file
3  * \brief System call entry point to the kernel and LRPC fast-path
4  */
5
6 /*
7  * Copyright (c) 2007, 2008, 2009, 2010, ETH Zurich.
8  * All rights reserved.
9  *
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.
13  */
14
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>
19 #include <x86.h>
20 #include <asmoffsets.h>
21
22     .text
23     .globl syscall_entry
24
25 syscall_entry:
26         /* is this an LRPC or a normal syscall? */
27         cmp $SYSCALL_LRPC, %rdi
28         jne  syscall_path   /* normal syscall, branch off */
29
30         /* Load pointer to current DCB */
31         mov     dcb_current(%rip), %rdi
32
33         /* TODO: Check that caller is not disabled */
34
35         /* dcb_current->disabled=false */
36         movb $0, OFFSETOF_DCB_DISABLED(%rdi)
37
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)
47
48         /* Load pointer to root CNode cap */
49         mov     dcb_current(%rip), %rdi
50         lea     OFFSETOF_DCB_CSPACE_CAP(%rdi), %rdi
51
52         /* Check that slot number is within CNode */
53         movb OFFSETOF_CAP_CNODE_BITS(%rdi), %cl
54         mov $1, %r15
55         shl %cl, %r15
56         cmp     %r15, %rsi
57         jae     err_slot
58
59         /* Load pointer to endpoint cap */
60         shl     $OBJBITS_CTE, %rsi
61         mov     OFFSETOF_CAP_CNODE_CNODE(%rdi), %rcx
62         mov     $0xffffff8000000000, %rdi       // phys_to_mem()
63         add     %rdi, %rcx
64         add     %rsi, %rcx
65
66         /* Check that it's an endpoint */
67         cmpl    $OBJTYPE_ENDPOINT, OFFSETOF_CAP_TYPE(%rcx)
68         jne     err_endpoint
69
70         /* TODO: Check rights on the endpoint */
71
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 */
75
76         /* Load pointer to listener's DCB */
77         mov     OFFSETOF_CAP_ENDPOINT_LISTENER(%rcx), %rsi
78
79         /* Check whether listener is runnable */
80 #if defined(CONFIG_SCHEDULER_RR)
81         cmpl    $0, OFFSETOF_DCB_RR_PREV(%rsi)
82         je      lrpc_make_runnable
83 #elif defined(CONFIG_SCHEDULER_RBED)
84         cmpl    $0, OFFSETOF_DCB_RBED_NEXT(%rsi)
85         je      lrpc_rbed_check_runnable
86 #else
87 # error Must define a kernel scheduling policy!
88 #endif
89
90 lrpc_check_runnable_continue:
91         /* Check whether listener is disabled */
92         cmpb    $0, OFFSETOF_DCB_DISABLED(%rsi)
93         jne     err_disabled
94
95         /* RCX = target dispatcher */
96         mov OFFSETOF_DCB_DISP(%rsi), %rcx
97
98         /* Remember LRPC entry point on target (R15) */
99         mov OFFSETOF_DISP_LRPC(%rcx), %r15
100
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 */
106
107         /*
108          *  newpos = delivered + len;
109          *  if (newpos >= consumed && consumed > delivered)
110          *    goto err_buflen;
111          *  if (newpos >= epbuflen) {
112          *    newpos -= epbuflen;
113          *    if (newpos >= consumed)
114          *      goto err_buflen;
115          *  }
116          *  delivered = newpos
117          */
118
119         add $(LRPC_MSG_LENGTH + LMP_RECV_HEADER_LENGTH), %r11d /* r11d (newpos) = delivered + len */
120
121         cmp %r14d, %r11d
122         jb 1f /* if newpos < consumed */
123         cmp %r12d, %r14d
124         ja err_buflen /* if consumed > delivered */
125
126 1:
127         cmp %r13d, %r11d
128         jb 2f /* if newpos < epbuflen */
129
130         /* newpos >= epbuflen */
131         sub %r13d, %r11d /* newpos (r11d) -= epbuflen (r13d) */
132         cmp %r14d, %r11d /* if newpos >= consumed */
133         jae err_buflen
134
135 2:      /* there's enough space, reserve it by updating delivered = newpos */
136         mov %r11d, OFFSETOF_LMP_ENDPOINT_DELIVERED(%rcx)
137
138         /* Set current domain to receiver */
139         mov     %rsi, dcb_current(%rip)
140
141         /* Switch to listener address space */
142         mov     OFFSETOF_DCB_VSPACE(%rsi), %rax
143         mov     %rax, %cr3
144
145         /* Zero registers to avoid the receiver getting hold of them
146          * FIXME: should zero all non-payload registers */
147         xor     %eax, %eax
148         mov     %eax, %fs
149         mov     %eax, %gs
150         
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)
160
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
165         jne load_ldt
166
167         cmp     current_ldt_npages(%rip), %r14
168         jne load_ldt
169
170 load_ldt_continue:
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 */
176         sysretq
177
178 load_ldt: /* Load a new LDT: r11 = base, r14 = npages, rcx = temp for descriptor */
179
180         /* If either base or npages is zero, load an invalid LDT */
181         cmpq    $0, %r11
182         je load_ldt_invalid
183         cmpq    $0, %r14
184         je load_ldt_invalid
185         
186         /* Update segment descriptor for LDT */
187
188         movq    %r11, current_ldt_base(%rip)
189         movq    %r14, current_ldt_npages(%rip)
190
191         /* Format of high word of descriptor is:
192          * 32 bits of zero/reserved
193          * Base bits 63-32 */
194         mov %r11, %rcx
195         shr $32, %rcx
196         shl $32, %rcx
197
198         // Store new descriptor (high half) to GDT
199         mov %rcx, (gdt + 8*LDT_HI_SEL)(%rip)
200
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
204          *   granularity = 1
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)
209          *   present = 1
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
215          */
216
217         // bits 24:31 of base
218         mov %r11, %rcx
219         shr $24, %rcx
220
221         // flags/misc
222         shl $16, %rcx
223         or  $0x80e2, %rcx
224
225         // low 24 bits of base
226         shl $24, %rcx
227         shl $40, %r11
228         shr $40, %r11
229         or  %r11, %rcx
230
231         // low 16 bits of limit
232         shl $16, %rcx
233         shl $48, %r14
234         shr $48, %r14
235         or  %r14, %rcx
236
237         // Store new descriptor (low half) to GDT
238         mov %rcx, (gdt + 8*LDT_LO_SEL)(%rip)
239         
240         // Construct segment selector and load it
241         mov     $LDT_SELECTOR, %cx        
242         lldt    %cx
243         jmp     load_ldt_continue
244
245 load_ldt_invalid:  /* Load an invalid LDT */
246         mov     $0, %cx
247         lldt    %cx
248         movq    $0, current_ldt_base(%rip)
249         movq    $0, current_ldt_npages(%rip)
250         jmp     load_ldt_continue
251
252 err_slot:       // Wrong slot
253         mov     $SYS_ERR_LRPC_SLOT_INVALID, %rax
254         jmp     err
255
256 err_endpoint:   // Not an endpoint
257         mov     $SYS_ERR_LRPC_NOT_ENDPOINT, %rax
258         /* jmp  err  - fall through */
259
260     /* An error occured */
261 err:
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
269     sysretq
270
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)
277
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 */
282
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)
289
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 */
294
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
300 #endif
301
302 lrpc_make_runnable:
303         /* Save user stack */
304         movq    %rsp, user_stack_save(%rip)
305
306         /* Get kernel stack */
307         lea     (x86_64_kernel_stack + X86_64_KERNEL_STACK_SIZE)(%rip), %rsp
308
309         // Save complete register state
310         pushq   %rdx
311         pushq   %rcx
312         pushq   %rbx
313         pushq   %rax
314         pushq   %r15
315         pushq   %r14
316         pushq   %r13
317         pushq   %r12
318         pushq   %r11
319         pushq   %r10
320         pushq   %r9
321         pushq   %r8
322         pushq   %rbp
323         pushq   %rdi
324         pushq   %rsi
325
326         // Call make runnable in C
327         movq    %rsi, %rdi
328         callq   make_runnable
329
330         // Restore complete register state
331         popq    %rsi
332         popq    %rdi
333         popq    %rbp
334         popq    %r8
335         popq    %r9
336         popq    %r10
337         popq    %r11
338         popq    %r12
339         popq    %r13
340         popq    %r14
341         popq    %r15
342         popq    %rax
343         popq    %rbx
344         popq    %rcx
345         popq    %rdx
346
347         /* Restore user stack */
348         movq    user_stack_save(%rip), %rsp
349
350         // Jump back
351         jmp     lrpc_check_runnable_continue
352
353
354 /* regular syscall path */
355 syscall_path:
356         /* Save user stack */
357         movq    %rsp, user_stack_save(%rip)
358
359         /* Get kernel stack */
360         lea (x86_64_kernel_stack + X86_64_KERNEL_STACK_SIZE)(%rip), %rsp
361
362         pushq   %rcx            /* Save user-space RIP */
363         pushq   %r11            /* Save user-space RFLAGS */
364
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 */
372         pushq   %r9             /* arg4 */
373         pushq   %r8             /* arg3 */
374         pushq   %r10            /* arg2 in r10, NOT rcx from syscall */
375
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 */
382
383         callq   sys_syscall     /* Process system call in C */
384
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 */
390
391         .bss
392         .comm   user_stack_save, 8