mirror of
https://github.com/openhwgroup/cvw
synced 2025-02-02 09:45:18 +00:00
Fixed PMP checking that top of access is still within range
This commit is contained in:
parent
029f9e2d55
commit
55b63ff486
@ -118,7 +118,7 @@ module mmu import cvw::*; #(parameter cvw_t P,
|
|||||||
if (P.PMP_ENTRIES > 0) begin : pmp
|
if (P.PMP_ENTRIES > 0) begin : pmp
|
||||||
pmpchecker #(P) pmpchecker(.PhysicalAddress, .PrivilegeModeW,
|
pmpchecker #(P) pmpchecker(.PhysicalAddress, .PrivilegeModeW,
|
||||||
.PMPCFG_ARRAY_REGW, .PMPADDR_ARRAY_REGW,
|
.PMPCFG_ARRAY_REGW, .PMPADDR_ARRAY_REGW,
|
||||||
.ExecuteAccessF, .WriteAccessM, .ReadAccessM, .CMOpM,
|
.ExecuteAccessF, .WriteAccessM, .ReadAccessM, .Size, .CMOpM,
|
||||||
.PMPInstrAccessFaultF, .PMPLoadAccessFaultM, .PMPStoreAmoAccessFaultM);
|
.PMPInstrAccessFaultF, .PMPLoadAccessFaultM, .PMPStoreAmoAccessFaultM);
|
||||||
end else begin
|
end else begin
|
||||||
assign PMPInstrAccessFaultF = 1'b0;
|
assign PMPInstrAccessFaultF = 1'b0;
|
||||||
@ -131,7 +131,7 @@ module mmu import cvw::*; #(parameter cvw_t P,
|
|||||||
|
|
||||||
// Misaligned faults
|
// Misaligned faults
|
||||||
always_comb // exclusion-tag: immu-wordaccess
|
always_comb // exclusion-tag: immu-wordaccess
|
||||||
case(Size[1:0])
|
case(Size)
|
||||||
2'b00: DataMisalignedM = 1'b0; // lb, sb, lbu
|
2'b00: DataMisalignedM = 1'b0; // lb, sb, lbu
|
||||||
2'b01: DataMisalignedM = VAdr[0]; // lh, sh, lhu
|
2'b01: DataMisalignedM = VAdr[0]; // lh, sh, lhu
|
||||||
2'b10: DataMisalignedM = VAdr[1] | VAdr[0]; // lw, sw, flw, fsw, lwu
|
2'b10: DataMisalignedM = VAdr[1] | VAdr[0]; // lw, sw, flw, fsw, lwu
|
||||||
|
@ -35,9 +35,11 @@ module pmpadrdec import cvw::*; #(parameter cvw_t P) (
|
|||||||
input logic [P.PA_BITS-1:0] PhysicalAddress,
|
input logic [P.PA_BITS-1:0] PhysicalAddress,
|
||||||
input logic [7:0] PMPCfg,
|
input logic [7:0] PMPCfg,
|
||||||
input logic [P.PA_BITS-3:0] PMPAdr,
|
input logic [P.PA_BITS-3:0] PMPAdr,
|
||||||
|
input logic FirstMatch,
|
||||||
input logic PAgePMPAdrIn,
|
input logic PAgePMPAdrIn,
|
||||||
output logic PAgePMPAdrOut,
|
output logic PAgePMPAdrOut,
|
||||||
output logic Match,
|
output logic Match,
|
||||||
|
output logic [P.PA_BITS-1:0] PMPTop,
|
||||||
output logic L, X, W, R
|
output logic L, X, W, R
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -50,6 +52,7 @@ module pmpadrdec import cvw::*; #(parameter cvw_t P) (
|
|||||||
logic PAltPMPAdr;
|
logic PAltPMPAdr;
|
||||||
logic [P.PA_BITS-1:0] CurrentAdrFull;
|
logic [P.PA_BITS-1:0] CurrentAdrFull;
|
||||||
logic [1:0] AdrMode;
|
logic [1:0] AdrMode;
|
||||||
|
logic [P.PA_BITS-1:0] PMPTop1;
|
||||||
|
|
||||||
assign AdrMode = PMPCfg[4:3];
|
assign AdrMode = PMPCfg[4:3];
|
||||||
|
|
||||||
@ -71,20 +74,22 @@ module pmpadrdec import cvw::*; #(parameter cvw_t P) (
|
|||||||
assign NAMask[P.PA_BITS-1:2] = (PMPAdr + {{(P.PA_BITS-3){1'b0}}, (AdrMode == NAPOT)}) ^ PMPAdr;
|
assign NAMask[P.PA_BITS-1:2] = (PMPAdr + {{(P.PA_BITS-3){1'b0}}, (AdrMode == NAPOT)}) ^ PMPAdr;
|
||||||
// form a mask where the bottom k bits are 1, corresponding to a size of 2^k bytes for this memory region.
|
// form a mask where the bottom k bits are 1, corresponding to a size of 2^k bytes for this memory region.
|
||||||
// This assumes we're using at least an NA4 region, but works for any size NAPOT region.
|
// This assumes we're using at least an NA4 region, but works for any size NAPOT region.
|
||||||
assign NABase = {(PMPAdr & ~NAMask[P.PA_BITS-1:2]), 2'b00}; // base physical address of the pmp.
|
assign NABase = {(PMPAdr & ~NAMask[P.PA_BITS-1:2]), 2'b00}; // base physical address of the pmp region
|
||||||
|
|
||||||
assign NAMatch = &((NABase ~^ PhysicalAddress) | NAMask); // check if upper bits of base address match, ignore lower bits correspoonding to inside the memory range
|
assign NAMatch = &((NABase ~^ PhysicalAddress) | NAMask); // check if upper bits of base address match, ignore lower bits correspoonding to inside the memory range
|
||||||
|
|
||||||
|
// finally pick the appropriate match for the access type
|
||||||
assign Match = (AdrMode == TOR) ? TORMatch :
|
assign Match = (AdrMode == TOR) ? TORMatch :
|
||||||
(AdrMode == NA4 | AdrMode == NAPOT) ? NAMatch :
|
(AdrMode == NA4 | AdrMode == NAPOT) ? NAMatch :
|
||||||
1'b0;
|
1'b0;
|
||||||
|
|
||||||
|
// Report top of region for first matching region
|
||||||
|
assign PMPTop1 = {PMPAdr,2'b00} | NAMask; // top of the pmp region. All 1s in the lower bits. Used to check the address doesn't pass the top
|
||||||
|
assign PMPTop = FirstMatch ? PMPTop1 : '0; // AND portion of distributed AND-OR mux (OR portion in pmpchhecker)
|
||||||
|
|
||||||
|
// PMP should match but fail if the size is too big (8-byte accesses spanning to TOR or NA4 region)
|
||||||
assign L = PMPCfg[7];
|
assign L = PMPCfg[7];
|
||||||
assign X = PMPCfg[2];
|
assign X = PMPCfg[2];
|
||||||
assign W = PMPCfg[1];
|
assign W = PMPCfg[1];
|
||||||
assign R = PMPCfg[0];
|
assign R = PMPCfg[0];
|
||||||
|
|
||||||
// known bug: The size of the access is not yet checked. For example, if an NA4 entry matches 0xC-0xF and the system
|
|
||||||
// attempts an 8-byte access to 0x8, the access should fail (see page 60 of privileged specification 20211203). This
|
|
||||||
// implementation will not detect the failure.
|
|
||||||
endmodule
|
endmodule
|
||||||
|
@ -43,6 +43,7 @@ module pmpchecker import cvw::*; #(parameter cvw_t P) (
|
|||||||
input var logic [7:0] PMPCFG_ARRAY_REGW[P.PMP_ENTRIES-1:0],
|
input var logic [7:0] PMPCFG_ARRAY_REGW[P.PMP_ENTRIES-1:0],
|
||||||
input var logic [P.PA_BITS-3:0] PMPADDR_ARRAY_REGW [P.PMP_ENTRIES-1:0],
|
input var logic [P.PA_BITS-3:0] PMPADDR_ARRAY_REGW [P.PMP_ENTRIES-1:0],
|
||||||
input logic ExecuteAccessF, WriteAccessM, ReadAccessM,
|
input logic ExecuteAccessF, WriteAccessM, ReadAccessM,
|
||||||
|
input logic [1:0] Size,
|
||||||
input logic [3:0] CMOpM,
|
input logic [3:0] CMOpM,
|
||||||
output logic PMPInstrAccessFaultF,
|
output logic PMPInstrAccessFaultF,
|
||||||
output logic PMPLoadAccessFaultM,
|
output logic PMPLoadAccessFaultM,
|
||||||
@ -55,29 +56,56 @@ module pmpchecker import cvw::*; #(parameter cvw_t P) (
|
|||||||
logic [P.PMP_ENTRIES-1:0] FirstMatch; // onehot encoding for the first pmpaddr to match the current address.
|
logic [P.PMP_ENTRIES-1:0] FirstMatch; // onehot encoding for the first pmpaddr to match the current address.
|
||||||
logic [P.PMP_ENTRIES-1:0] L, X, W, R; // PMP matches and has flag set
|
logic [P.PMP_ENTRIES-1:0] L, X, W, R; // PMP matches and has flag set
|
||||||
logic [P.PMP_ENTRIES-1:0] PAgePMPAdr; // for TOR PMP matching, PhysicalAddress > PMPAdr[i]
|
logic [P.PMP_ENTRIES-1:0] PAgePMPAdr; // for TOR PMP matching, PhysicalAddress > PMPAdr[i]
|
||||||
|
logic [P.PA_BITS-1:0] PMPTop[P.PMP_ENTRIES-1:0]; // Upper end of each region, for checking that the access is fully within the region
|
||||||
logic PMPCMOAccessFault, PMPCBOMAccessFault, PMPCBOZAccessFault;
|
logic PMPCMOAccessFault, PMPCBOMAccessFault, PMPCBOZAccessFault;
|
||||||
|
logic [2:0] SizeBytesMinus1;
|
||||||
|
logic MatchingR, MatchingW, MatchingX, MatchingL;
|
||||||
|
logic [P.PA_BITS-1:0] MatchingPMPTop, PhysicalAddressTop;
|
||||||
|
logic TooBig;
|
||||||
|
|
||||||
if (P.PMP_ENTRIES > 0) begin: pmp // prevent complaints about array of no elements when PMP_ENTRIES = 0
|
if (P.PMP_ENTRIES > 0) begin: pmp // prevent complaints about array of no elements when PMP_ENTRIES = 0
|
||||||
pmpadrdec #(P) pmpadrdecs[P.PMP_ENTRIES-1:0](
|
pmpadrdec #(P) pmpadrdecs[P.PMP_ENTRIES-1:0](
|
||||||
.PhysicalAddress,
|
.PhysicalAddress,
|
||||||
.PMPCfg(PMPCFG_ARRAY_REGW),
|
.PMPCfg(PMPCFG_ARRAY_REGW),
|
||||||
.PMPAdr(PMPADDR_ARRAY_REGW),
|
.PMPAdr(PMPADDR_ARRAY_REGW),
|
||||||
|
.FirstMatch,
|
||||||
.PAgePMPAdrIn({PAgePMPAdr[P.PMP_ENTRIES-2:0], 1'b1}),
|
.PAgePMPAdrIn({PAgePMPAdr[P.PMP_ENTRIES-2:0], 1'b1}),
|
||||||
.PAgePMPAdrOut(PAgePMPAdr),
|
.PAgePMPAdrOut(PAgePMPAdr),
|
||||||
.Match, .L, .X, .W, .R);
|
.Match, .PMPTop, .L, .X, .W, .R);
|
||||||
end
|
end
|
||||||
|
|
||||||
priorityonehot #(P.PMP_ENTRIES) pmppriority(.a(Match), .y(FirstMatch)); // combine the match signal from all the adress decoders to find the first one that matches.
|
priorityonehot #(P.PMP_ENTRIES) pmppriority(.a(Match), .y(FirstMatch)); // combine the match signal from all the adress decoders to find the first one that matches.
|
||||||
|
|
||||||
// Only enforce PMP checking for S and U modes or in Machine mode when L bit is set in selected region
|
// Distributed AND-OR mux to select the first matching results
|
||||||
assign EnforcePMP = (PrivilegeModeW != P.M_MODE) | (|(L & FirstMatch));
|
// If the access does not match all bytes of the PMP region, it is too big and the matches are disabled
|
||||||
|
assign MatchingR = |(R & FirstMatch) & ~TooBig;
|
||||||
|
assign MatchingW = |(W & FirstMatch) & ~TooBig;
|
||||||
|
assign MatchingX = |(X & FirstMatch) & ~TooBig;
|
||||||
|
assign MatchingL = |(L & FirstMatch);
|
||||||
|
or_rows #(P.PMP_ENTRIES, P.PA_BITS) PTEOr(PMPTop, MatchingPMPTop);
|
||||||
|
|
||||||
assign PMPCBOMAccessFault = EnforcePMP & (|CMOpM[2:0]) & ~|((R|W) & FirstMatch) ; // exclusion-tag: immu-pmpcbom
|
// Matching PMP entry must match all bytes of an access, or the access fails (Priv Spec 3.7.1.3)
|
||||||
assign PMPCBOZAccessFault = EnforcePMP & CMOpM[3] & ~|(W & FirstMatch) ; // exclusion-tag: immu-pmpcboz
|
// *** not fully implemented
|
||||||
|
// *** check R=0,W=1 WARL is enforced
|
||||||
|
// First find the size of the access in terms of the offset to the most significant byte
|
||||||
|
always_comb
|
||||||
|
case (Size)
|
||||||
|
2'b00: SizeBytesMinus1 = 3'd0;
|
||||||
|
2'b01: SizeBytesMinus1 = 3'd1;
|
||||||
|
2'b10: SizeBytesMinus1 = 3'd3;
|
||||||
|
2'b11: SizeBytesMinus1 = 3'd7;
|
||||||
|
endcase
|
||||||
|
assign PhysicalAddressTop = PhysicalAddress + {{P.PA_BITS-3{1'b0}}, SizeBytesMinus1}; // top of the access range
|
||||||
|
assign TooBig = PhysicalAddressTop > MatchingPMPTop; // check if the access goes beyond the top of the PMP region
|
||||||
|
|
||||||
|
// Only enforce PMP checking for S and U modes or in Machine mode when L bit is set in selected region
|
||||||
|
assign EnforcePMP = (PrivilegeModeW != P.M_MODE) | MatchingL;
|
||||||
|
|
||||||
|
assign PMPCBOMAccessFault = EnforcePMP & (|CMOpM[2:0]) & ~MatchingR ; // checking R is sufficient because W implies R in PMP // exclusion-tag: immu-pmpcbom
|
||||||
|
assign PMPCBOZAccessFault = EnforcePMP & CMOpM[3] & ~MatchingW ; // exclusion-tag: immu-pmpcboz
|
||||||
assign PMPCMOAccessFault = PMPCBOZAccessFault | PMPCBOMAccessFault; // exclusion-tag: immu-pmpcboaccess
|
assign PMPCMOAccessFault = PMPCBOZAccessFault | PMPCBOMAccessFault; // exclusion-tag: immu-pmpcboaccess
|
||||||
|
|
||||||
assign PMPInstrAccessFaultF = EnforcePMP & ExecuteAccessF & ~|(X & FirstMatch) ;
|
assign PMPInstrAccessFaultF = EnforcePMP & ExecuteAccessF & ~MatchingX ;
|
||||||
assign PMPStoreAmoAccessFaultM = (EnforcePMP & WriteAccessM & ~|(W & FirstMatch)) | PMPCMOAccessFault; // exclusion-tag: immu-pmpstoreamoaccessfault
|
assign PMPStoreAmoAccessFaultM = (EnforcePMP & WriteAccessM & ~MatchingW) | PMPCMOAccessFault; // exclusion-tag: immu-pmpstoreamoaccessfault
|
||||||
assign PMPLoadAccessFaultM = EnforcePMP & ReadAccessM & ~WriteAccessM & ~|(R & FirstMatch) ;
|
assign PMPLoadAccessFaultM = EnforcePMP & ReadAccessM & ~WriteAccessM & ~MatchingR;
|
||||||
endmodule
|
endmodule
|
||||||
|
Loading…
Reference in New Issue
Block a user