CapCop.bsv

Reference 1

CapCop.bsv

cheri/trunk/CapCop.bsv

Overview

Interface: CapCopIfc

Module: mkCapCop#(Bit#(16) coreId) (CapCopIfc)

Functions: getBase(cap), getLength(cap), getOffset(cap), getType(cap), getSealed(cap), checkRegAccess(Perms, CapReg), privileged(Perms), getPerms(CapFat).

mkCapCop

States:

  • Reg#(Capability) pcc <- mkConfigReg(defaultCap);
  • FIFOF#((BufferedPCC)) pccUpdate <- mkUGFIFOF1();
  • FIFO#(CapControlToken) inQ
  • FIFO#(CapControlToken) dec2exeQ
  • FIFO#(CapControlToken) exe2memQ
  • FIFO#(CapControlToken) mem2wbkQ
  • FIFOF#(ExceptionEvent) exception
  • Reg#(CapCause) causeReg
  • FIFOF#(CapCause) causeUpdate
  • FIFO#(LenCheck) lenChecks
  • FIFO#(CapCause) lenCause
  • Reg#(Bool) capBranchDelay
  • Reg#(CapState) capState
  • Reg#(UInt#(5)) count

Rules:

  • initialize: regFile.writeRaw for 32 times and set capState from Init to Ready.
  • doException. Check exception, an FIFOF#(ExceptionEvent)
  • inQtoBuffer

Methods:

  • getArchPc(…)
  • putCapInst(capInst)
  • getCapResponse(…)
  • getAddress()
  • commitWriteback()

getAddress()

Overview: do length checks for the first lenCheck in the queue of lenChecks. If success, response{.valid:True, }

input:

  • FIFO(LenCheck) lenChecks, will consume one element from it;
  • FIFO(CapCause) lenCause, will add one element to it;

output:

  • ActionValue#(CoProResponse): return response;
    • exception = None if check passes
  • lenCause.enq(cause)
    • cause.exp = None if check passes

Steps:

  • pass along one control token ct from exe2memQ to mem2wbkQ.
  • create one CapCause cause, update the value cause.exp if length check fails; put it in queue lenCause.
  • grab one lenCheck from lenChecks, and update the value if certain checks fail.

getCapResponse(CapReq capReq, ExecuteType opType)

condition

  • capState == Ready
  • ! (pccUpdate.notEmpty && dec2exeQ.first.jump): not if there is an outstanding jump and this is a jump
  • ! causeUpdate.notFull

input

  • CapReq capReq,
  • ExecuteType opType, can be Memory, Branch, Arithmetic

output

  • ActionValue#(CoProResponse).

globals:

  • pcc:
  • forwardedPCC: the pcc, or the pccUpdate.first.pcc;

