arch-riscv: add pma/pmp checks during page table walks

This change adds pma/pmp checks when page table entries
are accessed by hardware page table walker.

Change-Id: I161aad514bb7421e61a8c56af088c73969837704
Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/46279
Reviewed-by: Jason Lowe-Power <power.jg@gmail.com>
Maintainer: Jason Lowe-Power <power.jg@gmail.com>
Tested-by: kokoro <noreply+kokoro@google.com>
This commit is contained in:
Ayaz Akram
2021-05-29 01:51:07 -07:00
parent c05e086814
commit 00719e4257
3 changed files with 111 additions and 68 deletions

View File

@@ -295,76 +295,101 @@ Walker::WalkerState::stepWalk(PacketPtr &write)
DPRINTF(PageTableWalker, "Got level%d PTE: %#x\n", level, pte);
// step 2: TODO check PMA and PMP
// step 2:
// Performing PMA/PMP checks on physical address of PTE
// step 3:
if (!pte.v || (!pte.r && pte.w)) {
doEndWalk = true;
DPRINTF(PageTableWalker, "PTE invalid, raising PF\n");
fault = pageFault(pte.v);
}
else {
// step 4:
if (pte.r || pte.x) {
// step 5: leaf PTE
walker->pma->check(read->req);
// Effective privilege mode for pmp checks for page table
// walks is S mode according to specs
fault = walker->pmp->pmpCheck(read->req, mode,
RiscvISA::PrivilegeMode::PRV_S, tc, entry.vaddr);
if (fault == NoFault) {
// step 3:
if (!pte.v || (!pte.r && pte.w)) {
doEndWalk = true;
fault = walker->tlb->checkPermissions(status, pmode,
entry.vaddr, mode, pte);
// step 6
if (fault == NoFault) {
if (level >= 1 && pte.ppn0 != 0) {
DPRINTF(PageTableWalker,
"PTE has misaligned PPN, raising PF\n");
fault = pageFault(true);
}
else if (level == 2 && pte.ppn1 != 0) {
DPRINTF(PageTableWalker,
"PTE has misaligned PPN, raising PF\n");
fault = pageFault(true);
}
}
if (fault == NoFault) {
// step 7
if (!pte.a) {
pte.a = 1;
doWrite = true;
}
if (!pte.d && mode == TLB::Write) {
pte.d = 1;
doWrite = true;
}
// TODO check if this violates a PMA or PMP
// step 8
entry.logBytes = PageShift + (level * LEVEL_BITS);
entry.paddr = pte.ppn;
entry.vaddr &= ~((1 << entry.logBytes) - 1);
entry.pte = pte;
// put it non-writable into the TLB to detect writes and redo
// the page table walk in order to update the dirty flag.
if (!pte.d && mode != TLB::Write)
entry.pte.w = 0;
doTLBInsert = true;
}
DPRINTF(PageTableWalker, "PTE invalid, raising PF\n");
fault = pageFault(pte.v);
}
else {
level--;
if (level < 0) {
DPRINTF(PageTableWalker, "No leaf PTE found, raising PF\n");
// step 4:
if (pte.r || pte.x) {
// step 5: leaf PTE
doEndWalk = true;
fault = pageFault(true);
}
else {
Addr shift = (PageShift + LEVEL_BITS * level);
Addr idx = (entry.vaddr >> shift) & LEVEL_MASK;
nextRead = (pte.ppn << PageShift) + (idx * sizeof(pte));
nextState = Translate;
fault = walker->tlb->checkPermissions(status, pmode,
entry.vaddr, mode, pte);
// step 6
if (fault == NoFault) {
if (level >= 1 && pte.ppn0 != 0) {
DPRINTF(PageTableWalker,
"PTE has misaligned PPN, raising PF\n");
fault = pageFault(true);
}
else if (level == 2 && pte.ppn1 != 0) {
DPRINTF(PageTableWalker,
"PTE has misaligned PPN, raising PF\n");
fault = pageFault(true);
}
}
if (fault == NoFault) {
// step 7
if (!pte.a) {
pte.a = 1;
doWrite = true;
}
if (!pte.d && mode == TLB::Write) {
pte.d = 1;
doWrite = true;
}
// Performing PMA/PMP checks
if (doWrite) {
// this read will eventually become write
// if doWrite is True
walker->pma->check(read->req);
fault = walker->pmp->pmpCheck(read->req,
mode, pmode, tc, entry.vaddr);
}
// perform step 8 only if pmp checks pass
if (fault == NoFault) {
// step 8
entry.logBytes = PageShift + (level * LEVEL_BITS);
entry.paddr = pte.ppn;
entry.vaddr &= ~((1 << entry.logBytes) - 1);
entry.pte = pte;
// put it non-writable into the TLB to detect
// writes and redo the page table walk in order
// to update the dirty flag.
if (!pte.d && mode != TLB::Write)
entry.pte.w = 0;
doTLBInsert = true;
}
}
} else {
level--;
if (level < 0) {
DPRINTF(PageTableWalker, "No leaf PTE found,"
"raising PF\n");
doEndWalk = true;
fault = pageFault(true);
} else {
Addr shift = (PageShift + LEVEL_BITS * level);
Addr idx = (entry.vaddr >> shift) & LEVEL_MASK;
nextRead = (pte.ppn << PageShift) + (idx * sizeof(pte));
nextState = Translate;
}
}
}
} else {
doEndWalk = true;
}
PacketPtr oldRead = read;
Request::Flags flags = oldRead->req->getFlags();

View File

@@ -51,14 +51,21 @@ PMP::PMP(const Params &params) :
Fault
PMP::pmpCheck(const RequestPtr &req, BaseTLB::Mode mode,
RiscvISA::PrivilegeMode pmode, ThreadContext *tc)
RiscvISA::PrivilegeMode pmode, ThreadContext *tc,
Addr vaddr)
{
// First determine if pmp table should be consulted
if (!shouldCheckPMP(pmode, mode, tc))
return NoFault;
DPRINTF(PMP, "Checking pmp permissions for va: %#x , pa: %#x\n",
req->getVaddr(), req->getPaddr());
if (req->hasVaddr()) {
DPRINTF(PMP, "Checking pmp permissions for va: %#x , pa: %#x\n",
req->getVaddr(), req->getPaddr());
}
else { // this access is corresponding to a page table walk
DPRINTF(PMP, "Checking pmp permissions for pa: %#x\n",
req->getPaddr());
}
// An access should be successful if there are
// no rules defined yet or we are in M mode (based
@@ -100,12 +107,20 @@ PMP::pmpCheck(const RequestPtr &req, BaseTLB::Mode mode,
(PMP_EXEC & allowed_privs)) {
return NoFault;
} else {
return createAddrfault(req->getVaddr(), mode);
if (req->hasVaddr()) {
return createAddrfault(req->getVaddr(), mode);
} else {
return createAddrfault(vaddr, mode);
}
}
}
}
// if no entry matched and we are not in M mode return fault
return createAddrfault(req->getVaddr(), mode);
if (req->hasVaddr()) {
return createAddrfault(req->getVaddr(), mode);
} else {
return createAddrfault(vaddr, mode);
}
}
Fault

View File

@@ -109,10 +109,13 @@ class PMP : public SimObject
* @param mode mode of request (read, write, execute).
* @param pmode current privilege mode of execution (U, S, M).
* @param tc thread context.
* @param vaddr optional parameter to pass vaddr of original
* request for which a page table walk is consulted by pmp unit
* @return Fault.
*/
Fault pmpCheck(const RequestPtr &req, BaseTLB::Mode mode,
RiscvISA::PrivilegeMode pmode, ThreadContext *tc);
RiscvISA::PrivilegeMode pmode, ThreadContext *tc,
Addr vaddr = 0);
/**
* pmpUpdateCfg updates the pmpcfg for a pmp