/*- * Copyright (c) 2017-2019 Alexandre Joannou * 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 CHERICC; import CHERICap :: *; export CHERICCCap; export CHERICCBounds; `define div2(x) TDiv#(x, 2) `define sub2(x) TSub#(x, 2) `define i(x) valueOf(x) // CHERICCBounds Bounds type //////////////////////////////////////////////////////////////////////////////// // CHERICC compressed bounds type typedef union tagged { struct { Bit#(1) lenMSB; Bit#(`sub2(base_)) top; Bit#(base_) base; } Exp0; struct { Bit#(TSub#(`sub2(base_), `div2(e_))) top; Bit#(TSub#(base_, `div2(e_))) base; Bit#(e_) e; } EmbeddedExp; struct { Bit#(TSub#(`sub2(base_), TAdd#(`div2(t_), `div2(e_)))) top; Bit#(TSub#(base_, TAdd#(`div2(t_), `div2(e_)))) base; Bit#(t_) otype; Bit#(e_) e; } Sealed; } CHERICCBounds#(numeric type base_, numeric type e_, numeric type t_); instance Bits#(CHERICCBounds#(b_, e_, t_), TMul#(b_, 2)) provisos( // in pack Add#(TDiv#(e_, 2), a__, e_), // truncates on e Add#(TDiv#(t_, 2), b__, t_), // truncates on t Add#(2, c__, b_), // 2 bits stolen from top // in unpack Add#(d__, TDiv#(e_, 2), TMul#(b_, 2)), // truncates raw into e Add#(e__, TDiv#(t_, 2), TMul#(b_, 2)), // truncates raw into t Add#(2, f__, TSub#( TSub#( TAdd#(b_, TAdd#( TDiv#(t_, 2), TDiv#(e_, 2))), TDiv#(e_, 2)), TDiv#(t_, 2))) ); function pack(ccbounds) = case (ccbounds) matches tagged Exp0 .x: return {{{1'b0, x.lenMSB}, x.top}, x.base}; tagged EmbeddedExp .x: begin Bit#(`div2(e_)) eHi = truncateLSB(x.e); Bit#(`div2(e_)) eLo = truncate(x.e); return {{2'b10, x.top, eHi}, {x.base, eLo}}; end tagged Sealed .x: begin Bit#(`div2(t_)) tHi = truncateLSB(x.otype); Bit#(`div2(t_)) tLo = truncate(x.otype); Bit#(`div2(e_)) eHi = truncateLSB(x.e); Bit#(`div2(e_)) eLo = truncate(x.e); return {{2'b11, x.top, tHi, eHi}, {x.base, tLo, eLo}}; /* Bit#(TMul#(b_, 2)) acc = 0; acc = acc | zeroExtend(2'b11); acc = (acc << `i(b_)-2-`i(t_)/2-`i(e_)/2) | zeroExtend(x.top); acc = (acc << `i(t_)/2) | zeroExtend(tHi); acc = (acc << `i(e_)/2) | zeroExtend(eHi); acc = (acc << `i(b_)-`i(t_)/2-`i(e_)/2) | zeroExtend(x.base); acc = (acc << `i(t_)/2) | zeroExtend(tLo); acc = (acc << `i(e_)/2) | zeroExtend(eLo); return acc; */ end endcase; function unpack(raw); if (raw[2*`i(b_)-1] == 0) return Exp0 { lenMSB: raw[2*`i(b_)-2], top: raw[2*`i(b_)-3:`i(b_)], base: raw[`i(b_)-1:0] }; else if (raw[2*`i(b_)-2] == 0) begin Bit#(`div2(e_)) eHi = truncate(raw >> `i(b_)); Bit#(`div2(e_)) eLo = truncate(raw); // XXX Bit#(e_) new_e = {eHi, eLo}; XXX simpler provisos with equiv line below Bit#(e_) new_e = zeroExtend(eLo) | zeroExtend(eHi) << `i(e_)/2; return EmbeddedExp { top: raw[2*`i(b_)-3:`i(b_)+`i(e_)/2], base: raw[`i(b_)-1:`i(e_)/2], e: new_e }; end else begin Bit#(`div2(t_)) tHi = truncate(raw >> (`i(b_)+(`i(e_)/2))); Bit#(`div2(t_)) tLo = truncate(raw >> (`i(e_)/2)); // XXX Bit#(t_) new_t = {tHi, tLo}; XXX simpler provisos with equiv line below Bit#(t_) new_t = zeroExtend(tLo) | zeroExtend(tHi) << `i(t_)/2; Bit#(`div2(e_)) eHi = truncate(raw >> `i(b_)); Bit#(`div2(e_)) eLo = truncate(raw); // XXX Bit#(e_) new_e = {eHi, eLo}; XXX simpler provisos with equiv line below Bit#(e_) new_e = zeroExtend(eLo) | zeroExtend(eHi) << `i(e_)/2; return Sealed { top: raw[2*`i(b_)-3:`i(b_)+`i(e_)/2+`i(t_)/2], base: raw[`i(b_)-1:`i(e_)/2+`i(t_)/2], otype: new_t, e: new_e }; end endfunction endinstance // CHERICC capability type //////////////////////////////////////////////////////////////////////////////// `define CCSoftPerms Bit#(4) `define AllPermsSz TAdd#(SizeOf#(`CCSoftPerms), SizeOf#(HardPerms)) typedef struct { Bool isCap; `CCSoftPerms softperms; HardPerms hardperms; Bit#(TSub#(addr_, TAdd#(bounds_, `AllPermsSz))) res; // 15 permission bits and bounds_ bits to deduct CHERICCBounds#(`div2(bounds_), e_, t_) bounds; Bit#(addr_) addr; } CHERICCCap#(numeric type addr_, numeric type bounds_, numeric type e_, numeric type t_); instance Bits#(CHERICCCap#(addr_, bounds_, e_, t_), TAdd#(1, TAdd#(addr_, TAdd#(bounds_, TAdd#(res_, `AllPermsSz))))) provisos( Bits#(CHERICCBounds#(TDiv#(bounds_, 2), e_, t_), bounds_), Add#(TAdd#(bounds_, `AllPermsSz), res_, addr_) ); function pack(cap); Bit#(1) isCap = pack(cap.isCap); Bit#(SizeOf#(`CCSoftPerms)) softperms = cap.softperms; Bit#(SizeOf#(HardPerms)) hardperms = pack(cap.hardperms); Bit#(res_) res = cap.res; Bit#(bounds_) bounds = pack(cap.bounds); Bit#(addr_) addr = cap.addr; return {isCap, softperms, hardperms, res, bounds, addr}; endfunction //function pack(cap) = {cap.softperms, pack(cap.perms), cap.res, pack(cap.bounds), cap.addr}; function unpack(raw) = CHERICCCap { isCap: unpack(msb(raw)), softperms: raw[2*`i(addr_)-1:2*`i(addr_)-`i(SizeOf#(`CCSoftPerms))], hardperms: unpack(raw[2*`i(addr_)-5:2*`i(addr_)-`i(`AllPermsSz)]), res: raw[2*`i(addr_)-`i(`AllPermsSz)-1:`i(addr_)+`i(bounds_)], bounds: unpack(raw[`i(addr_)+`i(bounds_)-1:`i(addr_)]), addr: raw[`i(addr_)-1:0] }; endinstance `undef AllPermsSz `undef CCSoftPerms // CHERICCCap inner helpers //////////////////////////////////////////////////////////////////////////////// CHERICCCap#(addr_, bounds_, e_, t_) almightyCC = CHERICCCap { isCap: True, softperms: ~0, hardperms: unpack(~0), res: 0, bounds: EmbeddedExp { top: 0, // implied top bits of 01 base: 0, // position the 1 of top in the addr_'th bit e: fromInteger(`i(addr_)-((`i(bounds_)/2)-2)) }, addr: 0 }; CHERICCCap#(addr_, bounds_, e_, t_) nullCC = CHERICCCap { isCap: False, softperms: 0, hardperms: unpack(0), res: 0, bounds: EmbeddedExp { top: 0, // implied top bits of 01 base: 0, e: fromInteger(`i(addr_)-((`i(bounds_)/2)-2)) // position the 1 of top in the addr_'th bit }, addr: 0 }; function Bit#(e_) getExpCC(CHERICCCap#(addr_, bounds_, e_, t_) cap); case (cap.bounds) matches tagged Exp0 .b: return 0; tagged EmbeddedExp .b: return b.e; tagged Sealed .b: return b.e; endcase endfunction function Bit#(3) getRepBoundCC(CHERICCCap#(addr_, bounds_, e_, t_) cap) provisos (Add#(3, a__, `div2(bounds_))) = truncateLSB(cap.bounds.Exp0.base) - 3'b001; // always 1/8th of representable space below object function Int#(2) getRegionCorrectionCC(Bit#(3) a, Bit#(3) b, Bit#(3) rep) = ((b < rep) == (a < rep)) ? 0 : (((b < rep) && (a >= rep)) ? 1 : -1); function Bit#(`div2(bounds_)) getTopFieldCC(CHERICCCap#(addr_, bounds_, e_, t_) cap); Bit#(2) c_carry = 2'b00; Bit#(2) c_len = 2'b01; Bit#(`sub2(`div2(bounds_))) partialTop = 0; case (cap.bounds) matches tagged Exp0 .b: begin if (zeroExtend(b.top) < b.base) c_carry = 2'b01; c_len = {1'b0, b.lenMSB}; partialTop = b.top; end tagged EmbeddedExp .b: begin if (zeroExtend(b.top) < b.base) c_carry = 2'b01; partialTop = {b.top, 0}; end tagged Sealed .b: begin if (zeroExtend(b.top) < b.base) c_carry = 2'b01; partialTop = {b.top, 0}; end endcase return {truncateLSB(cap.bounds.Exp0.base) + c_carry + c_len, partialTop}; endfunction function Bit#(`div2(bounds_)) getBaseFieldCC(CHERICCCap#(addr_, bounds_, e_, t_) cap) = case (cap.bounds) matches tagged Exp0 .b: b.base; tagged EmbeddedExp .b: {b.base, 0}; tagged Sealed .b: {b.base, 0}; endcase; // CHERICCCap CHERICap instance //////////////////////////////////////////////////////////////////////////////// instance CHERICap#(CHERICCCap#(addr_, bounds_, e_, t_), t_, addr_) provisos ( Add#(3, a__, `div2(bounds_)), // 3 bits of bounds for 1/8th of rep space Add#(3, b__, addr_), // same for addr Add#(c__, TAdd#(2, `div2(bounds_)), addr_), // for base correction Add#(d__, TAdd#(2, `div2(bounds_)), TAdd#(addr_, 1)), // for top 2 bits of Int#(2) correction Add#(e__, `div2(bounds_), addr_), // slice addr into smaller bounds field Add#(f__, `div2(bounds_), TAdd#(addr_, 1)), // same for addr+1 Add#(g__, e_, TLog#(TAdd#(1, addr_))) // can fit result of countZerosMSB in e_ ); ////////////////////////////////////////////////////////////////////////////// function isValidCap(cap) = cap.isCap; ////////////////////////////////////////////////////////////////////////////// function setValidCap(cap, v); cap.isCap = v; return cap; endfunction ////////////////////////////////////////////////////////////////////////////// function getHardPerms(cap) = cap.hardperms; ////////////////////////////////////////////////////////////////////////////// function setHardPerms(cap, hardperms); cap.hardperms = hardperms; return cap; endfunction ////////////////////////////////////////////////////////////////////////////// function getSoftPerms(cap) = zeroExtend(cap.softperms); ////////////////////////////////////////////////////////////////////////////// function setSoftPerms(cap, softperms); cap.softperms = truncate(softperms); return cap; endfunction ////////////////////////////////////////////////////////////////////////////// function getKind(cap) = case (cap.bounds) matches tagged Sealed ._: return SEALED_WITH_TYPE; default: return UNSEALED; endcase; ////////////////////////////////////////////////////////////////////////////// function getType(cap) = case (cap.bounds) matches tagged Sealed .b: return zeroExtend(b.otype); default: return -1; endcase; ////////////////////////////////////////////////////////////////////////////// function setType(cap, otype); let new_cap = cap; let isExact = True; case (cap.bounds) matches tagged Sealed .b: if (otype == -1) begin //Bit#(addr_) addrBits = cap.address >> b.e; //let baseMid = addrBits[`sub1(TAdd#(`div2(t_), `div2(e))):`div2(e_)]; //let baseLo = addrBits[`sub1(`div2(e_)):0]; //let topMid = baseMid; //let topLo = baseLo; let baseHi = b.base; let topHi = b.top; if (b.e == 0) new_cap.bounds = Exp0 { lenMSB: 1, top: {topHi, 0}, base: {baseHi, 0} }; else new_cap.bounds = EmbeddedExp { top: {topHi, 0}, base: {baseHi, 0}, e: b.e }; end default: if (otype != -1) begin Bit#(e_) new_e = case (cap.bounds) matches tagged EmbeddedExp .b: b.e; default: 0; endcase; new_cap.bounds = Sealed { top: truncateLSB(cap.bounds.Exp0.top), base: truncateLSB(cap.bounds.Exp0.base), otype: otype, e: new_e }; Bit#(`div2(t_)) zero = 0; isExact = cap.bounds.Exp0.top[`i(t_)/2-1:0] == zero && cap.bounds.Exp0.base[`i(t_)/2-1:0] == zero; end endcase return Exact{exact: isExact, value: new_cap}; endfunction ////////////////////////////////////////////////////////////////////////////// function getAddr(cap) = cap.addr; ////////////////////////////////////////////////////////////////////////////// function setAddr(cap) = error("setAddr unimplemented"); ////////////////////////////////////////////////////////////////////////////// function getOffset(cap) = zeroExtend(getAddr(cap)) - getBase(cap); ////////////////////////////////////////////////////////////////////////////// function setOffset(cap, offset); Bit#(`div2(bounds_)) e0m = ~(~0 << ((`i(t_)/2)+(`i(e_)/2))); Bit#(TSub#(`div2(bounds_), `div2(e_))) eem = ~(~0 << (`i(t_)/2)); // extract specific useful values Bit#(e_) e = getExpCC(cap); Bit#(e_) almighty_e = fromInteger(`i(addr_)-((`i(bounds_)/2)-2)); // position the 1 of top in the addr_'th bit Bit#(addr_) i = offset - getOffset(cap); Bit#(`div2(bounds_)) imid = truncate(i >> e); Bit#(`div2(bounds_)) amid = truncate(cap.addr >> e); Bit#(`div2(bounds_)) r = {getRepBoundCC(cap), 0}; // perform inRange and inLimit tests Bit#(addr_) mask = ~0 << (e + fromInteger(`i(bounds_)/2)); Bool inRange = ((i & mask) == mask) || ((i & mask) == 0); Bool inLimits = (i >= 0) ? imid < (r - amid - 1) : imid >= (r - amid) && r != amid; Bool isExact = ((inRange && inLimits) || e >= almighty_e); // perform the offset update let new_cap = cap; new_cap.addr = truncate(getBase(cap) + offset); return Exact{exact: isExact, value: new_cap}; endfunction ////////////////////////////////////////////////////////////////////////////// function getBase(cap); let baseCC = getBaseFieldCC(cap); let e = getExpCC(cap); let correction = getRegionCorrectionCC(truncateLSB(cap.addr), truncateLSB(baseCC), getRepBoundCC(cap)); Bit#(addr_) mask = ~0 << (e + fromInteger(`i(bounds_)/2)); Bit#(addr_) acc = cap.addr & mask; return acc + (signExtend({pack(correction), baseCC}) << e); endfunction ////////////////////////////////////////////////////////////////////////////// function getTop(cap); let topCC = getTopFieldCC(cap); let e = getExpCC(cap); let correction = getRegionCorrectionCC(truncateLSB(cap.addr), truncateLSB(topCC), getRepBoundCC(cap)); Bit#(TAdd#(addr_, 1)) mask = ~0 << (e + fromInteger(`i(bounds_)/2)); Bit#(TAdd#(addr_, 1)) acc = zeroExtend(cap.addr) & mask; return acc + (signExtend({pack(correction), topCC}) << e); endfunction ////////////////////////////////////////////////////////////////////////////// function getLength(cap) = getTop(cap) - zeroExtend(getBase(cap)); ////////////////////////////////////////////////////////////////////////////// function setBounds(cap, length); let new_cap = cap; let isExact = True; // deriving new exponent Bit#(TLog#(TAdd#(1, addr_))) e = pack(fromInteger(`i(addr_)) - countZerosMSB(length >> ((`i(bounds_)/2)-1))); // deriving the new base Bit#(`div2(bounds_)) newBase = truncate(cap.addr >> e); // deriving the new top Bit#(TAdd#(addr_, 1)) fullTop = zeroExtend(cap.addr) + zeroExtend(length); Bit#(`div2(bounds_)) newTop = truncate(fullTop >> e); // fold the derived values back in the new cap if (e == 0) begin new_cap.bounds = Exp0 { lenMSB: length[(`i(bounds_)/2)-2], top: truncate(newTop), base: newBase }; end else begin // slice the top and base values appropriately Bit#(TSub#(`sub2(`div2(bounds_)), `div2(e_))) upperTop = truncateLSB(newTop); Bit#(TSub#(`div2(bounds_), `div2(e_))) upperBase = truncateLSB(newBase); // take care of loss of significant bits in the bits stolen/dropped from fullTop Bit#(TAdd#(addr_, 1)) mask = ~(~0 << (e + fromInteger(`i(e_)/2))); if ((fullTop & mask) != 0) upperTop = upperTop + 1; new_cap.bounds = EmbeddedExp { top: upperTop, base: upperBase, e: truncate(e) }; // check for exact or not Bit#(addr_) exactMask = ~(~0 << (e - fromInteger(`i(bounds_)/2 - `i(e_)/2 - 1))); if ((cap.addr & exactMask) != 0) isExact = False; if ((length & exactMask) != 0) isExact = False; end return Exact{exact: isExact, value: new_cap}; endfunction ////////////////////////////////////////////////////////////////////////////// function nullWithAddr(x); let cap = nullCap; cap.addr = x; return cap; endfunction ////////////////////////////////////////////////////////////////////////////// function almightyCap = almightyCC; ////////////////////////////////////////////////////////////////////////////// function nullCap = nullCC; ////////////////////////////////////////////////////////////////////////////// endinstance `undef div2 `undef sub2 `undef i endpackage