Add test to check MPRV/MPP behaviour when executing an MRET
This commit is contained in:
		
							parent
							
								
									f96a0ffb75
								
							
						
					
					
						commit
						2cfe6aa90e
					
				|  | @ -0,0 +1,111 @@ | |||
| #include "tb_cxxrtl_io.h" | ||||
| #include "hazard3_csr.h" | ||||
| #include "pmp.h" | ||||
| 
 | ||||
| // Check MPRV is cleared when returning to U mode, but not when returning to M
 | ||||
| // mode. Check that mret clears MPP, no matter which mode is returned to.
 | ||||
| 
 | ||||
| /*EXPECTED-OUTPUT***************************************************************
 | ||||
| 
 | ||||
| Enabling MPRV, with MPP=M | ||||
| mret to M, check MPRV | ||||
| mstatus   = 00020080  // mprv=1 mpp=U mpie=1
 | ||||
| mret to M, check MPP affects load/store | ||||
| mcause    = 7         // store fault
 | ||||
| mstatus   = 00021880  // mprv=1 mpp=M mpie=1
 | ||||
| mem[mepc] = c108 | ||||
| mret to U, check MPRV | ||||
| mcause    = 1         // instr access fault
 | ||||
| mstatus   = 00000080  // mprv=0 mpp=U mpie=1
 | ||||
| mem[mepc] = 0001      // c.nop
 | ||||
| 
 | ||||
| *******************************************************************************/ | ||||
| 
 | ||||
| volatile uint32_t scratch_word; | ||||
| 
 | ||||
| #define MPRV (1u << 17) | ||||
| #define MPP (3u << 11) | ||||
| 
 | ||||
| extern void handle_exception(); | ||||
| 
 | ||||