Steps:

  1. check opType: Arithmetic, Branch, Memory
  2. if Arithmetic: the operation (capInst.op) can be Move, SetOffset, IncBaseNull, IncOffset, …

    • Move; SetOffset, IncBaseNull, IncOffset; SetBounds, SetBoundsExact; AndPerm; SetConfig, GetConfig; ClearTag, GetTag; GetLen, GetBase, GetOffset, GetType; GetPCC, SetPCCOffset; ReportRegs;
    • GetPerm; GetSealed; Seal; Unseal;
    • Unseal: Unseal capB with sealing capability capA: check capB.sealed bit; writeback.otype = 0 after unseal.
    • GetRelBase; Subtract;
    • CheckPerms; CheckType;
    • CheckType: check capA.otype == capB.otype; if not match, set cause=CapCause{exp:Type, pcc: False, capReg: regB} as exception.
    • CmpEQ, CmpNE, CmpLT, CmpLE, CmpLTU, CmpLEU; CmpEQX;
    • Clear;
    • Call; Return;
    • Call: capA and capB must be sealed; check capA.otype == capB.otype; exception if not matched; check capA is code(permit_execute) and capB is data (!permit_execute); finally throw call exception;
    • Return: throw return exception.
  3. if Branch: operation (capInst.op) can be:

    • BranchTagSet; BranchTagUnset;
    • BranchEqZero; BranchNEqZero;
    • JALR; JR;
    • JALR: capA(code) is not sealed; update lenCheck; newPcc=capA; response.data=capA.offset; writeback=forwardedPCC; writeback.offset=pack(aCapReq.offset) // writeback contains link Cap Reg content.
    • JR: capA(code) is not sealed; update lenCheck; response.data=capA.offset; newPcc = capA;
    • CallFast;
    • capA(code) and capB(code) must be sealed;
    • unseal capA,capB;
    • newPcc=capA;
    • unseal capA, capB;
    • writeback=capB; response.data=capA.offset;
    • ERET; JumpRegister;
    • None; default
  4. if Memory: according to diferent operations (capInst.op), check capabilities, might set the lenCheck.valid, lenCheck.memSize, aCapReq.memOp, etc :

    • L, LegacyL, S, LegacyS; –> checking cause.exp, capA.perms.hard.permit_load/store, capA.sealed; if good, lenCheck.valid=True;
    • LC, load cap reg; –> check cause/capA.perms/capA.sealed; if good, set lenCheck.valid=True; lenCheck.memsize=CapWord; aCapReq.memOp=Read;
    • SC, store cap reg, –> check cause/CapA.perms.hard.permit_store_cap/permit_store_ephemeral_cap/sealed/CapB.perms.hard.non_ephemeral; if good, lenCheck.valid=True; lenCheck.memSize=CapWord; aCapReq.memOp=Write; response.storeData=tagged CapLine truncate(pack(capB)); writeback=capB;
    • None; default
  5. testPc=capReq.pc

  6. if jmp: set branch delay flag; testPc=capReq.pc+4; pccUpdate.enq(BufferedPCC{pcc: newPccc, epoch: ct.epoch})

  7. if not jmp: check testPc is in range of forwardedPCC.[base,length]

  8. exe2memQ.enq(ctOut) // after this,

  9. lenChecks.enq(lenCheck) // after this,

  10. check branch delay

getAddress()

  • input:
    • lenChecks. global variable.
    • exe2memQ.
    • mem2wbkQ.
  • output
    • ActionValue#(CoProResponse)

Steps:

  1. lenCheck = lenChecks.deq()
  2. CapControlToken ct <- toGet(exe2memQ).get()
  3. mem2wbkQ.enq(ct)

commitWriteback(CapWritebackRequest wbReq)

  • condition/guard
    • capState == Ready
    • !exception.notEmpty()
  • input
    • CapWriteBackRequest wbReq
    • CapControlToken ct (from mem2wbkQ)
    • CapCause lenCheckCause (from lenCause)
  • output
    • regFile:
    • regFile.writeReg(ct.writeCap, commit)
    • regFile.clearRegs(ct.newRegMask)
    • regFile.readRawPut(fetch)
    • return ct.writeCap

