Mock up of the getBoundsInfo API update

This commit is contained in:
gameboo
2021-09-23 19:23:28 +01:00
parent 806b687e8a
commit f1837b9fb6
2 changed files with 315 additions and 154 deletions

View File

@@ -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);

View File

@@ -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