1 // See LICENSE for license details.
3 // Test of PMP functionality.
10 volatile int trap_expected
;
12 #define INLINE inline __attribute__((always_inline))
14 uintptr_t handle_trap(uintptr_t cause
, uintptr_t epc
, uintptr_t regs
[32])
16 if (cause
== CAUSE_ILLEGAL_INSTRUCTION
)
17 exit(0); // no PMP support
19 if (!trap_expected
|| cause
!= CAUSE_LOAD_ACCESS
)
22 return epc
+ insn_len(epc
);
25 #define SCRATCH RISCV_PGSIZE
26 uintptr_t scratch
[RISCV_PGSIZE
/ sizeof(uintptr_t)] __attribute__((aligned(RISCV_PGSIZE
)));
27 uintptr_t l1pt
[RISCV_PGSIZE
/ sizeof(uintptr_t)] __attribute__((aligned(RISCV_PGSIZE
)));
28 uintptr_t l2pt
[RISCV_PGSIZE
/ sizeof(uintptr_t)] __attribute__((aligned(RISCV_PGSIZE
)));
29 #if __riscv_xlen == 64
30 uintptr_t l3pt
[RISCV_PGSIZE
/ sizeof(uintptr_t)] __attribute__((aligned(RISCV_PGSIZE
)));
37 l1pt
[0] = ((uintptr_t)l2pt
>> RISCV_PGSHIFT
<< PTE_PPN_SHIFT
) | PTE_V
;
38 l3pt
[SCRATCH
/ RISCV_PGSIZE
] = ((uintptr_t)scratch
>> RISCV_PGSHIFT
<< PTE_PPN_SHIFT
) | PTE_A
| PTE_D
| PTE_V
| PTE_R
| PTE_W
;
39 #if __riscv_xlen == 64
40 l2pt
[0] = ((uintptr_t)l3pt
>> RISCV_PGSHIFT
<< PTE_PPN_SHIFT
) | PTE_V
;
41 uintptr_t vm_choice
= SATP_MODE_SV39
;
43 uintptr_t vm_choice
= SATP_MODE_SV32
;
45 write_csr(sptbr
, ((uintptr_t)l1pt
>> RISCV_PGSHIFT
) |
46 (vm_choice
* (SATP_MODE
& ~(SATP_MODE
<<1))));
47 write_csr(pmpcfg0
, (PMP_NAPOT
| PMP_R
) << 16);
48 write_csr(pmpaddr2
, -1);
51 INLINE
uintptr_t va2pa(uintptr_t va
)
53 if (va
< SCRATCH
|| va
>= SCRATCH
+ RISCV_PGSIZE
)
55 return va
- SCRATCH
+ (uintptr_t)scratch
;
58 #define GRANULE (1UL << PMP_SHIFT)
66 INLINE
int pmp_ok(pmpcfg_t p
, uintptr_t addr
, uintptr_t size
)
68 if ((p
.cfg
& PMP_A
) == 0)
71 if ((p
.cfg
& PMP_A
) != PMP_TOR
) {
74 if ((p
.cfg
& PMP_A
) == PMP_NAPOT
) {
76 for (uintptr_t i
= 1; i
; i
<<= 1) {
93 for (uintptr_t i
= 0; i
< size
; i
+= GRANULE
) {
94 if (p
.a0
<= addr
+ i
&& addr
+ i
< p
.a1
)
98 return hits
== 0 || hits
>= size
;
101 INLINE
void test_one(uintptr_t addr
, uintptr_t size
)
103 uintptr_t new_mstatus
= (read_csr(mstatus
) & ~MSTATUS_MPP
) | (MSTATUS_MPP
& (MSTATUS_MPP
>> 1)) | MSTATUS_MPRV
;
105 case 1: asm volatile ("csrrw %0, mstatus, %0; lb x0, (%1); csrw mstatus, %0" : "+&r" (new_mstatus
) : "r" (addr
)); break;
106 case 2: asm volatile ("csrrw %0, mstatus, %0; lh x0, (%1); csrw mstatus, %0" : "+&r" (new_mstatus
) : "r" (addr
)); break;
107 case 4: asm volatile ("csrrw %0, mstatus, %0; lw x0, (%1); csrw mstatus, %0" : "+&r" (new_mstatus
) : "r" (addr
)); break;
108 #if __riscv_xlen >= 64
109 case 8: asm volatile ("csrrw %0, mstatus, %0; ld x0, (%1); csrw mstatus, %0" : "+&r" (new_mstatus
) : "r" (addr
)); break;
111 default: __builtin_unreachable();
115 INLINE
void test_all_sizes(pmpcfg_t p
, uintptr_t addr
)
117 for (size_t size
= 1; size
<= sizeof(uintptr_t); size
*= 2) {
118 if (addr
& (size
- 1))
120 trap_expected
= !pmp_ok(p
, addr
, size
);
121 test_one(addr
, size
);
127 INLINE
void test_range_once(pmpcfg_t p
, uintptr_t base
, uintptr_t range
)
129 for (uintptr_t addr
= base
; addr
< base
+ range
; addr
+= GRANULE
)
130 test_all_sizes(p
, addr
);
133 INLINE pmpcfg_t
set_pmp(pmpcfg_t p
)
135 uintptr_t cfg0
= read_csr(pmpcfg0
);
136 write_csr(pmpcfg0
, cfg0
& ~0xff00);
137 write_csr(pmpaddr0
, p
.a0
);
138 write_csr(pmpaddr1
, p
.a1
);
139 write_csr(pmpcfg0
, ((p
.cfg
<< 8) & 0xff00) | (cfg0
& ~0xff00));
140 asm volatile ("sfence.vma" ::: "memory");
144 INLINE pmpcfg_t
set_pmp_range(uintptr_t base
, uintptr_t range
)
147 p
.cfg
= PMP_TOR
| PMP_R
;
148 p
.a0
= base
>> PMP_SHIFT
;
149 p
.a1
= (base
+ range
) >> PMP_SHIFT
;
153 INLINE pmpcfg_t
set_pmp_napot(uintptr_t base
, uintptr_t range
)
156 p
.cfg
= PMP_R
| (range
> GRANULE
? PMP_NAPOT
: PMP_NA4
);
158 p
.a1
= (base
+ (range
/2 - 1)) >> PMP_SHIFT
;
162 static void test_range(uintptr_t addr
, uintptr_t range
)
164 pmpcfg_t p
= set_pmp_range(va2pa(addr
), range
);
165 test_range_once(p
, addr
, range
);
167 if ((range
& (range
- 1)) == 0 && (addr
& (range
- 1)) == 0) {
168 p
= set_pmp_napot(va2pa(addr
), range
);
169 test_range_once(p
, addr
, range
);
173 static void test_ranges(uintptr_t addr
, uintptr_t size
)
175 for (uintptr_t range
= GRANULE
; range
<= size
; range
+= GRANULE
)
176 test_range(addr
, range
);
179 static void exhaustive_test(uintptr_t addr
, uintptr_t size
)
181 for (uintptr_t base
= addr
; base
< addr
+ size
; base
+= GRANULE
)
182 test_ranges(base
, size
- (base
- addr
));
189 const int max_exhaustive
= 32;
190 exhaustive_test(SCRATCH
, max_exhaustive
);
191 exhaustive_test(SCRATCH
+ RISCV_PGSIZE
- max_exhaustive
, max_exhaustive
);
193 test_range(SCRATCH
, RISCV_PGSIZE
);
194 test_range(SCRATCH
, RISCV_PGSIZE
/ 2);
195 test_range(SCRATCH
+ RISCV_PGSIZE
/ 2, RISCV_PGSIZE
/ 2);