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. 3 * 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. 10 */ 11 12#include "kernel/kernel.h" /* configures the kernel */ 13 14/* sections */ 15 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" 26#include "sconst.h" 27#include <machine/multiboot.h> 28#include <machine/ipcconst.h> 29#include <machine/cpu.h> 30#include <arm/armreg.h> 31#include "bsp_intr.h" 32 33#include "arch_proto.h" /* K_STACK_SIZE */ 34 35IMPORT(svc_stack) 36 37/* 38 * Adjust lr, push pc/psr when exception triggered and switch to SVC mode 39 * The 'lr_offset' argument holds the adjustment. 40 * 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. 47 */ 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. */ 55.endm 56 57/* 58 * Test if the exception/interrupt occurred in the kernel. 59 * Jump to 'label' argument if it occurred in the kernel. 60 * 61 * NOTE: switch_to_svc must be called first */ 62.macro test_int_in_kernel, label 63 push {r3} 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. */ 69 pop {r3} 70 bne \label /* In-kernel handling. */ 71.endm 72 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 */ 76 /* the stack. */ 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. */ 84.endm 85 86.macro exception_handler exc_name, exc_num, lr_offset 87ENTRY(\exc_name\()_entry) 88 switch_to_svc \lr_offset 89 test_int_in_kernel \exc_name\()_entry_nested 90 91\exc_name\()entry_from_user: 92 save_process_ctx 93 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. */ 97 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) 102 103 /* 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. 107 */ 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) 113 114\exc_name\()_entry_nested: 115 push {r0-r12, lr} 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) 120 pop {r0-r12, lr} 121 rfeia sp! 122.endm 123 124 125/* Exception handlers */ 126exception_handler data_abort DATA_ABORT_VECTOR 8 127exception_handler prefetch_abort PREFETCH_ABORT_VECTOR 4 128exception_handler undefined_inst UNDEFINED_INST_VECTOR 4 129 130 131ENTRY(irq_entry) 132 switch_to_svc 4 133 test_int_in_kernel irq_entry_from_kernel 134 135irq_entry_from_user: 136 save_process_ctx 137 138 /* save the pointer to the current process */ 139 ldr fp, [sp] 140 141 push {fp} /* save caller proc ptr. */ 142 sub sp, sp, #4 /* maintain stack alignment. */ 143 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) 148 149 /* call handler */ 150 bl _C_LABEL(bsp_irq_handle) /* bsp_irq_handle(void) */ 151 152 add sp, sp, #4 153 pop {fp} /* caller proc ptr. */ 154 dsb /* data synchronization barrier. */ 155 156 b _C_LABEL(switch_to_user) 157 158irq_entry_from_kernel: 159 push {r0-r12, lr} 160 bl _C_LABEL(context_stop_idle) 161 162 /* call handler */ 163 bl _C_LABEL(bsp_irq_handle) /* bsp_irq_handle(void). */ 164 165 /* data synchronization barrier */ dsb 166 pop {r0-r12, lr} 167 rfeia sp! 168 169 170/* 171 * supervisor call (SVC) kernel entry point 172 */ 173ENTRY(svc_entry) 174 /* Store the LR and the SPSR of the current mode onto the SVC stack */ 175 srsdb sp!, #PSR_SVC32_MODE 176 save_process_ctx 177 178 /* save the pointer to the current process */ 179 ldr fp, [sp] 180 181 cmp r3, #KERVEC_INTR 182 beq kernel_call_entry 183 cmp r3, #IPCVEC_INTR 184 beq ipc_entry 185 186 /* return -1 to the current process as an invalid SWI was called .*/ 187 mov r0, #-1 188 str r0, [fp, #REG0] 189 b _C_LABEL(switch_to_user) 190 191/* 192 * kernel call is only from a process to kernel 193 */ 194ENTRY(kernel_call_entry) 195 /* 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. 199 */ 200 push {fp} /* save caller proc ptr. */ 201 push {r0} /* save msg ptr so it's not clobbered. */ 202 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) 207 208 pop {r0} /* first param: msg ptr. */ 209 pop {r1} /* second param: caller proc ptr. */ 210 bl _C_LABEL(kernel_call) 211 212 b _C_LABEL(switch_to_user) 213 214/* 215 * IPC is only from a process to kernel 216 */ 217ENTRY(ipc_entry) 218 /* 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 222 */ 223 push {fp} /* save caller proc ptr. */ 224 push {r0-r2} /* save regs so they're not clobbered. */ 225 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) 230 231 pop {r0-r2} /* restore regs */ 232 bl _C_LABEL(do_ipc) 233 234 /* restore the current process pointer and save the return value */ 235 pop {fp} /* caller proc ptr. */ 236 str r0, [fp, #REG0] 237 238 b _C_LABEL(switch_to_user) 239 240ENTRY(invalid_svc) 241 b . 242 243ENTRY(restore_user_context) 244 /* sp holds the proc ptr */ 245 mov sp, r0 246 247 /* Set SPSR and LR for return */ 248 ldr r0, [sp, #PSREG] 249 msr spsr_fsxc, r0 /* flags , status, extension control. */ 250 ldr lr, [sp, #PCREG] 251 252 /* Restore user-mode registers from proc struct */ 253 ldm sp, {r0-r14}^ 254 255 ldr sp, =_C_LABEL(svc_stack) 256 ldr sp, [sp] 257 258 /* To user mode! */ 259 movs pc, lr /* preferred way of returning from svc */ 260 261/*===========================================================================*/ 262/* data */ 263/*===========================================================================*/ 264 265.data 266.short 0x526F /* this must be the first data entry (magic #) */ 267.bss 268.data 269.balign 4 270k_initial_stack: 271.space K_STACK_SIZE 272LABEL(__k_unpaged_k_initial_stktop) 273 274/* 275 * the kernel stack 276 */ 277k_boot_stack: 278.space K_STACK_SIZE /* kernel stack */ /* FIXME use macro here */ 279LABEL(k_boot_stktop) /* top of kernel stack */ 280 281.balign K_STACK_SIZE 282LABEL(k_stacks_start) 283 284/* two pages for each stack, one for data, other as a sandbox */ 285.space 2 * (K_STACK_SIZE * CONFIG_MAX_CPUS) 286 287LABEL(k_stacks_end) 288 289/* top of kernel stack */ 290