mirror of
https://github.com/plctlab/riscv-operating-system-mooc.git
synced 2025-11-16 12:34:47 +00:00
The purpose is to facilitate porting to RV64. Signed-off-by: Wang Chen <wangchen20@iscas.ac.cn>
155 lines
4.2 KiB
ArmAsm
155 lines
4.2 KiB
ArmAsm
#define LOAD lw
|
|
#define STORE sw
|
|
#define SIZE_REG 4
|
|
|
|
# Save all General-Purpose(GP) registers to context.
|
|
# struct context *base = &ctx_task;
|
|
# base->ra = ra;
|
|
# ......
|
|
# These GP registers to be saved don't include gp
|
|
# and tp, because they are not caller-saved or
|
|
# callee-saved. These two registers are often used
|
|
# for special purpose. For example, in RVOS, 'tp'
|
|
# (aka "thread pointer") is used to store hartid,
|
|
# which is a global value and would not be changed
|
|
# during context-switch.
|
|
.macro reg_save base
|
|
STORE ra, 0*SIZE_REG(\base)
|
|
STORE sp, 1*SIZE_REG(\base)
|
|
STORE t0, 4*SIZE_REG(\base)
|
|
STORE t1, 5*SIZE_REG(\base)
|
|
STORE t2, 6*SIZE_REG(\base)
|
|
STORE s0, 7*SIZE_REG(\base)
|
|
STORE s1, 8*SIZE_REG(\base)
|
|
STORE a0, 9*SIZE_REG(\base)
|
|
STORE a1, 10*SIZE_REG(\base)
|
|
STORE a2, 11*SIZE_REG(\base)
|
|
STORE a3, 12*SIZE_REG(\base)
|
|
STORE a4, 13*SIZE_REG(\base)
|
|
STORE a5, 14*SIZE_REG(\base)
|
|
STORE a6, 15*SIZE_REG(\base)
|
|
STORE a7, 16*SIZE_REG(\base)
|
|
STORE s2, 17*SIZE_REG(\base)
|
|
STORE s3, 18*SIZE_REG(\base)
|
|
STORE s4, 19*SIZE_REG(\base)
|
|
STORE s5, 20*SIZE_REG(\base)
|
|
STORE s6, 21*SIZE_REG(\base)
|
|
STORE s7, 22*SIZE_REG(\base)
|
|
STORE s8, 23*SIZE_REG(\base)
|
|
STORE s9, 24*SIZE_REG(\base)
|
|
STORE s10, 25*SIZE_REG(\base)
|
|
STORE s11, 26*SIZE_REG(\base)
|
|
STORE t3, 27*SIZE_REG(\base)
|
|
STORE t4, 28*SIZE_REG(\base)
|
|
STORE t5, 29*SIZE_REG(\base)
|
|
# we don't save t6 here, due to we have used
|
|
# it as base, we have to save t6 in an extra step
|
|
# outside of reg_save
|
|
.endm
|
|
|
|
# restore all General-Purpose(GP) registers from the context
|
|
# except gp & tp.
|
|
# struct context *base = &ctx_task;
|
|
# ra = base->ra;
|
|
# ......
|
|
.macro reg_restore base
|
|
LOAD ra, 0*SIZE_REG(\base)
|
|
LOAD sp, 1*SIZE_REG(\base)
|
|
LOAD t0, 4*SIZE_REG(\base)
|
|
LOAD t1, 5*SIZE_REG(\base)
|
|
LOAD t2, 6*SIZE_REG(\base)
|
|
LOAD s0, 7*SIZE_REG(\base)
|
|
LOAD s1, 8*SIZE_REG(\base)
|
|
LOAD a0, 9*SIZE_REG(\base)
|
|
LOAD a1, 10*SIZE_REG(\base)
|
|
LOAD a2, 11*SIZE_REG(\base)
|
|
LOAD a3, 12*SIZE_REG(\base)
|
|
LOAD a4, 13*SIZE_REG(\base)
|
|
LOAD a5, 14*SIZE_REG(\base)
|
|
LOAD a6, 15*SIZE_REG(\base)
|
|
LOAD a7, 16*SIZE_REG(\base)
|
|
LOAD s2, 17*SIZE_REG(\base)
|
|
LOAD s3, 18*SIZE_REG(\base)
|
|
LOAD s4, 19*SIZE_REG(\base)
|
|
LOAD s5, 20*SIZE_REG(\base)
|
|
LOAD s6, 21*SIZE_REG(\base)
|
|
LOAD s7, 22*SIZE_REG(\base)
|
|
LOAD s8, 23*SIZE_REG(\base)
|
|
LOAD s9, 24*SIZE_REG(\base)
|
|
LOAD s10, 25*SIZE_REG(\base)
|
|
LOAD s11, 26*SIZE_REG(\base)
|
|
LOAD t3, 27*SIZE_REG(\base)
|
|
LOAD t4, 28*SIZE_REG(\base)
|
|
LOAD t5, 29*SIZE_REG(\base)
|
|
LOAD t6, 30*SIZE_REG(\base)
|
|
.endm
|
|
|
|
# Something to note about save/restore:
|
|
# - We use mscratch to hold a pointer to context of current task
|
|
# - We use t6 as the 'base' for reg_save/reg_restore, because it is the
|
|
# very bottom register (x31) and would not be overwritten during loading.
|
|
# Note: CSRs(mscratch) can not be used as 'base' due to load/restore
|
|
# instruction only accept general purpose registers.
|
|
|
|
.text
|
|
|
|
# interrupts and exceptions while in machine mode come here.
|
|
.globl trap_vector
|
|
# the trap vector base address must always be aligned on a 4-byte boundary
|
|
.balign 4
|
|
trap_vector:
|
|
# save context(registers).
|
|
csrrw t6, mscratch, t6 # swap t6 and mscratch
|
|
reg_save t6
|
|
|
|
# Save the actual t6 register, which we swapped into
|
|
# mscratch
|
|
mv t5, t6 # t5 points to the context of current task
|
|
csrr t6, mscratch # read t6 back from mscratch
|
|
STORE t6, 30*SIZE_REG(t5) # save t6 with t5 as base
|
|
|
|
# save mepc to context of current task
|
|
csrr a0, mepc
|
|
STORE a0, 31*SIZE_REG(t5)
|
|
|
|
# Restore the context pointer into mscratch
|
|
csrw mscratch, t5
|
|
|
|
# call the C trap handler in trap.c
|
|
csrr a0, mepc
|
|
csrr a1, mcause
|
|
call trap_handler
|
|
|
|
# trap_handler will return the return address via a0.
|
|
csrw mepc, a0
|
|
|
|
# restore context(registers).
|
|
csrr t6, mscratch
|
|
reg_restore t6
|
|
|
|
# return to whatever we were doing before trap.
|
|
mret
|
|
|
|
# void switch_to(struct context *next);
|
|
# a0: pointer to the context of the next task
|
|
.globl switch_to
|
|
.balign 4
|
|
switch_to:
|
|
# switch mscratch to point to the context of the next task
|
|
csrw mscratch, a0
|
|
# set mepc to the pc of the next task
|
|
LOAD a1, 31*SIZE_REG(a0)
|
|
csrw mepc, a1
|
|
|
|
# Restore all GP registers
|
|
# Use t6 to point to the context of the new task
|
|
mv t6, a0
|
|
reg_restore t6
|
|
|
|
# Do actual context switching.
|
|
# Notice this will enable global interrupt
|
|
mret
|
|
|
|
.end
|
|
|