diff --git a/CHERICC_Fat.bsv b/CHERICC_Fat.bsv index f8b2c08..e25f627 100644 --- a/CHERICC_Fat.bsv +++ b/CHERICC_Fat.bsv @@ -258,6 +258,98 @@ typedef MetaInfo TempFields; // Interface functions //------------------------------------------------------------------------------ +function BoundsInfo#(CapAddrW) getBoundsInfoFat (CapFat cap, TempFields tf); + // XXX TODO base top length repBase repTop repLength + // XXX DONE base top length + + // 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; + + // 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}; + + // Build a mask on the high bits of a full length value + Bit #(TSub #(CapAddrW, MW)) maskBase = ~0 << exp; + Bit #(TSub #(TAdd #(CapAddrW, 1), MW)) maskTop = ~0 << exp; + + // compute base + ////////////////////////////////////////////////////////////////////////////// + + // First, construct a full length value with the base bits and the correction + // bits above, and shift that value to the appropriate spot. + CapAddr addBase = signExtend (correctBase) << exp; + // Extract the high bits of the address (and append the implied zeros at the + // bottom), and add with the previously prepared value. + CapAddr base = {truncateLSB (cap.address) & maskBase, 0} + addBase; + + // compute top + ////////////////////////////////////////////////////////////////////////////// + + // First, construct a full length value with the top bits and the correction + // bits above, and shift that value to the appropriate spot. + CapAddrPlus1 addTop = signExtend (correctTop) << exp; + // Extract the high bits of the address (and append the implied zeros at the + // bottom), and add with the previously prepared value. + CapAddrPlus1 top = {truncateLSB ({1'b0, cap.address}) & maskTop, 0} + addTop; + // 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 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 + ////////////////////////////////////////////////////////////////////////////// + + CapAddr repBase = error ("TODO implement CapFat repBase"); + + // compute repTop + ////////////////////////////////////////////////////////////////////////////// + + CapAddrPlus1 repTop = error ("TODO implement CapFat repTop"); + + // compute repLength + ////////////////////////////////////////////////////////////////////////////// + + CapAddrPlus1 repLength = error ("TODO implement CapFat repLength"); + + // return populated BoundsInfo structure + ////////////////////////////////////////////////////////////////////////////// + + return BoundsInfo { base: base + , top: top + , length: length + , repBase: repBase + , repTop: repTop + , repLength: repLength }; +endfunction + function CapAddr getBotFat(CapFat cap, TempFields tf); // First, construct a full length value with the base bits and the // correction bits above, and shift that value to the appropriate spot. @@ -910,10 +1002,7 @@ instance CHERICap #(CapMem, OTypeW, FlagsW, CapAddrW, CapW, TSub #(MW, 3)); setAddrUnsafe(capMem, getAddr(capMem) + signExtend(inc)); function getOffset = error("getOffset not implemented for CapMem"); function modifyOffset = error("modifyOffset 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 getBoundsInfo = error("getBoundsInfo not implemented for CapMem"); function setBoundsCombined = error("setBoundsCombined not implemented for CapMem"); function nullWithAddr = setAddrUnsafe(packCap(null_cap)); function almightyCap; @@ -1044,10 +1133,7 @@ instance CHERICap #(CapReg, OTypeW, FlagsW, CapAddrW, CapW, TSub #(MW, 3)); function getOffset = error("getOffset not implemented for CapReg"); function modifyOffset = error("modifyOffset 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 getBoundsInfo = error("getBoundsInfo not implemented for CapReg"); function setBoundsCombined(cap, length) = setBoundsFat(cap, length); @@ -1182,6 +1268,8 @@ instance CHERICap #(CapPipe, OTypeW, FlagsW, CapAddrW, CapW, TSub#(MW, 3)); return Exact { exact: result.v, value: cap }; endfunction + function getBoundsInfo (cap) = getBoundsInfoFat (cap.capFat, cap.tempFields); + function getBase (cap) = getBotFat(cap.capFat, cap.tempFields); function getTop (cap) = getTopFat(cap.capFat, cap.tempFields); diff --git a/CHERICap.bsv b/CHERICap.bsv index 76632bf..a52fe5b 100644 --- a/CHERICap.bsv +++ b/CHERICap.bsv @@ -29,7 +29,17 @@ package CHERICap; -// CHERI public types +export SoftPerms; +export HardPerms; +export Kind; +export BoundsInfo; +export Exact; +export SetBoundsReturn; +export showCHERICap; +export CHERICap :: *; +export Cast; + +// CHERI capability types //////////////////////////////////////////////////////////////////////////////// // Permission bits @@ -64,15 +74,6 @@ instance Bitwise #(HardPerms); function lsb (x) = lsb(pack(x)); endinstance -// Helper type to return the result of an operation along with whether the -// operation was exact. In cases where no sensible inexact representation -// exists, the only guarantee is that the tag bit is not set. - -typedef struct { - Bool exact; - t value; -} 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". @@ -84,6 +85,29 @@ typedef union tagged { Bit #(ot) SEALED_WITH_TYPE; } Kind #(numeric type ot) deriving (Bits, Eq, FShow); +// helper type for gathering bounds information on a capability + +typedef struct { + Bit#(n) base; + Bit#(TAdd#(n, 1)) top; + Bit#(TAdd#(n, 1)) length; + Bit#(n) repBase; + Bit#(TAdd#(n, 1)) repTop; + Bit#(TAdd#(n, 1)) repLength; +} BoundsInfo #(numeric type n) deriving (Bits, Eq, FShow); + +// helper types and functions +//////////////////////////////////////////////////////////////////////////////// + +// Helper type to return the result of an operation along with whether the +// operation was exact. In cases where no sensible inexact representation +// exists, the only guarantee is that the tag bit is not set. + +typedef struct { + Bool exact; + t value; +} Exact #(type t) deriving (Bits); + // Helper type for the return value of the 'setBoundsCombined' method typedef struct { @@ -93,143 +117,15 @@ typedef struct { Bit #(n) mask; } SetBoundsReturn #(type t, numeric type n) 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 -//////////////////////////////////////////////////////////////////////////////// - -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 - +// XXX TODO augment with all architectural bounds/ repbounds ? function Fmt showCHERICap (t cap) provisos (CHERICap #(t , ot, flg, n, mem_sz, maskable_bits)); return $format( "Valid: 0x%0x", isValidCap(cap)) + @@ -251,4 +147,181 @@ instance Cast#(t, t); function cast = id; endinstance +// CHERI capability typeclass +//////////////////////////////////////////////////////////////////////////////// + +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)); + + // capability validity + ////////////////////////////////////////////////////////////////////////////// + + // 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); + + // capability flags + ////////////////////////////////////////////////////////////////////////////// + + // Get the flags field + function Bit#(flg) getFlags (t cap); + // Set the flags field + function t setFlags (t cap, Bit#(flg) flags); + + // capability permissions + ////////////////////////////////////////////////////////////////////////////// + + // 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])) ); + + // capability kind + ////////////////////////////////////////////////////////////////////////////// + // Manipulate the kind of the capability, i.e. whether it is sealed, sentry, + // unsealed, ... + + // get the kind of a capability + function Kind#(ot) getKind (t cap); + // set the kind of a capability + function t setKind (t cap, Kind#(ot) kind); + // XXX TODO Check if a type is valid + function Bool validAsType (t dummy, Bit#(n) checkType); + + // capability in-memory architectural representation + ////////////////////////////////////////////////////////////////////////////// + // Note that the following rule is expected to hold: + // fromMem (tuple2 (isValidCap (cap), {getMeta (cap), getAddr (cap)})) == cap + + // 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); + // Convert from in-memory architectural bit representation to capability type + function t fromMem (Tuple2#(Bool, Bit#(mem_sz)) mem_cap); + // Convert from capability type to in-memory architectural bit representation + function Tuple2#(Bool, Bit#(mem_sz)) toMem (t cap); + + // capability address/offset manipulation + ////////////////////////////////////////////////////////////////////////////// + + // 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); + // 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); + // 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); + + // capability architectural bounds queries + ////////////////////////////////////////////////////////////////////////////// + // Note that the following rules are expected to hold: + // getBase (cap) + getLength (cap) == getLength (cap) + // getRepBase (cap) + getRepLength (cap) == getRepLength (cap) + // isInRepBounds (cap) ==> isInRepBounds (cap) + + // Get all architectural bound information for a capability + function BoundsInfo#(n) getBoundsInfo (t cap); + // Get the base + function Bit#(n) getBase (t cap) = getBoundsInfo(cap).base; + // Get the top + function Bit#(TAdd#(n, 1)) getTop (t cap) = getBoundsInfo(cap).top; + // Get the length + function Bit#(TAdd#(n, 1)) getLength (t cap) = getBoundsInfo(cap).length; + // Assertion that the capability's address is between its base and top + function Bool isInBounds (t cap, Bool isTopIncluded) = + belongsToRange ( zeroExtend (getAddr (cap)) + , zeroExtend (getBase (cap)) + , getTop (cap) + , isTopIncluded ); + // Get the representable base + function Bit#(n) getRepBase (t cap) = getBoundsInfo(cap).repBase; + // Get the representable top + function Bit#(TAdd#(n, 1)) getRepTop (t cap) = getBoundsInfo(cap).repTop; + // Get the representable length + function Bit#(TAdd#(n, 1)) getRepLength (t cap) = + getBoundsInfo(cap).repLength; + // Assertion that the capability's address is between its representable + // base and top + function Bool isInRepBounds (t cap, Bool isRepTopIncluded) = + belongsToRange ( zeroExtend (getAddr (cap)) + , zeroExtend (getRepBase (cap)) + , getRepTop (cap) + , isRepTopIncluded ); + + // XXX TODO 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); + + // XXX TODO Get representable alignment mask + function Bit#(n) getRepresentableAlignmentMask ( t dummy + , Bit#(n) length_request) = + setBoundsCombined(nullCapFromDummy(dummy), length_request).mask; + + // XXX TODO Get representable length + function Bit#(n) getRepresentableLength (t dummy, Bit#(n) length_request) = + setBoundsCombined(nullCapFromDummy(dummy), length_request).length; + + // capability derivation (bounds set) + ////////////////////////////////////////////////////////////////////////////// + + // 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 + // XXX TODO + function SetBoundsReturn#(t, n) setBoundsCombined (t cap, Bit#(n) length); + + // common capabilities + ////////////////////////////////////////////////////////////////////////////// + + // the null capability + function t nullCap = nullCapFromDummy(?); + // a null capability with a given address set + function t nullWithAddr (Bit#(n) addr); + // maximally permissive capability (initial register state) + function t almightyCap; + // XXX TODO Workaround to allow null cap to be derived in default + // implementations + function t nullCapFromDummy(t dummy); + + // Assert that the encoding is valid + ////////////////////////////////////////////////////////////////////////////// + function Bool isDerivable (t cap); + +endtypeclass + endpackage