| int main() { | ||||
| 	// Leave PMP in default state (no U permissions).
 | ||||
| 	scratch_word = 0; | ||||
| 
 | ||||
| 	tb_puts("Enabling MPRV, with MPP=M\n"); | ||||
| 	set_csr(mstatus, MPP | MPRV); | ||||
| 
 | ||||
| 	// Check that mret to M does not clear MPRV (also, opportunistically,
 | ||||
| 	// check that mret to M clears MPP)
 | ||||
| 	tb_puts("mret to M, check MPRV\n"); | ||||
| 	write_csr(mtvec, (uintptr_t)&handle_exception); | ||||
| 	uint32_t mstatus_check; | ||||
| 	asm volatile ( | ||||
| 		"la %0, 1f\n" | ||||
| 		"csrw mepc, %0\n" | ||||
| 		"mret\n" | ||||
| 		".p2align 2\n" | ||||
| 		"1:\n" | ||||
| 		// Note MPP is cleared by the MRET, so must be re-set immediately so
 | ||||
| 		// we can do load/stores again. We read out the current mstatus value
 | ||||
| 		// at the same time we modify it.
 | ||||
| 		"li %0, 0x1800\n" | ||||
| 		"csrrs %0, mstatus, %0\n" | ||||
| 		: "+r" (mstatus_check) | ||||
| 	); | ||||
| 	// MPP should be U. MPRV should still be set.
 | ||||
| 	tb_printf("mstatus   = %08x\n", mstatus_check); | ||||
| 	// The actual value of MPP is now M again, since we fixed it up.
 | ||||
| 
 | ||||
| 	// Check that clearing of MPP upon mret immediately affects the
 | ||||
| 	// MPRV-modified load/store privilege level.
 | ||||
| 	tb_puts("mret to M, check MPP affects load/store\n"); | ||||
| 	write_csr(mcause, 0); | ||||
| 	asm ( | ||||
| 		"la a0, 1f\n" | ||||
| 		"csrw mepc, a0\n" | ||||
| 		"la a0, 2f\n" | ||||
| 		"csrw mtvec, a0\n" | ||||
| 		"la a0, scratch_word\n" | ||||
| 		"mret\n" | ||||
| 		".p2align 2\n" | ||||
| 		"1:\n" | ||||
| 		// We just executed a return from M mode to M mode, but our MPP was
 | ||||
| 		// cleared in the process, and our MPRV should still be set.
 | ||||
| 		// Therefore our effective load/store privilege is U:
 | ||||
| 		"c.sw a0, (a0)\n" | ||||
| 		// Catch the trap we just caused, and restore MPP to M.
 | ||||
| 		".p2align 2\n" | ||||
| 		"2:\n" | ||||
| 		"li a0, 0x1800\n" | ||||
| 		"csrs mstatus, a0\n" | ||||
| 		: : : "a0" | ||||
| 	); | ||||
| 	tb_printf("mcause    = %u\n", read_csr(mcause)); | ||||
| 	tb_printf("mstatus   = %08x\n", read_csr(mstatus)); | ||||
| 	tb_printf("mem[mepc] = %04x\n", *(uint16_t*)read_csr(mepc)); | ||||
| 
 | ||||
| 	// Check that mret to U (followed by trapping back to M, as we can't
 | ||||
| 	// actually check MPRV from U) *does* clear MPRV
 | ||||
| 	tb_puts("mret to U, check MPRV\n"); | ||||
| 	asm ( | ||||
| 		"la a0, 1f\n" | ||||
| 		"csrw mepc, a0\n" | ||||
| 		"csrw mtvec, a0\n" | ||||
| 		"li a0, 0x1800\n" | ||||
| 		"csrc mstatus, a0\n" | ||||
| 		"mret\n" | ||||
| 		".p2align 2\n" | ||||
| 		// We execute this address twice, first in U-mode then in M-mode:
 | ||||
| 		"1:\n" | ||||
| 		"c.nop\n" | ||||
| 		: : : "a0" | ||||
| 	); | ||||
| 	tb_printf("mcause    = %u\n", read_csr(mcause)); | ||||
| 	tb_printf("mstatus   = %08x\n", read_csr(mstatus)); | ||||
| 	tb_printf("mem[mepc] = %04x\n", *(uint16_t*)read_csr(mepc)); | ||||
| 	tb_assert(read_csr(mepc) == read_csr(mtvec), "Should trap to same address that trapped\n"); | ||||
| 
 | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
|  | @ -3,24 +3,21 @@ | |||
| #include "pmp.h" | ||||
| 
 | ||||
| // Check that PMP enforces U-mode read/write permissions in M-mode, if both
 | ||||
| // MPRV=1 and MPP=U. Check that this does not affect execute permissions.
 | ||||
| // Check MPRV is cleared when returning to U mode.
 | ||||
| 
 | ||||
| #define MCAUSE_LOAD_FAULT 5 | ||||
| #define MCAUSE_STORE_FAULT 7 | ||||
| #define MCAUSE_ECALL_UMODE 8 | ||||
| // MPRV=1 and MPP=U.
 | ||||
| //
 | ||||
| // Check that this does not affect execute permissions.
 | ||||
| 
 | ||||
| /*EXPECTED-OUTPUT***************************************************************
 | ||||
| 
 | ||||
| Enabling MPRV, with MPP=M | ||||
| Set MPP=U, then read memory | ||||
| mcause    = 5                   // load fault
 | ||||
| mstatus   = 00021800            // MPRV = 1 MPP = M
 | ||||
| mem[mepc] = 4108                // c.lw a0, (a0)
 | ||||
| mcause    = 5         // load fault
 | ||||
| mstatus   = 00021800  // mprv=1 mpp=M
 | ||||
| mem[mepc] = 4108      // c.lw
 | ||||
| Set MPP=U, then write memory | ||||
| mcause    = 7                   // store fault
 | ||||
| mstatus   = 00021800            // MPRV = 1 MPP = M
 | ||||
| mem[mepc] = c108                // c.sw a0, (a0)
 | ||||
| mcause    = 7         // store fault
 | ||||
| mstatus   = 00021800  // mprv=1 mpp=M
 | ||||
| mem[mepc] = c108      // c.sw
 | ||||
| 
 | ||||
| *******************************************************************************/ | ||||
| 
 | ||||
|  | @ -30,11 +27,7 @@ volatile uint32_t scratch_word; | |||
| #define MPP (3u << 11) | ||||
| 
 | ||||
| int main() { | ||||
| 	// Give U-mode RW permissions on testbench IO *only* (so any address >=
 | ||||
| 	// 0x800 megabytes). This means we can always print and exit the test.
 | ||||
| 	write_pmpcfg(0, PMPCFG_A_NAPOT << PMPCFG_A_LSB | PMPCFG_R_BITS | PMPCFG_W_BITS); | ||||
| 	write_pmpaddr(0, (0x80000000u | 0x3fffffffu) >> 2); | ||||
| 
 | ||||
| 	// Leave PMP in default state (no U permissions).
 | ||||
| 	scratch_word = 0; | ||||
| 
 | ||||
| 	tb_puts("Enabling MPRV, with MPP=M\n"); | ||||
|  | @ -52,7 +45,7 @@ int main() { | |||
| 		"la a0, 1f\n" | ||||
| 		"csrw mtvec, a0\n" | ||||
| 		"la a0, scratch_word\n" | ||||
| 		"la a1, 0x1800\n" | ||||
| 		"li a1, 0x1800\n" | ||||
| 
 | ||||
| 		// Note the nop is to check that we have not lost X permissions. The
 | ||||
| 		// trap should come from the lw.
 | ||||
|  | @ -76,7 +69,7 @@ int main() { | |||
| 		"la a0, 1f\n" | ||||
| 		"csrw mtvec, a0\n" | ||||
| 		"la a0, scratch_word\n" | ||||
| 		"la a1, 0x1800\n" | ||||
| 		"li a1, 0x1800\n" | ||||
| 
 | ||||
| 		"csrc mstatus, a1\n" | ||||
| 		"nop\n" | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue