Skip to content

PMP-denied load can hang instead of raising a load access fault #61

Description

@jimmymtest

I found an issue that OpenC910 can fail to deliver the expected PMP access-fault exception for a PMP-denied load. I provide a minimal testcase that triggers this by programming a locked no-access NAPOT PMP entry for a target page, printing BEFORE_DENIED_ACCESS immediately before the denied load, and then observing that the simulation runs until cutoff without reaching either the trap handler or the post-load success path.

Expected behavior

For a load denied by a matching PMP entry, the hart should raise a load access fault:

mcause = 5

The testcase installs a trap handler that prints EXPECTED_ACCESS_FAULT when mcause == 5, so the expected output is:

BEFORE_DENIED_ACCESS
EXPECTED_ACCESS_FAULT

Here BEFORE_DENIED_ACCESS means the testcase reached the instruction immediately before the PMP-denied load, and EXPECTED_ACCESS_FAULT means the expected trap handler ran.

Actual behavior

With the failing layout, OpenC910 prints only:

BEFORE_DENIED_ACCESS
[sim_cutoff] time=50001

The testcase does not print EXPECTED_ACCESS_FAULT, so the expected load access-fault handler was not reached. It also does not print UNEXPECTED_ACCESS_RETURNED, so the denied load did not visibly return through the success path.

With the same PMP setup and target address, adding one nop before the denied load changes the result to:

BEFORE_DENIED_ACCESS
EXPECTED_ACCESS_FAULT
[sim_cutoff] time=50001

This one-nop control suggests a timing or pipeline handoff problem in delivering the PMP fault, rather than a bad PMP configuration in the testcase.

Minimal testcase

The testcase sets mtvec, configures PMP0 as a locked no-access NAPOT entry, prints BEFORE_DENIED_ACCESS, and then executes one denied lw.

.include "openc910_min_common.inc"

#ifndef DELAY_NOPS
#define DELAY_NOPS 0
#endif

.equ TARGET_ADDR, 0x60080
.equ TARGET_PAGE, 0x60000
.equ PMP_CFG,     0x98   # L=1, A=NAPOT, R/W/X=0

.text
.align 6
.global main
main:
  la    t0, trap_handler
  csrw  mtvec, t0

  li    t0, TARGET_PAGE
  srli  t0, t0, 2
  li    t1, 0x1ff
  or    t0, t0, t1
  csrw  pmpaddr0, t0

  li    t0, PMP_CFG
  csrw  pmpcfg0, t0
  fence

  .rept DELAY_NOPS
  nop
  .endr

  la    a0, msg_before_denied_access
  jal   ra, oracle_puts

  li    t0, TARGET_ADDR
  lw    t1, 0(t0)

  la    a0, msg_unexpected_return
  jal   ra, oracle_puts
1:
  j     1b

trap_handler:
  csrr  t0, mcause
  li    t1, 5
  beq   t0, t1, expected_trap

  la    a0, msg_unexpected_trap
  jal   ra, oracle_puts
2:
  j     2b

expected_trap:
  la    a0, msg_expected_fault
  jal   ra, oracle_puts
3:
  j     3b

.section .rodata
msg_before_denied_access:
  .asciz "BEFORE_DENIED_ACCESS\n"
msg_expected_fault:
  .asciz "EXPECTED_ACCESS_FAULT\n"
msg_unexpected_return:
  .asciz "UNEXPECTED_ACCESS_RETURNED\n"
msg_unexpected_trap:
  .asciz "UNEXPECTED_TRAP\n"

Output meanings:

  • BEFORE_DENIED_ACCESS: reached the instruction immediately before the PMP-denied load.
  • EXPECTED_ACCESS_FAULT: reached the expected load access-fault handler.
  • UNEXPECTED_ACCESS_RETURNED: the denied load returned successfully.
  • UNEXPECTED_TRAP: reached a trap, but not the expected load access fault.

Source Code and dependencies:

appendix.zip

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions