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
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:
|