1 /* This file is part of the lowest layer of the MINIX kernel. (The other part
2 * is "proc.c".) The lowest layer does process switching and message handling.
4 * Kernel is entered either because of kernel-calls, ipc-calls, interrupts or
5 * exceptions. TSS is set so that the kernel stack is loaded. The user context is
6 * saved to the proc table and the handler of the event is called. Once the
7 * handler is done, switch_to_user() function is called to pick a new process,
8 * finish what needs to be done for the next process to run, sets its context
9 * and switch to userspace.
12 #include "kernel/kernel.h" /* configures the kernel */
16 #include <machine/vm.h>
17 #include "kernel/kernel.h"
18 #include <minix/config.h>
19 #include <minix/const.h>
20 #include <minix/com.h>
21 #include <machine/asm.h>
22 #include <machine/interrupt.h>
23 #include "archconst.h"
24 #include "kernel/const.h"
25 #include "kernel/proc.h"
27 #include <machine/multiboot.h>
28 #include <machine/ipcconst.h>
29 #include <machine/cpu.h>
30 #include <arm/armreg.h>
33 #include "arch_proto.h" /* K_STACK_SIZE */
38 * Adjust lr, push pc/psr when exception triggered and switch to SVC mode
39 * The 'lr_offset' argument holds the adjustment.
41 * When an instruction causes the ARM core to enter the exception handler
42 * the value of pc is stored in the link register (lr). By default on ARM
43 * the program counter is 3 instruction a head of the current instruction
44 * being executed (because of the 3 stage pipeline). Depending on where in
45 * the pipeline the exception happens lr will need to de adjusted to find
46 * the proper return address.
48 .macro switch_to_svc lr_offset
49 sub lr, lr, #\lr_offset /* do the adjustment */
50 srsdb sp!, #PSR_SVC32_MODE /* store the saved the return */
51 /* address and program status */
52 /* register onto the kernel stack */
53 /* Also modify the stack pointer. */
54 cps #PSR_SVC32_MODE /* do the switch to SVC. */
58 * Test if the exception/interrupt occurred in the kernel.
59 * Jump to 'label' argument if it occurred in the kernel.
61 * NOTE: switch_to_svc must be called first */
62 .macro test_int_in_kernel, label
64 ldr r3, [sp, #8] /* get spsr. */
65 orr r3, r3, #(PSR_F | PSR_I) /* mask interrupts on return. */
66 str r3, [sp, #8] /* store spsr. */
67 and r3, r3, #PSR_MODE /* mask the ARM mode. */
68 cmp r3, #PSR_USR32_MODE /* compare it to user mode. */
70 bne \label /* In-kernel handling. */
73 /* Save the register context to the proc structure */
74 .macro save_process_ctx
75 add sp, sp, #8 /* We expect srsdb pushed cpsr and lr on */
77 ldr lr, [sp] /* lr = proc_ptr. */
78 stm lr, {r0-r14}^ /* store the user mode registers */
79 /* proc_ptr->p_reg.r0-r14 = r0-r14. */
80 ldr r12, [sp, #-8] /* r12 = pc stored on the stack. */
81 str r12, [lr, #PCREG] /* proc_ptr->p_reg.pc = r12. */
82 ldr r12, [sp, #-4] /* r12 = cpsr stored on the stack. */
83 str r12, [lr, #PSREG] /* proc_ptr->p_reg.psr = r12. */
86 .macro exception_handler exc_name, exc_num, lr_offset
87 ENTRY(\exc_name\()_entry)
88 switch_to_svc \lr_offset
89 test_int_in_kernel \exc_name\()_entry_nested
91 \exc_name\()entry_from_user:
94 ldr fp, [sp] /* save the pointer to the current process. */
95 add r4, fp, #PCREG /* save the exception pc (saved lr_user) */
96 /* r4-r9 are callee save. */
98 /* stop user process cycles */
99 mov r0, fp /* first param: caller proc ptr. */
100 mov fp, #0 /* for stack trace. */
101 bl _C_LABEL(context_stop)
104 * push a pointer to the interrupt state pushed by the cpu and the
105 * vector number pushed by the vector handler just before calling
106 * exception_entry and call the exception handler.
108 mov r0, #0 /* it is not a nested exception. */
109 mov r1, r4 /* saved lr. */
110 mov r2, #\exc_num /* vector number */
111 bl _C_LABEL(exception_handler)
112 b _C_LABEL(switch_to_user)
114 \exc_name\()_entry_nested:
116 mov r0, #1 /* it is a nested exception. */
117 add r1, sp, #56 /* saved lr */
118 mov r2, #\exc_num /* vector number */
119 bl _C_LABEL(exception_handler)
125 /* Exception handlers */
126 exception_handler data_abort DATA_ABORT_VECTOR 8
127 exception_handler prefetch_abort PREFETCH_ABORT_VECTOR 4
128 exception_handler undefined_inst UNDEFINED_INST_VECTOR 4
133 test_int_in_kernel irq_entry_from_kernel
138 /* save the pointer to the current process */
141 push {fp} /* save caller proc ptr. */
142 sub sp, sp, #4 /* maintain stack alignment. */
144 /* stop user process cycles */
145 mov r0, fp /* first param: caller proc ptr. */
146 mov fp, #0 /* for stack trace. */
147 bl _C_LABEL(context_stop)
150 bl _C_LABEL(bsp_irq_handle) /* bsp_irq_handle(void) */
153 pop {fp} /* caller proc ptr. */
154 dsb /* data synchronization barrier. */
156 b _C_LABEL(switch_to_user)
158 irq_entry_from_kernel:
160 bl _C_LABEL(context_stop_idle)
163 bl _C_LABEL(bsp_irq_handle) /* bsp_irq_handle(void). */
165 /* data synchronization barrier */ dsb
171 * supervisor call (SVC) kernel entry point
174 /* Store the LR and the SPSR of the current mode onto the SVC stack */
175 srsdb sp!, #PSR_SVC32_MODE
178 /* save the pointer to the current process */
182 beq kernel_call_entry
186 /* return -1 to the current process as an invalid SWI was called .*/
189 b _C_LABEL(switch_to_user)
192 * kernel call is only from a process to kernel
194 ENTRY(kernel_call_entry)
196 * pass the syscall arguments from userspace to the handler.
197 * save_process_ctx() does not clobber these registers, they are still
198 * set as the userspace has set them.
200 push {fp} /* save caller proc ptr. */
201 push {r0} /* save msg ptr so it's not clobbered. */
203 /* stop user process cycles */
204 mov r0, fp /* first param: caller proc ptr */
205 mov fp, #0 /* for stack trace */
206 bl _C_LABEL(context_stop)
208 pop {r0} /* first param: msg ptr. */
209 pop {r1} /* second param: caller proc ptr. */
210 bl _C_LABEL(kernel_call)
212 b _C_LABEL(switch_to_user)
215 * IPC is only from a process to kernel
219 * pass the syscall arguments from userspace to the handler.
220 * save_process_ctx() does not clobber these registers, they are still
221 * set as the userspace have set them
223 push {fp} /* save caller proc ptr. */
224 push {r0-r2} /* save regs so they're not clobbered. */
226 /* stop user process cycles */
227 mov r0, fp /* first param: caller proc ptr. */
228 mov fp, #0 /* for stack trace. */
229 bl _C_LABEL(context_stop)
231 pop {r0-r2} /* restore regs */
234 /* restore the current process pointer and save the return value */
235 pop {fp} /* caller proc ptr. */
238 b _C_LABEL(switch_to_user)
243 ENTRY(restore_user_context)
244 /* sp holds the proc ptr */
247 /* Set SPSR and LR for return */
249 msr spsr_fsxc, r0 /* flags , status, extension control. */
252 /* Restore user-mode registers from proc struct */
255 ldr sp, =_C_LABEL(svc_stack)
259 movs pc, lr /* preferred way of returning from svc */
261 /*===========================================================================*/
263 /*===========================================================================*/
266 .short 0x526F /* this must be the first data entry (magic #) */
272 LABEL(__k_unpaged_k_initial_stktop)
278 .space K_STACK_SIZE /* kernel stack */ /* FIXME use macro here */
279 LABEL(k_boot_stktop) /* top of kernel stack */
282 LABEL(k_stacks_start)
284 /* two pages for each stack, one for data, other as a sandbox */
285 .space 2 * (K_STACK_SIZE * CONFIG_MAX_CPUS)
289 /* top of kernel stack */