/* * \brief Transition between kernel/userland * \author Martin Stein * \author Stefan Kalkowski * \date 2011-11-15 */ /* * Copyright (C) 2011-2017 Genode Labs GmbH * * This file is part of the Genode OS framework, which is distributed * under the terms of the GNU Affero General Public License version 3. */ #include /********************* ** Constant values ** *********************/ .set USR_MODE, 16 .set FIQ_MODE, 17 .set IRQ_MODE, 18 .set SVC_MODE, 19 .set ABT_MODE, 23 .set UND_MODE, 27 .set SYS_MODE, 31 .set RST_TYPE, 1 .set UND_TYPE, 2 .set SVC_TYPE, 3 .set PAB_TYPE, 4 .set DAB_TYPE, 5 .set IRQ_TYPE, 6 .set FIQ_TYPE, 7 .set RST_PC_ADJUST, 0 .set UND_PC_ADJUST, 4 .set SVC_PC_ADJUST, 0 .set PAB_PC_ADJUST, 4 .set DAB_PC_ADJUST, 8 .set IRQ_PC_ADJUST, 4 .set FIQ_PC_ADJUST, 4 /* offsets of CPU context members */ .set PC_OFFSET, 15 * 4 .set STACK_OFFSET, 17 * 4 /************ ** Macros ** ************/ /** * Save an interrupted user context and switch to kernel context * * \param exception_type kernel name of exception type * \param mode mode number of program status register * \param pc_adjust value that gets subtracted from saved user PC */ .macro _user_to_kernel exception_type, mode, pc_adjust cpsid f, #SVC_MODE /* disable interrupts and change to SVC mode */ stm sp, {r0-r14}^ /* the sp_svc contains the user context pointer */ add r0, sp, #PC_OFFSET ldr sp, [sp, #STACK_OFFSET] /* restore kernel stack pointer */ cps #\mode sub r1, lr, #\pc_adjust /* calculate user program counter */ mrs r2, spsr /* get user cpsr */ mov r3, #\exception_type b _common_kernel_entry .endm .section .text.crt0 /*********************** ** Exception entries ** ***********************/ b _rst_entry /* 0x00: reset */ b _und_entry /* 0x04: undefined instruction */ b _svc_entry /* 0x08: supervisor call */ b _pab_entry /* 0x0c: prefetch abort */ b _dab_entry /* 0x10: data abort */ nop /* 0x14: reserved */ b _irq_entry /* 0x18: interrupt request */ /* * Fast interrupt exception entry 0x1c. * * If the previous mode was not the user mode, it means a previous * exception got interrupted by a fast interrupt. * In that case, we disable fast interrupts and return to the * previous exception handling routine. */ mrs r8, spsr and r9, r8, #0b11111 cmp r9, #USR_MODE cmpne r9, #SYS_MODE beq _fiq_entry orr r8, #0b1000000 msr spsr_cxsf, r8 subs pc, lr, #4 _rst_entry: _user_to_kernel RST_TYPE, SVC_MODE, RST_PC_ADJUST _und_entry: _user_to_kernel UND_TYPE, UND_MODE, UND_PC_ADJUST _svc_entry: _user_to_kernel SVC_TYPE, SVC_MODE, SVC_PC_ADJUST _pab_entry: _user_to_kernel PAB_TYPE, ABT_MODE, PAB_PC_ADJUST _dab_entry: _user_to_kernel DAB_TYPE, ABT_MODE, DAB_PC_ADJUST _irq_entry: _user_to_kernel IRQ_TYPE, IRQ_MODE, IRQ_PC_ADJUST _fiq_entry: _user_to_kernel FIQ_TYPE, FIQ_MODE, FIQ_PC_ADJUST _common_kernel_entry: stmia r0!, {r1-r3} /* save pc, cpsr and exception type */ clrex /* clear exclusive access needed for cmpxchg */ cps #SVC_MODE mov r1, #1 /* clear exception state of the VFP */ lsl r1, #30 vmsr fpexc, r1 adr r1, _fpu_save ldr r1, [r1] blx r1 SYSTEM_REGISTER_SYNC_BARRIER /* synchronize after the context switch */ /* * Go to kernel entry code */ adr lr, _kernel_entry ldr lr, [lr] bx lr _kernel_entry: .long kernel _fpu_save: .long vfp_save_fpu_context .section .text /******************************* ** idle loop for idle thread ** *******************************/ .global idle_thread_main idle_thread_main: wfi b idle_thread_main /***************************** ** kernel to userland switch ** *******************************/ .global kernel_to_user_context_switch kernel_to_user_context_switch: push { r0 } mov r0, r1 bl vfp_load_fpu_context pop { r0 } mov sp, r0 ldr lr, [sp, #15*4] ldr r1, [sp, #16*4] msr spsr_cxsf, r1 ldm sp, {r0-r14}^ SYSTEM_REGISTER_SYNC_BARRIER /* synchronize after the context switch */ subs pc, lr, #0