/* * \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: