From 0a6e1d108fe58191072f69620d90524f2e7c9817 Mon Sep 17 00:00:00 2001 From: Kip Macsai-Goren Date: Wed, 20 Apr 2022 17:33:40 +0000 Subject: [PATCH] updated 32 bit test lib to mirror 64 bit one in interrupt handling, trap stacks --- .../rv32i_m/privilege/src/WALLY-TEST-LIB-32.h | 350 +++++++++++++----- 1 file changed, 264 insertions(+), 86 deletions(-) diff --git a/tests/wally-riscv-arch-test/riscv-test-suite/rv32i_m/privilege/src/WALLY-TEST-LIB-32.h b/tests/wally-riscv-arch-test/riscv-test-suite/rv32i_m/privilege/src/WALLY-TEST-LIB-32.h index 2400173b..3263bc2c 100644 --- a/tests/wally-riscv-arch-test/riscv-test-suite/rv32i_m/privilege/src/WALLY-TEST-LIB-32.h +++ b/tests/wally-riscv-arch-test/riscv-test-suite/rv32i_m/privilege/src/WALLY-TEST-LIB-32.h @@ -39,8 +39,7 @@ RVTEST_CODE_BEGIN // // Initialize x6 as a virtual pointer to the test results // Initialize x16 as a physical pointer to the test results - // Set up stack pointer (sp = x2) - // Set up the exception Handler, keeping the original handler in x4. + // Set up stack pointer, mscratch, sscratch // // --------------------------------------------------------------------------------------------- @@ -49,8 +48,12 @@ RVTEST_CODE_BEGIN la x16, test_1_res // x16 reserved for the physical address equivalent of x6 to be used in trap handlers // any time either is used, both must be updated. - // address for stack - la sp, top_of_stack + // address for normal user stack, mscratch stack, and sscratch stack + la sp, mscratch_top + csrw mscratch, sp + la sp, sscratch_top + csrw sscratch, sp + la sp, stack_top .endm @@ -61,11 +64,13 @@ j end_trap_triggers // The following tests involve causing many of the interrupts and exceptions that are easily done in a few lines // This effectively includes everything that isn't to do with page faults (virtual memory) - +// +// INPUTS: a3 (x13): the number of times one of the infinitely looping interrupt causes should loop before giving up and continuing without the interrupt firing. +// cause_instr_addr_misaligned: // cause a misaligned address trap auipc x28, 0 // get current PC, which is aligned - addi x28, x28, 0x3 // add 1 to pc to create misaligned address + addi x28, x28, 0x2 // add 2 to pc to create misaligned address (Assumes compressed instructions are disabled) jr x28 // cause instruction address midaligned trap ret @@ -79,10 +84,10 @@ cause_instr_access: ret cause_illegal_instr: - .word 0x00000000 // a 32 bit zros is an illegal instruction + .word 0x00000000 // 32 bit zero is an illegal instruction ret -cause_breakpnt: // **** +cause_breakpnt: ebreak ret @@ -113,10 +118,11 @@ cause_ecall: ecall ret -cause_time_interrupt: +cause_m_time_interrupt: // The following code works for both RV32 and RV64. // RV64 alone would be easier using double-word adds and stores li x28, 0x30 // Desired offset from the present time + mv a3, x28 // copy value in to know to stop waiting for interrupt after this many cycles la x29, 0x02004000 // MTIMECMP register in CLINT la x30, 0x0200BFF8 // MTIME register in CLINT lw x7, 0(x30) // low word of MTIME @@ -127,21 +133,99 @@ cause_time_interrupt: sw x31,4(x29) // store into most significant word of MTIMECMP nowrap: sw x28, 0(x29) // store into least significant word of MTIMECMP - loop: j loop // wait until interrupt occurs +time_loop: + //wfi // *** this may now spin us forever in the loop??? + addi a3, a3, -1 + bnez a3, m_ext_loop // go through this loop for [a3 value] iterations before returning without performing interrupt ret -cause_soft_interrupt: +cause_s_time_interrupt: + li x28, 0x20 + csrs mip, x28 // set supervisor time interrupt pending. SIP is a subset of MIP, so writing this should also change MIP. + nop // added extra nops in so the csrs can get through the pipeline before returning. + ret + +cause_m_soft_interrupt: la x28, 0x02000000 // MSIP register in CLINT li x29, 1 // 1 in the lsb sw x29, 0(x28) // Write MSIP bit ret -cause_ext_interrupt: +cause_s_soft_interrupt: + li x28, 0x2 + csrs sip, x28 // set supervisor software interrupt pending. SIP is a subset of MIP, so writing this should also change MIP. + ret + +cause_m_ext_interrupt: + # ========== Configure PLIC ========== + # m priority threshold = 0 + li x28, 0xC200000 + li x29, 0 + sw x29, 0(x28) + # s priority threshold = 7 + li x28, 0xC201000 + li x29, 7 + sw x29, 0(x28) + # source 3 (GPIO) priority = 1 + li x28, 0xC000000 + li x29, 1 + sw x29, 0x0C(x28) + # enable source 3 in M Mode + li x28, 0x0C002000 + li x29, 0b1000 + sw x29, 0(x28) + li x28, 0x10060000 // load base GPIO memory location li x29, 0x1 - sw x29, 8(x28) // enable the first pin as an output - sw x29, 28(x28) // set first pin to high interrupt enable - sw x29, 40(x28) // write a 1 to the first output pin (cause interrupt) + sw x29, 0x08(x28) // enable the first pin as an output + + sw x0, 0x1C(x28) // clear rise_ip + sw x0, 0x24(x28) // clear fall_ip + sw x0, 0x2C(x28) // clear high_ip + sw x0, 0x34(x28) // clear low_ip + + sw x29, 0x28(x28) // set first pin to interrupt on a rising value + sw x29, 0x0C(x28) // write a 1 to the first output pin (cause interrupt) +m_ext_loop: + //wfi + addi a3, a3, -1 + bnez a3, m_ext_loop // go through this loop for [a3 value] iterations before returning without performing interrupt + ret + +cause_s_ext_interrupt_GPIO: + # ========== Configure PLIC ========== + # s priority threshold = 0 + li x28, 0xC201000 + li x29, 0 + sw x29, 0(x28) + # m priority threshold = 7 + li x28, 0xC200000 + li x29, 7 + sw x29, 0(x28) + # source 3 (GPIO) priority = 1 + li x28, 0xC000000 + li x29, 1 + sw x29, 0x0C(x28) + # enable source 3 in S mode + li x28, 0x0C002080 + li x29, 0b1000 + sw x29, 0(x28) + + li x28, 0x10060000 // load base GPIO memory location + li x29, 0x1 + sw x29, 0x08(x28) // enable the first pin as an output + + sw x0, 0x1C(x28) // clear rise_ip + sw x0, 0x24(x28) // clear fall_ip + sw x0, 0x2C(x28) // clear high_ip + sw x0, 0x34(x28) // clear low_ip + + sw x29, 0x28(x28) // set first pin to interrupt on a rising value + sw x29, 0x0C(x28) // write a 1 to the first output pin (cause interrupt) +s_ext_loop: + //wfi + addi a3, a3, -1 + bnez a3, m_ext_loop // go through this loop for [a3 value] iterations before returning without performing interrupt ret end_trap_triggers: @@ -149,7 +233,7 @@ end_trap_triggers: .macro TRAP_HANDLER MODE, VECTORED=1, DEBUG=0 // MODE decides which mode this trap handler will be taken in (M or S mode) - // Vectored decides whether interrumpts are handled with the vector table at trap_handler_MODE (1) + // Vectored decides whether interrupts are handled with the vector table at trap_handler_MODE (1) // vs Using the non-vector approach the rest of the trap handler takes (0) // DEBUG decides whether we will print mtval a string with status.mpie, status.mie, and status.mpp to the signature (1) // vs not saving that info to the signature (0) @@ -214,24 +298,28 @@ trap_handler_\MODE\(): // *** ASSUMES that a cause value of 0 for an interrupt is unimplemented // otherwise, a vectored interrupt handler should jump to trap_handler_\MODE\() + 4 * Interrupt cause code // No matter the value of VECTORED, exceptions (not interrupts) are handled in an unvecotred way - j soft_interrupt_\MODE\() // 1: instruction access fault // the zero spot is taken up by the instruction to skip this table. + j s_soft_vector_\MODE\() // 1: instruction access fault // the zero spot is taken up by the instruction to skip this table. j segfault_\MODE\() // 2: reserved - j soft_interrupt_\MODE\() // 3: breakpoint + j m_soft_vector_\MODE\() // 3: breakpoint j segfault_\MODE\() // 4: reserved - j time_interrupt_\MODE\() // 5: load access fault + j s_time_vector_\MODE\() // 5: load access fault j segfault_\MODE\() // 6: reserved - j time_interrupt_\MODE\() // 7: store access fault + j m_time_vector_\MODE\() // 7: store access fault j segfault_\MODE\() // 8: reserved - j ext_interrupt_\MODE\() // 9: ecall from S-mode + j s_ext_vector_\MODE\() // 9: ecall from S-mode j segfault_\MODE\() // 10: reserved - j ext_interrupt_\MODE\() // 11: ecall from M-mode + j m_ext_vector_\MODE\() // 11: ecall from M-mode // 12 through >=16 are reserved or designated for platform use trap_unvectored_\MODE\(): - // The processor is always in machine mode when a trap takes us here + csrrw sp, \MODE\()scratch, sp // swap sp and scratch so we can use the scratch stack in the trap hanler without messing up sp's value or the stack itself. + // *** NOTE: this means that nested traps will be screwed up but they shouldn't happen in any of these tests + +trap_stack_saved_\MODE\(): // jump here after handling vectored interupt since we already switch sp and scratch there // save registers on stack before using sw x1, -4(sp) - sw x5, -8(sp) + sw x5, -8(sp) + sw x7, -12(sp) // Record trap csrr x1, \MODE\()cause // record the mcause @@ -261,49 +349,36 @@ trap_unvectored_\MODE\(): // Respond to trap based on cause // All interrupts should return after being logged csrr x1, \MODE\()cause - li x5, 0x8000000000000000 // if msb is set, it is an interrupt + li x5, 0x80000000 // if msb is set, it is an interrupt and x5, x5, x1 - bnez x5, trapreturn_\MODE\() // return from interrupt + bnez x5, interrupt_handler_\MODE\() // Other trap handling is specified in the vector Table - slli x1, x1, 2 // multiply cause by 4 to get offset in vector Table la x5, exception_vector_table_\MODE\() + slli x1, x1, 2 // multiply cause by 4 to get offset in vector Table add x5, x5, x1 // compute address of vector in Table lw x5, 0(x5) // fectch address of handler from vector Table jr x5 // and jump to the handler - + +interrupt_handler_\MODE\(): + la x5, interrupt_vector_table_\MODE\() // NOTE THIS IS NOT THE SAME AS VECTORED INTERRUPTS!!! + slli x1, x1, 2 // multiply cause by 4 to get offset in vector Table + add x5, x5, x1 // compute address of vector in Table + lw x5, 0(x5) // fectch address of handler from vector Table + jr x5 // and jump to the handler + segfault_\MODE\(): - lw x5, -8(sp) // restore registers from stack before faulting + lw x7, -12(sp) // restore registers from stack before faulting + lw x5, -8(sp) lw x1, -4(sp) j terminate_test // halt program. trapreturn_\MODE\(): - // look at the instruction to figure out whether to add 2 or 4 bytes to PC, or go to address specified in a1 csrr x1, \MODE\()epc // get the mepc - addi x1, x1, 4 // *** should be 2 for compressed instructions, see note. - - -// ****** KMG: the following is no longer as easy to determine. mepc gets the virtual address of the trapped instruction, -// ******** but in the handler, we work in M mode with physical addresses -// This means the address in mepc is suddenly pointing somewhere else. -// to get this to work, We could either retranslate the vaddr back into a paddr (probably on the scale of difficult to intractible) -// or we could come up with some other ingenious way to stay in M mode and see if the instruction was compressed. - -// lw x5, 0(x1) // read the faulting instruction -// li x1, 3 // check bottom 2 bits of instruction to see if compressed -// and x5, x5, x1 // mask the other bits -// beq x5, x1, trapreturn_uncompressed // if 11, the instruction is return_uncompressed - -// trapreturn_compressed: -// csrr x1, mepc // get the mepc again -// addi x1, x1, 2 // add 2 to find the next instruction -// j trapreturn_specified // and return - -// trapreturn_uncompressed: -// csrr x1, mepc // get the mepc again -// addi x1, x1, 4 // add 4 to find the next instruction + addi x1, x1, 4 trapreturn_specified_\MODE\(): // reset the necessary pointers and registers (x1, x5, x6, and the return address going to mepc) + // note that we don't need to change x7 since it was a temporary register with no important address in it. // so that when we return to a new virtual address, they're all in the right spot as well. beqz a1, trapreturn_finished_\MODE\() // either update values, of go to default return address. @@ -333,7 +408,7 @@ trapreturn_specified_\MODE\(): and x7, x5, x6 // x7 = offset for x6 add x6, x7, a1 // x6 = new address for the result pointer - // set return address, stored temporarily in x1, to the next instruction, but in the new virtual page. + // reset x1, which temporarily holds the return address that will be written to mepc. and x1, x5, x1 // x1 = offset for the return address add x1, x1, a1 // x1 = new return address. @@ -342,13 +417,16 @@ trapreturn_specified_\MODE\(): trapreturn_finished_\MODE\(): csrw \MODE\()epc, x1 // update the mepc with address of next instruction - lw x5, -8(sp) // restore registers from stack before returning + lw x7, -12(sp) // restore registers from stack before returning + lw x5, -8(sp) lw x1, -4(sp) + csrrw sp, \MODE\()scratch, sp // switch sp and scratch stack back to restore the non-trap stack pointer \MODE\()ret // return from trap +// specific exception handlers + ecallhandler_\MODE\(): // Check input parameter a0. encoding above. - // *** ASSUMES: that this trap is being handled in machine mode. in other words, that nothing odd has been written to the medeleg or mideleg csrs. li x5, 2 // case 2: change to machine mode beq a0, x5, ecallhandler_changetomachinemode_\MODE\() li x5, 3 // case 3: change to supervisor mode @@ -359,22 +437,23 @@ ecallhandler_\MODE\(): j segfault_\MODE\() ecallhandler_changetomachinemode_\MODE\(): - // Force mstatus.MPP (bits 12:11) to 11 to enter machine mode after mret + // Force status.MPP (bits 12:11) to 11 to enter machine mode after mret + // note that it is impossible to return to M mode after a trap delegated to S mode li x1, 0b1100000000000 csrs \MODE\()status, x1 - j trapreturn_\MODE\() + j trapreturn_\MODE\() ecallhandler_changetosupervisormode_\MODE\(): - // Force mstatus.MPP (bits 12:11) to 01 to enter supervisor mode after mret - li x1, 0b1100000000000 + // Force status.MPP (bits 12:11) and status.SPP (bit 8) to 01 to enter supervisor mode after (m/s)ret + li x1, 0b1000000000000 csrc \MODE\()status, x1 - li x1, 0b0100000000000 + li x1, 0b0100100000000 csrs \MODE\()status, x1 j trapreturn_\MODE\() ecallhandler_changetousermode_\MODE\(): - // Force mstatus.MPP (bits 12:11) to 00 to enter user mode after mret - li x1, 0b1100000000000 + // Force status.MPP (bits 12:11) and status.SPP (bit 8) to 00 to enter user mode after (m/s)ret + li x1, 0b1100100000000 csrc \MODE\()status, x1 j trapreturn_\MODE\() @@ -390,7 +469,6 @@ illegalinstr_\MODE\(): j trapreturn_\MODE\() // return to the code after recording the mcause accessfault_\MODE\(): - // *** What do I have to do here? j trapreturn_\MODE\() addr_misaligned_\MODE\(): @@ -399,34 +477,107 @@ addr_misaligned_\MODE\(): breakpt_\MODE\(): j trapreturn_\MODE\() -soft_interrupt_\MODE\(): - li x5, 0x7EC // write 0x7EC (looks like VEC) to the output before the mcause and extras to indicate that this trap was handled with a vector table. - sw x5, 0(x16) +// Vectored interrupt handlers: record the fact that the handler went to the correct vector and then continue to handling +// note: does not mess up any registers, saves and restores them to the stack instead. + +s_soft_vector_\MODE\(): + csrrw sp, \MODE\()scratch, sp // swap sp and scratch so we can use the scratch stack in the trap hanler without messing up sp's value or the stack itself. + sw x5, -4(sp) // put x5 on the scratch stack before messing with it + li x5, 0x7EC01 // write 0x7ec01 (for "VEC"tored and 01 for the interrupt code) + j vectored_int_end_\MODE\() + +m_soft_vector_\MODE\(): + csrrw sp, \MODE\()scratch, sp // swap sp and scratch so we can use the scratch stack in the trap hanler without messing up sp's value or the stack itself. + sw x5, -4(sp) // put x5 on the scratch stack before messing with it + li x5, 0x7EC03 // write 0x7ec03 (for "VEC"tored and 03 for the interrupt code) + j vectored_int_end_\MODE\() + +s_time_vector_\MODE\(): + csrrw sp, \MODE\()scratch, sp // swap sp and scratch so we can use the scratch stack in the trap hanler without messing up sp's value or the stack itself. + sw x5, -4(sp) // put x5 on the scratch stack before messing with it + li x5, 0x7EC05 // write 0x7ec05 (for "VEC"tored and 05 for the interrupt code) + j vectored_int_end_\MODE\() + +m_time_vector_\MODE\(): + csrrw sp, \MODE\()scratch, sp // swap sp and scratch so we can use the scratch stack in the trap hanler without messing up sp's value or the stack itself. + sw x5, -4(sp) // put x5 on the scratch stack before messing with it + li x5, 0x7EC07 // write 0x7ec07 (for "VEC"tored and 07 for the interrupt code) + j vectored_int_end_\MODE\() + +s_ext_vector_\MODE\(): + csrrw sp, \MODE\()scratch, sp // swap sp and scratch so we can use the scratch stack in the trap hanler without messing up sp's value or the stack itself. + sw x5, -4(sp) // put x5 on the scratch stack before messing with it + li x5, 0x7EC09 // write 0x7ec09 (for "VEC"tored and 08 for the interrupt code) + j vectored_int_end_\MODE\() + +m_ext_vector_\MODE\(): + csrrw sp, \MODE\()scratch, sp // swap sp and scratch so we can use the scratch stack in the trap hanler without messing up sp's value or the stack itself. + sw x5, -4(sp) // put x5 on the scratch stack before messing with it + li x5, 0x7EC0B // write 0x7ec0B (for "VEC"tored and 0B for the interrupt code) + j vectored_int_end_\MODE\() + +vectored_int_end_\MODE\(): + sw x5, 0(x16) // store to signature to show vectored interrupts succeeded. addi x6, x6, 4 addi x16, x16, 4 - la x28, 0x02000000 // Reset by clearing MSIP interrupt from CLINT - sw x0, 0(x28) - j trap_unvectored_\MODE\() + lw x5, -4(sp) // restore x5 before continuing to handle trap in case its needed. + j trap_stack_saved_\MODE\() + +// specific interrupt handlers + +soft_interrupt_\MODE\(): + la x5, 0x02000000 // Reset by clearing MSIP interrupt from CLINT + sw x0, 0(x5) + + csrci \MODE\()ip, 0x2 // clear supervisor software interrupt pending bit + lw x1, -4(sp) // load return address from stack into ra (the address to return to after causing this interrupt) + // Note: we do this because the mepc loads in the address of the instruction after the sw that causes the interrupt + // This means that this trap handler will return to the next address after that one, which might be unpredictable behavior. + j trapreturn_finished_\MODE\() // return to the code at ra value from before trap time_interrupt_\MODE\(): - li x5, 0x7EC - sw x5, 0(x16) - addi x6, x6, 4 - addi x16, x16, 4 - la x29, 0x02004000 // MTIMECMP register in CLINT - li x30, 0xFFFFFFFF - sw x30, 0(x29) // reset interrupt by setting mtimecmp to 0xFFFFFFFF - j trap_unvectored_\MODE\() + la x5, 0x02004000 // MTIMECMP register in CLINT + li x7, 0xFFFFFFFF + sw x7, 0(x5) // reset interrupt by setting mtimecmp to 0xFFFFFFFF + + li x5, 0x20 + csrc \MODE\()ip, x5 + lw x1, -4(sp) // load return address from stack into ra (the address to return to after the loop is complete) + j trapreturn_finished_\MODE\() // return to the code at ra value from before trap ext_interrupt_\MODE\(): - li x5, 0x7EC - sw x5, 0(x16) - addi x6, x6, 4 - addi x16, x16, 4 li x28, 0x10060000 // reset interrupt by clearing all the GPIO bits sw x0, 8(x28) // disable the first pin as an output sw x0, 40(x28) // write a 0 to the first output pin (reset interrupt) - j trap_unvectored_\MODE\() + + # reset PLIC to turn off external interrupts + # m priority threshold = 7 + li x28, 0xC200000 + li x5, 0x7 + sw x5, 0(x28) + # s priority threshold = 7 + li x28, 0xC201000 + li x5, 0x7 + sw x5, 0(x28) + # source 3 (GPIO) priority = 0 + li x28, 0xC000000 + li x5, 0 + sw x5, 0x0C(x28) + # disable source 3 in M mode + li x28, 0x0C002000 + li x5, 0b0000 + sw x5, 0(x28) + # enable source 3 in S mode + li x28, 0x0C002080 + li x29, 0b0000 + sw x29, 0(x28) + + li x5, 0x200 + csrc \MODE\()ip, x5 + + lw x1, -4(sp) // load return address from stack into ra (the address to return to after the loop is complete) + j trapreturn_finished_\MODE\() // return to the code at ra value from before trap + // Table of trap behavior // lists what to do on each exception (not interrupts) @@ -452,6 +603,22 @@ exception_vector_table_\MODE\(): .4byte segfault_\MODE\() // 14: reserved .4byte trapreturn_\MODE\() // 15: store page fault + .align 2 // aligns this data table to an 4 byte boundary +interrupt_vector_table_\MODE\(): + .4byte segfault_\MODE\() // 0: reserved + .4byte soft_interrupt_\MODE\() // 1: instruction access fault // the zero spot is taken up by the instruction to skip this table. + .4byte segfault_\MODE\() // 2: reserved + .4byte soft_interrupt_\MODE\() // 3: breakpoint + .4byte segfault_\MODE\() // 4: reserved + .4byte time_interrupt_\MODE\() // 5: load access fault + .4byte segfault_\MODE\() // 6: reserved + .4byte time_interrupt_\MODE\() // 7: store access fault + .4byte segfault_\MODE\() // 8: reserved + .4byte ext_interrupt_\MODE\() // 9: ecall from S-mode + .4byte segfault_\MODE\() // 10: reserved + .4byte ext_interrupt_\MODE\() // 11: ecall from M-mode + + .align 2 trap_return_pagetype_table_\MODE\(): .4byte 0xC // 0: kilopage has 12 offset bits @@ -589,7 +756,8 @@ trap_handler_end_\MODE\(): // place to jump to so we can skip the trap handler a // Turn translation off li x7, 0 // satp.MODE value for bare metal (0) slli x7, x7, 31 - sfence.vma x0, x0 // *** flushes global pte's as well + csrw satp, x7 + //sfence.vma x0, x0 // *** flushes global pte's as well .endm .macro GOTO_SV32 ASID BASE_PPN @@ -602,7 +770,7 @@ trap_handler_end_\MODE\(): // place to jump to so we can skip the trap handler a li x28, \BASE_PPN // Base Pagetable physical page number, satp.PPN field. add x7, x7, x28 csrw satp, x7 - sfence.vma x0, x0 // *** flushes global pte's as well + //sfence.vma x0, x0 // *** flushes global pte's as well .endm .macro WRITE_READ_CSR CSR VAL @@ -996,9 +1164,19 @@ rvtest_data: RVTEST_DATA_END .align 2 // align stack to 4 byte boundary -bottom_of_stack: +stack_bottom: .fill 1024, 4, 0xdeadbeef -top_of_stack: +stack_top: + +.align 2 +mscratch_bottom: + .fill 512, 4, 0xdeadbeef +mscratch_top: + +.align 2 +sscratch_bottom: + .fill 512, 4, 0xdeadbeef +sscratch_top: RVMODEL_DATA_BEGIN