#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