Steps:

  • parse information in wbReq and control token, and update the regFile accordingly

    // cheri/trunk/CapCop.bsv
    
    method ActionValue#(CapFat) commitWriteback(CapWritebackRequest wbReq) if (capState == Ready/* && !exception.notEmpty()*/);
    //CapCause fetchCheckCause = fetchCause.first;
    //fetchCause.deq;
    CapCause lenCheckCause <- toGet(lenCause).get();
    CapControlToken ct     <- toGet(mem2wbkQ).get();
    Bool commit = (!wbReq.dead && wbReq.mipsExp == None);
    Capability newPcc = pcc;
    if (ct.jump && commit) begin
      newPcc = pccUpdate.first.pcc;
      Capability dc = pccUpdate.first.pcc;
      trace($display("Time:%0d, Core:%0d, Thread:0 :: PCC <- tag:%d s:%d perms:0x%x type:0x%x offset:0x%x base:0x%x length:0x%x", $time, coreId, dc.isCapability, dc.sealed, dc.perms, dc.otype, dc.offset-dc.base, dc.base, getLength(dc)));
    end// else pcc.offset <= wbReq.pc;
    if (ct.jump && pccUpdate.notEmpty) pccUpdate.deq;
    if (ct.capInst.op==SetConfig && causeUpdate.notEmpty) causeUpdate.deq;
    if (ct.doWrite && commit) begin
      if (ct.capInst.op == LC) ct.writeCap = wbReq.memResponse;
      `ifdef BLUESIM
        debugCaps[ct.writeReg] <= ct.writeCap;
      `endif
      Capability dc = ct.writeCap;
      trace($display("Time:%0d, Core:%0d, Thread:0 :: CapReg %d <- tag:%d s:%d perms:0x%x type:0x%x offset:0x%x base:0x%x length:0x%x", $time, coreId, ct.writeReg, dc.isCapability, dc.sealed, dc.perms, dc.otype, dc.offset-dc.base, dc.base, getLength(dc)));
    end
    regFile.writeReg(ct.writeCap, commit);
    if (!wbReq.dead && (wbReq.mipsExp==CAP  ||wbReq.mipsExp==CAPCALL||
                        wbReq.mipsExp==CTLBS||wbReq.mipsExp==ICAP)) begin
      // Length exception has priority over Call exception.
      if (ct.cause.exp==None || ct.cause.exp==Call) begin
        if (lenCheckCause.exp != None) ct.cause = lenCheckCause;
      end
      if (wbReq.mipsExp==CTLBS) ct.cause.exp = Ctlbs;
      if (wbReq.mipsExp==ICAP) ct.cause = CapCause{exp: Len, pcc: True, capReg: ?};
      causeReg <= ct.cause;
      trace($display("Time:%0d, Core:%0d, Thread:0 :: Exception CapCause <- CapExpCode: 0x%x CauseReg: %d", $time, coreId, ct.cause.exp, ct.cause.capReg));
    end else if (ct.capInst.op == SetConfig && commit) begin
      causeReg <= ct.cause;
      trace($display("Time:%0d, Core:%0d, Thread:0 :: SetConfig CapCause <- CapExpCode: 0x%x CauseReg: %d", $time, coreId, ct.cause.exp, ct.cause.capReg));
    end
    if (commit && ct.writeRegMask) regFile.clearRegs(ct.newRegMask);
    if (!wbReq.dead) begin
      ExceptionEvent ee = None;
      if (wbReq.mipsExp!=None) ee = Except;
      else if (ct.capInst.op==ERET) ee = Return;
      if (ee != None) begin
        newPcc.offset = wbReq.pc;
        // Request KCC (register 29) from the register file to be placed in PCC
        // Or request EPCC (register 31) from the register file to be returned to PCC
        CapReg fetch = (ee==Except) ? 29:31;
        regFile.readRawPut(fetch);
        exception.enq(ee);
      end
    end
    `ifdef BLUESIM
      if (reportCapRegs.notEmpty) begin
        debugInst($display("======   RegFile   ======"));
        debugInst($display("DEBUG CAP COREID %d", coreId));
        debugInst($display("DEBUG CAP PCC t:%d s:%d perms:0x%x type:0x%x offset:0x%x base:0x%x length:0x%x", pcc.isCapability, pcc.sealed, pcc.perms, pcc.otype, pcc.offset-pcc.base, pcc.base, getLength(pcc)));
        for (Integer i = 0; i<32; i=i+1) begin
          Capability dc = debugCaps[i];
          debugInst($display("DEBUG CAP REG %d t:%d s:%d perms:0x%x type:0x%x offset:0x%x base:0x%x length:0x%x", i, dc.isCapability, 
          dc.sealed, dc.perms, dc.otype, dc.offset-dc.base, dc.base, getLength(dc)));
        end
        debugInst(reportCapRegs.deq());
      end
    `endif
    if (!exception.notEmpty()) pcc <= newPcc;
    debug2("cap", $display("CapCop Writeback, instID:%d==capWBTags.id:%d, capWBTags.valid:%d, capWB.first.instID:%d", wbReq.instId, ct.instId, ct.doWrite, ct.instId));
    return ct.writeCap;
    endmethod

  1. reference ↩
Created Apr 4, 2020 // Last Updated May 18, 2021

If you could revise
the fundmental principles of
computer system design
to improve security...

... what would you change?