335 lines
14 KiB
Plaintext
335 lines
14 KiB
Plaintext
/*-
|
|
* Copyright (c) 2018-2021 Alexandre Joannou
|
|
* Copyright (c) 2019 Peter Rugg
|
|
* All rights reserved.
|
|
*
|
|
* This software was developed by SRI International and the University of
|
|
* Cambridge Computer Laboratory (Department of Computer Science and
|
|
* Technology) under DARPA contract HR0011-18-C-0016 ("ECATS"), as part of the
|
|
* DARPA SSITH research programme.
|
|
*
|
|
* @BERI_LICENSE_HEADER_START@
|
|
*
|
|
* Licensed to BERI Open Systems C.I.C. (BERI) under one or more contributor
|
|
* license agreements. See the NOTICE file distributed with this work for
|
|
* additional information regarding copyright ownership. BERI licenses this
|
|
* file to you under the BERI Hardware-Software License, Version 1.0 (the
|
|
* "License"); you may not use this file except in compliance with the
|
|
* License. You may obtain a copy of the License at:
|
|
*
|
|
* http://www.beri-open-systems.org/legal/license-1-0.txt
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, Work distributed
|
|
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
|
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
|
* specific language governing permissions and limitations under the License.
|
|
*
|
|
* @BERI_LICENSE_HEADER_END@
|
|
*/
|
|
|
|
package CHERICap;
|
|
|
|
// CHERI capability types
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Permission bits
|
|
|
|
typedef Bit #(16) SoftPerms;
|
|
|
|
typedef struct {
|
|
Bool permitSetCID;
|
|
Bool accessSysRegs;
|
|
Bool permitUnseal;
|
|
Bool permitCCall;
|
|
Bool permitSeal;
|
|
Bool permitStoreLocalCap;
|
|
Bool permitStoreCap;
|
|
Bool permitLoadCap;
|
|
Bool permitStore;
|
|
Bool permitLoad;
|
|
Bool permitExecute;
|
|
Bool global;
|
|
} HardPerms deriving(Bits, Eq, FShow);
|
|
|
|
instance Bitwise #(HardPerms);
|
|
function \& (x1, x2) = unpack(pack(x1) & pack(x2));
|
|
function \| (x1, x2) = unpack(pack(x1) | pack(x2));
|
|
function \^ (x1, x2) = unpack(pack(x1) ^ pack(x2));
|
|
function \~^ (x1, x2) = unpack(pack(x1) ~^ pack(x2));
|
|
function \^~ (x1, x2) = unpack(pack(x1) ^~ pack(x2));
|
|
function invert (x) = unpack(invert (pack(x))); //XXX Bluespec ref guide uses x1 here but simply x for other single arg methods...
|
|
function \<< (x1, x2) = unpack(pack(x1) << x2);
|
|
function \>> (x1, x2) = unpack(pack(x1) >> x2);
|
|
function msb (x) = msb(pack(x));
|
|
function lsb (x) = lsb(pack(x));
|
|
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 #(addrW) 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
|
|
// 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 {
|
|
capT cap;
|
|
Bool exact;
|
|
Bool inBounds;
|
|
Bit #(addrW) length;
|
|
Bit #(addrW) mask;
|
|
} 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
|
|
|
|
// XXX TODO augment with all architectural bounds/ repbounds ?
|
|
function Fmt showCHERICap (capT cap)
|
|
provisos (CHERICap #(capT , otypeW, flgW, addrW, inMemW, maskableW, delta));
|
|
return $format( "Valid: 0x%0x", isValidCap(cap)) +
|
|
$format(" Perms: 0x%0x", getPerms(cap)) +
|
|
$format(" Kind: ", fshow(getKind(cap))) +
|
|
$format(" Addr: 0x%0x", getAddr(cap)) +
|
|
$format(" Base: 0x%0x", getBase(cap)) +
|
|
$format(" Length: 0x%0x", getLength(cap));
|
|
endfunction
|
|
|
|
// Cast typeclass to convert from one type to another. Helpful for converting
|
|
// a capability format to another.
|
|
|
|
typeclass Cast #(type src, type dest);
|
|
function dest cast (src x);
|
|
endtypeclass
|
|
|
|
instance Cast #(capT, capT);
|
|
function cast = id;
|
|
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
|
|
, numeric type delta // size of delta (tlb bypass)
|
|
)
|
|
dependencies (capT determines (otypeW, flgW, addrW, inMemW, maskableW, delta));
|
|
|
|
// 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 #(addrW) 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);
|
|
|
|
// define set delta function
|
|
function capT setDeltaValue (capT cap, Bit #(delta) length);
|
|
|
|
// Assert that the encoding is valid
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
function Bool isDerivable (capT cap);
|
|
|
|
endtypeclass
|
|
|
|
endpackage
|