genode/repos/base-hw/src/core/spec/riscv/mode_transition.s
Mark Vels 1668983efa base-hw: RISC-V Rocket Core on Zynq
This commit adds rocket core on the Zynq FPGA support to base HW. It also takes
advantage of the new timer infrastructure introduced with the privileged 1.8 and
adds improved TLB flush support.

fixes #1880
2016-02-26 11:36:51 +01:00

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: