242 lines
6.2 KiB
ArmAsm
242 lines
6.2 KiB
ArmAsm
/*
|
|
* \brief Transition between kernel and userland
|
|
* \author Martin stein
|
|
* \date 2011-11-15
|
|
*/
|
|
|
|
/*
|
|
* Copyright (C) 2011-2013 Genode Labs GmbH
|
|
*
|
|
* This file is part of the Genode OS framework, which is distributed
|
|
* under the terms of the GNU General Public License version 2.
|
|
*/
|
|
|
|
/* core includes */
|
|
.include "macros.s"
|
|
|
|
|
|
/***************
|
|
** Constants **
|
|
***************/
|
|
|
|
/* size of local variables */
|
|
.set BUFFER_SIZE, 1 * 4
|
|
|
|
|
|
/************
|
|
** Macros **
|
|
************/
|
|
|
|
/**
|
|
* Invalidate all entries of the branch prediction cache
|
|
*
|
|
* FIXME branch prediction shall not be activated for now because we have no
|
|
* support for instruction barriers. The manual says that one should
|
|
* implement this via 'swi 0xf00000', but when we do this in SVC mode it
|
|
* pollutes our SP and this is not acceptable with the current mode
|
|
* transition implementation
|
|
*/
|
|
.macro _flush_branch_predictor
|
|
mcr p15, 0, sp, c7, c5, 6
|
|
/*swi 0xf00000 */
|
|
.endm
|
|
|
|
/**
|
|
* Switch from an interrupted user context to a kernel context
|
|
*
|
|
* \param exception_type immediate exception type ID
|
|
* \param pc_adjust immediate value that gets subtracted from the
|
|
* user PC before it gets saved
|
|
*/
|
|
.macro _user_to_kernel_pic exception_type, pc_adjust
|
|
|
|
/*
|
|
* We expect that privileged modes are never interrupted by an
|
|
* exception. Thus we can assume that we always come from
|
|
* user mode at this point.
|
|
*/
|
|
|
|
/************************************************
|
|
** We're still in the user protection domain, **
|
|
** so we must avoid access to kernel memory **
|
|
************************************************/
|
|
|
|
/* load kernel cidr */
|
|
adr sp, _mt_master_context_begin
|
|
ldr sp, [sp, #CIDR_OFFSET]
|
|
mcr p15, 0, sp, c13, c0, 1
|
|
_flush_branch_predictor
|
|
|
|
/* load kernel ttbr0 */
|
|
adr sp, _mt_master_context_begin
|
|
ldr sp, [sp, #TTBR0_OFFSET]
|
|
mcr p15, 0, sp, c2, c0, 0
|
|
_flush_branch_predictor
|
|
|
|
/*******************************************
|
|
** Now it's save to access kernel memory **
|
|
*******************************************/
|
|
|
|
/* get user context pointer */
|
|
ldr sp, _mt_client_context_ptr
|
|
|
|
/*
|
|
* Save user r0 ... r12. We explicitely target user registers
|
|
* via '^' because we might be in FIQ exception-mode where
|
|
* some of them are banked. Doesn't affect other modes.
|
|
*/
|
|
stmia sp, {r0-r12}^
|
|
|
|
/* save user lr and sp */
|
|
add r0, sp, #SP_OFFSET
|
|
stmia r0, {sp,lr}^
|
|
|
|
/* adjust and save user pc */
|
|
.if \pc_adjust != 0
|
|
sub lr, lr, #\pc_adjust
|
|
.endif
|
|
str lr, [sp, #PC_OFFSET]
|
|
|
|
/* save user psr */
|
|
mrs r0, spsr
|
|
str r0, [sp, #PSR_OFFSET]
|
|
|
|
/* save type of exception that interrupted the user */
|
|
mov r0, #\exception_type
|
|
str r0, [sp, #EXCEPTION_TYPE_OFFSET]
|
|
|
|
/*
|
|
* Switch to supervisor mode
|
|
*
|
|
* FIXME This is done due to incorrect behavior when running the kernel
|
|
* high-level-code in FIQ-exception mode. Please debug this behavior
|
|
* and remove this switch.
|
|
*/
|
|
cps #19
|
|
|
|
/* apply kernel sp */
|
|
adr r0, _mt_master_context_begin
|
|
_restore_kernel_sp r0, r1, r2
|
|
|
|
/* load kernel context */
|
|
add r0, r0, #LR_OFFSET
|
|
ldm r0, {lr, pc}
|
|
|
|
.endm
|
|
|
|
|
|
/**********************************
|
|
** Linked into the text section **
|
|
**********************************/
|
|
|
|
.section .text
|
|
|
|
/*
|
|
* Page aligned base of mode transition code.
|
|
*
|
|
* This position independent code switches between a kernel context and a
|
|
* user context and thereby between their address spaces. Due to the latter
|
|
* it must be mapped executable to the same region in every address space.
|
|
* To enable such switching, the kernel context must be stored within this
|
|
* region, thus one should map it solely accessable for privileged modes.
|
|
*/
|
|
.p2align MIN_PAGE_SIZE_LOG2
|
|
.global _mt_begin
|
|
_mt_begin:
|
|
|
|
/*
|
|
* On user exceptions the CPU has to jump to one of the following
|
|
* seven entry vectors to switch to a kernel context.
|
|
*/
|
|
.global _mt_kernel_entry_pic
|
|
_mt_kernel_entry_pic:
|
|
|
|
b _rst_entry /* 0x00: reset */
|
|
b _und_entry /* 0x04: undefined instruction */
|
|
b _swi_entry /* 0x08: software interrupt */
|
|
b _pab_entry /* 0x0c: prefetch abort */
|
|
b _dab_entry /* 0x10: data abort */
|
|
nop /* 0x14: reserved */
|
|
b _irq_entry /* 0x18: interrupt request */
|
|
b _fiq_entry /* 0x1c: fast interrupt request */
|
|
|
|
/* PICs that switch from a user exception to the kernel */
|
|
_rst_entry: _user_to_kernel_pic RST_TYPE, RST_PC_ADJUST
|
|
_und_entry: _user_to_kernel_pic UND_TYPE, UND_PC_ADJUST
|
|
_swi_entry:
|
|
|
|
/*
|
|
* FIXME fast SWI routines pollute the SVC SP but we have
|
|
* to call them especially in SVC mode
|
|
*/
|
|
|
|
/* check if SWI requests a fast service routine */
|
|
/*ldr sp, [r14, #-0x4]*/
|
|
/*and sp, sp, #0xffffff*/
|
|
|
|
/* fast "instruction barrier" service routine */
|
|
/*cmp sp, #0xf00000*/
|
|
/*bne _slow_swi_entry*/
|
|
/*movs pc, r14*/
|
|
|
|
/* slow high level service routine */
|
|
_slow_swi_entry: _user_to_kernel_pic SVC_TYPE, SVC_PC_ADJUST
|
|
|
|
_pab_entry: _user_to_kernel_pic PAB_TYPE, PAB_PC_ADJUST
|
|
_dab_entry: _user_to_kernel_pic DAB_TYPE, DAB_PC_ADJUST
|
|
_irq_entry: _user_to_kernel_pic IRQ_TYPE, IRQ_PC_ADJUST
|
|
_fiq_entry: _user_to_kernel_pic FIQ_TYPE, FIQ_PC_ADJUST
|
|
|
|
/* kernel must jump to this point to switch to a user context */
|
|
.p2align 2
|
|
.global _mt_user_entry_pic
|
|
_mt_user_entry_pic:
|
|
|
|
/* get user context pointer */
|
|
ldr lr, _mt_client_context_ptr
|
|
|
|
/* buffer user pc */
|
|
ldr r0, [lr, #PC_OFFSET]
|
|
adr r1, _mt_buffer
|
|
str r0, [r1]
|
|
|
|
/* buffer user psr */
|
|
ldr r0, [lr, #PSR_OFFSET]
|
|
msr spsr, r0
|
|
|
|
/* load user r0 ... r12 */
|
|
ldm lr, {r0-r12}
|
|
|
|
/* load user sp and lr */
|
|
add sp, lr, #SP_OFFSET
|
|
ldm sp, {sp,lr}^
|
|
|
|
/* get user cidr and ttbr0 */
|
|
ldr sp, [lr, #CIDR_OFFSET]
|
|
ldr lr, [lr, #TTBR0_OFFSET]
|
|
|
|
/********************************************************
|
|
** From now on, until we leave kernel mode, we must **
|
|
** avoid access to memory that is not mapped globally **
|
|
********************************************************/
|
|
|
|
/* apply user contextidr and section table */
|
|
mcr p15, 0, sp, c13, c0, 1
|
|
mcr p15, 0, lr, c2, c0, 0
|
|
_flush_branch_predictor
|
|
|
|
/* load user pc (implies application of the user psr) */
|
|
adr lr, _mt_buffer
|
|
ldm lr, {pc}^
|
|
|
|
_mt_local_variables
|
|
|
|
.p2align 2
|
|
.global _mt_end
|
|
_mt_end:
|
|
|
|
/* FIXME exists only because _vm_mon_entry pollutes generic kernel code */
|
|
.global _mt_vm_entry_pic
|
|
_mt_vm_entry_pic:
|
|
1: b 1b
|