2012-10-08 03:38:03 +02:00
|
|
|
/* This file is part of the lowest layer of the MINIX kernel. (The other part
|
|
|
|
* is "proc.c".) The lowest layer does process switching and message handling.
|
|
|
|
*
|
|
|
|
* Kernel is entered either because of kernel-calls, ipc-calls, interrupts or
|
|
|
|
* exceptions. TSS is set so that the kernel stack is loaded. The user context is
|
|
|
|
* saved to the proc table and the handler of the event is called. Once the
|
|
|
|
* handler is done, switch_to_user() function is called to pick a new process,
|
|
|
|
* finish what needs to be done for the next process to run, sets its context
|
|
|
|
* and switch to userspace.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "kernel/kernel.h" /* configures the kernel */
|
|
|
|
|
|
|
|
/* sections */
|
|
|
|
|
|
|
|
#include <machine/vm.h>
|
2012-11-15 12:06:41 +01:00
|
|
|
#include "kernel/kernel.h"
|
2012-10-08 03:38:03 +02:00
|
|
|
#include <minix/config.h>
|
|
|
|
#include <minix/const.h>
|
|
|
|
#include <minix/com.h>
|
|
|
|
#include <machine/asm.h>
|
|
|
|
#include <machine/interrupt.h>
|
|
|
|
#include "archconst.h"
|
|
|
|
#include "kernel/const.h"
|
|
|
|
#include "kernel/proc.h"
|
|
|
|
#include "sconst.h"
|
|
|
|
#include <machine/multiboot.h>
|
|
|
|
#include <machine/ipcconst.h>
|
|
|
|
#include <machine/cpu.h>
|
2014-02-07 08:46:29 +01:00
|
|
|
#include "bsp_intr.h"
|
2012-10-08 03:38:03 +02:00
|
|
|
|
|
|
|
#include "arch_proto.h" /* K_STACK_SIZE */
|
|
|
|
|
|
|
|
IMPORT(svc_stack)
|
|
|
|
|
|
|
|
/*
|
2014-02-10 13:12:21 +01:00
|
|
|
* Adjust lr, push pc/psr when exception triggered and switch to SVC mode
|
|
|
|
* The 'lr_offset' argument holds the adjustment.
|
|
|
|
*
|
|
|
|
* When an instruction causes the ARM core to enter the exception handler
|
|
|
|
* the value of pc is stored in the link register (lr). By default on ARM
|
|
|
|
* the program counter is 3 instruction a head of the current instruction
|
|
|
|
* being executed (because of the 3 stage pipeline). Depending on where in
|
|
|
|
* the pipeline the exception happens lr will need to de adjusted to find
|
|
|
|
* the proper return address.
|
2012-10-08 03:38:03 +02:00
|
|
|
*/
|
|
|
|
.macro switch_to_svc lr_offset
|
2014-02-10 13:12:21 +01:00
|
|
|
sub lr, lr, #\lr_offset /* do the adjustment */
|
|
|
|
srsdb sp!, #MODE_SVC /* store the saved the return */
|
|
|
|
/* address and program status */
|
|
|
|
/* register onto the kernel stack */
|
|
|
|
/* Also modify the stack pointer. */
|
|
|
|
cps #MODE_SVC /* do the switch to SVC. */
|
2012-10-08 03:38:03 +02:00
|
|
|
.endm
|
|
|
|
|
|
|
|
/*
|
2014-02-10 13:12:21 +01:00
|
|
|
* Test if the exception/interrupt occurred in the kernel.
|
2012-10-08 03:38:03 +02:00
|
|
|
* Jump to 'label' argument if it occurred in the kernel.
|
|
|
|
*
|
2014-02-10 13:12:21 +01:00
|
|
|
* NOTE: switch_to_svc must be called first */
|
2012-10-08 03:38:03 +02:00
|
|
|
.macro test_int_in_kernel, label
|
|
|
|
push {r3}
|
2014-02-10 13:12:21 +01:00
|
|
|
ldr r3, [sp, #8] /* get spsr. */
|
|
|
|
orr r3, r3, #(PSR_F | PSR_I) /* mask interrupts on return. */
|
|
|
|
str r3, [sp, #8] /* store spsr. */
|
|
|
|
and r3, r3, #PSR_MODE_MASK /* mask the ARM mode. */
|
|
|
|
cmp r3, #MODE_USR /* compare it to user mode. */
|
2012-10-08 03:38:03 +02:00
|
|
|
pop {r3}
|
2014-02-10 13:12:21 +01:00
|
|
|
bne \label /* In-kernel handling. */
|
2012-10-08 03:38:03 +02:00
|
|
|
.endm
|
|
|
|
|
|
|
|
/* Save the register context to the proc structure */
|
|
|
|
.macro save_process_ctx
|
2014-02-10 13:12:21 +01:00
|
|
|
add sp, sp, #8 /* We expect srsdb pushed cpsr and lr on */
|
|
|
|
/* the stack. */
|
|
|
|
ldr lr, [sp] /* lr = proc_ptr. */
|
|
|
|
stm lr, {r0-r14}^ /* store the user mode registers */
|
|
|
|
/* proc_ptr->p_reg.r0-r14 = r0-r14. */
|
|
|
|
ldr r12, [sp, #-8] /* r12 = pc stored on the stack. */
|
|
|
|
str r12, [lr, #PCREG] /* proc_ptr->p_reg.pc = r12. */
|
|
|
|
ldr r12, [sp, #-4] /* r12 = cpsr stored on the stack. */
|
|
|
|
str r12, [lr, #PSREG] /* proc_ptr->p_reg.psr = r12. */
|
2012-10-08 03:38:03 +02:00
|
|
|
.endm
|
|
|
|
|
|
|
|
.macro exception_handler exc_name, exc_num, lr_offset
|
|
|
|
ENTRY(\exc_name\()_entry)
|
|
|
|
switch_to_svc \lr_offset
|
|
|
|
test_int_in_kernel \exc_name\()_entry_nested
|
|
|
|
|
|
|
|
\exc_name\()entry_from_user:
|
|
|
|
save_process_ctx
|
|
|
|
|
2014-02-10 13:12:21 +01:00
|
|
|
ldr fp, [sp] /* save the pointer to the current process. */
|
|
|
|
add r4, fp, #PCREG /* save the exception pc (saved lr_user) */
|
|
|
|
/* r4-r9 are callee save. */
|
2012-10-08 03:38:03 +02:00
|
|
|
|
|
|
|
/* stop user process cycles */
|
2014-02-10 13:12:21 +01:00
|
|
|
mov r0, fp /* first param: caller proc ptr. */
|
|
|
|
mov fp, #0 /* for stack trace. */
|
2012-10-08 03:38:03 +02:00
|
|
|
bl _C_LABEL(context_stop)
|
|
|
|
|
|
|
|
/*
|
|
|
|
* push a pointer to the interrupt state pushed by the cpu and the
|
|
|
|
* vector number pushed by the vector handler just before calling
|
|
|
|
* exception_entry and call the exception handler.
|
|
|
|
*/
|
2014-02-10 13:12:21 +01:00
|
|
|
mov r0, #0 /* it is not a nested exception. */
|
|
|
|
mov r1, r4 /* saved lr. */
|
2012-10-08 03:38:03 +02:00
|
|
|
mov r2, #\exc_num /* vector number */
|
|
|
|
bl _C_LABEL(exception_handler)
|
|
|
|
b _C_LABEL(switch_to_user)
|
|
|
|
|
|
|
|
\exc_name\()_entry_nested:
|
|
|
|
push {r0-r12, lr}
|
2014-02-10 13:12:21 +01:00
|
|
|
mov r0, #1 /* it is a nested exception. */
|
|
|
|
add r1, sp, #56 /* saved lr */
|
2012-10-08 03:38:03 +02:00
|
|
|
mov r2, #\exc_num /* vector number */
|
|
|
|
bl _C_LABEL(exception_handler)
|
|
|
|
pop {r0-r12, lr}
|
|
|
|
rfeia sp!
|
|
|
|
.endm
|
|
|
|
|
|
|
|
|
|
|
|
/* Exception handlers */
|
|
|
|
exception_handler data_abort DATA_ABORT_VECTOR 8
|
|
|
|
exception_handler prefetch_abort PREFETCH_ABORT_VECTOR 4
|
|
|
|
exception_handler undefined_inst UNDEFINED_INST_VECTOR 4
|
|
|
|
|
|
|
|
|
|
|
|
ENTRY(irq_entry)
|
|
|
|
switch_to_svc 4
|
|
|
|
test_int_in_kernel irq_entry_from_kernel
|
|
|
|
|
|
|
|
irq_entry_from_user:
|
|
|
|
save_process_ctx
|
|
|
|
|
|
|
|
/* save the pointer to the current process */
|
|
|
|
ldr fp, [sp]
|
|
|
|
|
2014-02-10 13:12:21 +01:00
|
|
|
push {fp} /* save caller proc ptr. */
|
|
|
|
sub sp, sp, #4 /* maintain stack alignment. */
|
2012-10-08 03:38:03 +02:00
|
|
|
|
|
|
|
/* stop user process cycles */
|
2014-02-10 13:12:21 +01:00
|
|
|
mov r0, fp /* first param: caller proc ptr. */
|
|
|
|
mov fp, #0 /* for stack trace. */
|
2012-10-08 03:38:03 +02:00
|
|
|
bl _C_LABEL(context_stop)
|
|
|
|
|
|
|
|
/* call handler */
|
2014-02-07 08:46:29 +01:00
|
|
|
bl _C_LABEL(bsp_irq_handle) /* bsp_irq_handle(void) */
|
2012-10-08 03:38:03 +02:00
|
|
|
|
2013-06-24 14:35:25 +02:00
|
|
|
add sp, sp, #4
|
2014-02-10 13:12:21 +01:00
|
|
|
pop {fp} /* caller proc ptr. */
|
|
|
|
dsb /* data synchronization barrier. */
|
2012-10-08 03:38:03 +02:00
|
|
|
|
|
|
|
b _C_LABEL(switch_to_user)
|
|
|
|
|
|
|
|
irq_entry_from_kernel:
|
|
|
|
push {r0-r12, lr}
|
|
|
|
bl _C_LABEL(context_stop_idle)
|
|
|
|
|
|
|
|
/* call handler */
|
2014-02-10 13:12:21 +01:00
|
|
|
bl _C_LABEL(bsp_irq_handle) /* bsp_irq_handle(void). */
|
2012-10-08 03:38:03 +02:00
|
|
|
|
2014-02-10 13:12:21 +01:00
|
|
|
/* data synchronization barrier */ dsb
|
2012-10-08 03:38:03 +02:00
|
|
|
pop {r0-r12, lr}
|
|
|
|
rfeia sp!
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* supervisor call (SVC) kernel entry point
|
|
|
|
*/
|
|
|
|
ENTRY(svc_entry)
|
2014-02-10 13:12:21 +01:00
|
|
|
/* Store the LR and the SPSR of the current mode onto the SVC stack */
|
2013-01-25 14:31:40 +01:00
|
|
|
srsdb sp!, #MODE_SVC
|
2012-10-08 03:38:03 +02:00
|
|
|
save_process_ctx
|
|
|
|
|
|
|
|
/* save the pointer to the current process */
|
|
|
|
ldr fp, [sp]
|
|
|
|
|
2013-11-01 13:34:14 +01:00
|
|
|
cmp r3, #KERVEC_INTR
|
2012-10-08 03:38:03 +02:00
|
|
|
beq kernel_call_entry
|
2013-11-01 13:34:14 +01:00
|
|
|
cmp r3, #IPCVEC_INTR
|
2012-10-08 03:38:03 +02:00
|
|
|
beq ipc_entry
|
2013-02-19 13:17:54 +01:00
|
|
|
|
|
|
|
/* return -1 to the current process as an invalid SWI was called .*/
|
|
|
|
mov r0, #-1
|
|
|
|
str r0, [fp, #REG0]
|
|
|
|
b _C_LABEL(switch_to_user)
|
2012-10-08 03:38:03 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* kernel call is only from a process to kernel
|
|
|
|
*/
|
|
|
|
ENTRY(kernel_call_entry)
|
|
|
|
/*
|
2014-02-10 13:12:21 +01:00
|
|
|
* pass the syscall arguments from userspace to the handler.
|
2012-10-08 03:38:03 +02:00
|
|
|
* save_process_ctx() does not clobber these registers, they are still
|
2014-02-10 13:12:21 +01:00
|
|
|
* set as the userspace has set them.
|
2012-10-08 03:38:03 +02:00
|
|
|
*/
|
2014-02-10 13:12:21 +01:00
|
|
|
push {fp} /* save caller proc ptr. */
|
|
|
|
push {r0} /* save msg ptr so it's not clobbered. */
|
2012-10-08 03:38:03 +02:00
|
|
|
|
|
|
|
/* stop user process cycles */
|
2014-02-10 13:12:21 +01:00
|
|
|
mov r0, fp /* first param: caller proc ptr */
|
|
|
|
mov fp, #0 /* for stack trace */
|
2012-10-08 03:38:03 +02:00
|
|
|
bl _C_LABEL(context_stop)
|
|
|
|
|
2014-02-10 13:12:21 +01:00
|
|
|
pop {r0} /* first param: msg ptr. */
|
|
|
|
pop {r1} /* second param: caller proc ptr. */
|
2012-10-08 03:38:03 +02:00
|
|
|
bl _C_LABEL(kernel_call)
|
|
|
|
|
|
|
|
b _C_LABEL(switch_to_user)
|
|
|
|
|
|
|
|
/*
|
|
|
|
* IPC is only from a process to kernel
|
|
|
|
*/
|
|
|
|
ENTRY(ipc_entry)
|
|
|
|
/*
|
|
|
|
* pass the syscall arguments from userspace to the handler.
|
|
|
|
* save_process_ctx() does not clobber these registers, they are still
|
|
|
|
* set as the userspace have set them
|
|
|
|
*/
|
2014-02-10 13:12:21 +01:00
|
|
|
push {fp} /* save caller proc ptr. */
|
|
|
|
push {r0-r2} /* save regs so they're not clobbered. */
|
2012-10-08 03:38:03 +02:00
|
|
|
|
|
|
|
/* stop user process cycles */
|
2014-02-10 13:12:21 +01:00
|
|
|
mov r0, fp /* first param: caller proc ptr. */
|
|
|
|
mov fp, #0 /* for stack trace. */
|
2012-10-08 03:38:03 +02:00
|
|
|
bl _C_LABEL(context_stop)
|
|
|
|
|
|
|
|
pop {r0-r2} /* restore regs */
|
|
|
|
bl _C_LABEL(do_ipc)
|
|
|
|
|
|
|
|
/* restore the current process pointer and save the return value */
|
2014-02-10 13:12:21 +01:00
|
|
|
pop {fp} /* caller proc ptr. */
|
2012-10-08 03:38:03 +02:00
|
|
|
str r0, [fp, #REG0]
|
|
|
|
|
|
|
|
b _C_LABEL(switch_to_user)
|
|
|
|
|
|
|
|
ENTRY(invalid_svc)
|
2013-02-19 13:17:54 +01:00
|
|
|
b .
|
2012-10-08 03:38:03 +02:00
|
|
|
|
|
|
|
ENTRY(restore_user_context)
|
|
|
|
/* sp holds the proc ptr */
|
|
|
|
mov sp, r0
|
|
|
|
|
|
|
|
/* Set SPSR and LR for return */
|
|
|
|
ldr r0, [sp, #PSREG]
|
2014-02-10 13:12:21 +01:00
|
|
|
msr spsr_fsxc, r0 /* flags , status, extension control. */
|
2012-10-08 03:38:03 +02:00
|
|
|
ldr lr, [sp, #PCREG]
|
|
|
|
|
|
|
|
/* Restore user-mode registers from proc struct */
|
|
|
|
ldm sp, {r0-r14}^
|
|
|
|
|
|
|
|
ldr sp, =_C_LABEL(svc_stack)
|
|
|
|
ldr sp, [sp]
|
|
|
|
|
|
|
|
/* To user mode! */
|
2014-02-10 13:12:21 +01:00
|
|
|
movs pc, lr /* preferred way of returning from svc */
|
2012-10-08 03:38:03 +02:00
|
|
|
|
|
|
|
/*===========================================================================*/
|
|
|
|
/* data */
|
|
|
|
/*===========================================================================*/
|
|
|
|
|
|
|
|
.data
|
|
|
|
.short 0x526F /* this must be the first data entry (magic #) */
|
|
|
|
.bss
|
|
|
|
.data
|
|
|
|
.balign 4
|
|
|
|
k_initial_stack:
|
|
|
|
.space K_STACK_SIZE
|
|
|
|
LABEL(__k_unpaged_k_initial_stktop)
|
|
|
|
|
|
|
|
/*
|
|
|
|
* the kernel stack
|
|
|
|
*/
|
|
|
|
k_boot_stack:
|
|
|
|
.space K_STACK_SIZE /* kernel stack */ /* FIXME use macro here */
|
|
|
|
LABEL(k_boot_stktop) /* top of kernel stack */
|
|
|
|
|
|
|
|
.balign K_STACK_SIZE
|
|
|
|
LABEL(k_stacks_start)
|
|
|
|
|
|
|
|
/* two pages for each stack, one for data, other as a sandbox */
|
|
|
|
.space 2 * (K_STACK_SIZE * CONFIG_MAX_CPUS)
|
|
|
|
|
|
|
|
LABEL(k_stacks_end)
|
|
|
|
|
|
|
|
/* top of kernel stack */
|