Add PMP U-mode read/write permission test
This commit is contained in:
		
							parent
							
								
									c8afcdbb8f
								
							
						
					
					
						commit
						71eff7649d
					
				| 
						 | 
					@ -0,0 +1,220 @@
 | 
				
			||||||
 | 
					#include "tb_cxxrtl_io.h"
 | 
				
			||||||
 | 
					#include "hazard3_csr.h"
 | 
				
			||||||
 | 
					#include "pmp.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Check that PMP correctly controls U-mode R/W permissions, for all three
 | 
				
			||||||
 | 
					// access sizes.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define MCAUSE_LOAD_FAULT 5
 | 
				
			||||||
 | 
					#define MCAUSE_STORE_FAULT 7
 | 
				
			||||||
 | 
					#define MCAUSE_ECALL_UMODE 8
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*EXPECTED-OUTPUT***************************************************************
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Initial word read/write check
 | 
				
			||||||
 | 
					mcause = 8
 | 
				
			||||||
 | 
					mcause = 8
 | 
				
			||||||
 | 
					Remove read permission, word read/write
 | 
				
			||||||
 | 
					mcause = 8
 | 
				
			||||||
 | 
					mcause = 5
 | 
				
			||||||
 | 
					Restore read permission, remove write permission, word read/write
 | 
				
			||||||
 | 
					mcause = 7
 | 
				
			||||||
 | 
					mcause = 8
 | 
				
			||||||
 | 
					Remove read permission, halfword read/write
 | 
				
			||||||
 | 
					mcause = 8
 | 
				
			||||||
 | 
					mcause = 5
 | 
				
			||||||
 | 
					Restore read permission, remove write permission, halfword read/write
 | 
				
			||||||
 | 
					mcause = 7
 | 
				
			||||||
 | 
					mcause = 8
 | 
				
			||||||
 | 
					Remove read permission, byte read/write
 | 
				
			||||||
 | 
					mcause = 8
 | 
				
			||||||
 | 
					mcause = 5
 | 
				
			||||||
 | 
					Restore read permission, remove write permission, byte read/write
 | 
				
			||||||
 | 
					mcause = 7
 | 
				
			||||||
 | 
					mcause = 8
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					*******************************************************************************/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					typedef uint32_t uxlen_t;
 | 
				
			||||||
 | 
					typedef uxlen_t (*umode_func_2a_1r)(uxlen_t, uxlen_t);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// /!\ Unconventional control flow ahead
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Call a function in U-mode, from M-mode, with two register arguments and one
 | 
				
			||||||
 | 
					// register result. If the function traps, mcause/mepc indicate the trap
 | 
				
			||||||
 | 
					// cause. Otherwise mcause is set to U-mode ecall (= 8).	
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					uxlen_t __attribute__((naked)) call_umode(umode_func_2a_1r f, uxlen_t a0, uxlen_t a1) {
 | 
				
			||||||
 | 
						asm (
 | 
				
			||||||
 | 
							"addi sp, sp, -16                                                     \n"
 | 
				
			||||||
 | 
							"sw s0, 0(sp)                                                         \n"
 | 
				
			||||||
 | 
							"sw ra, 4(sp)                                                         \n"
 | 
				
			||||||
 | 
							// Set up mret target address and mode
 | 
				
			||||||
 | 
							"csrw mepc, a0                                                        \n"
 | 
				
			||||||
 | 
							"li a0, 0x1800                                                        \n"
 | 
				
			||||||
 | 
							"csrc mstatus, a0                                                     \n"
 | 
				
			||||||
 | 
							// Set up arguments
 | 
				
			||||||
 | 
							"mv a0, a1                                                            \n"
 | 
				
			||||||
 | 
							"mv a1, a2                                                            \n"
 | 
				
			||||||
 | 
							// Set up two return paths: trap -> mtvec, or ret -> ecall -> mtvec
 | 
				
			||||||
 | 
							"la s0, 1f                                                            \n"
 | 
				
			||||||
 | 
							"addi ra, s0, -4                                                      \n"
 | 
				
			||||||
 | 
							"csrrw s0, mtvec, s0                                                  \n"
 | 
				
			||||||
 | 
							// Call the function
 | 
				
			||||||
 | 
							"mret                                                                 \n"
 | 
				
			||||||
 | 
							// Join return paths and restore mtvec
 | 
				
			||||||
 | 
							".p2align 2                                                           \n"
 | 
				
			||||||
 | 
							"ecall                                                                \n"
 | 
				
			||||||
 | 
							"1:                                                                   \n"
 | 
				
			||||||
 | 
							"csrw mtvec, s0                                                       \n"
 | 
				
			||||||
 | 
							// Then return via saved ra
 | 
				
			||||||
 | 
							"lw s0, 0(sp)                                                         \n"
 | 
				
			||||||
 | 
							"lw ra, 4(sp)                                                         \n"
 | 
				
			||||||
 | 
							"addi sp, sp, 16                                                      \n"
 | 
				
			||||||
 | 
							"ret                                                                  \n"
 | 
				
			||||||
 | 
						);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void __attribute__((naked)) do_sw(uxlen_t a, uxlen_t d) {
 | 
				
			||||||
 | 
						asm volatile (
 | 
				
			||||||
 | 
							"sw a1, (a0)\n"
 | 
				
			||||||
 | 
							"ecall\n"
 | 
				
			||||||
 | 
						);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void __attribute__((naked)) do_sh(uxlen_t a, uxlen_t d) {
 | 
				
			||||||
 | 
						asm volatile (
 | 
				
			||||||
 | 
							"sh a1, (a0)\n"
 | 
				
			||||||
 | 
							"ecall\n"
 | 
				
			||||||
 | 
						);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void __attribute__((naked)) do_sb(uxlen_t a, uxlen_t d) {
 | 
				
			||||||
 | 
						asm volatile (
 | 
				
			||||||
 | 
							"sb a1, (a0)\n"
 | 
				
			||||||
 | 
							"ecall\n"
 | 
				
			||||||
 | 
						);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					uxlen_t __attribute__((naked)) do_lw(uxlen_t a) {
 | 
				
			||||||
 | 
						asm volatile (
 | 
				
			||||||
 | 
							"lw a0, (a0)\n"
 | 
				
			||||||
 | 
							"ecall"
 | 
				
			||||||
 | 
						);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					uxlen_t __attribute__((naked)) do_lhu(uxlen_t a) {
 | 
				
			||||||
 | 
						asm volatile (
 | 
				
			||||||
 | 
							"lhu a0, (a0)\n"
 | 
				
			||||||
 | 
							"ecall"
 | 
				
			||||||
 | 
						);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					uxlen_t __attribute__((naked)) do_lbu(uxlen_t a) {
 | 
				
			||||||
 | 
						asm volatile (
 | 
				
			||||||
 | 
							"lbu a0, (a0)\n"
 | 
				
			||||||
 | 
							"ecall"
 | 
				
			||||||
 | 
						);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					volatile uint32_t scratch_word;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int main() {
 | 
				
			||||||
 | 
						// We will keep PMP region 1 as an all-permission grant on all of memory,
 | 
				
			||||||
 | 
						// and then use PMP region 0 to alter the permissions of only the
 | 
				
			||||||
 | 
						// targeted scratch word.
 | 
				
			||||||
 | 
						write_pmpcfg(1, PMPCFG_A_NAPOT << PMPCFG_A_LSB | PMPCFG_R_BITS | PMPCFG_W_BITS | PMPCFG_X_BITS);
 | 
				
			||||||
 | 
						write_pmpaddr(1, -1u);
 | 
				
			||||||
 | 
						write_pmpaddr(0, (uint32_t)&scratch_word >> 2);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Region 1 is not yet active, so we should have full permissions on this word.
 | 
				
			||||||
 | 
						tb_puts("Initial word read/write check\n");
 | 
				
			||||||
 | 
						scratch_word = 0;
 | 
				
			||||||
 | 
						(void)call_umode((umode_func_2a_1r)&do_sw, (uintptr_t)&scratch_word, 0x12345678);
 | 
				
			||||||
 | 
						tb_printf("mcause = %u\n", read_csr(mcause));
 | 
				
			||||||
 | 
						tb_assert(scratch_word == 0x12345678, "Failed to write\n");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						uint32_t readback = call_umode((umode_func_2a_1r)&do_lw, (uintptr_t)&scratch_word, 0);
 | 
				
			||||||
 | 
						tb_printf("mcause = %u\n", read_csr(mcause));
 | 
				
			||||||
 | 
						tb_assert(readback == scratch_word, "Failed to read\n");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Change permissions to WX, and repeat previous test, making sure we get
 | 
				
			||||||
 | 
						// the correct traps from the correct places, and trapped instructions
 | 
				
			||||||
 | 
						// have their side effects suppressed.
 | 
				
			||||||
 | 
						tb_puts("Remove read permission, word read/write\n");
 | 
				
			||||||
 | 
						write_pmpcfg(0, PMPCFG_A_NA4 << PMPCFG_A_LSB | PMPCFG_W_BITS | PMPCFG_X_BITS);
 | 
				
			||||||
 | 
						scratch_word = 0;
 | 
				
			||||||
 | 
						(void)call_umode((umode_func_2a_1r)&do_sw, (uintptr_t)&scratch_word, 0x12345678);
 | 
				
			||||||
 | 
						tb_printf("mcause = %u\n", read_csr(mcause));
 | 
				
			||||||
 | 
						tb_assert(scratch_word == 0x12345678, "Failed to write\n");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						readback = call_umode((umode_func_2a_1r)&do_lw, (uintptr_t)&scratch_word, 0);
 | 
				
			||||||
 | 
						tb_printf("mcause = %u\n", read_csr(mcause));
 | 
				
			||||||
 | 
						tb_assert(read_csr(mepc) == (uint32_t)&do_lw, "Trap should come from load instruction\n");
 | 
				
			||||||
 | 
						tb_assert(readback == (uintptr_t)&scratch_word, "Load instruction should not have written back\n");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Now again, with permissions set to RX.
 | 
				
			||||||
 | 
						tb_puts("Restore read permission, remove write permission, word read/write\n");
 | 
				
			||||||
 | 
						write_pmpcfg(0, PMPCFG_A_NA4 << PMPCFG_A_LSB | PMPCFG_R_BITS | PMPCFG_X_BITS);
 | 
				
			||||||
 | 
						scratch_word = 0xdeadbeefu;
 | 
				
			||||||
 | 
						(void)call_umode((umode_func_2a_1r)&do_sw, (uintptr_t)&scratch_word, 0x12345678);
 | 
				
			||||||
 | 
						tb_printf("mcause = %u\n", read_csr(mcause));
 | 
				
			||||||
 | 
						tb_assert(read_csr(mepc) == (uint32_t)&do_sw, "Trap should come from store instruction\n");
 | 
				
			||||||
 | 
						tb_assert(scratch_word == 0xdeadbeefu, "Should not have written to memory\n");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						readback = call_umode((umode_func_2a_1r)&do_lw, (uintptr_t)&scratch_word, 0);
 | 
				
			||||||
 | 
						tb_printf("mcause = %u\n", read_csr(mcause));
 | 
				
			||||||
 | 
						tb_assert(readback == scratch_word, "Failed to read\n");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Repeat previous two tests with halfword access at top of region
 | 
				
			||||||
 | 
						tb_puts("Remove read permission, halfword read/write\n");
 | 
				
			||||||
 | 
						write_pmpcfg(0, PMPCFG_A_NA4 << PMPCFG_A_LSB | PMPCFG_W_BITS | PMPCFG_X_BITS);
 | 
				
			||||||
 | 
						scratch_word = 0;
 | 
				
			||||||
 | 
						(void)call_umode((umode_func_2a_1r)&do_sh, (uintptr_t)&scratch_word + 2, 0x12345678);
 | 
				
			||||||
 | 
						tb_printf("mcause = %u\n", read_csr(mcause));
 | 
				
			||||||
 | 
						tb_assert(scratch_word == 0x56780000u, "Failed to write\n");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						readback = call_umode((umode_func_2a_1r)&do_lhu, (uintptr_t)&scratch_word + 2, 0);
 | 
				
			||||||
 | 
						tb_printf("mcause = %u\n", read_csr(mcause));
 | 
				
			||||||
 | 
						tb_assert(read_csr(mepc) == (uint32_t)&do_lhu, "Trap should come from load instruction\n");
 | 
				
			||||||
 | 
						tb_assert(readback == (uintptr_t)&scratch_word + 2, "Load instruction should not have written back\n");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						tb_puts("Restore read permission, remove write permission, halfword read/write\n");
 | 
				
			||||||
 | 
						write_pmpcfg(0, PMPCFG_A_NA4 << PMPCFG_A_LSB | PMPCFG_R_BITS | PMPCFG_X_BITS);
 | 
				
			||||||
 | 
						scratch_word = 0xdeadbeefu;
 | 
				
			||||||
 | 
						(void)call_umode((umode_func_2a_1r)&do_sh, (uintptr_t)&scratch_word + 2, 0x12345678);
 | 
				
			||||||
 | 
						tb_printf("mcause = %u\n", read_csr(mcause));
 | 
				
			||||||
 | 
						tb_assert(read_csr(mepc) == (uint32_t)&do_sh, "Trap should come from store instruction\n");
 | 
				
			||||||
 | 
						tb_assert(scratch_word == 0xdeadbeefu, "Should not have written to memory\n");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						readback = call_umode((umode_func_2a_1r)&do_lhu, (uintptr_t)&scratch_word + 2, 0);
 | 
				
			||||||
 | 
						tb_printf("mcause = %u\n", read_csr(mcause));
 | 
				
			||||||
 | 
						tb_assert(readback == scratch_word >> 16, "Failed to read\n");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Repeat previous two tests with byte access at top of region
 | 
				
			||||||
 | 
						tb_puts("Remove read permission, byte read/write\n");
 | 
				
			||||||
 | 
						write_pmpcfg(0, PMPCFG_A_NA4 << PMPCFG_A_LSB | PMPCFG_W_BITS | PMPCFG_X_BITS);
 | 
				
			||||||
 | 
						scratch_word = 0;
 | 
				
			||||||
 | 
						(void)call_umode((umode_func_2a_1r)&do_sb, (uintptr_t)&scratch_word + 3, 0x12345678);
 | 
				
			||||||
 | 
						tb_printf("mcause = %u\n", read_csr(mcause));
 | 
				
			||||||
 | 
						tb_assert(scratch_word == 0x78000000u, "Failed to write\n");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						readback = call_umode((umode_func_2a_1r)&do_lbu, (uintptr_t)&scratch_word + 3, 0);
 | 
				
			||||||
 | 
						tb_printf("mcause = %u\n", read_csr(mcause));
 | 
				
			||||||
 | 
						tb_assert(read_csr(mepc) == (uint32_t)&do_lbu, "Trap should come from load instruction\n");
 | 
				
			||||||
 | 
						tb_assert(readback == (uintptr_t)&scratch_word + 3, "Load instruction should not have written back\n");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						tb_puts("Restore read permission, remove write permission, byte read/write\n");
 | 
				
			||||||
 | 
						write_pmpcfg(0, PMPCFG_A_NA4 << PMPCFG_A_LSB | PMPCFG_R_BITS | PMPCFG_X_BITS);
 | 
				
			||||||
 | 
						scratch_word = 0xdeadbeefu;
 | 
				
			||||||
 | 
						(void)call_umode((umode_func_2a_1r)&do_sb, (uintptr_t)&scratch_word + 3, 0x12345678);
 | 
				
			||||||
 | 
						tb_printf("mcause = %u\n", read_csr(mcause));
 | 
				
			||||||
 | 
						tb_assert(read_csr(mepc) == (uint32_t)&do_sb, "Trap should come from store instruction\n");
 | 
				
			||||||
 | 
						tb_assert(scratch_word == 0xdeadbeefu, "Should not have written to memory\n");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						readback = call_umode((umode_func_2a_1r)&do_lbu, (uintptr_t)&scratch_word + 3, 0);
 | 
				
			||||||
 | 
						tb_printf("mcause = %u\n", read_csr(mcause));
 | 
				
			||||||
 | 
						tb_assert(readback == scratch_word >> 24, "Failed to read\n");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -3,8 +3,9 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Check that U-mode execution of a wfi causes an illegal opcode exception if
 | 
					// Check that U-mode execution of a wfi causes an illegal opcode exception if
 | 
				
			||||||
// and only if the mstatus timeout wait bit is set.
 | 
					// and only if the mstatus timeout wait bit is set.
 | 
				
			||||||
// Also check that a U-mode WFI which fails PMP X check does not stall the
 | 
					//
 | 
				
			||||||
// procesor.
 | 
					// Also check that a U-mode WFI which fails PMP X check or TW=1 check does not
 | 
				
			||||||
 | 
					// stall the processor.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*EXPECTED-OUTPUT***************************************************************
 | 
					/*EXPECTED-OUTPUT***************************************************************
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue