Skip to content

fix(RVH): check HLVX final physical execute permission - #1040

Open
fuhuakai wants to merge 1 commit into
masterfrom
fix-rvh-hlvx-pmp-pma
Open

fix(RVH): check HLVX final physical execute permission#1040
fuhuakai wants to merge 1 commit into
masterfrom
fix-rvh-hlvx-pmp-pma

Conversation

@fuhuakai

@fuhuakai fuhuakai commented May 25, 2026

Copy link
Copy Markdown
Contributor

The RISC-V HLVX instructions are loads for exception reporting, but their target address must also satisfy execute permission checks. NEMU already modeled this during page-table permission checks by using the global HLVX state when checking PTE permissions.

The original code has a bug in the final physical PMP and PMA checks. The translated HLVX access reached paddr_read as a normal read, so PMP and PMA checked only read permission. A region with R=1 and X=0 could be read by HLVX, which made the directed HLVX PMA test miss the expected load access fault.

Fix this by adding a MEM_TYPE_READ_EXEC access type for the final physical read. Keep the trap classification as read, require both read and execute permission in PMP and PMA checks.

This has been verified using the workload provided at OpenXiangShan/XiangShan#5995. After the XiangShan cores and NEMU jointly fixed this bug, enabling the difftest can successfully pass the test.

