448 lines
8.3 KiB
ArmAsm
448 lines
8.3 KiB
ArmAsm
/*
|
|
* \brief Transition between kernel/userland
|
|
* \author Sebastian Sumpf
|
|
* \author Mark Vels
|
|
* \date 2015-06-22
|
|
*/
|
|
|
|
/*
|
|
* Copyright (C) 2015-2016 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.
|
|
*/
|
|
|
|
.set USER_MODE, 0
|
|
.set SUPERVISOR_MODE, 1
|
|
.set MACHINE_MODE, 3
|
|
|
|
.set CALL_PUT_CHAR, 0x100
|
|
.set CALL_SET_SYS_TIMER, 0x101
|
|
.set CALL_IS_USER_MODE, 0x102
|
|
|
|
.set CPU_IP, 0
|
|
.set CPU_EXCEPTION, 8
|
|
.set CPU_X1, 2*8
|
|
.set CPU_SP, 3*8
|
|
.set CPU_SASID, 33*8
|
|
.set CPU_SPTBR, 34*8
|
|
|
|
|
|
# From encoding.h (riscv-opcode)
|
|
.set MIP_MTIP, 0x00000020
|
|
.set MIP_SSIP, 0x00000002
|
|
.set MIP_HSIP, 0x00000004
|
|
.set MIP_MSIP, 0x00000008
|
|
.set MIP_STIP, 0x00000020
|
|
.set MIP_HTIP, 0x00000040
|
|
.set MIP_MTIP, 0x00000080
|
|
.set MSTATUS_IE, 0x00000001
|
|
.set MSTATUS_PRV, 0x00000006
|
|
.set MSTATUS_IE1, 0x00000008
|
|
.set MSTATUS_PRV1, 0x00000030
|
|
.set MSTATUS_IE2, 0x00000040
|
|
.set MSTATUS_PRV2, 0x00000180
|
|
.set MSTATUS_IE3, 0x00000200
|
|
.set MSTATUS_PRV3, 0x00000C00
|
|
.set MSTATUS_FS, 0x00003000
|
|
.set MSTATUS_XS, 0x0000C000
|
|
.set MSTATUS_MPRV, 0x00010000
|
|
.set MSTATUS_VM, 0x003E0000
|
|
.set MSTATUS64_SD, 0x8000000000000000
|
|
|
|
.set TRAP_ECALL_FROM_USER, 8
|
|
.set TRAP_ECALL_FROM_SUPERVISOR, 9
|
|
.set TRAP_ECALL_FROM_HYPERVISOR, 10
|
|
.set TRAP_ECALL_FROM_MACHINE, 11
|
|
|
|
.set TRAP_INTERRUPT_BITNR, 63
|
|
.set IRQ_SOFT, 0x0
|
|
.set IRQ_TIMER, 0x1
|
|
.set IRQ_HOST, 0x2
|
|
.set IRQ_COP, 0x3
|
|
|
|
|
|
|
|
.macro _save_scratch_registers mode
|
|
|
|
.if \mode == USER_MODE
|
|
csrrw sp, mscratch, sp
|
|
.endif
|
|
|
|
addi sp, sp, -24
|
|
sd t0, 0(sp)
|
|
sd t1, 8(sp)
|
|
sd t2, 16(sp)
|
|
.endm
|
|
|
|
.macro _restore_scratch_registers mode
|
|
ld t0, 0(sp)
|
|
ld t1, 8(sp)
|
|
ld t2, 16(sp)
|
|
addi sp, sp, 24
|
|
|
|
.if \mode == USER_MODE
|
|
csrrw sp, mscratch, sp
|
|
.endif
|
|
.endm
|
|
|
|
.macro _handle_trap mode
|
|
|
|
csrr t0, mcause
|
|
|
|
# If IRQ bit not setup, goto trap handler.
|
|
# If an interrupt has occurred, the MSB will be set and
|
|
# hence mcause will be negative
|
|
#
|
|
bgez t0, 11f
|
|
|
|
# The bit was not set so we're handling an interrupt
|
|
# Valid interrupts are :
|
|
# - Software IRQ - 0
|
|
# - Timer IRQ - 1
|
|
# - HOST HTIF - 2
|
|
# - COP - 3
|
|
#
|
|
|
|
sll t0, t0, 1 # discard MSB
|
|
|
|
# If interrupt source is IRQ TIMER ....
|
|
li t1, IRQ_TIMER * 2
|
|
bne t0, t1, 2f
|
|
|
|
# Forward handling of timer IRQ to SUPERVISOR
|
|
li t0, MIP_MTIP
|
|
csrc mip, t0
|
|
csrc mie, t0
|
|
li t1, MIP_STIP
|
|
csrs mip, t1
|
|
|
|
# If irq from supervisor and MSTATUS.IE1 is not set,
|
|
# then bail out using 'eret'
|
|
#
|
|
.if \mode == SUPERVISOR_MODE
|
|
csrr t1, mstatus
|
|
and t0, t1, MSTATUS_IE1
|
|
bne zero, t0, 1f
|
|
|
|
# So, IE1 is not set.
|
|
_restore_scratch_registers \mode
|
|
eret
|
|
|
|
.endif
|
|
|
|
1:
|
|
# should cause a interrupt trap in supervisor mode
|
|
_restore_scratch_registers \mode
|
|
mrts
|
|
2:
|
|
# If interrupt source is IRQ HOST ....
|
|
li t1, IRQ_HOST * 2
|
|
bne t0, t1, 9f
|
|
|
|
3:
|
|
# Empty mfromhost
|
|
li t0, 0
|
|
csrrw t0, mfromhost, t0
|
|
bne zero,t0, 3b
|
|
j 9f
|
|
|
|
# Future implementation check for more interrupt sources
|
|
# to handle here.....
|
|
|
|
9:
|
|
#******** IRQ OUT *********
|
|
_restore_scratch_registers \mode
|
|
eret
|
|
|
|
11:
|
|
# Handle trap
|
|
|
|
# check if ecall (8..11):
|
|
# 8 : Environment call from U-mode
|
|
# 9 : Environment call from S-mode
|
|
# 10 : Environment call from H-mode
|
|
# 11 : Environment call from M-mode
|
|
#
|
|
# If not, jump to end of macro.
|
|
#
|
|
|
|
li t1, TRAP_ECALL_FROM_USER
|
|
bltu t0, t1, 19f
|
|
li t1, TRAP_ECALL_FROM_MACHINE
|
|
bgt t0, t1, 19f
|
|
|
|
# Switch on ecall number
|
|
li t1, CALL_PUT_CHAR
|
|
beq t1, a0, 12f
|
|
|
|
li t1, CALL_SET_SYS_TIMER
|
|
beq t1, a0, 13f
|
|
|
|
li t1, CALL_IS_USER_MODE
|
|
beq t1, a0, 14f
|
|
|
|
# else, unknown ecall number
|
|
.if \mode == USER_MODE
|
|
# Assume that Genode (supervisor trap handler)
|
|
# knows what to do then.
|
|
_restore_scratch_registers \mode
|
|
mrts
|
|
.endif
|
|
j 15f
|
|
|
|
12:
|
|
# output character but first wait until mtohost reads 0 atomically
|
|
# to make sure any previous character is gone..
|
|
csrr t1, mtohost
|
|
bne zero, t1, 12b
|
|
|
|
csrw mtohost, a1
|
|
j 15f
|
|
|
|
13:
|
|
# Only allow timer fiddling from supervisor mode
|
|
.if \mode == SUPERVISOR_MODE
|
|
# Clear any pending STIP
|
|
li t0, MIP_STIP
|
|
csrc mip, t0
|
|
|
|
# Set system timer
|
|
csrw mtimecmp, a1
|
|
|
|
# enable timer interrupt in M-mode
|
|
li t0, MIP_MTIP
|
|
csrrs t0, mie, t0
|
|
.endif
|
|
j 15f
|
|
|
|
14:
|
|
mv a0, x0
|
|
.if \mode == USER_MODE
|
|
li a0, 1
|
|
.endif
|
|
j 15f
|
|
|
|
15:
|
|
#******* ECALL OUT *********
|
|
# Empty mfromhost
|
|
li t0, 0
|
|
csrrw t0, mfromhost, t0
|
|
bne zero,t0, 14b
|
|
|
|
# advance epc
|
|
csrr t0, mepc
|
|
addi t0, t0, 4
|
|
csrw mepc, t0
|
|
|
|
_restore_scratch_registers \mode
|
|
eret
|
|
19:
|
|
.endm
|
|
|
|
.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 8
|
|
.global _machine_begin
|
|
_machine_begin:
|
|
|
|
# 0x100 user mode
|
|
j user_trap
|
|
.space 0x3c
|
|
# 0x140 supervisor
|
|
j supervisor_trap
|
|
.space 0x3c
|
|
# 0x180 hypervisor
|
|
1: j 1b
|
|
.space 0x3c
|
|
# 0x1c0 machine
|
|
j machine_trap
|
|
.space 0x38
|
|
# 0x1fc non-maksable interrupt
|
|
1: j 1b
|
|
|
|
user_trap:
|
|
|
|
_save_scratch_registers USER_MODE
|
|
_handle_trap USER_MODE
|
|
_restore_scratch_registers USER_MODE
|
|
mrts
|
|
|
|
supervisor_trap:
|
|
|
|
_save_scratch_registers SUPERVISOR_MODE
|
|
_handle_trap SUPERVISOR_MODE
|
|
j fault
|
|
|
|
machine_trap:
|
|
|
|
_save_scratch_registers MACHINE_MODE
|
|
_handle_trap MACHINE_MODE
|
|
j fault
|
|
|
|
|
|
fault:j fault # TODO: handle trap from supervisor or machine mode
|
|
|
|
.global _machine_end
|
|
_machine_end:
|
|
|
|
.p2align 12
|
|
.global _mt_begin
|
|
_mt_begin:
|
|
|
|
# 0x100 user mode
|
|
j _mt_kernel_entry_pic
|
|
.space 0x3c
|
|
# 0x140 supervisor
|
|
1: j 1b
|
|
.space 0x3c
|
|
# 0x180 hypervisor
|
|
1: j 1b
|
|
.space 0x3c
|
|
# 0x1c0 machine
|
|
1: j 1b
|
|
.space 0x38
|
|
# 0x1fc non-maksable interrupt
|
|
1: j 1b
|
|
|
|
/* space for a client context-pointer per CPU */
|
|
.p2align 3
|
|
.global _mt_client_context_ptr
|
|
_mt_client_context_ptr:
|
|
.space 8
|
|
|
|
/* space for a copy of the kernel context */
|
|
.global _mt_master_context_begin
|
|
_mt_master_context_begin:
|
|
|
|
/* space must be at least as large as 'Context' */
|
|
.space 35*8
|
|
|
|
.global _mt_master_context_end
|
|
_mt_master_context_end:
|
|
|
|
.global _mt_kernel_entry_pic
|
|
_mt_kernel_entry_pic:
|
|
|
|
# master context
|
|
csrrw x31, sscratch, x31
|
|
addi x31, x31, 8
|
|
|
|
# save x29, x30 in master
|
|
sd x29, CPU_X1 + 8 * 28(x31)
|
|
sd x30, CPU_X1 + 8 * 29(x31)
|
|
|
|
# load kernel page table
|
|
ld x29, CPU_SASID(x31)
|
|
ld x30, CPU_SPTBR(x31)
|
|
|
|
csrw sasid, x29
|
|
csrw sptbr, x30
|
|
|
|
#
|
|
# FIXME
|
|
# A TLB flush. Might be necessary to remove this in the near future again
|
|
# because on real hardware we currently get problems without.
|
|
#
|
|
sfence.vm x0
|
|
|
|
# save x29 - x31 in user context
|
|
mv x29, x31
|
|
addi x29, x29, -8
|
|
ld x29, (x29)
|
|
|
|
.irp reg,29,30
|
|
ld x30, CPU_X1 + 8 * (\reg - 1)(x31)
|
|
sd x30, CPU_X1 + 8 * (\reg - 1)(x29)
|
|
.endr
|
|
|
|
csrr x30, sscratch /* x31 */
|
|
sd x30, CPU_X1 + 8 * 30(x29)
|
|
|
|
# save x1 - x28
|
|
.irp reg,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28
|
|
sd x\reg, CPU_X1 + 8 * (\reg - 1)(x29)
|
|
.endr
|
|
|
|
# trap reason
|
|
csrr x30, scause
|
|
sd x30, CPU_EXCEPTION(x29)
|
|
|
|
# ip
|
|
csrr x30, sepc
|
|
sd x30, CPU_IP(x29)
|
|
|
|
# load kernel stack and ip
|
|
ld sp, CPU_SP(x31)
|
|
ld x30, CPU_IP(x31)
|
|
|
|
# restore scratch
|
|
addi x31, x31, -8
|
|
csrw sscratch, x31
|
|
|
|
jalr x30
|
|
|
|
|
|
.global _mt_user_entry_pic
|
|
_mt_user_entry_pic:
|
|
|
|
# client context pointer
|
|
csrr x30, sscratch
|
|
ld x30, (x30)
|
|
|
|
# set return IP
|
|
ld x31, CPU_IP(x30)
|
|
csrw sepc, x31
|
|
|
|
# restore x1-x28
|
|
.irp reg,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28
|
|
ld x\reg, CPU_X1 + 8 * (\reg - 1)(x30)
|
|
.endr
|
|
|
|
# save x29, x30, x31 to master context
|
|
csrr x29, sscratch
|
|
addi x29, x29, 8 # master context
|
|
|
|
.irp reg,29,30,31
|
|
ld x31, CPU_X1 + 8 * (\reg - 1)(x30)
|
|
sd x31, CPU_X1 + 8 * (\reg - 1)(x29)
|
|
.endr
|
|
|
|
# switch page table
|
|
ld x31, CPU_SASID(x30)
|
|
ld x30, CPU_SPTBR(x30)
|
|
|
|
csrw sasid, x31
|
|
csrw sptbr, x30
|
|
|
|
#
|
|
# FIXME
|
|
# A TLB flush. Might be necessary to remove this in the near future again
|
|
# because on real hardware we currently get problems without.
|
|
#
|
|
|
|
sfence.vm x0
|
|
|
|
# restore x29 - x31 from master context
|
|
.irp reg,31,30,29
|
|
ld x\reg, CPU_X1 + 8 * (\reg - 1)(x29)
|
|
.endr
|
|
|
|
eret
|
|
|
|
# end of the mode transition code
|
|
.global _mt_end
|
|
_mt_end:
|