Summary
OpenC910 exposes a 2KB PMP granularity through pmpaddr readback, but a locked 2KB TOR deny region is not enforced. The minimal testcase installs a TOR deny region for [0x60000, 0x60800) and then executes a single lw from 0x60400, which is inside that denied range.
This is different from the previously reported NA4 bypass: this testcase does not use NA4, and it does not rely on a lower-priority NAPOT allow entry.
It only tests whether a 2KB TOR deny region is enforced after PMP CSR readback advertises a 2KB grain.
Expected result:
- the load from
0x60400 raises mcause = 5 (load access fault)
- the testcase prints
TRAP
Actual result on OpenC910:
- the load retires successfully
- the testcase prints
BUG
Test Case
The testcase first probes PMP granularity by writing all ones to pmpaddr0 while pmpcfg0=0. OpenC910 reads back low bits indicating a 2KB grain. It then programs:
PMP0: TOR lower bound 0x60000
PMP1: locked no-access TOR upper bound 0x60800
- access:
lw from 0x60400
.include "openc910_min_common.inc"
.equ TARGET_BASE, 0x60000
.equ TARGET_ADDR, 0x60400
.equ TOR_TOP_2K, 0x60800
.equ TOR_TOP_4K, 0x61000
.equ PMPCFG_TOR_DENY_L_ENTRY1, 0x8800
.text
.align 6
.global main
main:
la t0, trap_handler
csrw mtvec, t0
# Probe pmpaddr granularity. Low 10 bits should read as 0x200,
# which advertises a 2KB PMP grain.
csrw pmpcfg0, zero
li t0, -1
csrw pmpaddr0, t0
csrr t1, pmpaddr0
li t2, 0x3ff
and t1, t1, t2
li t2, 0x200
bne t1, t2, setup_failed
# Initialize the target word while PMP is off.
li t0, TARGET_ADDR
li t1, 0x11223344
sw t1, 0(t0)
fence
# PMP0 is the TOR lower bound.
li t0, TARGET_BASE
srli t0, t0, 2
csrw pmpaddr0, t0
# PMP1 is the locked no-access TOR entry.
li t0, TOR_TOP_2K
srli t0, t0, 2
csrw pmpaddr1, t0
li t0, PMPCFG_TOR_DENY_L_ENTRY1
csrw pmpcfg0, t0
fence
# This load is inside [0x60000, 0x60800), so it should trap.
li t0, TARGET_ADDR
lw t1, 0(t0)
la a0, msg_bug
jal ra, oracle_puts
1:
j 1b
trap_handler:
csrr t0, mcause
li t1, 5
beq t0, t1, expected_trap
la a0, msg_wrong
jal ra, oracle_puts
2:
j 2b
expected_trap:
la a0, msg_trap
jal ra, oracle_puts
3:
j 3b
setup_failed:
la a0, msg_setup_failed
jal ra, oracle_puts
4:
j 4b
.section .rodata
msg_bug:
.asciz "BUG\n"
msg_trap:
.asciz "TRAP\n"
msg_wrong:
.asciz "WRONG\n"
msg_setup_failed:
.asciz "SETUP_FAIL\n"
The attached seed has one optional control switch, REGION_4K_CONTROL=1, which changes only the TOR upper bound from 0x60800 to 0x61000. That control prints TRAP, showing that the trap handler and locked PMP deny path work when the TOR region is widened to 4KB.
How to reproduce
Build OpenC910's native Verilator simulator
Run this once:
cd openc910/smart_run
make compile SIM=verilator THREADS=8
make buildVerilator
Compile and run the failing 2KB TOR testcase
/opt/riscv/bin/riscv64-unknown-elf-gcc -c -march=rv64gc_zicsr_zifencei -mabi=lp64d -O2 openc910_min_crt0.S -o openc910_min_crt0.o
/opt/riscv/bin/riscv64-unknown-elf-gcc -c -march=rv64gc_zicsr_zifencei -mabi=lp64d -O2 -I. -DREGION_4K_CONTROL=0 seeds/openc910_tor_2kb_grain_bypass_min.S -o openc910_tor_2kb_grain_bypass_min.o
/opt/riscv/bin/riscv64-unknown-elf-gcc -nostdlib -nostartfiles -march=rv64gc_zicsr_zifencei -mabi=lp64d -T openc910_min_linker.ld openc910_min_crt0.o openc910_tor_2kb_grain_bypass_min.o -o openc910_tor_2kb_grain_bypass_min.elf
/opt/riscv/bin/riscv64-unknown-elf-objcopy -O srec openc910_tor_2kb_grain_bypass_min.elf openc910_tor_2kb_grain_bypass_min_inst.hex -j .text* -j .rodata* -j .eh_frame*
/opt/riscv/bin/riscv64-unknown-elf-objcopy -O srec openc910_tor_2kb_grain_bypass_min.elf openc910_tor_2kb_grain_bypass_min_data.hex -j .data* -j .bss -j .sbss -j .COMMON
../tests/bin/Srec2vmem openc910_tor_2kb_grain_bypass_min_inst.hex inst.pat
../tests/bin/Srec2vmem openc910_tor_2kb_grain_bypass_min_data.hex data.pat
./obj_dir/Vtop
Observed failing output:
Meaning:
BUG = the load from the locked 2KB TOR deny region retired successfully
TRAP = correct behavior; the load raised mcause = 5
WRONG = some unexpected trap happened, but not the expected load access fault
SETUP_FAIL = the PMP granularity readback check did not observe the expected 2KB grain
test.zip
Summary
OpenC910 exposes a 2KB PMP granularity through
pmpaddrreadback, but a locked 2KB TOR deny region is not enforced. The minimal testcase installs a TOR deny region for[0x60000, 0x60800)and then executes a singlelwfrom0x60400, which is inside that denied range.This is different from the previously reported NA4 bypass: this testcase does not use
NA4, and it does not rely on a lower-priority NAPOT allow entry.It only tests whether a 2KB TOR deny region is enforced after PMP CSR readback advertises a 2KB grain.
Expected result:
0x60400raisesmcause = 5(load access fault)TRAPActual result on OpenC910:
BUGTest Case
The testcase first probes PMP granularity by writing all ones to
pmpaddr0whilepmpcfg0=0. OpenC910 reads back low bits indicating a 2KB grain. It then programs:PMP0: TOR lower bound0x60000PMP1: locked no-access TOR upper bound0x60800lwfrom0x60400The attached seed has one optional control switch,
REGION_4K_CONTROL=1, which changes only the TOR upper bound from0x60800to0x61000. That control printsTRAP, showing that the trap handler and locked PMP deny path work when the TOR region is widened to 4KB.How to reproduce
Build OpenC910's native Verilator simulator
Run this once:
cd openc910/smart_run make compile SIM=verilator THREADS=8 make buildVerilatorCompile and run the failing 2KB TOR testcase
Observed failing output:
BUGMeaning:
BUG= the load from the locked 2KB TOR deny region retired successfullyTRAP= correct behavior; the load raisedmcause = 5WRONG= some unexpected trap happened, but not the expected load access faultSETUP_FAIL= the PMP granularity readback check did not observe the expected 2KB graintest.zip