Comment thread src/memory/host-tlb.c Outdated
Comment on lines +116 to +119
if (paddr_type == MEM_TYPE_READ_EXEC) {
paddr_t paddr = va2pa(s, vaddr, len, type);
return paddr_read(paddr, len, paddr_type, type, cpu.mode, vaddr);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need a separate check here? If this is HLVX, hld_st will be true in has_two_stage_translation(), so it will not take the fast path anyway. Is it sufficient to just make sure paddr_type is passed correctly?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, this code is indeed redundant. HLVX will not take this path. I will streamline the code to ensure there are no unnecessary logical elements.

Comment thread include/memory/host-tlb.h Outdated

struct Decode;
word_t hosttlb_read(struct Decode *s, vaddr_t vaddr, int len, int type);
word_t hosttlb_read(struct Decode *s, vaddr_t vaddr, int len, int type, int paddr_type);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It still feels a bit hard to distinguish them this way. Would it make sense to rename them to access_type and trap_type for better clarity?

Also, I think we should add some comments somewhere to explicitly explain the difference between these two types. At the moment, is HLVX the only case where they differ, or are there other instructions that also use different semantics for access checking vs. trap classification?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good suggestion. I will try to distinguish these two types and add comments to explain clearly the differences and various uses of them.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes HLVX final physical permission handling so HLVX loads still report load access faults while requiring execute permission in PMP/PMA checks.

Changes:

  • Adds MEM_TYPE_READ_EXEC to represent HLVX physical reads requiring both read and execute permissions.
  • Splits physical access permission type from trap classification through virtual/host TLB read paths.
  • Updates RISC-V PMP/PMA permission checks to enforce R+X for HLVX reads.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
src/memory/vaddr.c Propagates separate physical access type and trap type through virtual reads.
src/memory/paddr.c Documents and asserts the new read-execute physical access type.
src/memory/host-tlb.c Passes separate access and trap types through host TLB reads.
src/isa/riscv64/system/mmu.c Enforces R+X permission for MEM_TYPE_READ_EXEC in PMP/PMA checks.
include/memory/host-tlb.h Updates host TLB read prototype.
include/isa.h Adds the new memory access type enum value.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/memory/paddr.c Outdated
Comment on lines +273 to +283
// or EX_IAF). For most accesses they are the same value. Some special cases:
// - MEM_TYPE_IFETCH_READ: PTW reads a PTE on behalf of ifetch.
// type = IFETCH_READ (checks R), trap_type = IFETCH_READ (raises EX_IAF).
// - MEM_TYPE_WRITE_READ: PTW reads a PTE on behalf of store/AMO.
// type = WRITE_READ (checks R), trap_type = WRITE_READ (raises EX_SAF).
// - MEM_TYPE_READ_EXEC: HLVX load.
// type = READ_EXEC (checks R+X), trap_type = READ (raises EX_LAF).
// PTW types encode both roles in one value (raise_read_access_fault recognizes
// them directly). HLVX is the only case where type and trap_type are different
// values, because MEM_TYPE_READ_EXEC encodes the access semantics while the
// trap must still be a load fault.

@fuhuakai fuhuakai May 27, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fine. I'll chage my expression a little.

@fuhuakai
fuhuakai force-pushed the fix-rvh-hlvx-pmp-pma branch from 848ab41 to f1e1fa4 Compare May 27, 2026 04:05
@OpenXiangShan OpenXiangShan deleted a comment from github-actions Bot May 27, 2026
@OpenXiangShan OpenXiangShan deleted a comment from github-actions Bot May 27, 2026
@github-actions

Copy link
Copy Markdown

NEMU Performance Results - XS Interpreter

Test Guest Instructions Host Instructions Estimated Host Throughput (instr/s) Actual NEMU Throughput (instr/s) Baseline Host Instructions Baseline Actual NEMU Throughput (instr/s) Change vs Baseline (Instructions) Change vs Baseline (Throughput)
bitmanip.bin 1.385e+05 5.142e+07 2.694e+07 2.818e+07 5.157e+07 3.056e+07 +0.29% -7.79%
coremark-riscv64-xs-rv64gc-o2.bin 3.354e+06 1.742e+08 1.925e+08 1.815e+08 1.731e+08 1.875e+08 -0.63% -3.18%
coremark-riscv64-xs-rv64gc-o3.bin 3.394e+06 1.737e+08 1.954e+08 1.856e+08 1.726e+08 1.908e+08 -0.63% -2.71%
coremark-riscv64-xs-rv64gcb-o3.bin 3.035e+06 1.709e+08 1.776e+08 1.736e+08 1.698e+08 1.740e+08 -0.64% -0.24%
amtest-riscv64-xs.bin 1.587e+04 8.697e+06 1.825e+07 1.570e+07 8.690e+06 2.284e+07 -0.08% -31.26%
aliastest-riscv64-xs.bin 1.376e+03 7.702e+06 1.786e+06 3.909e+06 7.702e+06 3.004e+06 +0.00% +30.11%
softprefetchtest-riscv64-xs.bin 2.643e+03 7.750e+06 3.410e+06 5.873e+06 7.750e+06 5.886e+06 -0.00% -0.22%
zacas-riscv64-xs.bin 6.470e+04 1.219e+07 5.309e+07 2.003e+07 1.218e+07 2.023e+07 -0.09% -1.02%
linux-hello 7.545e+07 1.870e+10 4.034e+07 4.710e+07 1.855e+10 4.830e+07 -0.80% -2.49%

NEMU Performance Results - XS Ref Shared Object

Test Guest Instructions Host Instructions Estimated Host Throughput (instr/s) Actual NEMU Throughput (instr/s) Baseline Host Instructions Baseline Actual NEMU Throughput (instr/s) Change vs Baseline (Instructions) Change vs Baseline (Throughput)
bitmanip.bin 1.385e+05 1.130e+09 1.226e+06 1.018e+06 1.129e+09 9.912e+05 -0.06% +2.71%
coremark-riscv64-xs-rv64gc-o2.bin 3.354e+06 7.058e+09 4.752e+06 3.570e+06 7.053e+09 3.660e+06 -0.07% -2.45%
coremark-riscv64-xs-rv64gc-o3.bin 3.394e+06 7.093e+09 4.785e+06 3.633e+06 7.088e+09 3.854e+06 -0.07% -5.73%
coremark-riscv64-xs-rv64gcb-o3.bin 3.035e+06 6.645e+09 4.567e+06 3.703e+06 6.640e+09 3.608e+06 -0.08% +2.64%
amtest-riscv64-xs.bin 1.588e+04 3.918e+07 4.052e+06 1.680e+06 3.916e+07 1.701e+06 -0.06% -1.22%
aliastest-riscv64-xs.bin 1.379e+03 6.805e+06 2.027e+06 7.123e+05 6.803e+06 6.898e+05 -0.03% +3.25%
softprefetchtest-riscv64-xs.bin 2.646e+03 9.142e+06 2.894e+06 1.168e+06 9.139e+06 1.165e+06 -0.03% +0.22%
zacas-riscv64-xs.bin 6.471e+04 1.433e+08 4.516e+06 1.909e+06 1.432e+08 2.030e+06 -0.05% -5.97%
linux-hello 7.522e+07 6.927e+11 1.086e+06 1.066e+06 6.921e+11 1.055e+06 -0.08% +1.01%
  • Host Instructions is measured by DynamoRIO's inscount client.
  • Estimated Host Throughput assumes a fixed 4GHz CPU and IPC=2.5.
  • Actual NEMU Throughput is a single native NEMU run and may vary with host CPU performance.
  • Baseline columns are populated on pull_request runs when the PR base contains the same defconfig.
  • Change vs Baseline (Instructions) is computed from host instruction count; positive means fewer host instructions than baseline.
  • Change vs Baseline (Throughput) is computed from native throughput; positive means faster than baseline.

@fuhuakai
fuhuakai requested a review from cebarobot May 27, 2026 05:50
The RISC-V HLVX instructions are loads for exception reporting, but
their target address must also satisfy execute permission checks at
the final physical level. NEMU already modeled this during page-table
permission checks by using the global HLVX state when checking PTE
permissions.

The original code has a bug in the final physical PMP and PMA checks.
The translated HLVX access reached paddr_read as a normal read, so
PMP and PMA checked only read permission. A region with R=1 and X=0
could be read by HLVX, which made the directed HLVX PMA test miss the
expected load access fault.

Fix this by adding a MEM_TYPE_READ_EXEC access type for the final
physical read. HLVX requires two distinct values for permission
checking vs. trap classification: the PTW and page-fault logic must
see MEM_TYPE_READ (since isa_mmu_translate uses type to select fault
class and index internal arrays), while the final PMP/PMA check must
see MEM_TYPE_READ_EXEC (requiring both R and X). The intermediate-
layer functions (hosttlb_read, vaddr_mmu_read, vaddr_read_cross_page)
therefore carry both values, named "type" for the permission-check
type and "trap_type" for the fault-classification type, matching the
convention already used by paddr_read.

This has been verified using the workload provided at
OpenXiangShan/XiangShan#5995. After the XiangShan cores and NEMU
jointly fixed this bug, enabling the difftest can successfully pass
the test.
@fuhuakai
fuhuakai force-pushed the fix-rvh-hlvx-pmp-pma branch from f1e1fa4 to 7937dba Compare July 27, 2026 03:43
@github-actions

Copy link
Copy Markdown

NEMU Performance Results - XS Interpreter

Test Guest Instructions Host Instructions Estimated Host Throughput (instr/s) Actual NEMU Throughput (instr/s) Baseline Host Instructions Baseline Actual NEMU Throughput (instr/s) Change vs Baseline (Instructions)
bitmanip.bin 1.385e+05 4.809e+07 2.880e+07 2.839e+07 4.809e+07 3.095e+07 -0.00%
coremark-riscv64-xs-rv64gc-o2.bin 3.354e+06 1.725e+08 1.944e+08 1.668e+08 1.760e+08 1.647e+08 +1.96%
coremark-riscv64-xs-rv64gc-o3.bin 3.394e+06 1.720e+08 1.974e+08 1.708e+08 1.754e+08 1.190e+08 +1.97%
coremark-riscv64-xs-rv64gcb-o3.bin 3.035e+06 1.691e+08 1.795e+08 1.506e+08 1.726e+08 1.127e+08 +2.01%
amtest-riscv64-xs.bin 1.588e+04 8.714e+06 1.822e+07 1.609e+07 8.720e+06 1.702e+07 +0.08%
aliastest-riscv64-xs.bin 1.382e+03 7.717e+06 1.791e+06 3.031e+06 7.716e+06 2.947e+06 -0.01%
softprefetchtest-riscv64-xs.bin 2.649e+03 7.768e+06 3.410e+06 7.861e+06 7.766e+06 6.368e+06 -0.02%
zacas-riscv64-xs.bin 6.471e+04 1.219e+07 5.309e+07 1.858e+07 1.219e+07 1.771e+07 +0.03%
rvv-workload-suite 4.570e+04 9.666e+07 4.728e+06 4.967e+06 9.646e+07 6.040e+06 -0.21%
linux-hello 6.676e+07 1.392e+10 4.797e+07 5.462e+07 1.382e+10 5.571e+07 -0.70%

NEMU Performance Results - XS Ref Shared Object

Test Guest Instructions Host Instructions Estimated Host Throughput (instr/s) Actual NEMU Throughput (instr/s) Baseline Host Instructions Baseline Actual NEMU Throughput (instr/s) Change vs Baseline (Instructions)
bitmanip.bin 1.385e+05 3.497e+08 3.961e+06 3.787e+06 3.515e+08 4.209e+06 +0.52%
coremark-riscv64-xs-rv64gc-o2.bin 3.354e+06 2.978e+09 1.126e+07 9.603e+06 2.981e+09 9.227e+06 +0.10%
coremark-riscv64-xs-rv64gc-o3.bin 3.394e+06 3.002e+09 1.131e+07 9.793e+06 3.005e+09 9.792e+06 +0.09%
coremark-riscv64-xs-rv64gcb-o3.bin 3.035e+06 2.768e+09 1.097e+07 9.777e+06 2.770e+09 9.304e+06 +0.10%
amtest-riscv64-xs.bin 1.588e+04 1.583e+07 1.003e+07 3.523e+06 1.585e+07 3.046e+06 +0.13%
aliastest-riscv64-xs.bin 1.382e+03 2.284e+06 6.051e+06 9.698e+05 2.289e+06 9.857e+05 +0.21%
softprefetchtest-riscv64-xs.bin 2.649e+03 3.359e+06 7.886e+06 1.824e+06 3.365e+06 1.801e+06 +0.18%
zacas-riscv64-xs.bin 6.471e+04 5.998e+07 1.079e+07 2.596e+06 6.006e+07 2.520e+06 +0.13%
rvv-workload-suite 4.570e+04 1.830e+08 2.497e+06 1.559e+06 1.837e+08 1.395e+06 +0.35%
linux-hello 7.528e+07 2.902e+11 2.594e+06 3.396e+06 2.903e+11 3.191e+06 +0.05%
  • Host Instructions is measured by DynamoRIO's inscount client.
  • Estimated Host Throughput assumes a fixed 4GHz CPU and IPC=2.5.
  • Actual NEMU Throughput is a single native NEMU run and may vary with host CPU performance.
  • Baseline columns are populated on pull_request runs when the PR base contains the same defconfig.
  • Change vs Baseline (Instructions) is computed from host instruction count; positive means fewer host instructions than baseline.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Variant of HLV.WU ignores SPVP=VU effective privilege for final PMP checks found on NEMU

3 participants