updated 32 bit test lib to mirror 64 bit one in interrupt handling, trap stacks

This commit is contained in:
Kip Macsai-Goren 2022-04-20 17:33:40 +00:00
parent fe14b9f188
commit 0a6e1d108f

View File

@ -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