Merge pull request #9 from CTSRD-CHERI/wip-aj443-bounds-info-api-refactor

bounds info api refactor
This commit is contained in:
Jonathan Woodruff
2022-10-25 10:43:07 +01:00
committed by GitHub
2 changed files with 541 additions and 258 deletions

View File

@@ -37,9 +37,6 @@ export CapMem;
export CapReg; export CapReg;
export CapPipe; export CapPipe;
// -----
// Auxiliary requried imports. TODO find a way around this?
export CapFat; export CapFat;
export MW; export MW;
export OTypeW; export OTypeW;
@@ -258,6 +255,137 @@ typedef MetaInfo TempFields;
// Interface functions // Interface functions
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
function BoundsInfo#(CapAddrW) getBoundsInfoFat (CapFat cap, TempFields tf)
provisos ( NumAlias #(fullW, TAdd #(CapAddrW, 1))
, NumAlias #(upperW, TSub #(fullW, MW))
, NumAlias #(lowerW, MW) );
// shared useful bindings and precomputed values
//////////////////////////////////////////////////////////////////////////////
// bind the Bounds field of the CapFat to shorter handy names
Exp exp = cap.bounds.exp;
Bit #(MW) baseBits = cap.bounds.baseBits;
Bit #(MW) topBits = cap.bounds.topBits;
// prepare representable bound bits
Bit #(MW) repBoundBits = {tf.repBoundTopBits, 0};
// prepare typed "lower" MW zeroes for simpler concatenation
Bit #(lowerW) lowerZeroes = 0;
// prepare "full" version for baseBits, topBits and repBoundBits
Bit #(fullW) baseBitsFull = zeroExtend (baseBits) << exp;
Bit #(fullW) topBitsFull = zeroExtend (topBits) << exp;
Bit #(fullW) repBoundBitsFull = zeroExtend (repBoundBits) << exp;
// other helper values
CapAddr capAddr0 = 0;
CapAddrPlus1 addrSpaceTop = {1'b1, capAddr0};
Bool alwaysRep = exp >= resetExp - 2;
// shared +1 and -1/~0 shifted by exponent
Bit #(upperW) allOnesExpShifted = ~0 << exp;
let mask = allOnesExpShifted;
let minusOne = allOnesExpShifted;
Bit #(upperW) oneExpShifted = 1 << exp;
let plusOne = oneExpShifted;
// Prepare "upper" address and its "hi" and "lo" region versions
Bit #(upperW) addrUpperBits = truncateLSB ({1'b0, cap.address}) & mask;
Bit #(upperW) addrUpperHi = addrUpperBits + (tf.addrHi ? 0 : plusOne);
Bit #(upperW) addrUpperLo = addrUpperBits + (tf.addrHi ? minusOne : 0);
function addrUpper (isHi) = isHi ? addrUpperHi : addrUpperLo;
// compute base
//////////////////////////////////////////////////////////////////////////////
// Use the appropriate upper bits of the address based on whether the base is
// in the "hi" or the "lo" region, append implied zeroes in the lower bits,
// and or in the base bits
CapAddr base =
truncate ({addrUpper (tf.baseHi), lowerZeroes} | baseBitsFull);
// compute top
//////////////////////////////////////////////////////////////////////////////
// Use the appropriate upper bits of the address based on whether the top is
// in the "hi" or the "lo" region, append implied zeroes in the lower bits,
// and or in the top bits
CapAddrPlus1 top = {addrUpper (tf.topHi), lowerZeroes} | topBitsFull;
// If the base and top are more than an address space away from eachother,
// invert the 64th/32nd bit of Top. This corrects for errors that happen when
// the representable space wraps the address space.
Bit #(2) topTip = truncateLSB (top);
Bit #(2) baseTip = {1'b0, msb (base)};
// If the bit we're interested in are actually coming from baseBits, select
// the correct one from there.
// exp == (resetExp - 1) doesn't matter since we will not flip unless
// exp < resetExp - 1.
if (exp == (resetExp - 2)) baseTip = {1'b0, baseBits[valueOf(MW) - 1]};
// Do the final check.
// If exp >= resetExp - 1, the bits we're looking at are coming directly from
// topBits and baseBits, are not being inferred, and therefore do not need
// correction. If we are below this range, check that the difference between
// the resulting top and bottom is less than one address space. If not, flip
// the msb of the top.
if (exp < (resetExp - 1) && (topTip - baseTip) > 1)
top[valueOf(CapAddrW)] = ~top[valueOf(CapAddrW)];
// compute length
//////////////////////////////////////////////////////////////////////////////
// Get the top and base bits with the 2 correction bits prepended
Bit #(TAdd #(MW, 2)) correctBase = {pack (tf.baseCorrection), baseBits};
Bit #(TAdd #(MW, 2)) correctTop = {pack (tf.topCorrection), topBits};
// Get the length by subtracting base from top and shifting appropriately, and
// saturate in case of big exponent
CapAddrPlus1 length =
(exp >= resetExp) ? ~0 : zeroExtend (correctTop - correctBase) << exp;
// compute repBase
//////////////////////////////////////////////////////////////////////////////
// Use the "lo" region upper bits of the address, append implied zeroes in the
// lower bits, and or in the representable bound bit.
// Saturate to zero when in the "always representable" case,
// i.e. exp >= resetExp - 2.
CapAddr repBase =
alwaysRep ? capAddr0
: truncate ({addrUpperLo, lowerZeroes} | repBoundBitsFull);
// compute repTop
//////////////////////////////////////////////////////////////////////////////
// Use the "hi" region upper bits of the address, append implied zeroes in the
// lower bits, and or in the representable bound bits
// Saturate to 1 and all zeroes when in the "always representable" case,
// i.e. exp >= resetExp - 2.
CapAddrPlus1 repTop =
alwaysRep ? addrSpaceTop
: {addrUpperHi, lowerZeroes} | repBoundBitsFull;
// compute repLength
//////////////////////////////////////////////////////////////////////////////
CapAddrPlus1 repLength = {oneExpShifted, lowerZeroes};
// compute split of representable space
//////////////////////////////////////////////////////////////////////////////
Bool repSplit = alwaysRep ? False : ! unpack (reduceOr (addrUpperHi));
// return populated BoundsInfo structure
//////////////////////////////////////////////////////////////////////////////
return BoundsInfo { base: base
, top: top
, length: length
, repBase: repBase
, repTop: repTop
, repLength: repLength
, repSplit: repSplit };
endfunction
function CapAddr getBotFat(CapFat cap, TempFields tf); function CapAddr getBotFat(CapFat cap, TempFields tf);
// First, construct a full length value with the base bits and the // First, construct a full length value with the base bits and the
// correction bits above, and shift that value to the appropriate spot. // correction bits above, and shift that value to the appropriate spot.
@@ -834,10 +962,13 @@ function MetaInfo getMetaInfo (CapFat cap);
, baseCorrection : baseCorrection }; , baseCorrection : baseCorrection };
endfunction endfunction
// =============================================================================== // XXX TODO
// Typeclass instance for interface // to avoid an orphan instance here, we should make CapMem a "newtype",
// basically:
typedef Bit#(TAdd#(1, CapW)) CapMem; // typedef struct {
// Bit #(TAdd #(1, CapW)) cap;
// } CapMem;
typedef Bit #(TAdd #(1, CapW)) CapMem;
typedef CapFat CapReg; typedef CapFat CapReg;
@@ -846,27 +977,41 @@ typedef struct {
TempFields tempFields; TempFields tempFields;
} CapPipe deriving (Bits); } CapPipe deriving (Bits);
// CapMem CHERICap instance
////////////////////////////////////////////////////////////////////////////////
// Note: commented out methods have a provided default implementation in the
// CHERICap typeclass definition
instance CHERICap #(CapMem, OTypeW, FlagsW, CapAddrW, CapW, TSub #(MW, 3)); instance CHERICap #(CapMem, OTypeW, FlagsW, CapAddrW, CapW, TSub #(MW, 3));
// capability validity
//////////////////////////////////////////////////////////////////////////////
function isValidCap (capMem); function isValidCap (capMem);
CapabilityInMemory cap = unpack(capMem); CapabilityInMemory cap = unpack (capMem);
return cap.isCapability; return cap.isCapability;
endfunction endfunction
function setValidCap (capMem, v); function setValidCap (capMem, v);
CapabilityInMemory cap = unpack(capMem); CapabilityInMemory cap = unpack (capMem);
cap.isCapability = v; cap.isCapability = v;
return pack(cap); return pack (cap);
endfunction endfunction
// capability flags
//////////////////////////////////////////////////////////////////////////////
function getFlags (capMem); function getFlags (capMem);
CapabilityInMemory cap = unpack(capMem); CapabilityInMemory cap = unpack (capMem);
return cap.flags; return cap.flags;
endfunction endfunction
function setFlags (capMem, f); function setFlags (capMem, f);
CapabilityInMemory cap = unpack(capMem); CapabilityInMemory cap = unpack (capMem);
cap.flags = f; cap.flags = f;
return pack(cap); return pack (cap);
endfunction endfunction
// capability permissions
//////////////////////////////////////////////////////////////////////////////
function getHardPerms (capMem); function getHardPerms (capMem);
CapabilityInMemory cap = unpack(capMem); CapabilityInMemory cap = unpack (capMem);
return HardPerms { return HardPerms {
permitSetCID: cap.perms.hard.permit_set_CID permitSetCID: cap.perms.hard.permit_set_CID
, accessSysRegs: cap.perms.hard.access_sys_regs , accessSysRegs: cap.perms.hard.access_sys_regs
@@ -881,54 +1026,91 @@ instance CHERICap #(CapMem, OTypeW, FlagsW, CapAddrW, CapW, TSub #(MW, 3));
, permitExecute: cap.perms.hard.permit_execute , permitExecute: cap.perms.hard.permit_execute
, global: cap.perms.hard.non_ephemeral }; , global: cap.perms.hard.non_ephemeral };
endfunction endfunction
function setHardPerms = error("setHardPerms not implemented for CapMem"); function setHardPerms = error ("setHardPerms not implemented for CapMem");
function getSoftPerms = error("getSoftPerms not implemented for CapMem"); function getSoftPerms = error ("getSoftPerms not implemented for CapMem");
function setSoftPerms = error("setSoftPerms not implemented for CapMem"); function setSoftPerms = error ("setSoftPerms not implemented for CapMem");
function getKind = error("getKind not implemented for CapMem"); //function getPerms = error ("getPerms not implemented for CapMem");
function setKind = error("setKind not implemented for CapMem"); //function setPerms = error ("setPerms not implemented for CapMem");
function getMeta(capMem);
CapabilityInMemory cap = unpack(capMem); // capability kind
//////////////////////////////////////////////////////////////////////////////
function getKind = error ("getKind not implemented for CapMem");
function setKind = error ("setKind not implemented for CapMem");
function validAsType (dummy, checkType);
UInt #(CapAddrW) checkTypeUnsigned = unpack (checkType);
UInt #(CapAddrW) otypeMaxUnsigned = unpack (zeroExtend (otype_max));
return checkTypeUnsigned <= otypeMaxUnsigned;
endfunction
// capability in-memory architectural representation
//////////////////////////////////////////////////////////////////////////////
function getMeta (capMem);
CapabilityInMemory cap = unpack (capMem);
return { pack (cap.perms) return { pack (cap.perms)
, pack (cap.reserved) , pack (cap.reserved)
, pack (cap.flags) , pack (cap.flags)
, pack (cap.otype) , pack (cap.otype)
, pack (cap.bounds) }; , pack (cap.bounds) };
endfunction endfunction
function getAddr(capMem); function getAddr (capMem);
CapabilityInMemory cap = unpack(capMem); CapabilityInMemory cap = unpack (capMem);
return pack (cap.address); return pack (cap.address);
endfunction endfunction
function setAddr = error("setAddr not implemented for CapMem"); function fromMem = error ("fromMem not implemented for CapMem");
function toMem = error ("toMem not implemented for CapMem");
// capability address/offset manipulation
//////////////////////////////////////////////////////////////////////////////
function setAddr = error ("setAddr not implemented for CapMem");
function setAddrUnsafe (capMem, address); function setAddrUnsafe (capMem, address);
CapabilityInMemory cap = unpack(capMem); CapabilityInMemory cap = unpack (capMem);
cap.address = address; cap.address = address;
return pack(cap); return pack (cap);
endfunction endfunction
function addAddrUnsafe (capMem, inc) = function addAddrUnsafe (capMem, inc) =
setAddrUnsafe(capMem, getAddr(capMem) + signExtend(inc)); setAddrUnsafe (capMem, getAddr (capMem) + signExtend (inc));
function getOffset = error("getOffset not implemented for CapMem"); function maskAddr = error ("maskAddr not implemented for CapMem");
function modifyOffset = error("modifyOffset not implemented for CapMem"); //function getOffset = error ("getOffset not implemented for CapMem");
function getBase = error("getBase not implemented for CapMem"); function modifyOffset = error ("modifyOffset not implemented for CapMem");
function getTop = error("getTop not implemented for CapMem"); //function setOffset = error ("setOffset not implemented for CapMem");
function getLength = error("getLength not implemented for CapMem"); //function incOffset = error ("incOffset not implemented for CapMem");
function isInBounds = error("isInBounds not implemented for CapMem");
function setBoundsCombined = error("setBoundsCombined not implemented for CapMem"); // capability architectural bounds queries
function nullWithAddr = setAddrUnsafe(packCap(null_cap)); //////////////////////////////////////////////////////////////////////////////
function getBoundsInfo = error ("getBoundsInfo not implemented for CapMem");
//function getBase = error ("getBase not implemented for CapMem");
//function getTop = error ("getTop not implemented for CapMem");
//function getLength = error ("getLength not implemented for CapMem");
//function isInBounds = error ("isInBounds not implemented for CapMem");
//function getRepBase = error ("getRepBase not implemented for CapMem");
//function getRepTop = error ("getRepTop not implemented for CapMem");
//function getRepLength = error ("getRepLength not implemented for CapMem");
//function isInRepBounds = error ("isInRepBounds not implemented for CapMem");
function getBaseAlignment =
error ("getBaseAlignment not implemented for CapMem");
// capability derivation (bounds set)
//////////////////////////////////////////////////////////////////////////////
function setBoundsCombined =
error ("setBoundsCombined not implemented for CapMem");
//function setBounds = error ("setBounds not implemented for CapMem");
//function roundLength = error ("roundLength not implemented for CapMem");
//function alignmentMask = error ("alignmentMask not implemented for CapMem");
// common capabilities
//////////////////////////////////////////////////////////////////////////////
//function nullCap = error ("nullCap not implemented for CapMem");
function nullWithAddr = setAddrUnsafe (packCap (null_cap));
function almightyCap; function almightyCap;
CapReg res = almightyCap; CapReg res = almightyCap;
return cast(res); return cast (res);
endfunction endfunction
function nullCapFromDummy (dummy) = packCap(null_cap); function nullCapFromDummy (dummy) = packCap (null_cap);
function validAsType (dummy, checkType);
UInt#(CapAddrW) checkTypeUnsigned = unpack(checkType); // Assert that the encoding is valid
UInt#(CapAddrW) otypeMaxUnsigned = unpack(zeroExtend(otype_max)); //////////////////////////////////////////////////////////////////////////////
return checkTypeUnsigned <= otypeMaxUnsigned; function isDerivable = error ("isDerivable not implemented for CapMem");
endfunction
function fromMem = error("fromMem not implemented for CapMem");
function toMem = error("toMem not implemented for CapMem");
function maskAddr = error("maskAddr not implemented for CapMem");
function getBaseAlignment = error("getBaseAlignment not implemented for CapMem");
function isDerivable = error("isDerivable not implemented for CapMem");
endinstance endinstance
instance FShow #(CapPipe); instance FShow #(CapPipe);
@@ -953,22 +1135,31 @@ instance Eq #(CapReg);
// function Bool \/= (CapPipe x, CapPipe y); // function Bool \/= (CapPipe x, CapPipe y);
endinstance endinstance
// CapReg CHERICap instance
////////////////////////////////////////////////////////////////////////////////
// Note: commented out methods have a provided default implementation in the
// CHERICap typeclass definition
instance CHERICap #(CapReg, OTypeW, FlagsW, CapAddrW, CapW, TSub #(MW, 3)); instance CHERICap #(CapReg, OTypeW, FlagsW, CapAddrW, CapW, TSub #(MW, 3));
// capability validity
//////////////////////////////////////////////////////////////////////////////
function isValidCap (x) = x.isCapability; function isValidCap (x) = x.isCapability;
function setValidCap (cap, tag); function setValidCap (cap, tag);
cap.isCapability = tag; cap.isCapability = tag;
return cap; return cap;
endfunction endfunction
// capability flags
//////////////////////////////////////////////////////////////////////////////
function getFlags (cap) = cap.flags; function getFlags (cap) = cap.flags;
function setFlags (cap, flags); function setFlags (cap, flags);
cap.flags = flags; cap.flags = flags;
return cap; return cap;
endfunction endfunction
// capability permissions
//////////////////////////////////////////////////////////////////////////////
function getHardPerms (cap) = HardPerms { function getHardPerms (cap) = HardPerms {
permitSetCID: cap.perms.hard.permit_set_CID permitSetCID: cap.perms.hard.permit_set_CID
, accessSysRegs: cap.perms.hard.access_sys_regs , accessSysRegs: cap.perms.hard.access_sys_regs
@@ -982,7 +1173,6 @@ instance CHERICap #(CapReg, OTypeW, FlagsW, CapAddrW, CapW, TSub #(MW, 3));
, permitLoad: cap.perms.hard.permit_load , permitLoad: cap.perms.hard.permit_load
, permitExecute: cap.perms.hard.permit_execute , permitExecute: cap.perms.hard.permit_execute
, global: cap.perms.hard.non_ephemeral }; , global: cap.perms.hard.non_ephemeral };
function setHardPerms (cap, perms); function setHardPerms (cap, perms);
cap.perms.hard = HPerms { cap.perms.hard = HPerms {
permit_set_CID: perms.permitSetCID permit_set_CID: perms.permitSetCID
@@ -999,14 +1189,16 @@ instance CHERICap #(CapReg, OTypeW, FlagsW, CapAddrW, CapW, TSub #(MW, 3));
, non_ephemeral: perms.global }; , non_ephemeral: perms.global };
return cap; return cap;
endfunction endfunction
function getSoftPerms (cap) = zeroExtend (cap.perms.soft);
function getSoftPerms (cap) = zeroExtend(cap.perms.soft);
function setSoftPerms (cap, perms); function setSoftPerms (cap, perms);
cap.perms.soft = truncate(perms); cap.perms.soft = truncate (perms);
return cap; return cap;
endfunction endfunction
//function getPerms = error ("getPerms not implemented for CapReg");
//function setPerms = error ("setPerms not implemented for CapReg");
// capability kind
//////////////////////////////////////////////////////////////////////////////
function getKind (cap) = case (cap.otype) function getKind (cap) = case (cap.otype)
otype_unsealed: UNSEALED; otype_unsealed: UNSEALED;
otype_sentry: SENTRY; otype_sentry: SENTRY;
@@ -1014,71 +1206,85 @@ instance CHERICap #(CapReg, OTypeW, FlagsW, CapAddrW, CapW, TSub #(MW, 3));
otype_res1: RES1; otype_res1: RES1;
default: SEALED_WITH_TYPE (cap.otype); default: SEALED_WITH_TYPE (cap.otype);
endcase; endcase;
function setKind (cap, kind) = case (kind) matches function setKind (cap, kind) = case (kind) matches
tagged UNSEALED: unseal(cap, ?); tagged UNSEALED: unseal (cap, ?);
tagged SENTRY: seal(cap, ?, VnD {v: True, d:otype_sentry}); tagged SENTRY: seal (cap, ?, VnD {v: True, d:otype_sentry});
tagged RES0: seal(cap, ?, VnD {v: True, d:otype_res0}); tagged RES0: seal (cap, ?, VnD {v: True, d:otype_res0});
tagged RES1: seal(cap, ?, VnD {v: True, d:otype_res1}); tagged RES1: seal (cap, ?, VnD {v: True, d:otype_res1});
tagged SEALED_WITH_TYPE .ot: seal(cap, ?, VnD {v: True, d:ot}); tagged SEALED_WITH_TYPE .ot: seal (cap, ?, VnD {v: True, d:ot});
endcase; endcase;
function validAsType (dummy, checkType);
CapMem nullC = nullCap;
return validAsType (nullC, checkType);
endfunction
function getMeta(capReg); // capability in-memory architectural representation
//////////////////////////////////////////////////////////////////////////////
function getMeta (capReg);
CapMem cap = unpack (pack (toMem (capReg))); CapMem cap = unpack (pack (toMem (capReg)));
return getMeta (cap); return getMeta (cap);
endfunction endfunction
function getAddr (capReg); function getAddr (capReg);
CapMem cap = unpack (pack (toMem (capReg))); CapMem cap = unpack (pack (toMem (capReg)));
return getAddr (cap); return getAddr (cap);
endfunction endfunction
function fromMem (x) = cast (pack (x));
function toMem (x) = unpack (cast (x));
function setAddr = error("setAddr not implemented for CapReg"); // capability address/offset manipulation
//////////////////////////////////////////////////////////////////////////////
function setAddrUnsafe (cap, address) = setCapPointer(cap, address); function setAddr = error ("setAddr not implemented for CapReg");
function setAddrUnsafe (cap, address) = setCapPointer (cap, address);
function addAddrUnsafe (cap, inc) = function addAddrUnsafe (cap, inc) =
setAddrUnsafe(cap, getAddr(cap) + signExtend(inc)); setAddrUnsafe (cap, getAddr (cap) + signExtend (inc));
function maskAddr (cap, mask) = setCapPointer (cap, cap.address & {~0, mask});
function getOffset = error("getOffset not implemented for CapReg"); function getOffset = error ("getOffset not implemented for CapReg");
function modifyOffset = error("modifyOffset not implemented for CapReg"); function modifyOffset = error ("modifyOffset not implemented for CapReg");
function getBase = error("getBase not implemented for CapReg"); //function setOffset = error ("setOffset not implemented for CapReg");
function getTop = error("getTop not implemented for CapReg"); //function incOffset = error ("incOffset not implemented for CapReg");
function getLength = error("getLength not implemented for CapReg");
function isInBounds = error("isInBounds not implemented for CapReg");
function setBoundsCombined(cap, length) = setBoundsFat(cap, length);
function nullWithAddr (addr) = setAddrUnsafe(null_cap, addr);
function almightyCap = defaultCapFat;
function nullCapFromDummy (x) = null_cap;
function fromMem (x) = cast(pack(x));
function toMem (x) = unpack(cast(x));
function maskAddr (cap, mask) = setCapPointer(cap, cap.address & {~0,mask});
function validAsType (dummy, checkType);
CapMem nullC = nullCap;
return validAsType(nullC, checkType);
endfunction
// capability architectural bounds queries
//////////////////////////////////////////////////////////////////////////////
function getBoundsInfo = error ("getBoundsInfo not implemented for CapReg");
//function getBase = error ("getBase not implemented for CapReg");
//function getTop = error ("getTop not implemented for CapReg");
//function getLength = error ("getLength not implemented for CapReg");
//function isInBounds = error ("isInBounds not implemented for CapReg");
//function getRepBase = error ("getRepBase not implemented for CapReg");
//function getRepTop = error ("getRepTop not implemented for CapReg");
//function getRepLength = error ("getRepLength not implemented for CapReg");
//function isInRepBounds = error ("isInRepBounds not implemented for CapReg");
function getBaseAlignment (cap) = function getBaseAlignment (cap) =
// If cap exp is non-zero, we have internal exponent, so the least significant // If cap exp is non-zero, we have internal exponent, so the least
// two bits of the base are implicitly zero. // significant two bits of the base are implicitly zero. Otherwise, we
// Otherwise, we have a zero exponent, so the least significant two bits // have a zero exponent, so the least significant two bits of the base are
// of the base are the least significant bits of the encoded base // the least significant bits of the encoded base
(cap.bounds.exp == 0) ? cap.bounds.baseBits[1:0] : 2'b0; (cap.bounds.exp == 0) ? cap.bounds.baseBits[1:0] : 2'b0;
// capability derivation (bounds set)
//////////////////////////////////////////////////////////////////////////////
function setBoundsCombined (cap, length) = setBoundsFat (cap, length);
//function setBounds = error ("setBounds not implemented for CapReg");
//function roundLength = error ("roundLength not implemented for CapReg");
//function alignmentMask = error ("alignmentMask not implemented for CapReg");
// common capabilities
//////////////////////////////////////////////////////////////////////////////
//function nullCap = error ("nullCap not implemented for CapReg");
function nullWithAddr (addr) = setAddrUnsafe (null_cap, addr);
function almightyCap = defaultCapFat;
function nullCapFromDummy (x) = null_cap;
// Assert that the encoding is valid
//////////////////////////////////////////////////////////////////////////////
function isDerivable (cap) = function isDerivable (cap) =
cap.bounds.exp <= resetExp && (cap.bounds.exp <= resetExp)
!(cap.bounds.exp == resetExp && ((truncateLSB(cap.bounds.topBits) != 1'b0) || && !( (cap.bounds.exp == resetExp)
(truncateLSB(cap.bounds.baseBits) != 2'b0))) && && ( (truncateLSB (cap.bounds.topBits) != 1'b0)
!(cap.bounds.exp == resetExp-1 && (truncateLSB(cap.bounds.baseBits) != 1'b0)) && || (truncateLSB (cap.bounds.baseBits) != 2'b0) ))
(cap.reserved == 0); && !( (cap.bounds.exp == resetExp-1)
&& (truncateLSB (cap.bounds.baseBits) != 1'b0))
&& (cap.reserved == 0);
endinstance endinstance
@@ -1180,6 +1386,8 @@ instance CHERICap #(CapPipe, OTypeW, FlagsW, CapAddrW, CapW, TSub#(MW, 3));
return Exact { exact: result.v, value: cap }; return Exact { exact: result.v, value: cap };
endfunction endfunction
function getBoundsInfo (cap) = getBoundsInfoFat (cap.capFat, cap.tempFields);
function getBase (cap) = getBotFat(cap.capFat, cap.tempFields); function getBase (cap) = getBotFat(cap.capFat, cap.tempFields);
function getTop (cap) = getTopFat(cap.capFat, cap.tempFields); function getTop (cap) = getTopFat(cap.capFat, cap.tempFields);

View File

@@ -29,7 +29,7 @@
package CHERICap; package CHERICap;
// CHERI public types // CHERI capability types
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// Permission bits // Permission bits
@@ -64,6 +64,32 @@ instance Bitwise #(HardPerms);
function lsb (x) = lsb(pack(x)); function lsb (x) = lsb(pack(x));
endinstance endinstance
// Kind of a capability, that is whether it is "sealed with a given otype", or
// if it is a "sentry" or simply "unsealed".
typedef union tagged {
void UNSEALED;
void SENTRY;
void RES0;
void RES1;
Bit #(otypeW) SEALED_WITH_TYPE;
} Kind #(numeric type otypeW) deriving (Bits, Eq, FShow);
// helper type for gathering bounds information on a capability
typedef struct {
Bit #(addrW) base;
Bit #(TAdd #(addrW, 1)) top;
Bit #(TAdd #(addrW, 1)) length;
Bit #(addrW) repBase;
Bit #(TAdd #(addrW, 1)) repTop;
Bit #(TAdd #(addrW, 1)) repLength;
Bool repSplit;
} BoundsInfo #(numeric type addrW) deriving (Bits, Eq, FShow);
// helper types and functions
////////////////////////////////////////////////////////////////////////////////
// Helper type to return the result of an operation along with whether the // Helper type to return the result of an operation along with whether the
// operation was exact. In cases where no sensible inexact representation // operation was exact. In cases where no sensible inexact representation
// exists, the only guarantee is that the tag bit is not set. // exists, the only guarantee is that the tag bit is not set.
@@ -73,165 +99,26 @@ typedef struct {
t value; t value;
} Exact #(type t) deriving (Bits); } Exact #(type t) deriving (Bits);
// Kind of a capability, that is whether it is "sealed with a given otype", or
// if it is a "sentry" or simply "unsealed".
typedef union tagged {
void UNSEALED;
void SENTRY;
void RES0;
void RES1;
Bit #(ot) SEALED_WITH_TYPE;
} Kind #(numeric type ot) deriving (Bits, Eq, FShow);
// Helper type for the return value of the 'setBoundsCombined' method // Helper type for the return value of the 'setBoundsCombined' method
typedef struct { typedef struct {
t cap; capT cap;
Bool exact; Bool exact;
Bit #(n) length; Bit #(addrW) length;
Bit #(n) mask; Bit #(addrW) mask;
} SetBoundsReturn #(type t, numeric type n) deriving (Bits, Eq, FShow); } SetBoundsReturn #(type capT, numeric type addrW) deriving (Bits, Eq, FShow);
// helper function to test belonging to a range
function Bool belongsToRange ( Bit #(n) x, Bit #(n) low, Bit #(n) high
, Bool highIncluded);
Bool notTooHigh = highIncluded ? x <= high : x < high;
Bool notTooLow = x >= low;
return notTooLow && notTooHigh;
endfunction
// CHERI capability typeclass // XXX TODO augment with all architectural bounds/ repbounds ?
//////////////////////////////////////////////////////////////////////////////// function Fmt showCHERICap (capT cap)
provisos (CHERICap #(capT , otypeW, flgW, addrW, inMemW, maskableW));
typeclass CHERICap #( type t
, numeric type ot
, numeric type flg
, numeric type n
, numeric type mem_sz
, numeric type maskable_bits )
dependencies (t determines (ot, flg, n, mem_sz, maskable_bits));
// Return whether the Capability is valid
function Bool isValidCap (t cap);
// Set the capability as valid. All fields left unchanged
function t setValidCap (t cap, Bool valid);
// Get the flags field
function Bit#(flg) getFlags (t cap);
// Set the flags field
function t setFlags (t cap, Bit#(flg) flags);
// Get the hardware permissions
function HardPerms getHardPerms (t cap);
// Set the hardware permissions
function t setHardPerms (t cap, HardPerms hardperms);
// Get the software permissions
function SoftPerms getSoftPerms (t cap);
// Set the software permissions
function t setSoftPerms (t cap, SoftPerms softperms);
// Get the architectural permissions
function Bit#(31) getPerms (t cap) =
zeroExtend({pack(getSoftPerms(cap)), 3'h0, pack(getHardPerms(cap))});
// Set the architectural permissions
function t setPerms (t cap, Bit#(31) perms) =
setSoftPerms ( setHardPerms(cap, unpack(perms[11:0]))
, unpack(truncate(perms[30:15])) );
// Manipulate the kind of the capability, i.e. whether it is sealed, sentry,
// unsealed, ...
function Kind#(ot) getKind (t cap);
function t setKind (t cap, Kind#(ot) kind);
// Get the in-memory architectural representation of the capability metadata
function Bit #(TSub #(mem_sz, n)) getMeta (t cap);
// Get the in-memory architectural representation of the capability address
function Bit #(n) getAddr (t cap);
// Note that the following rule is expected to hold:
// fromMem (tuple2 (isValidCap (cap), {getMeta (cap), getAddr (cap)})) == cap
// Set the address of the capability. Result invalid if unrepresentable
function Exact#(t) setAddr (t cap, Bit#(n) addr);
// Set the address of the capability. Result assumed to be representable
function t setAddrUnsafe (t cap, Bit#(n) addr);
// Add to the address of the capability. Result assumed to be representable
function t addAddrUnsafe (t cap, Bit#(maskable_bits) inc);
// Get the offset of the capability
function Bit#(n) getOffset (t cap) = getAddr(cap) - getBase(cap);
// Modify the offset of the capability. Result invalid if unrepresentable
function Exact#(t) modifyOffset (t cap, Bit#(n) offset, Bool doInc);
// Set the offset of the capability. Result invalid if unrepresentable
function Exact#(t) setOffset (t cap, Bit#(n) offset) =
modifyOffset(cap, offset, False);
// Set the offset of the capability. Result invalid if unrepresentable
function Exact#(t) incOffset (t cap, Bit#(n) inc) =
modifyOffset(cap, inc, True);
// Get the base
function Bit#(n) getBase (t cap);
// Get the top
function Bit#(TAdd#(n, 1)) getTop (t cap);
// Get the length
function Bit#(TAdd#(n, 1)) getLength (t cap);
// Assertion that address is between base and top
function Bool isInBounds (t cap, Bool isTopIncluded);
Bool isNotTooHigh = isTopIncluded ? zeroExtend(getAddr(cap)) <= getTop(cap)
: zeroExtend(getAddr(cap)) < getTop(cap);
Bool isNotTooLow = getAddr(cap) >= getBase(cap);
return isNotTooLow && isNotTooHigh;
endfunction
// Set the length of the capability. Inexact: result length may be different
// to requested
function Exact#(t) setBounds (t cap, Bit#(n) length);
let combinedResult = setBoundsCombined(cap, length);
return Exact {exact: combinedResult.exact, value: combinedResult.cap};
endfunction
function SetBoundsReturn#(t, n) setBoundsCombined (t cap, Bit#(n) length);
// Returns a null cap with an address set to the argument
function t nullWithAddr (Bit#(n) addr);
// Workaround to allow null cap to be derived in default implementations
function t nullCapFromDummy(t dummy);
// Return the maximally permissive capability (initial register state)
function t almightyCap;
// Return the null capability
function t nullCap = nullCapFromDummy(?);
// Check if a type is valid
function Bool validAsType (t dummy, Bit#(n) checkType);
// Convert from and to bit memory representation
function t fromMem (Tuple2#(Bool, Bit#(mem_sz)) mem_cap);
function Tuple2#(Bool, Bit#(mem_sz)) toMem (t cap);
// Functions that can be cheap by relying on current capability representation
// Mask the least significant bits of capability address with a mask
// maskable_width should be small enough to make this
// safe with respect to representability
function t maskAddr (t cap, Bit#(maskable_bits) mask);
// Check the alignment of the base, giving least significant 2 bits.
// This relies on the fact that internal exponents take up 2 bits of the
// base.
function Bit#(2) getBaseAlignment (t cap);
// Get representable alignment mask
function Bit#(n) getRepresentableAlignmentMask ( t dummy
, Bit#(n) length_request) =
setBoundsCombined(nullCapFromDummy(dummy), length_request).mask;
// Get representable length
function Bit#(n) getRepresentableLength (t dummy, Bit#(n) length_request) =
setBoundsCombined(nullCapFromDummy(dummy), length_request).length;
// Assert that the encoding is valid
function Bool isDerivable (t cap);
endtypeclass
function Fmt showCHERICap (t cap)
provisos (CHERICap #(t , ot, flg, n, mem_sz, maskable_bits));
return $format( "Valid: 0x%0x", isValidCap(cap)) + return $format( "Valid: 0x%0x", isValidCap(cap)) +
$format(" Perms: 0x%0x", getPerms(cap)) + $format(" Perms: 0x%0x", getPerms(cap)) +
$format(" Kind: ", fshow(getKind(cap))) + $format(" Kind: ", fshow(getKind(cap))) +
@@ -243,12 +130,200 @@ endfunction
// Cast typeclass to convert from one type to another. Helpful for converting // Cast typeclass to convert from one type to another. Helpful for converting
// a capability format to another. // a capability format to another.
typeclass Cast#(type src, type dest); typeclass Cast #(type src, type dest);
function dest cast (src x); function dest cast (src x);
endtypeclass endtypeclass
instance Cast#(t, t); instance Cast #(capT, capT);
function cast = id; function cast = id;
endinstance endinstance
// CHERI capability typeclass
////////////////////////////////////////////////////////////////////////////////
// Note: Some class methods receive a "dummy" capability as a type proxy
// argument. This is useful for methods to know which capability format is
// being operated on without requiring a specific capability value.
// (A more elegant way to achieve this would be to use something along the
// lines of haskell's "@type" type application mechanism)
typeclass CHERICap #( type capT // type of the CHERICap capability
, numeric type otypeW // width of the object type
, numeric type flgW // width of the flags field
, numeric type addrW // width of the address
, numeric type inMemW // width of the capability in mem
, numeric type maskableW // width of maskable bits
)
dependencies (capT determines (otypeW, flgW, addrW, inMemW, maskableW));
// capability validity
//////////////////////////////////////////////////////////////////////////////
// Return whether the Capability is valid
function Bool isValidCap (capT cap);
// Set the capability as valid. All fields left unchanged
function capT setValidCap (capT cap, Bool valid);
// capability flags
//////////////////////////////////////////////////////////////////////////////
// Get the flags field
function Bit #(flgW) getFlags (capT cap);
// Set the flags field
function capT setFlags (capT cap, Bit #(flgW) flags);
// capability permissions
//////////////////////////////////////////////////////////////////////////////
// Get the hardware permissions
function HardPerms getHardPerms (capT cap);
// Set the hardware permissions
function capT setHardPerms (capT cap, HardPerms hardperms);
// Get the software permissions
function SoftPerms getSoftPerms (capT cap);
// Set the software permissions
function capT setSoftPerms (capT cap, SoftPerms softperms);
// Get the architectural permissions
function Bit #(31) getPerms (capT cap) =
zeroExtend ({pack (getSoftPerms (cap)), 3'h0, pack (getHardPerms (cap))});
// Set the architectural permissions
function capT setPerms (capT cap, Bit #(31) perms) =
setSoftPerms ( setHardPerms (cap, unpack (perms[11:0]))
, unpack (truncate (perms[30:15])) );
// capability kind
//////////////////////////////////////////////////////////////////////////////
// Manipulate the kind of the capability, i.e. whether it is sealed, sentry,
// unsealed, ...
// get the kind of a capability
function Kind #(otypeW) getKind (capT cap);
// set the kind of a capability
function capT setKind (capT cap, Kind #(otypeW) kind);
// Check if a type is valid (requires a dummy proxy)
function Bool validAsType (capT dummy, Bit #(addrW) checkType);
// capability in-memory architectural representation
//////////////////////////////////////////////////////////////////////////////
// Note that the following rule is expected to hold:
// fromMem (toMem (cap)) == cap
// fromMem (tuple2 (isValidCap (cap), {getMeta (cap), getAddr (cap)})) == cap
// Get the in-memory architectural representation of the capability metadata
function Bit #(TSub #(inMemW, addrW)) getMeta (capT cap);
// Get the in-memory architectural representation of the capability address
function Bit #(addrW) getAddr (capT cap);
// Convert from in-memory architectural bit representation to capability type
function capT fromMem (Tuple2 #(Bool, Bit #(inMemW)) mem_cap);
// Convert from capability type to in-memory architectural bit representation
function Tuple2 #(Bool, Bit #(inMemW)) toMem (capT cap);
// capability address/offset manipulation
//////////////////////////////////////////////////////////////////////////////
// Set the address of the capability. Result invalid if unrepresentable
function Exact #(capT) setAddr (capT cap, Bit #(addrW) addr);
// Set the address of the capability. Result assumed to be representable
function capT setAddrUnsafe (capT cap, Bit #(addrW) addr);
// Add to the address of the capability. Result assumed to be representable
function capT addAddrUnsafe (capT cap, Bit #(maskableW) inc);
// Mask the least significant bits of capability address with a mask
// maskable_width should be small enough to make this
// safe with respect to representability
function capT maskAddr (capT cap, Bit #(maskableW) mask);
// Get the offset of the capability
function Bit #(addrW) getOffset (capT cap) = getAddr(cap) - getBase(cap);
// Modify the offset of the capability. Result invalid if unrepresentable
function Exact #(capT) modifyOffset ( capT cap
, Bit #(addrW) offset
, Bool doInc);
// Set the offset of the capability. Result invalid if unrepresentable
function Exact #(capT) setOffset (capT cap, Bit #(addrW) offset) =
modifyOffset(cap, offset, False);
// Set the offset of the capability. Result invalid if unrepresentable
function Exact #(capT) incOffset (capT cap, Bit #(addrW) inc) =
modifyOffset(cap, inc, True);
// capability architectural bounds queries
//////////////////////////////////////////////////////////////////////////////
// Note that the following rules are expected to hold:
// getBase (cap) + getLength (cap) == getTop (cap)
// getRepBase (cap) + getRepLength (cap) == getRepTop (cap)
// isInBounds (cap) ==> isInRepBounds (cap)
// Get all architectural bound information for a capability
function BoundsInfo #(addrW) getBoundsInfo (capT cap);
// Get the base
function Bit #(addrW) getBase (capT cap) = getBoundsInfo(cap).base;
// Get the top
function Bit #(TAdd #(addrW, 1)) getTop (capT cap) = getBoundsInfo(cap).top;
// Get the length
function Bit #(TAdd #(addrW, 1)) getLength (capT cap) =
getBoundsInfo(cap).length;
// Assertion that the capability's address is between its base and top
function Bool isInBounds (capT cap, Bool isTopIncluded) =
belongsToRange ( zeroExtend (getAddr (cap))
, zeroExtend (getBase (cap))
, getTop (cap)
, isTopIncluded );
// Get the representable base
function Bit #(addrW) getRepBase (capT cap) = getBoundsInfo(cap).repBase;
// Get the representable top
function Bit #(TAdd #(addrW, 1)) getRepTop (capT cap) =
getBoundsInfo(cap).repTop;
// Get the representable length
function Bit #(TAdd #(addrW, 1)) getRepLength (capT cap) =
getBoundsInfo(cap).repLength;
// Check if the capapbility's representable region is split (i.e. wrapping the
// address space)
function Bool isRepSplit (capT cap) = getBoundsInfo(cap).repSplit;
// Assertion that the capability's address is between its representable
// base and top
function Bool isInRepBounds (capT cap);
let addr = getAddr (cap);
let bInfo = getBoundsInfo (cap);
let okLo = addr >= bInfo.repBase;
let okHi = zeroExtend (addr) < bInfo.repTop;
return (okLo && okHi) || (bInfo.repSplit && (okLo != okHi));
endfunction
// Check the alignment of the base, giving least significant 2 bits.
function Bit #(2) getBaseAlignment (capT cap) = getBoundsInfo (cap).base[1:0];
// capability derivation (bounds set)
//////////////////////////////////////////////////////////////////////////////
// Set the length of the capability
function SetBoundsReturn #(capT, addrW)
setBoundsCombined (capT cap, Bit #(addrW) length);
// Set the length of the capability. Inexact: result length may be different
// to requested
function Exact #(capT) setBounds (capT cap, Bit #(addrW) length);
let combinedResult = setBoundsCombined (cap, length);
return Exact {exact: combinedResult.exact, value: combinedResult.cap};
endfunction
// Round a requested length (requires a dummy proxy)
function Bit #(addrW) roundLength (capT dummy, Bit #(addrW) reqLength) =
setBoundsCombined (nullCapFromDummy (dummy), reqLength).length;
// Get alignment mask for a requested length (requires a dummy proxy)
function Bit #(addrW) alignmentMask (capT dummy, Bit #(addrW) reqLength) =
setBoundsCombined (nullCapFromDummy (dummy), reqLength).mask;
// common capabilities
//////////////////////////////////////////////////////////////////////////////
// the null capability
function capT nullCap = nullCapFromDummy (?);
// a null capability with a given address set
function capT nullWithAddr (Bit #(addrW) addr);
// maximally permissive capability (initial register state)
function capT almightyCap;
// the null capability (requires a dummy proxy)
function capT nullCapFromDummy (capT dummy);
// Assert that the encoding is valid
//////////////////////////////////////////////////////////////////////////////
function Bool isDerivable (capT cap);
endtypeclass
endpackage endpackage