Skip to content

Commit

Permalink
Store user-stack-pointer on kernel stack when pushing/popping GPRs
Browse files Browse the repository at this point in the history
The current implementation for pushing/popping GPRs stores the USP in
xSCRATCH, however this CSR is also used for a counter value by the
code section inserted by (cfg.enable_nested_interrupt) within
riscv_asm_program_gen::gen_interrupt_handler_section(). This corrupts the
program state because the user-program GPRs can no longer be restored.

Instead, push the USP onto the kernel stack before pushing the GPRs.
Pop it off again after popping the GPRs themselves.
  • Loading branch information
hcallahan-lowrisc committed Oct 27, 2022
1 parent ada58fc commit aee6c4d
Showing 1 changed file with 20 additions and 14 deletions.
34 changes: 20 additions & 14 deletions src/riscv_instr_pkg.sv
Original file line number Diff line number Diff line change
Expand Up @@ -1379,18 +1379,18 @@ package riscv_instr_pkg;
ref string instr[$]);
string store_instr = (XLEN == 32) ? "sw" : "sd";
if (scratch inside {implemented_csr}) begin
// Use kernal stack for handling exceptions
// Save the user mode stack pointer to the scratch register
instr.push_back($sformatf("csrrw x%0d, 0x%0x, x%0d", sp, scratch, sp));
// Move TP to SP
// Push USP from gpr.SP onto the kernel stack
instr.push_back($sformatf("addi x%0d, x%0d, -4", tp, tp));
instr.push_back($sformatf("%0s x%0d, (x%0d)", store_instr, sp, tp));
// Move KSP to gpr.SP
instr.push_back($sformatf("add x%0d, x%0d, zero", sp, tp));
end
// If MPRV is set and MPP is S/U mode, it means the address translation and memory protection
// for load/store instruction is the same as the mode indicated by MPP. In this case, we
// need to use the virtual address to access the kernel stack.
if((status == MSTATUS) && (SATP_MODE != BARE)) begin
// We temporarily use tp to check mstatus to avoid changing other GPR. The value of sp has
// been saved to xScratch and can be restored later.
// We temporarily use tp to check mstatus to avoid changing other GPR.
// (The value of sp has been pushed to the kernel stack, so can be recovered later)
if(mprv) begin
instr.push_back($sformatf("csrr x%0d, 0x%0x // MSTATUS", tp, status));
instr.push_back($sformatf("srli x%0d, x%0d, 11", tp, tp)); // Move MPP to bit 0
Expand All @@ -1401,14 +1401,18 @@ package riscv_instr_pkg;
// Use virtual address for stack pointer
instr.push_back($sformatf("slli x%0d, x%0d, %0d", sp, sp, XLEN - MAX_USED_VADDR_BITS));
instr.push_back($sformatf("srli x%0d, x%0d, %0d", sp, sp, XLEN - MAX_USED_VADDR_BITS));
instr.push_back("1: nop");
end
end
// Reserve space from kernel stack to save all 32 GPR except for x0
instr.push_back($sformatf("1: addi x%0d, x%0d, -%0d", sp, sp, 31 * (XLEN/8)));
// Push all GPRs to kernel stack
// Push all GPRs (except for x0) to kernel stack
// (gpr.SP currently holds the KSP)
instr.push_back($sformatf("addi x%0d, x%0d, -%0d", sp, sp, 31 * (XLEN/8)));
for(int i = 1; i < 32; i++) begin
instr.push_back($sformatf("%0s x%0d, %0d(x%0d)", store_instr, i, i * (XLEN/8), sp));
end
// Move KSP back to gpr.TP
// (this is needed if we again take a interrupt (nested) before restoring our USP)
instr.push_back($sformatf("add x%0d, x%0d, zero", tp, sp));
endfunction

// Pop general purpose register from stack, this is needed before returning to user program
Expand All @@ -1419,17 +1423,19 @@ package riscv_instr_pkg;
riscv_reg_t tp,
ref string instr[$]);
string load_instr = (XLEN == 32) ? "lw" : "ld";
// Pop user mode GPRs from kernel stack
// Move KSP to gpr.SP
instr.push_back($sformatf("add x%0d, x%0d, zero", sp, tp));
// Pop GPRs from kernel stack
for(int i = 1; i < 32; i++) begin
instr.push_back($sformatf("%0s x%0d, %0d(x%0d)", load_instr, i, i * (XLEN/8), sp));
end
// Restore kernel stack pointer
instr.push_back($sformatf("addi x%0d, x%0d, %0d", sp, sp, 31 * (XLEN/8)));
if (scratch inside {implemented_csr}) begin
// Move SP to TP
// Move KSP back to gpr.TP
instr.push_back($sformatf("add x%0d, x%0d, zero", tp, sp));
// Restore user mode stack pointer
instr.push_back($sformatf("csrrw x%0d, 0x%0x, x%0d", sp, scratch, sp));
// Pop USP from the kernel stack, move back to gpr.SP
instr.push_back($sformatf("%0s x%0d, (x%0d)", load_instr, sp, tp));
instr.push_back($sformatf("addi x%0d, x%0d, 4", tp, tp));
end
endfunction

Expand Down

0 comments on commit aee6c4d

Please sign in to comment.