1*433d6423SLionel Sambuc/* This file is part of the lowest layer of the MINIX kernel. (The other part 2*433d6423SLionel Sambuc * is "proc.c".) The lowest layer does process switching and message handling. 3*433d6423SLionel Sambuc * 4*433d6423SLionel Sambuc * Kernel is entered either because of kernel-calls, ipc-calls, interrupts or 5*433d6423SLionel Sambuc * exceptions. TSS is set so that the kernel stack is loaded. The user context is 6*433d6423SLionel Sambuc * saved to the proc table and the handler of the event is called. Once the 7*433d6423SLionel Sambuc * handler is done, switch_to_user() function is called to pick a new process, 8*433d6423SLionel Sambuc * finish what needs to be done for the next process to run, sets its context 9*433d6423SLionel Sambuc * and switch to userspace. 10*433d6423SLionel Sambuc */ 11*433d6423SLionel Sambuc 12*433d6423SLionel Sambuc#include "kernel/kernel.h" /* configures the kernel */ 13*433d6423SLionel Sambuc 14*433d6423SLionel Sambuc/* sections */ 15*433d6423SLionel Sambuc 16*433d6423SLionel Sambuc#include <machine/vm.h> 17*433d6423SLionel Sambuc#include "kernel/kernel.h" 18*433d6423SLionel Sambuc#include <minix/config.h> 19*433d6423SLionel Sambuc#include <minix/const.h> 20*433d6423SLionel Sambuc#include <minix/com.h> 21*433d6423SLionel Sambuc#include <machine/asm.h> 22*433d6423SLionel Sambuc#include <machine/interrupt.h> 23*433d6423SLionel Sambuc#include "archconst.h" 24*433d6423SLionel Sambuc#include "kernel/const.h" 25*433d6423SLionel Sambuc#include "kernel/proc.h" 26*433d6423SLionel Sambuc#include "sconst.h" 27*433d6423SLionel Sambuc#include <machine/multiboot.h> 28*433d6423SLionel Sambuc#include <machine/ipcconst.h> 29*433d6423SLionel Sambuc#include <machine/cpu.h> 30*433d6423SLionel Sambuc#include <arm/armreg.h> 31*433d6423SLionel Sambuc#include "bsp_intr.h" 32*433d6423SLionel Sambuc 33*433d6423SLionel Sambuc#include "arch_proto.h" /* K_STACK_SIZE */ 34*433d6423SLionel Sambuc 35*433d6423SLionel SambucIMPORT(svc_stack) 36*433d6423SLionel Sambuc 37*433d6423SLionel Sambuc/* 38*433d6423SLionel Sambuc * Adjust lr, push pc/psr when exception triggered and switch to SVC mode 39*433d6423SLionel Sambuc * The 'lr_offset' argument holds the adjustment. 40*433d6423SLionel Sambuc * 41*433d6423SLionel Sambuc * When an instruction causes the ARM core to enter the exception handler 42*433d6423SLionel Sambuc * the value of pc is stored in the link register (lr). By default on ARM 43*433d6423SLionel Sambuc * the program counter is 3 instruction a head of the current instruction 44*433d6423SLionel Sambuc * being executed (because of the 3 stage pipeline). Depending on where in 45*433d6423SLionel Sambuc * the pipeline the exception happens lr will need to de adjusted to find 46*433d6423SLionel Sambuc * the proper return address. 47*433d6423SLionel Sambuc */ 48*433d6423SLionel Sambuc.macro switch_to_svc lr_offset 49*433d6423SLionel Sambuc sub lr, lr, #\lr_offset /* do the adjustment */ 50*433d6423SLionel Sambuc srsdb sp!, #PSR_SVC32_MODE /* store the saved the return */ 51*433d6423SLionel Sambuc /* address and program status */ 52*433d6423SLionel Sambuc /* register onto the kernel stack */ 53*433d6423SLionel Sambuc /* Also modify the stack pointer. */ 54*433d6423SLionel Sambuc cps #PSR_SVC32_MODE /* do the switch to SVC. */ 55*433d6423SLionel Sambuc.endm 56*433d6423SLionel Sambuc 57*433d6423SLionel Sambuc/* 58*433d6423SLionel Sambuc * Test if the exception/interrupt occurred in the kernel. 59*433d6423SLionel Sambuc * Jump to 'label' argument if it occurred in the kernel. 60*433d6423SLionel Sambuc * 61*433d6423SLionel Sambuc * NOTE: switch_to_svc must be called first */ 62*433d6423SLionel Sambuc.macro test_int_in_kernel, label 63*433d6423SLionel Sambuc push {r3} 64*433d6423SLionel Sambuc ldr r3, [sp, #8] /* get spsr. */ 65*433d6423SLionel Sambuc orr r3, r3, #(PSR_F | PSR_I) /* mask interrupts on return. */ 66*433d6423SLionel Sambuc str r3, [sp, #8] /* store spsr. */ 67*433d6423SLionel Sambuc and r3, r3, #PSR_MODE /* mask the ARM mode. */ 68*433d6423SLionel Sambuc cmp r3, #PSR_USR32_MODE /* compare it to user mode. */ 69*433d6423SLionel Sambuc pop {r3} 70*433d6423SLionel Sambuc bne \label /* In-kernel handling. */ 71*433d6423SLionel Sambuc.endm 72*433d6423SLionel Sambuc 73*433d6423SLionel Sambuc/* Save the register context to the proc structure */ 74*433d6423SLionel Sambuc.macro save_process_ctx 75*433d6423SLionel Sambuc add sp, sp, #8 /* We expect srsdb pushed cpsr and lr on */ 76*433d6423SLionel Sambuc /* the stack. */ 77*433d6423SLionel Sambuc ldr lr, [sp] /* lr = proc_ptr. */ 78*433d6423SLionel Sambuc stm lr, {r0-r14}^ /* store the user mode registers */ 79*433d6423SLionel Sambuc /* proc_ptr->p_reg.r0-r14 = r0-r14. */ 80*433d6423SLionel Sambuc ldr r12, [sp, #-8] /* r12 = pc stored on the stack. */ 81*433d6423SLionel Sambuc str r12, [lr, #PCREG] /* proc_ptr->p_reg.pc = r12. */ 82*433d6423SLionel Sambuc ldr r12, [sp, #-4] /* r12 = cpsr stored on the stack. */ 83*433d6423SLionel Sambuc str r12, [lr, #PSREG] /* proc_ptr->p_reg.psr = r12. */ 84*433d6423SLionel Sambuc.endm 85*433d6423SLionel Sambuc 86*433d6423SLionel Sambuc.macro exception_handler exc_name, exc_num, lr_offset 87*433d6423SLionel SambucENTRY(\exc_name\()_entry) 88*433d6423SLionel Sambuc switch_to_svc \lr_offset 89*433d6423SLionel Sambuc test_int_in_kernel \exc_name\()_entry_nested 90*433d6423SLionel Sambuc 91*433d6423SLionel Sambuc\exc_name\()entry_from_user: 92*433d6423SLionel Sambuc save_process_ctx 93*433d6423SLionel Sambuc 94*433d6423SLionel Sambuc ldr fp, [sp] /* save the pointer to the current process. */ 95*433d6423SLionel Sambuc add r4, fp, #PCREG /* save the exception pc (saved lr_user) */ 96*433d6423SLionel Sambuc /* r4-r9 are callee save. */ 97*433d6423SLionel Sambuc 98*433d6423SLionel Sambuc /* stop user process cycles */ 99*433d6423SLionel Sambuc mov r0, fp /* first param: caller proc ptr. */ 100*433d6423SLionel Sambuc mov fp, #0 /* for stack trace. */ 101*433d6423SLionel Sambuc bl _C_LABEL(context_stop) 102*433d6423SLionel Sambuc 103*433d6423SLionel Sambuc /* 104*433d6423SLionel Sambuc * push a pointer to the interrupt state pushed by the cpu and the 105*433d6423SLionel Sambuc * vector number pushed by the vector handler just before calling 106*433d6423SLionel Sambuc * exception_entry and call the exception handler. 107*433d6423SLionel Sambuc */ 108*433d6423SLionel Sambuc mov r0, #0 /* it is not a nested exception. */ 109*433d6423SLionel Sambuc mov r1, r4 /* saved lr. */ 110*433d6423SLionel Sambuc mov r2, #\exc_num /* vector number */ 111*433d6423SLionel Sambuc bl _C_LABEL(exception_handler) 112*433d6423SLionel Sambuc b _C_LABEL(switch_to_user) 113*433d6423SLionel Sambuc 114*433d6423SLionel Sambuc\exc_name\()_entry_nested: 115*433d6423SLionel Sambuc push {r0-r12, lr} 116*433d6423SLionel Sambuc mov r0, #1 /* it is a nested exception. */ 117*433d6423SLionel Sambuc add r1, sp, #56 /* saved lr */ 118*433d6423SLionel Sambuc mov r2, #\exc_num /* vector number */ 119*433d6423SLionel Sambuc bl _C_LABEL(exception_handler) 120*433d6423SLionel Sambuc pop {r0-r12, lr} 121*433d6423SLionel Sambuc rfeia sp! 122*433d6423SLionel Sambuc.endm 123*433d6423SLionel Sambuc 124*433d6423SLionel Sambuc 125*433d6423SLionel Sambuc/* Exception handlers */ 126*433d6423SLionel Sambucexception_handler data_abort DATA_ABORT_VECTOR 8 127*433d6423SLionel Sambucexception_handler prefetch_abort PREFETCH_ABORT_VECTOR 4 128*433d6423SLionel Sambucexception_handler undefined_inst UNDEFINED_INST_VECTOR 4 129*433d6423SLionel Sambuc 130*433d6423SLionel Sambuc 131*433d6423SLionel SambucENTRY(irq_entry) 132*433d6423SLionel Sambuc switch_to_svc 4 133*433d6423SLionel Sambuc test_int_in_kernel irq_entry_from_kernel 134*433d6423SLionel Sambuc 135*433d6423SLionel Sambucirq_entry_from_user: 136*433d6423SLionel Sambuc save_process_ctx 137*433d6423SLionel Sambuc 138*433d6423SLionel Sambuc /* save the pointer to the current process */ 139*433d6423SLionel Sambuc ldr fp, [sp] 140*433d6423SLionel Sambuc 141*433d6423SLionel Sambuc push {fp} /* save caller proc ptr. */ 142*433d6423SLionel Sambuc sub sp, sp, #4 /* maintain stack alignment. */ 143*433d6423SLionel Sambuc 144*433d6423SLionel Sambuc /* stop user process cycles */ 145*433d6423SLionel Sambuc mov r0, fp /* first param: caller proc ptr. */ 146*433d6423SLionel Sambuc mov fp, #0 /* for stack trace. */ 147*433d6423SLionel Sambuc bl _C_LABEL(context_stop) 148*433d6423SLionel Sambuc 149*433d6423SLionel Sambuc /* call handler */ 150*433d6423SLionel Sambuc bl _C_LABEL(bsp_irq_handle) /* bsp_irq_handle(void) */ 151*433d6423SLionel Sambuc 152*433d6423SLionel Sambuc add sp, sp, #4 153*433d6423SLionel Sambuc pop {fp} /* caller proc ptr. */ 154*433d6423SLionel Sambuc dsb /* data synchronization barrier. */ 155*433d6423SLionel Sambuc 156*433d6423SLionel Sambuc b _C_LABEL(switch_to_user) 157*433d6423SLionel Sambuc 158*433d6423SLionel Sambucirq_entry_from_kernel: 159*433d6423SLionel Sambuc push {r0-r12, lr} 160*433d6423SLionel Sambuc bl _C_LABEL(context_stop_idle) 161*433d6423SLionel Sambuc 162*433d6423SLionel Sambuc /* call handler */ 163*433d6423SLionel Sambuc bl _C_LABEL(bsp_irq_handle) /* bsp_irq_handle(void). */ 164*433d6423SLionel Sambuc 165*433d6423SLionel Sambuc /* data synchronization barrier */ dsb 166*433d6423SLionel Sambuc pop {r0-r12, lr} 167*433d6423SLionel Sambuc rfeia sp! 168*433d6423SLionel Sambuc 169*433d6423SLionel Sambuc 170*433d6423SLionel Sambuc/* 171*433d6423SLionel Sambuc * supervisor call (SVC) kernel entry point 172*433d6423SLionel Sambuc */ 173*433d6423SLionel SambucENTRY(svc_entry) 174*433d6423SLionel Sambuc /* Store the LR and the SPSR of the current mode onto the SVC stack */ 175*433d6423SLionel Sambuc srsdb sp!, #PSR_SVC32_MODE 176*433d6423SLionel Sambuc save_process_ctx 177*433d6423SLionel Sambuc 178*433d6423SLionel Sambuc /* save the pointer to the current process */ 179*433d6423SLionel Sambuc ldr fp, [sp] 180*433d6423SLionel Sambuc 181*433d6423SLionel Sambuc cmp r3, #KERVEC_INTR 182*433d6423SLionel Sambuc beq kernel_call_entry 183*433d6423SLionel Sambuc cmp r3, #IPCVEC_INTR 184*433d6423SLionel Sambuc beq ipc_entry 185*433d6423SLionel Sambuc 186*433d6423SLionel Sambuc /* return -1 to the current process as an invalid SWI was called .*/ 187*433d6423SLionel Sambuc mov r0, #-1 188*433d6423SLionel Sambuc str r0, [fp, #REG0] 189*433d6423SLionel Sambuc b _C_LABEL(switch_to_user) 190*433d6423SLionel Sambuc 191*433d6423SLionel Sambuc/* 192*433d6423SLionel Sambuc * kernel call is only from a process to kernel 193*433d6423SLionel Sambuc */ 194*433d6423SLionel SambucENTRY(kernel_call_entry) 195*433d6423SLionel Sambuc /* 196*433d6423SLionel Sambuc * pass the syscall arguments from userspace to the handler. 197*433d6423SLionel Sambuc * save_process_ctx() does not clobber these registers, they are still 198*433d6423SLionel Sambuc * set as the userspace has set them. 199*433d6423SLionel Sambuc */ 200*433d6423SLionel Sambuc push {fp} /* save caller proc ptr. */ 201*433d6423SLionel Sambuc push {r0} /* save msg ptr so it's not clobbered. */ 202*433d6423SLionel Sambuc 203*433d6423SLionel Sambuc /* stop user process cycles */ 204*433d6423SLionel Sambuc mov r0, fp /* first param: caller proc ptr */ 205*433d6423SLionel Sambuc mov fp, #0 /* for stack trace */ 206*433d6423SLionel Sambuc bl _C_LABEL(context_stop) 207*433d6423SLionel Sambuc 208*433d6423SLionel Sambuc pop {r0} /* first param: msg ptr. */ 209*433d6423SLionel Sambuc pop {r1} /* second param: caller proc ptr. */ 210*433d6423SLionel Sambuc bl _C_LABEL(kernel_call) 211*433d6423SLionel Sambuc 212*433d6423SLionel Sambuc b _C_LABEL(switch_to_user) 213*433d6423SLionel Sambuc 214*433d6423SLionel Sambuc/* 215*433d6423SLionel Sambuc * IPC is only from a process to kernel 216*433d6423SLionel Sambuc */ 217*433d6423SLionel SambucENTRY(ipc_entry) 218*433d6423SLionel Sambuc /* 219*433d6423SLionel Sambuc * pass the syscall arguments from userspace to the handler. 220*433d6423SLionel Sambuc * save_process_ctx() does not clobber these registers, they are still 221*433d6423SLionel Sambuc * set as the userspace have set them 222*433d6423SLionel Sambuc */ 223*433d6423SLionel Sambuc push {fp} /* save caller proc ptr. */ 224*433d6423SLionel Sambuc push {r0-r2} /* save regs so they're not clobbered. */ 225*433d6423SLionel Sambuc 226*433d6423SLionel Sambuc /* stop user process cycles */ 227*433d6423SLionel Sambuc mov r0, fp /* first param: caller proc ptr. */ 228*433d6423SLionel Sambuc mov fp, #0 /* for stack trace. */ 229*433d6423SLionel Sambuc bl _C_LABEL(context_stop) 230*433d6423SLionel Sambuc 231*433d6423SLionel Sambuc pop {r0-r2} /* restore regs */ 232*433d6423SLionel Sambuc bl _C_LABEL(do_ipc) 233*433d6423SLionel Sambuc 234*433d6423SLionel Sambuc /* restore the current process pointer and save the return value */ 235*433d6423SLionel Sambuc pop {fp} /* caller proc ptr. */ 236*433d6423SLionel Sambuc str r0, [fp, #REG0] 237*433d6423SLionel Sambuc 238*433d6423SLionel Sambuc b _C_LABEL(switch_to_user) 239*433d6423SLionel Sambuc 240*433d6423SLionel SambucENTRY(invalid_svc) 241*433d6423SLionel Sambuc b . 242*433d6423SLionel Sambuc 243*433d6423SLionel SambucENTRY(restore_user_context) 244*433d6423SLionel Sambuc /* sp holds the proc ptr */ 245*433d6423SLionel Sambuc mov sp, r0 246*433d6423SLionel Sambuc 247*433d6423SLionel Sambuc /* Set SPSR and LR for return */ 248*433d6423SLionel Sambuc ldr r0, [sp, #PSREG] 249*433d6423SLionel Sambuc msr spsr_fsxc, r0 /* flags , status, extension control. */ 250*433d6423SLionel Sambuc ldr lr, [sp, #PCREG] 251*433d6423SLionel Sambuc 252*433d6423SLionel Sambuc /* Restore user-mode registers from proc struct */ 253*433d6423SLionel Sambuc ldm sp, {r0-r14}^ 254*433d6423SLionel Sambuc 255*433d6423SLionel Sambuc ldr sp, =_C_LABEL(svc_stack) 256*433d6423SLionel Sambuc ldr sp, [sp] 257*433d6423SLionel Sambuc 258*433d6423SLionel Sambuc /* To user mode! */ 259*433d6423SLionel Sambuc movs pc, lr /* preferred way of returning from svc */ 260*433d6423SLionel Sambuc 261*433d6423SLionel Sambuc/*===========================================================================*/ 262*433d6423SLionel Sambuc/* data */ 263*433d6423SLionel Sambuc/*===========================================================================*/ 264*433d6423SLionel Sambuc 265*433d6423SLionel Sambuc.data 266*433d6423SLionel Sambuc.short 0x526F /* this must be the first data entry (magic #) */ 267*433d6423SLionel Sambuc.bss 268*433d6423SLionel Sambuc.data 269*433d6423SLionel Sambuc.balign 4 270*433d6423SLionel Sambuck_initial_stack: 271*433d6423SLionel Sambuc.space K_STACK_SIZE 272*433d6423SLionel SambucLABEL(__k_unpaged_k_initial_stktop) 273*433d6423SLionel Sambuc 274*433d6423SLionel Sambuc/* 275*433d6423SLionel Sambuc * the kernel stack 276*433d6423SLionel Sambuc */ 277*433d6423SLionel Sambuck_boot_stack: 278*433d6423SLionel Sambuc.space K_STACK_SIZE /* kernel stack */ /* FIXME use macro here */ 279*433d6423SLionel SambucLABEL(k_boot_stktop) /* top of kernel stack */ 280*433d6423SLionel Sambuc 281*433d6423SLionel Sambuc.balign K_STACK_SIZE 282*433d6423SLionel SambucLABEL(k_stacks_start) 283*433d6423SLionel Sambuc 284*433d6423SLionel Sambuc/* two pages for each stack, one for data, other as a sandbox */ 285*433d6423SLionel Sambuc.space 2 * (K_STACK_SIZE * CONFIG_MAX_CPUS) 286*433d6423SLionel Sambuc 287*433d6423SLionel SambucLABEL(k_stacks_end) 288*433d6423SLionel Sambuc 289*433d6423SLionel Sambuc/* top of kernel stack */ 290