diff --git a/src/hypervisor.adoc b/src/hypervisor.adoc index bafdf1ac2..74e9604a7 100644 --- a/src/hypervisor.adoc +++ b/src/hypervisor.adoc @@ -580,7 +580,24 @@ mode V=1. [[henvcfg]] .Hypervisor environment configuration register (`henvcfg`). -include::images/bytefield/henvcfg.edn[] +[wavedrom, ,svg] +.... +{reg: [ + {bits: 1, name: 'FIOM'}, + {bits: 1, name: 'WPRI'}, + {bits: 1, name: 'LPE'}, + {bits: 1, name: 'SSE'}, + {bits: 2, name: 'CBIE'}, + {bits: 1, name: 'CBCFE'}, + {bits: 1, name: 'CBZE'}, + {bits: 24, name: 'WPRI'}, + {bits: 2, name: 'PMM'}, + {bits: 27, name: 'WPRI'}, + {bits: 1, name: 'ADUE'}, + {bits: 1, name: 'PBMTE'}, + {bits: 1, name: 'STCE'}, +], config:{lanes: 4, hspace:1024}} +.... If bit FIOM (Fence of I/O implies Memory) is set to one in `henvcfg`, FENCE instructions executed when V=1 are modified so the requirement to @@ -638,6 +655,26 @@ The definition of the PMM field will be furnished by the forthcoming Ssnpm extension. Its allocation within `henvcfg` may change prior to the ratification of that extension. +The Zicfilp extension adds the `LPE` field in `henvcfg`. When the `LPE` field +is set to 1, the Zicfilp extension is enabled in VS-mode. When the `LPE` field +is 0, the Zicfilp extension is not enabled in VS-mode and the following rules +apply to VS-mode: + +* The hart does not update the `ELP` state; it remains as `NO_LP_EXPECTED`. +* The `LPAD` instruction operates as a no-op. + +The Zicfiss extension adds the `SSE` field in `henvcfg`. If the `SSE` field is +set to 1, the Zicfiss extension is activated in VS-mode. When the `SSE` field is +0, the Zicfiss extension remains inactive in VS-mode, and the following rules +apply when `V=1`: + +* 32-bit Zicfiss instructions will revert to their behavior as defined by Zimop. +* 16-bit Zicfiss instructions will revert to their behavior as defined by Zcmop. +* The `pte.xwr=010b` encoding in VS-stage page tables becomes reserved. +* The `senvcfg.SSE` field will read as zero and is read-only. +* When `menvcfg.SSE` is one, `SSAMOSWAP.W/D` raises a virtual instruction + exception. + When XLEN=32, `henvcfgh` is a 32-bit read/write register that aliases bits 63:32 of `henvcfg`. Register `henvcfgh` does not exist when @@ -894,11 +931,57 @@ normally read or modify `sstatus` actually access `vsstatus` instead. [[vsstatusreg-rv32]] .Virtual supervisor status (`vstatus`) register when VSXLEN=32. -include::images/bytefield/vsstatusreg-rv32.edn[] +[wavedrom, ,svg] +.... +{reg: [ + {bits: 1, name: 'WPRI'}, + {bits: 1, name: 'SIE'}, + {bits: 3, name: 'WPRI'}, + {bits: 1, name: 'SPIE'}, + {bits: 1, name: 'UBE'}, + {bits: 1, name: 'WPRI'}, + {bits: 1, name: 'SPP'}, + {bits: 2, name: 'VS[1:0]'}, + {bits: 2, name: 'WPRI'}, + {bits: 2, name: 'FS[1:0]'}, + {bits: 2, name: 'XS[1:0]'}, + {bits: 1, name: 'WPRI'}, + {bits: 1, name: 'SUM'}, + {bits: 1, name: 'MXR'}, + {bits: 3, name: 'WPRI'}, + {bits: 1, name: 'SPELP'}, + {bits: 7, name: 'WPRI'}, + {bits: 1, name: 'SD'}, +], config:{lanes: 2, hspace:1024}} +.... [[vsstatusreg]] .Virtual supervisor status (`vsstatus`) register when VSXLEN=64. -include::images/bytefield/vsstatusreg.edn[] +[wavedrom, ,svg] +.... +{reg: [ + {bits: 1, name: 'WPRI'}, + {bits: 1, name: 'SIE'}, + {bits: 3, name: 'WPRI'}, + {bits: 1, name: 'SPIE'}, + {bits: 1, name: 'UBE'}, + {bits: 1, name: 'WPRI'}, + {bits: 1, name: 'SPP'}, + {bits: 2, name: 'VS[1:0]'}, + {bits: 2, name: 'WPRI'}, + {bits: 2, name: 'FS[1:0]'}, + {bits: 2, name: 'XS[1:0]'}, + {bits: 1, name: 'WPRI'}, + {bits: 1, name: 'SUM'}, + {bits: 1, name: 'MXR'}, + {bits: 3, name: 'WPRI'}, + {bits: 1, name: 'SPELP'}, + {bits: 8, name: 'WPRI'}, + {bits: 2, name: 'UXL[1:0]'}, + {bits: 29, name: 'WPRI'}, + {bits: 1, name: 'SD'}, +], config:{lanes: 4, hspace:1024}} +.... The UXL field controls the effective XLEN for VU-mode, which may differ from the XLEN for VS-mode (VSXLEN). When VSXLEN=32, the UXL field does @@ -945,6 +1028,13 @@ machine, unless a virtual-machine load/store (HLV, HLVX, or HSV) or the MPRV feature in the `mstatus` register is used to execute a load or store _as though_ V=1. +The Zicfilp extension adds the `SPELP` field that holds the previous `ELP`, and +is updated as specified in <>. The `SPELP` field is +encoded as follows: + +* 0 - `NO_LP_EXPECTED` - no landing pad instruction expected. +* 1 - `LP_EXPECTED` - a landing pad instruction is expected. + ==== Virtual Supervisor Interrupt (`vsip` and `vsie`) Registers The `vsip` and `vsie` registers are VSXLEN-bit read/write registers that diff --git a/src/images/bytefield/henvcfg.edn b/src/images/bytefield/henvcfg.edn deleted file mode 100644 index 4c03b507f..000000000 --- a/src/images/bytefield/henvcfg.edn +++ /dev/null @@ -1,50 +0,0 @@ -[bytefield] ----- -(defattrs :plain [:plain {:font-family "M+ 1p Fallback" :font-size 24}]) -(def row-height 40 ) -(def row-header-fn nil) -(def left-margin 30) -(def right-margin 30) -(def boxes-per-row 33) - -(draw-box "63" {:span 2 :borders {}}) -(draw-box "62" {:span 2 :borders {}}) -(draw-box "61" {:span 2 :borders {}}) -(draw-box "60" {:span 3 :text-anchor "start" :borders {}}) -(draw-box "34" {:span 4 :text-anchor "end" :borders {}}) -(draw-box "33" {:span 2 :text-anchor "start" :borders {}}) -(draw-box "32" {:span 1 :text-anchor "end" :borders {}}) -(draw-box "31" {:span 4 :text-anchor "start" :borders {}}) -(draw-box "8" {:span 3 :text-anchor "end" :borders {}}) -(draw-box "7" {:span 2 :borders {}}) -(draw-box "6" {:span 2 :borders {}}) -(draw-box "5" {:borders {}}) -(draw-box "4" {:borders {}}) -(draw-box "3" {:borders {}}) -(draw-box "1" {:borders {}}) -(draw-box "0" {:span 2 :borders {}}) - -(draw-box "STCE" {:span 2}) -(draw-box "PBMTE" {:font-size 22 :span 2}) -(draw-box "ADUE" {:span 2}) -(draw-box (text "WPRI" {:font-size 24 :font-weight "bold"}) {:span 7}) -(draw-box "PMM" {:span 3}) -(draw-box (text "WPRI" {:font-size 24 :font-weight "bold"}) {:span 7}) -(draw-box "CBZE" {:span 2}) -(draw-box "CBCFE" {:span 2}) -(draw-box "CBIE" {:span 2}) -(draw-box (text "WPRI" {:font-size 24 :font-weight "bold"}) {:span 2}) -(draw-box "FIOM" {:span 2}) - -(draw-box "1" {:span 2 :borders {}}) -(draw-box "1" {:span 2 :borders {}}) -(draw-box "1" {:span 2 :borders {}}) -(draw-box "27" {:span 7 :borders {}}) -(draw-box "2" {:span 3 :borders {}}) -(draw-box "24" {:span 7 :borders {}}) -(draw-box "1" {:span 2 :borders {}}) -(draw-box "1" {:span 2 :borders {}}) -(draw-box "2" {:span 2 :borders {}}) -(draw-box "3" {:span 2 :borders {}}) -(draw-box "1" {:span 2 :borders {}}) ----- diff --git a/src/images/bytefield/menvcfgreg.adoc b/src/images/bytefield/menvcfgreg.adoc deleted file mode 100644 index 62f1d6dd8..000000000 --- a/src/images/bytefield/menvcfgreg.adoc +++ /dev/null @@ -1,34 +0,0 @@ -[bytefield] ----- -(defattrs :plain [:plain {:font-family "M+ 1p Fallback"}]) -(def row-height 45) -(def row-header-fn nil) -(def boxes-per-row 34) -(draw-column-headers {:height 20 :font-size 18 :labels (reverse ["0" "" "1" "3" "4" "5" "6" "" "7" "" "8" "" "" "" "" "" "31" "32" "" "33" "34" "" "" "" "" "59" "" "60" "" "61" "" "62" "" "63"])}) - -(draw-box "STCE" {:span 2}) -(draw-box "PBMTE" {:span 2}) -(draw-box "ADUE" {:span 2}) -(draw-box "CDE" {:span 2}) -(draw-box (text "WPRI" {:font-weight "bold"}) {:span 7}) -(draw-box "PMM" {:span 3}) -(draw-box (text "WPRI" {:font-weight "bold"}) {:span 6}) -(draw-box "CBZE" {:span 2}) -(draw-box "CBCFE" {:span 2}) -(draw-box "CBIE" {:span 2}) -(draw-box "WPRI" {:span 2}) -(draw-box "FIOM" {:span 2}) - -(draw-box "1" {:span 2 :borders {}}) -(draw-box "1" {:span 2 :borders {}}) -(draw-box "1" {:span 2 :borders {}}) -(draw-box "1" {:span 2 :borders {}}) -(draw-box "26" {:span 7 :borders {}}) -(draw-box "2" {:span 3 :borders {}}) -(draw-box "24" {:span 6 :borders {}}) -(draw-box "1" {:span 2 :borders {}}) -(draw-box "1" {:span 2 :borders {}}) -(draw-box "2" {:span 2 :borders {}}) -(draw-box "3" {:span 2 :borders {}}) -(draw-box "1" {:span 2 :borders {}}) ----- diff --git a/src/images/bytefield/mstatushreg.adoc b/src/images/bytefield/mstatushreg.adoc deleted file mode 100644 index facdaab12..000000000 --- a/src/images/bytefield/mstatushreg.adoc +++ /dev/null @@ -1,20 +0,0 @@ -[bytefield] ----- -(defattrs :plain [:plain {:font-size 14 :font-family "M+ 1p Fallback" :vertical-align "middle"}]) -(def row-height 45 ) -(def row-header-fn nil) -(def left-margin 0) -(def right-margin 0) -(def boxes-per-row 32) -(draw-column-headers {:height 20 :font-size 18 :labels (reverse ["0" "" "" "3" "4" "5" "6" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "31"])}) - -(draw-box (text "WPRI" {:font-weight "bold"}) {:span 26}) -(draw-box "MBE" {:span 1}) -(draw-box "SBE" {:span 1}) -(draw-box (text "WPRI" {:font-weight "bold"}) {:span 4}) - -(draw-box "26" {:span 26 :borders {}}) -(draw-box "1" {:span 1 :borders {}}) -(draw-box "1" {:span 1 :borders {}}) -(draw-box "4" {:span 4 :borders {}}) ----- \ No newline at end of file diff --git a/src/images/bytefield/mstatusreg-rv32.adoc b/src/images/bytefield/mstatusreg-rv32.adoc deleted file mode 100644 index dde9484c2..000000000 --- a/src/images/bytefield/mstatusreg-rv32.adoc +++ /dev/null @@ -1,54 +0,0 @@ -[bytefield] ----- -(defattrs :plain [:plain {:font-size 14 :font-family "M+ 1p Fallback" :vertical-align "middle"}]) -(def row-height 45 ) -(def row-header-fn nil) -(def left-margin 0) -(def right-margin 0) -(def boxes-per-row 32) -(draw-column-headers {:height 20 :font-size 18 :labels (reverse ["0" "1" "2" "3" "4" "5" "6" "7" "8" "9" "10" "11" "12" "13" "14" "15" "16" "17" "18" "19" "20" "21" "22" "23" "" "" "" "" "" "" "30" "31"])}) - -(draw-box "SD" {:span 1}) -(draw-box (text "WPRI" {:font-weight "bold"}) {:span 8}) -(draw-box "TSR" {:span 1}) -(draw-box "TW" {:span 1}) -(draw-box "TVM" {:span 1}) -(draw-box "MXR" {:span 1}) -(draw-box "SUM" {:span 1}) -(draw-box "MPRV" {:span 1}) -(draw-box "XS[1:0]" {:span 2}) -(draw-box "FS[1:0]" {:span 2}) -(draw-box "MPP[1:0]" {:span 2}) -(draw-box "VS[1:0]" {:span 2}) -(draw-box "SPP" {:span 1}) -(draw-box "MPIE" {:span 1}) -(draw-box "UBE" {:span 1}) -(draw-box "SPIE" {:span 1}) -(draw-box (text "WPRI" {:font-weight "bold"}) {:span 1}) -(draw-box "MIE" {:span 1}) -(draw-box (text "WPRI" {:font-weight "bold"}) {:span 1}) -(draw-box "SIE" {:span 1}) -(draw-box (text "WPRI" {:font-weight "bold"}) {:span 1}) - -(draw-box "1" {:span 1 :borders {}}) -(draw-box "8" {:span 8 :borders {}}) -(draw-box "1" {:span 1 :borders {}}) -(draw-box "1" {:span 1 :borders {}}) -(draw-box "1" {:span 1 :borders {}}) -(draw-box "1" {:span 1 :borders {}}) -(draw-box "1" {:span 1 :borders {}}) -(draw-box "1" {:span 1 :borders {}}) -(draw-box "2" {:span 2 :borders {}}) -(draw-box "2" {:span 2 :borders {}}) -(draw-box "2" {:span 2 :borders {}}) -(draw-box "2" {:span 2 :borders {}}) -(draw-box "1" {:span 1 :borders {}}) -(draw-box "1" {:span 1 :borders {}}) -(draw-box "1" {:span 1 :borders {}}) -(draw-box "1" {:span 1 :borders {}}) -(draw-box "1" {:span 1 :borders {}}) -(draw-box "1" {:span 1 :borders {}}) -(draw-box "1" {:span 1 :borders {}}) -(draw-box "1" {:span 1 :borders {}}) -(draw-box "1" {:span 1 :borders {}}) ----- \ No newline at end of file diff --git a/src/images/bytefield/mstatusreg.adoc b/src/images/bytefield/mstatusreg.adoc deleted file mode 100644 index 46ca3ab89..000000000 --- a/src/images/bytefield/mstatusreg.adoc +++ /dev/null @@ -1,26 +0,0 @@ -[bytefield] ----- -(defattrs :plain [:plain {:font-size 14 :font-family "M+ 1p Fallback" :vertical-align "middle"}]) -(def row-height 45 ) -(def row-header-fn nil) -(def left-margin 0) -(def right-margin 0) -(def boxes-per-row 33) -(draw-column-headers {:height 20 :font-size 18 :labels (reverse ["" "32" "33" "34" "35" "36" "37" "38" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "62" "63"])}) - -(draw-box "SD" {:span 1}) -(draw-box (text "WPRI" {:font-weight "bold"}) {:span 25}) -(draw-box "MBE" {:span 1}) -(draw-box "SBE" {:span 1}) -(draw-box "SXL[1:0]" {:span 2}) -(draw-box "UXL[1:0]" {:span 2}) -(draw-box "" {:span 1 :borders {:top :border-unrelated :bottom :border-unrelated}}) - -(draw-box "1" {:span 1 :borders {}}) -(draw-box "25" {:span 25 :borders {}}) -(draw-box "1" {:span 1 :borders {}}) -(draw-box "1" {:span 1 :borders {}}) -(draw-box "2" {:span 2 :borders {}}) -(draw-box "2" {:span 2 :borders {}}) -(draw-box "" {:span 1 :borders {}}) ----- \ No newline at end of file diff --git a/src/images/bytefield/mstatusreg2.adoc b/src/images/bytefield/mstatusreg2.adoc deleted file mode 100644 index 8fa09ea42..000000000 --- a/src/images/bytefield/mstatusreg2.adoc +++ /dev/null @@ -1,54 +0,0 @@ -[bytefield] ----- -(defattrs :plain [:plain {:font-size 14 :font-family "M+ 1p Fallback" :vertical-align "middle"}]) -(def row-height 45 ) -(def row-header-fn nil) -(def left-margin 0) -(def right-margin 0) -(def boxes-per-row 33) -(draw-column-headers {:height 20 :font-size 18 :labels (reverse ["0" "1" "2" "3" "4" "5" "6" "7" "8" "9" "10" "11" "12" "13" "14" "15" "16" "17" "18" "19" "20" "21" "22" "23" "" "" "" "" "" "" "30" "31" ""])}) - -(draw-box "" {:span 1 :borders{:top :border-unrelated :bottom :border-unrelated}}) -(draw-box (text "WPRI" {:font-eight "bold"}) {:span 9}) -(draw-box "TSR" {:span 1}) -(draw-box "TW" {:span 1}) -(draw-box "TVM" {:span 1}) -(draw-box "MXR" {:span 1}) -(draw-box "SUM" {:span 1}) -(draw-box "MPRV" {:span 1}) -(draw-box "XS[1:0]" {:span 2}) -(draw-box "FS[1:0]" {:span 2}) -(draw-box "MPP[1:0]" {:span 2}) -(draw-box "VS[1:0]" {:span 2}) -(draw-box "SPP" {:span 1}) -(draw-box "MPIE" {:span 1}) -(draw-box "UBE" {:span 1}) -(draw-box "SPIE" {:span 1}) -(draw-box (text "WPRI" {:font-weight "bold"}) {:span 1}) -(draw-box "MIE" {:span 1}) -(draw-box (text "WPRI" {:font-weight "bold"}) {:span 1}) -(draw-box "SIE" {:span 1}) -(draw-box (text "WPRI" {:font-weight "bold"}) {:span 1}) - -(draw-box "" {:span 1 :borders {}}) -(draw-box "9" {:span 9 :borders {}}) -(draw-box "1" {:span 1 :borders {}}) -(draw-box "1" {:span 1 :borders {}}) -(draw-box "1" {:span 1 :borders {}}) -(draw-box "1" {:span 1 :borders {}}) -(draw-box "1" {:span 1 :borders {}}) -(draw-box "1" {:span 1 :borders {}}) -(draw-box "2" {:span 2 :borders {}}) -(draw-box "2" {:span 2 :borders {}}) -(draw-box "2" {:span 2 :borders {}}) -(draw-box "2" {:span 2 :borders {}}) -(draw-box "1" {:span 1 :borders {}}) -(draw-box "1" {:span 1 :borders {}}) -(draw-box "1" {:span 1 :borders {}}) -(draw-box "1" {:span 1 :borders {}}) -(draw-box "1" {:span 1 :borders {}}) -(draw-box "1" {:span 1 :borders {}}) -(draw-box "1" {:span 1 :borders {}}) -(draw-box "1" {:span 1 :borders {}}) -(draw-box "1" {:span 1 :borders {}}) ----- \ No newline at end of file diff --git a/src/images/bytefield/senvcfg.edn b/src/images/bytefield/senvcfg.edn deleted file mode 100644 index a60cfa687..000000000 --- a/src/images/bytefield/senvcfg.edn +++ /dev/null @@ -1,42 +0,0 @@ -[bytefield] ----- -(defattrs :plain [:plain {:font-family "M+ 1p Fallback" :font-size 24}]) -(def row-height 40 ) -(def row-header-fn nil) -(def left-margin 30) -(def right-margin 30) -(def boxes-per-row 32) - -(draw-box "SXLEN-1" {:span 5 :text-anchor "start" :borders {}}) -(draw-box "34" {:span 5 :text-anchor "end" :borders {}}) -(draw-box "33" {:span 2 :text-anchor "start" :borders {}}) -(draw-box "32" {:span 1 :text-anchor "end" :borders {}}) -(draw-box "31" {:span 5 :text-anchor "start" :borders {}}) -(draw-box "8" {:span 4 :text-anchor "end" :borders {}}) -(draw-box "7" {:span 2 :borders {}}) -(draw-box "6" {:span 2 :borders {}}) -(draw-box "5" {:text-anchor "start" :borders {}}) -(draw-box "4" {:text-anchor "end" :borders {}}) -(draw-box "3" {:text-anchor "start" :borders {}}) -(draw-box "1" {:text-anchor "end" :borders {}}) -(draw-box "0" {:span 2 :borders {}}) - -(draw-box (text "WPRI" {:font-size 24 :font-weight "bold"}) {:span 10}) -(draw-box "PMM" {:span 3}) -(draw-box (text "WPRI" {:font-size 24 :font-weight "bold"}) {:span 9}) -(draw-box "CBZE" {:span 2}) -(draw-box "CBCFE" {:span 2}) -(draw-box "CBIE" {:span 2}) -(draw-box (text "WPRI" {:font-weight "bold" :font-size 24}) {:span 2}) -(draw-box "FIOM" {:span 2}) - -(draw-box "SXLEN-34" {:span 10 :borders {}}) -(draw-box "2" {:span 3 :borders {}}) -(draw-box "24" {:span 9 :borders {}}) -(draw-box "1" {:span 2 :borders {}}) -(draw-box "1" {:span 2 :borders {}}) -(draw-box "2" {:span 2 :borders {}}) -(draw-box "3" {:span 2 :borders {}}) -(draw-box "1" {:span 2 :borders {}}) - ----- diff --git a/src/images/bytefield/sstatus32-1.edn b/src/images/bytefield/sstatus32-1.edn deleted file mode 100644 index 2c0e102b6..000000000 --- a/src/images/bytefield/sstatus32-1.edn +++ /dev/null @@ -1,36 +0,0 @@ -[bytefield] ----- -(defattrs :plain [:plain {:font-family "M+ 1p Fallback" :font-size 24}]) -(def row-height 40 ) -(def row-header-fn nil) -(def left-margin 30) -(def right-margin 30) -(def boxes-per-row 32) - -(draw-box nil {:span 6 :borders {}}) -(draw-box "31" {:span 2 :borders {}}) -(draw-box "30" {:span 4 :text-anchor "start" :borders {}}) -(draw-box "20" {:span 4 :text-anchor "end" :borders {}}) -(draw-box "19" {:span 3 :borders {}}) -(draw-box "18" {:span 3 :borders {}}) -(draw-box "17" {:span 3 :borders {}}) -(draw-box nil {:span 7 :borders {}}) - -(draw-box nil {:span 6 :borders{}}) -(draw-box "SD" {:span 2}) -(draw-box (text "WPRI" {:font-weight "bold" :font-size 24}) {:span 8}) -(draw-box "MXR" {:span 3}) -(draw-box "SUM" {:span 3}) -(draw-box (text "WPRI" {:font-weight "bold" :font-size 24}) {:span 3}) -(draw-box nil {:span 1 :borders {:top :border-unrelated :bottom :border-unrelated :left :border-unrelated}}) -(draw-box nil {:span 6 :borders {}}) - -(draw-box nil {:span 6 :borders{}}) -(draw-box "1" {:span 2 :borders {}}) -(draw-box "11" {:span 8 :borders {}}) -(draw-box "1" {:span 3 :borders {}}) -(draw-box "1" {:span 3 :borders {}}) -(draw-box "1" {:span 3 :borders {}}) -(draw-box nil {:span 7 :borders {}}) - ----- \ No newline at end of file diff --git a/src/images/bytefield/sstatus32-2.edn b/src/images/bytefield/sstatus32-2.edn deleted file mode 100644 index 55f01e400..000000000 --- a/src/images/bytefield/sstatus32-2.edn +++ /dev/null @@ -1,53 +0,0 @@ -[bytefield] ----- -(defattrs :plain [:plain {:font-family "M+ 1p Fallback" :font-size 24}]) -(def row-height 40 ) -(def row-header-fn nil) -(def left-margin 30) -(def right-margin 30) -(def boxes-per-row 32) - -(draw-box nil {:span 1 :borders {}}) -(draw-box "16" {:span 2 :borders {}}) -(draw-box "15" {:span 2 :borders {}}) -(draw-box "14" {:span 2 :borders {}}) -(draw-box "13" {:span 2 :borders {}}) -(draw-box "12" {:span 2 :borders {}}) -(draw-box "11" {:span 2 :borders {}}) -(draw-box "10" {:span 2 :borders {}}) -(draw-box "9" {:span 2 :borders {}}) -(draw-box "8" {:span 2 :borders {}}) -(draw-box "7" {:span 2 :borders {}}) -(draw-box "6" {:span 2 :borders {}}) -(draw-box "5" {:span 2 :borders {}}) -(draw-box "4" {:span 2 :text-anchor "start" :borders {}}) -(draw-box "2" {:span 1 :text-anchor "end" :borders {}}) -(draw-box "1" {:span 2 :borders {}}) -(draw-box "0" {:span 2 :borders {}}) - -(draw-box nil {:span 1 :borders {:top :border-unrelated :bottom :border-unrelated}}) -(draw-box "XS[1:0]" {:span 4 }) -(draw-box "FS[1:0]" {:span 4}) -(draw-box (text "WPRI" {:font-weight "bold" :font-size 24}) {:span 4}) -(draw-box "VS[1:0]" {:span 4}) -(draw-box "SPP" {:span 2}) -(draw-box (text "WPRI" {:font-weight "bold" :font-size 24}) {:span 2}) -(draw-box "UBE" {:span 2}) -(draw-box "SPIE" {:span 2}) -(draw-box (text "WPRI" {:font-weight "bold" :font-size 24}) {:span 3}) -(draw-box "SIE" {:span 2}) -(draw-box (text "WPRI" {:font-weight "bold" :font-size 24}) {:span 2}) - -(draw-box nil {:span 1 :borders {}}) -(draw-box "2" {:span 4 :borders {}}) -(draw-box "2" {:span 4 :borders {}}) -(draw-box "2" {:span 4 :borders {}}) -(draw-box "2" {:span 4 :borders {}}) -(draw-box "1" {:span 2 :borders {}}) -(draw-box "1" {:span 2 :borders {}}) -(draw-box "1" {:span 2 :borders {}}) -(draw-box "1" {:span 2 :borders {}}) -(draw-box "3" {:span 3 :borders {}}) -(draw-box "1" {:span 2 :borders {}}) -(draw-box "1" {:span 2 :borders {}}) ----- \ No newline at end of file diff --git a/src/images/bytefield/sstatus64.edn b/src/images/bytefield/sstatus64.edn deleted file mode 100644 index 0dfe23089..000000000 --- a/src/images/bytefield/sstatus64.edn +++ /dev/null @@ -1,48 +0,0 @@ -[bytefield] ----- -(defattrs :plain [:plain {:font-family "M+ 1p Fallback" :font-size 24}]) -(def row-height 40 ) -(def row-header-fn nil) -(def left-margin 30) -(def right-margin 30) -(def boxes-per-row 32) - -(draw-box nil {:span 4 :borders {}}) -(draw-box "63" {:span 2 :borders {}}) -(draw-box "62" {:span 4 :text-anchor "start" :borders {}}) -(draw-box "34" {:span 4 :text-anchor "end" :borders {}}) -(draw-box "33" {:span 1 :borders {}}) -(draw-box nil {:span 1 :borders {}}) -(draw-box "32" {:span 1 :borders {}}) -(draw-box "31" {:span 2 :text-anchor "start" :borders {}}) -(draw-box "20" {:span 2 :text-anchor "end" :borders {}}) -(draw-box "19" {:span 2 :borders {}}) -(draw-box "18" {:span 2 :borders {}}) -(draw-box "17" {:span 2 :borders {}}) -(draw-box nil {:borders {}}) -(draw-box nil {:span 4 :borders {}}) - -(draw-box nil {:span 4 :borders {}}) -(draw-box "SD" {:span 2}) -(draw-box (text "WPRI" {:font-weight "bold" :font-size 24}) {:span 8}) -(draw-box "UXL[1:0]" {:span 3}) -(draw-box (text "WPRI" {:font-weight "bold" :font-size 24}) {:span 4}) -(draw-box "MXR" {:span 2}) -(draw-box "SUM" {:span 2}) -(draw-box (text "WPRI" {:font-weight "bold" :font-size 24}) {:span 2}) -(draw-box nil {:borders {:top :border-unrelated :bottom :border-unrelated :left :border-unrelated}}) -(draw-box nil {:span 4 :borders {}}) - -(draw-box nil {:span 4 :borders {}}) -(draw-box "1" {:span 2 :borders {}}) -(draw-box "29" {:span 8 :borders {}}) -(draw-box "2" {:span 3 :borders {}}) -(draw-box "12" {:span 4 :borders {}}) -(draw-box "1" {:span 2 :borders {}}) -(draw-box "1" {:span 2 :borders {}}) -(draw-box "1" {:span 2 :borders {}}) -(draw-box nil {:borders {}}) -(draw-box nil {:span 4 :borders {}}) - - ----- \ No newline at end of file diff --git a/src/machine.adoc b/src/machine.adoc index 76f42c3d6..f275d993c 100644 --- a/src/machine.adoc +++ b/src/machine.adoc @@ -368,20 +368,87 @@ S-level ISA. [[mstatusreg-rv32]] .Machine-mode status (`mstatus`) register for RV32 -include::images/bytefield/mstatusreg-rv32.adoc[] +[wavedrom, ,svg] +.... +{reg: [ + {bits: 1, name: 'WPRI'}, + {bits: 1, name: 'SIE'}, + {bits: 1, name: 'WPRI'}, + {bits: 1, name: 'MIE'}, + {bits: 1, name: 'WPRI'}, + {bits: 1, name: 'SPIE'}, + {bits: 1, name: 'UBE'}, + {bits: 1, name: 'MPIE'}, + {bits: 1, name: 'SPP'}, + {bits: 2, name: 'VS[1:0]'}, + {bits: 2, name: 'MPP[1:0]'}, + {bits: 2, name: 'FS[1:0]'}, + {bits: 2, name: 'XS[1:0]'}, + {bits: 1, name: 'MPRV'}, + {bits: 1, name: 'SUM'}, + {bits: 1, name: 'MXR'}, + {bits: 1, name: 'TVM'}, + {bits: 1, name: 'TW'}, + {bits: 1, name: 'TSR'}, + {bits: 1, name: 'SPELP'}, + {bits: 7, name: 'WPRI'}, + {bits: 1, name: 'SD'}, +], config:{lanes: 2, hspace:1024}} +.... -include::images/bytefield/mstatusreg.adoc[] [[mstatusreg]] .Machine-mode status (`mstatus`) register for RV64 -include::images/bytefield/mstatusreg2.adoc[] - +[wavedrom, ,svg] +.... +{reg: [ + {bits: 1, name: 'WPRI'}, + {bits: 1, name: 'SIE'}, + {bits: 1, name: 'WPRI'}, + {bits: 1, name: 'MIE'}, + {bits: 1, name: 'WPRI'}, + {bits: 1, name: 'SPIE'}, + {bits: 1, name: 'UBE'}, + {bits: 1, name: 'MPIE'}, + {bits: 1, name: 'SPP'}, + {bits: 2, name: 'VS[1:0]'}, + {bits: 2, name: 'MPP[1:0]'}, + {bits: 2, name: 'FS[1:0]'}, + {bits: 2, name: 'XS[1:0]'}, + {bits: 1, name: 'MPRV'}, + {bits: 1, name: 'SUM'}, + {bits: 1, name: 'MXR'}, + {bits: 1, name: 'TVM'}, + {bits: 1, name: 'TW'}, + {bits: 1, name: 'TSR'}, + {bits: 1, name: 'SPELP'}, + {bits: 8, name: 'WPRI'}, + {bits: 2, name: 'UXL[1:0]'}, + {bits: 2, name: 'SXL[1:0]'}, + {bits: 1, name: 'SBE'}, + {bits: 1, name: 'MBE'}, + {bits: 1, name: 'GVA'}, + {bits: 1, name: 'MPV'}, + {bits: 1, name: 'WPRI'}, + {bits: 1, name: 'MPELP'}, + {bits: 21, name: 'WPRI'}, + {bits: 1, name: 'SD'}, +], config:{lanes: 4, hspace:1024}} +.... For RV32 only, `mstatush` is a 32-bit read/write register formatted as shown in <>. Bits 30:4 of `mstatush` generally contain the same fields found in bits 62:36 of `mstatus` for RV64. Fields SD, SXL, and UXL do not exist in `mstatush`. [[mstatushreg]] .Additional machine-mode status (`mstatush`) register for RV32. -include::images/bytefield/mstatushreg.adoc[] +[wavedrom, ,svg] +.... +{reg: [ + {bits: 4, name: 'WPRI'}, + {bits: 1, name: 'SBE'}, + {bits: 1, name: 'MBE'}, + {bits: 26, name: 'WPRI'}, +], config:{lanes: 1, hspace:1024}} +.... [[privstack]] ===== Privilege and Global Interrupt-Enable Stack in `mstatus` register @@ -1067,6 +1134,15 @@ their context to be saved and restored to service asynchronous interrupts, unless the interrupt results in a user-level context swap. ==== +===== Previous Expected Landing Pad (ELP) State in `mstatus` Register + +The Zicfilp extension adds the `SPELP` and `MPELP` fields that hold the previous +`ELP`, and are updated as specified in <>. The +*__x__*`PELP` fields are encoded as follows: + +* 0 - `NO_LP_EXPECTED` - no landing pad instruction expected. +* 1 - `LP_EXPECTED` - a landing pad instruction is expected. + ==== Machine Trap-Vector Base-Address (`mtvec`) Register The `mtvec` register is an MXLEN-bit *WARL* read/write register that holds @@ -1942,6 +2018,13 @@ two cases (or alternatively, the system configuration information can be interrogated to install the appropriate trap handling before runtime). ==== +On a trap caused by a software check exception, the `mtval` register holds +the cause for the exception. The following encodings are defined: + +* 0 - No information provided. +* 2 - Landing Pad Fault. Defined by the Zicfilp extension (<>). +* 3 - Shadow Stack Fault. Defined by the Zicfiss extension (<>). + For other traps, `mtval` is set to zero, but a future standard may redefine `mtval`’s setting for other traps. @@ -2000,7 +2083,25 @@ privileged than M. [#menvcfgreg] .Machine environment configuration (`menvcfg`) register. -include::images/bytefield/menvcfgreg.adoc[] +[wavedrom, ,svg] +.... +{reg: [ + {bits: 1, name: 'FIOM'}, + {bits: 1, name: 'WPRI'}, + {bits: 1, name: 'LPE'}, + {bits: 1, name: 'SSE'}, + {bits: 2, name: 'CBIE'}, + {bits: 1, name: 'CBCFE'}, + {bits: 1, name: 'CBZE'}, + {bits: 24, name: 'WPRI'}, + {bits: 2, name: 'PMM'}, + {bits: 26, name: 'WPRI'}, + {bits: 1, name: 'CDE'}, + {bits: 1, name: 'ADUE'}, + {bits: 1, name: 'PBMTE'}, + {bits: 1, name: 'STCE'}, +], config:{lanes: 4, hspace:1024}} +.... If bit FIOM (Fence of I/O implies Memory) is set to one in `menvcfg`, FENCE instructions executed in modes less privileged than M are modified @@ -2091,6 +2192,26 @@ The definition of the PMM field will be furnished by the forthcoming Smnpm extension. Its allocation within `menvcfg` may change prior to the ratification of that extension. +The Zicfilp extension adds the `LPE` field in `menvcfg`. When the `LPE` field is +set to 1 and S-mode is implemented, the Zicfilp extension is enabled in S-mode. +If `LPE` field is set to 1 and S-mode is not implemented, the Zicfilp extension +is enabled in U-mode. When the `LPE` field is 0, the Zicfilp extension is not +enabled in S-mode, and the following rules apply to S-mode. If the `LPE` field +is 0 and S-mode is not implemented, then the same rules apply to U-mode. + +* The hart does not update the `ELP` state; it remains as `NO_LP_EXPECTED`. +* The `LPAD` instruction operates as a no-op. + +The Zicfiss extension adds the `SSE` field to `menvcfg`. When the `SSE` field is +set to 1 the Zicfiss extension is activated in S-mode. When `SSE` field is 0, +the following rules apply to privilege modes that are less than M: + +* 32-bit Zicfiss instructions will revert to their behavior as defined by Zimop. +* 16-bit Zicfiss instructions will revert to their behavior as defined by Zcmop. +* The `pte.xwr=010b` encoding in VS/S-stage page tables becomes reserved. +* The `henvcfg.SSE` and `senvcfg.SSE` fields will read as zero and are read-only. +* `SSAMOSWAP.W/D` raises an illegal-instruction exception. + When XLEN=32, `menvcfgh` is a 32-bit read/write register that aliases bits 63:32 of `menvcfg`. The `menvcfgh` register does not exist when XLEN=64. @@ -2105,7 +2226,19 @@ shown in <>, that controls security features. [[mseccfg]] .Machine security configuration (`mseccfg`) register. -include::images/bytefield/mseccfg.adoc[] +[wavedrom, ,svg] +.... +{reg: [ + {bits: 1, name: 'MML'}, + {bits: 1, name: 'MMWP'}, + {bits: 1, name: 'RLB'}, + {bits: 5, name: 'WPRI'}, + {bits: 1, name: 'USEED'}, + {bits: 1, name: 'SSEED'}, + {bits: 1, name: 'MLPE'}, + {bits: 53, name: 'WPRI'}, +], config:{lanes: 4, hspace:1024}} +.... The definitions of the SSEED and USEED fields will be furnished by the forthcoming entropy-source extension, Zkr. Their allocations within @@ -2119,6 +2252,14 @@ The definition of the PMM field will be furnished by the forthcoming Smmpm extension. Its allocation within `mseccfg` may change prior to the ratification of that extension. +The Zicfilp extension adds the `MLPE` field in `mseccfg`. When `MLPE` field is +1, Zicfilp extension is enabled in M-mode. When the `MLPE` field is 0, the +Zicfilp extension is not enabled in M-mode and the following rules apply to +M-mode. + +* The hart does not update the `ELP` state; it remains as `NO_LP_EXPECTED`. +* The `LPAD` instruction operates as a no-op. + When XLEN=32 only, `mseccfgh` is a 32-bit read/write register that aliases bits 63:32 of `mseccfg`. Register `mseccfgh` does not exist when XLEN=64. diff --git a/src/priv-cfi.adoc b/src/priv-cfi.adoc new file mode 100644 index 000000000..1b0325714 --- /dev/null +++ b/src/priv-cfi.adoc @@ -0,0 +1,374 @@ +[[priv-cfi]] +== Control-flow Integrity (CFI) + +Control-flow Integrity (CFI) capabilities help defend against Return-Oriented +Programming (ROP) and Call/Jump-Oriented Programming (COP/JOP) style +control-flow subversion attacks. The Zicfiss and Zicfilp extensions provide +backward-edge and forward-edge control flow integrity respectively. Please see +the Control-flow Integrity chapter of the Unprivileged ISA specification for further +details on these CFI capabilities and the associated Unprivileged ISA. + +[[priv-forward]] +=== Landing Pad (Zicfilp) + +This section specifies the Privileged ISA for the Zicfilp extension. + +[[FCIFIACT]] +==== Landing-Pad-Enabled (LPE) State + +The term `xLPE` is used to determine if forward-edge CFI using landing pads +provided by the Zicfilp extension is enabled at a privilege mode. + +When S-mode is implemented, it is determined as follows: + +.`xLPE` determination when S-mode is implemented +[width=100%] +[%header, cols="^4,^12"] +|=== +|Privilege Mode| xLPE +| M | `mseccfg.MLPE` +| S or HS | `menvcfg.LPE` +| VS | `henvcfg.LPE` +| U or VU | `senvcfg.LPE` +|=== + +When S-mode is not implemented, it is determined as follows: + +.`xLPE` determination when S-mode is not implemented +[width=100%] +[%header, cols="^4,^12"] +|=== +|Privilege Mode| xLPE +| M | `mseccfg.MLPE` +| U | `menvcfg.LPE` +|=== + +[NOTE] +==== +The Zicfilp must be explicitly enabled for use at each privilege mode. + +Programs compiled with the `LPAD` instruction continue to function correctly, +but without forward-edge CFI protection, when the Zicfilp extension is not +implemented or is not enabled. +==== + +<<< + +[[ZICFILP_FORWARD_TRAPS]] +==== Preserving Expected Landing Pad State on Traps + +A trap may need to be delivered to the same or to a higher privilege mode upon +completion of `JALR`/`C.JALR`/`C.JR`, but before the instruction at the target +of indirect call/jump was decoded, due to: + +* Asynchronous interrupts. +* Synchronous exceptions with priority higher than that of a software-check + exception with `__x__tval` set to "landing pad fault (code=2)" (See + <> of Privileged Specification). + +The software-check exception caused by Zicfilp has higher priority than an +illegal-instruction exception but lower priority than instruction access-fault. + +The software-check exception due to the instruction not being an `LPAD` +instruction when `ELP` is `LP_EXPECTED` or an software-check exception caused by +the `LPAD` instruction itself (See <>) leads to a trap being delivered +to the same or to a higher privilege mode. + +In such cases, the `ELP` prior to the trap, the previous `ELP`, must be +preserved by the trap delivery such that it can be restored on a return from the +trap. To store the previous `ELP` state on trap delivery to M-mode, an `MPELP` +bit is provided in the `mstatus` CSR. To store the previous `ELP` state on trap +delivery to S/HS-mode, an `SPELP` bit is provided in the `mstatus` CSR. The +`SPELP` bit in `mstatus` can be accessed through the `sstatus` CSR. To store +the previous `ELP` state on traps to VS-mode, a `SPELP` bit is defined in the +`vsstatus` (VS-modes version of `sstatus`). To store the previous `ELP` state on +transition to Debug Mode, a `pelp` bit is defined in the `dcsr` register. + +When a trap is taken into privilege mode `x`, the `__x__PELP` is set to `ELP` +and `ELP` is set to `NO_LP_EXPECTED`. + +An `MRET` or `SRET` instruction is used to return from a trap in M-mode or +S-mode, respectively. When executing an `__x__RET` instruction, if `__x__PP` +holds the value `y`, then `ELP` is set to the value of `__x__PELP` if `__y__LPE` +is 1; otherwise, it is set to `NO_LP_EXPECTED`; `__x__PELP` is set to +`NO_LP_EXPECTED`. + +Upon entry into Debug Mode, the `pelp` bit in `dcsr` is updated with the `ELP` +at the privilege level the hart was previously in, and the `ELP` is set to +`NO_LP_EXPECTED`. When a hart resumes from Debug Mode, if `dcsr.prv` holds the +value `y`, then `ELP` is set to the value of `pelp` if `__y__LPE` is 1; +otherwise, it is set to `NO_LP_EXPECTED`. + +When the Smrnmi extension is implemented, a `MNPELP` field (bit 9) +is provided in the `mnstatus` CSR to hold the previous `ELP` state on a trap to +the RNMI handler. When a RNMI trap is delivered, the `MNPELP` is set to `ELP` +and `ELP` set to `NO_LP_EXPECTED`. Upon a `MNRET`, if the `mnstatus.MNPP` holds +the value `y`, then `ELP` is set to the value of `MNPELP` if `yLPE` is 1; +otherwise, it is set to `NO_LP_EXPECTED`. + +[NOTE] +==== +The trap handler in privilege mode `x` must save the `__x__PELP` bit and the +`x7` register before performing an indirect call/jump if `xLPE=1`. If the +privilege mode `x` can respond to interrupts and `xLPE=1`, then the trap handler +should also save these values before enabling interrupts. + +The trap handler in privilege mode `x` must restore the saved `__x__PELP` bit +and the `x7` register before executing the `__x__RET` instruction to return from +a trap. +==== + +<<< + +[[priv-backward]] +=== Shadow Stack (Zicfiss) + +This section specifies the Privileged ISA for the Zicfiss extension. + +==== Shadow Stack Pointer (`ssp`) CSR access control + +Attempts to access the `ssp` CSR may result in either an illegal-instruction +exception or a virtual instruction exception, contingent upon the state of the +*__x__*`envcfg.SSE` fields. The conditions are specified as follows: + +* If the privilege mode is less than M and `menvcfg.SSE` is 0, an + illegal-instruction exception is raised. +* Otherwise, if in U-mode and `senvcfg.SSE` is 0, an illegal-instruction + exception is raised. +* Otherwise, if in VS-mode and `henvcfg.SSE` is 0, a virtual instruction + exception is raised. +* Otherwise, if in VU-mode and either `henvcfg.SSE` or `senvcfg.SSE` is 0, + a virtual instruction exception is raised. +* Otherwise, the access is allowed. + +==== Shadow-Stack-Enabled (SSE) State + +The term `xSSE` is used to determine if backward-edge CFI using shadow stacks +provided by the Zicfiss extension is enabled at a privilege mode. + +When S-mode is implemented, it is determined as follows: + +.`xSSE` determination when S-mode is implemented +[width=100%] +[%header, cols="^4,^12"] +|=== +|Privilege Mode| `xSSE` +| M | `0` +| S or HS | `menvcfg.SSE` +| VS | `henvcfg.SSE` +| U or VU | `senvcfg.SSE` +|=== + +When S-mode is not implemented, then `xSSE` is 0 at both M and U privilege modes. + +[NOTE] +==== +Activating Zicfiss in U-mode must be done explicitly per process. Not activating +Zicfiss at U-mode for a process when that application is not compiled with +Zicfiss allows it to invoke shared libraries that may contain Zicfiss +instructions. The Zicfiss instructions in the shared library revert to their +Zimop/Zcmop-defined behavior in this case. + +When Zicfiss is enabled in S-mode it is benign to use an operating system that is +not compiled with Zicfiss instructions. Such an operating system that does +not use backward-edge CFI for S-mode execution may still activate Zicfiss for +U-mode applications. + +When programs that use Zicfiss instructions are installed on a processor that +supports the Zicfiss extension but the extension is not enabled at the privilege +mode where the program executes, the program continues to function correctly but +without backward-edge CFI protection as the Zicfiss instructions will revert to +their Zimop/Zcmop-defined behavior. + +When programs that use Zicfiss instructions are installed on a processor that +does not support the Zicfiss extension but supports the Zimop and Zcmop +extensions, the programs continues to function correctly but without +backward-edge CFI protection as the Zicfiss instructions will revert to their +Zimop/Zcmop-defined behavior. + +On processors that do not support Zimop/Zcmop extensions, all Zimop/Zcmop code +points including those used for Zicfiss instructions may cause an +illegal-instruction exception. Execution of programs that use these instructions +on such machines is not supported. + +Activating Zicfiss in M-mode is currently not supported. Additionally, when +S-mode is not implemented, activation in U-mode is also not supported. These +functionalities may be introduced in a future standard extension. +==== + +[[SSMP]] +==== Shadow Stack Memory Protection + +To protect shadow stack memory, the memory is associated with a new page type – +the Shadow Stack (SS) page – in the single-stage and VS-stage page tables. The +encoding `R=0`, `W=1`, and `X=0`, is defined to represent an SS page. When +`menvcfg.SSE=0`, this encoding remains reserved. Similarly, when `V=1` and +`henvcfg.SSE=0`, this encoding remains reserved at `VS` and `VU` levels. + +If `satp.MODE` (or `vsatp.MODE` when `V=1`) is set to `Bare` and the effective +privilege mode is below M, shadow stack memory accesses are prohibited, and +shadow stack instructions will raise a store/AMO access-fault exception. When +the effective privilege mode is M, any memory access by an `SSAMOSWAP.W/D` +instruction will result in a store/AMO access-fault exception. + +Memory mapped as an SS page cannot be written to by instructions other than +`SSAMOSWAP.W/D`, `SSPUSH`, and `C.SSPUSH`. Attempts will raise a store/AMO +access-fault exception. Implicit accesses, including instruction fetches to an SS +page, are not permitted. Such accesses will raise an access-fault exception +appropriate to the access type. However, the shadow stack is readable by all +instructions that only load from memory. + +[NOTE] +==== +Stores to shadow stack pages by instructions other than `SSAMOSWAP`, `SSPUSH`, +and `C.SSPUSH` will trigger a store/AMO access-fault exception, not a store/AMO +page-fault exception, signaling a fatal error. A store/AMO page-fault suggests +that the operating system could address and rectify the fault, which is not +feasible in this scenario. Hence, the page fault handler must decode the opcode +of the faulting instruction to discern whether the fault was caused by a +non-shadow-stack instruction writing to an SS page (a fatal condition) or by a +shadow stack instruction to a non-resident page (a recoverable condition). The +performance-critical nature of operating system page fault handlers necessitates +triggering an access-fault instead of a page fault, allowing for a +straightforward distinction between fatal conditions and recoverable faults. + +Operating systems must ensure that no writable, non-shadow-stack alias virtual +address mappings exist for the physical memory backing the shadow stack. +Furthermore, in systems where an address-misaligned exception supersedes the +access-fault exception, handlers emulating misaligned stores must be designed to +cause an access-fault exception when the store is directed to a shadow stack +page. + +All instructions that perform load operations are allowed to read from the +shadow stack. This feature facilitates debugging and performance profiling by +allowing examination of the link register values backed up in the shadow stack. +==== + +[NOTE] +==== +As of the drafting of this specification, instruction fetches are the sole type +of implicit access subjected to single- or VS-stage address translation. +==== + +If a shadow stack (SS) instruction raises an access-fault, page-fault, or +guest-page-fault exception that is supposed to indicate the original instruction +type (load or store/AMO), then the reported exception cause is respectively a +store/AMO access fault (code 7), a store/AMO page fault (code 15), or a +store/AMO guest-page fault (code 23). For shadow stack instructions, the +reported instruction type is always as though it were a store or AMO, even for +instructions `SSPOPCHK` and `C.SSPOPCHK` that only read from memory and do not +write to it. + +[NOTE] +==== +When Zicfiss is implemented, the existing "store/AMO" exceptions can be thought +of as "store/AMO/SS" exceptions, indicating that the trapping instruction is +either a store, an AMO, or a shadow stack instruction. +==== + +[NOTE] +==== +The H (hypervisor) extension specifies that when a guest-page fault is caused by +an implicit memory access of VS-stage address translation, the reported +exception is either a load or store/AMO guest-page fault based not on the +original instruction type but rather on whether the memory access attempted for +VS-stage translation was a read or a write of memory. VS-stage address +translation can thus cause a shadow stack instruction to raise a load +guest-page-fault exception. +==== + +Shadow stack instructions are restricted to accessing shadow stack +(`pte.xwr=010b`) pages. Should a shadow stack instruction access a page that is +not designated as a shadow stack page and is not marked as read-only +(`pte.xwr=001`), a store/AMO access-fault exception will be invoked. Conversely, +if the page being accessed by a shadow stack instruction is a read-only page, a +store/AMO page-fault exception will be triggered. + + +[NOTE] +==== +Shadow stack loads and stores will trigger a store/AMO page-fault if the +accessed page is read-only, to support copy-on-write (COW) of a shadow stack +page. If the page has been marked read-only for COW tracking, the page fault +handler responds by creating a copy of the page and updates the `pte.xwr` to +`010b`, thereby designating each copy as a shadow stack page. Conversely, if +the access targets a genuinely read-only page, the fault being reported as a +store/AMO page-fault signals to the operating system that the fault is fatal +and non-recoverable. Reporting the fault as a store/AMO page-fault, even for +`SSPOPCHK` initiated memory access, aids in the determination of fatality; if +these were reported as load page-faults, access to a truly read-only page +might be mistakenly treated as a recoverable fault, leading to the faulting +instruction being retried indefinitely. The PTE does not provide a read-only +shadow stack encoding. + +Attempts by shadow stack instructions to access pages marked as read-write, +read-write-execute, read-execute, or execute-only result in a store/AMO +access-fault exception, similarly indicating a fatal condition. + +Shadow stacks should be bounded at each end by guard pages to prevent accidental +underflows or overflows from one shadow stack into another. Conventionally, a +guard page for a stack is a page that is not accessible by the process that owns +the stack. +==== + +<<< + +If the virtual address in `ssp` is not `XLEN` aligned, then the `SSPUSH`/ +`C.SSPUSH`/`SSPOPCHK`/`C.SSPOPCHK` instructions cause a store/AMO access-fault +exception. + +[NOTE] +==== +Misaligned accesses to shadow stack are not required and enforcing alignment is +more secure to detect errors in the program. An access-fault exception is raised +instead of address-misaligned exception in such cases to indicate fatality and +that the instruction must not be emulated by a trap handler. +==== + +Correct execution of shadow stack instructions that access memory requires the +the accessed memory to be idempotent. If the memory referenced by +`SSPUSH`/`C.SSPUSH`/`SSPOPCHK`/`C.SSPOPCHK`/`SSAMOSWAP.W/D` instructions is not +idempotent, then the instructions cause a store/AMO access-fault exception. + +[NOTE] +==== +The `SSPOPCHK` instruction performs a load followed by a check of the loaded +data value with the link register as source. If the check against the link +register faults, and the instruction is restarted by the trap handler, then the +instruction will perform a load again. If the memory from which the load is +performed is non-idempotent, then the second load may cause unexpected side +effects. Shadow stack instructions that access the shadow stack require the +memory referenced by `ssp` to be idempotent to avoid such concerns. Locating +shadow stacks in non-idempotent memory, such as non-idempotent device memory, +is not an expected usage, and requiring memory referenced to be idempotent +does not pose a significant restriction. +==== + +The `U` and `SUM` bit enforcement is performed normally for shadow stack +instruction initiated memory accesses. The state of the `MXR` bit does not +affect read access to a shadow stack page as the shadow stack page is always +readable by all instructions that load from memory. + +The G-stage address translation and protections remain unaffected by the Zicfiss +extension. The `xwr == 010b` encoding in the G-stage PTE remains reserved. When +G-stage page tables are active, the shadow stack instructions that access memory +require the G-stage page table to have read-write permission for the accessed +memory; else a store/AMO guest-page fault exception is raised. + +[NOTE] +==== +A future extension may define a shadow stack encoding in the G-stage page table +to support use cases such as a hypervisor enforcing shadow stack protections for +its guests. +==== + +Svpbmt and Svnapot extensions are supported for shadow stack pages. + +The PMA checks are extended to require memory referenced by shadow stack +instructions to be idempotent. The PMP checks are extended to require read-write +permission for memory accessed by shadow stack instructions. If the PMP does not +provide read-write permissions or if the accessed memory is not idempotent then +a store/AMO access-fault exception is raised. + +The `SSAMOSWAP.W/D` instructions require the PMA of the accessed memory range to +provide AMOSwap level support. diff --git a/src/priv-csrs.adoc b/src/priv-csrs.adoc index e799b51b1..ab6c3cd92 100644 --- a/src/priv-csrs.adoc +++ b/src/priv-csrs.adoc @@ -153,6 +153,12 @@ URW Floating-Point Dynamic Rounding Mode. + Floating-Point Control and Status Register (`frm` +`fflags`). +4+^|Unprivileged Zicfiss extension CSR +|`0x011` + +|URW + +|`ssp` + +|Shadow Stack Pointer. + + 4+^|Unprivileged Counter/Timers |`0xC00` + diff --git a/src/riscv-privileged.adoc b/src/riscv-privileged.adoc index 4067424b0..ffd1da7ff 100644 --- a/src/riscv-privileged.adoc +++ b/src/riscv-privileged.adoc @@ -90,6 +90,7 @@ include::supervisor.adoc[] include::sstc.adoc[] include::sscofpmf.adoc[] include::hypervisor.adoc[] +include::priv-cfi.adoc[] include::priv-insns.adoc[] include::priv-history.adoc[] include::bibliography.adoc[] diff --git a/src/riscv-unprivileged.adoc b/src/riscv-unprivileged.adoc index 0afe79945..4718ea305 100644 --- a/src/riscv-unprivileged.adoc +++ b/src/riscv-unprivileged.adoc @@ -189,6 +189,7 @@ include::p-st-ext.adoc[] include::v-st-ext.adoc[] include::scalar-crypto.adoc[] include::vector-crypto.adoc[] +include::unpriv-cfi.adoc[] include::rv-32-64g.adoc[] include::extending.adoc[] include::naming.adoc[] diff --git a/src/supervisor.adoc b/src/supervisor.adoc index 3e7289302..6c2276dd1 100644 --- a/src/supervisor.adoc +++ b/src/supervisor.adoc @@ -43,17 +43,59 @@ shown in <> when SXLEN=32 and <> when SXLEN=64. The `sstatus` register keeps track of the processor's current operating state. -include::images/bytefield/sstatus32-1.edn[] - [[sstatusreg-rv32]] .Supervisor-mode status (`sstatus`) register when SXLEN=32. -include::images/bytefield/sstatus32-2.edn[] - -include::images/bytefield/sstatus64.edn[] +[wavedrom, ,svg] +.... +{reg: [ + {bits: 1, name: 'WPRI'}, + {bits: 1, name: 'SIE'}, + {bits: 3, name: 'WPRI'}, + {bits: 1, name: 'SPIE'}, + {bits: 1, name: 'UBE'}, + {bits: 1, name: 'WPRI'}, + {bits: 1, name: 'SPP'}, + {bits: 2, name: 'VS[1:0]'}, + {bits: 2, name: 'WPRI'}, + {bits: 2, name: 'FS[1:0]'}, + {bits: 2, name: 'XS[1:0]'}, + {bits: 1, name: 'WPRI'}, + {bits: 1, name: 'SUM'}, + {bits: 1, name: 'MXR'}, + {bits: 3, name: 'WPRI'}, + {bits: 1, name: 'SPELP'}, + {bits: 7, name: 'WPRI'}, + {bits: 1, name: 'SD'}, +], config:{lanes: 2, hspace:1024}} +.... [[sstatusreg]] .Supervisor-mode status (`sstatus`) register when SXLEN=64. -include::images/bytefield/sstatus32-2.edn[] +[wavedrom, ,svg] +.... +{reg: [ + {bits: 1, name: 'WPRI'}, + {bits: 1, name: 'SIE'}, + {bits: 3, name: 'WPRI'}, + {bits: 1, name: 'SPIE'}, + {bits: 1, name: 'UBE'}, + {bits: 1, name: 'WPRI'}, + {bits: 1, name: 'SPP'}, + {bits: 2, name: 'VS[1:0]'}, + {bits: 2, name: 'WPRI'}, + {bits: 2, name: 'FS[1:0]'}, + {bits: 2, name: 'XS[1:0]'}, + {bits: 1, name: 'WPRI'}, + {bits: 1, name: 'SUM'}, + {bits: 1, name: 'MXR'}, + {bits: 3, name: 'WPRI'}, + {bits: 1, name: 'SPELP'}, + {bits: 8, name: 'WPRI'}, + {bits: 2, name: 'UXL[1:0]'}, + {bits: 29, name: 'WPRI'}, + {bits: 1, name: 'SD'}, +], config:{lanes: 4, hspace:1024}} +.... The SPP bit indicates the privilege level at which a hart was executing before entering supervisor mode. When a trap is taken, SPP is set to 0 @@ -176,6 +218,12 @@ of one endianness to execute user-mode programs of the opposite endianness. ==== +===== Previous Expected Landing Pad (ELP) State in `sstatus` Register + +Access to the `SPELP` field, added by Zicfilp, accesses the homonymous +fields of `mstatus` when `V=0`, and the homonymous fields of `vsstatus` +when `V=1`. + ==== Supervisor Trap Vector Base Address (`stvec`) Register The `stvec` register is an SXLEN-bit read/write register that holds trap @@ -571,6 +619,13 @@ will contain the shortest of: The value loaded into `stval` on an illegal-instruction exception is right-justified and all unused upper bits are cleared to zero. +On a trap caused by a software check exception, the `stval` register holds the +cause for the exception. The following encodings are defined: + +* 0 - No information provided. +* 2 - Landing Pad Fault. Defined by the Zicfilp extension (<>). +* 3 - Shadow Stack Fault. Defined by the Zicfiss extension (<>). + For other traps, `stval` is set to zero, but a future standard may redefine `stval`’s setting for other traps. @@ -590,8 +645,37 @@ shown in <>, that controls certain characteristics of the U-mode execution environment. [[senvcfg]] -.Supervisor environment configuration register (`senvcfg`). -include::images/bytefield/senvcfg.edn[] +.Supervisor environment configuration register (`senvcfg`) for RV64. +[wavedrom, ,svg] +.... +{reg: [ + {bits: 1, name: 'FIOM'}, + {bits: 1, name: 'WPRI'}, + {bits: 1, name: 'LPE'}, + {bits: 1, name: 'SSE'}, + {bits: 2, name: 'CBIE'}, + {bits: 1, name: 'CBCFE'}, + {bits: 1, name: 'CBZE'}, + {bits: 24, name: 'WPRI'}, + {bits: 2, name: 'PMM'}, + {bits: 30, name: 'WPRI'}, +], config:{lanes: 4, hspace:1024}} +.... + +.Supervisor environment configuration register (`senvcfg`) for RV32. +[wavedrom, ,svg] +.... +{reg: [ + {bits: 1, name: 'FIOM'}, + {bits: 1, name: 'WPRI'}, + {bits: 1, name: 'LPE'}, + {bits: 1, name: 'WPRI'}, + {bits: 2, name: 'CBIE'}, + {bits: 1, name: 'CBCFE'}, + {bits: 1, name: 'CBZE'}, + {bits: 24, name: 'WPRI'}, +], config:{lanes: 2, hspace:1024}} +.... If bit FIOM (Fence of I/O implies Memory) is set to one in `senvcfg`, FENCE instructions executed in U-mode are modified so the requirement to @@ -670,6 +754,24 @@ The definition of the PMM field will be furnished by the forthcoming Ssnpm extension. Its allocation within `senvcfg` may change prior to the ratification of that extension. +The Zicfilp extension adds the `LPE` field in `senvcfg`. When the `LPE` field is +set to 1, the Zicfilp extension is enabled in VU/U-mode. When the `LPE` field is +0, the Zicfilp extension is not enabled in VU/U-mode and the following rules +apply to VU/U-mode: + +* The hart does not update the `ELP` state; it remains as `NO_LP_EXPECTED`. +* The `LPAD` instruction operates as a no-op. + +The Zicfiss extension adds the `SSE` field in `senvcfg`. When the `SSE` field is +set to 1, the Zicfiss extension is activated in VU/U-mode. When the `SSE` field +is 0, the Zicfiss extension remains inactive in VU/U-mode, and the following +rules apply: + +* 32-bit Zicfiss instructions will revert to their behavior as defined by Zimop. +* 16-bit Zicfiss instructions will revert to their behavior as defined by Zcmop. +* When `menvcfg.SSE` is one, `SSAMOSWAP.W/D` raises an illegal-instruction + exception in U-mode and a virtual instruction exception in VU-mode. + [[satp]] ==== Supervisor Address Translation and Protection (`satp`) Register diff --git a/src/unpriv-cfi.adoc b/src/unpriv-cfi.adoc new file mode 100644 index 000000000..1615d62cf --- /dev/null +++ b/src/unpriv-cfi.adoc @@ -0,0 +1,874 @@ +== Control-flow Integrity (CFI) + +Control-flow Integrity (CFI) capabilities help defend against Return-Oriented +Programming (ROP) and Call/Jump-Oriented Programming (COP/JOP) style +control-flow subversion attacks. These attack methodologies use code sequences +in authorized modules, with at least one instruction in the sequence being a +control transfer instruction that depends on attacker-controlled data either in +the return stack or in memory used to obtain the target address for a call or +jump. Attackers stitch these sequences together by diverting the control flow +instructions (e.g., `JALR`, `C.JR`, `C.JALR`), from their original target +address to a new target via modification in the return stack or in the memory +used to obtain the jump/call target address. + +RV32/RV64 provides two types of control transfer instructions - unconditional +jumps and conditional branches. Conditional branches encode an offset in the +immediate field of the instruction and are thus direct branches that are not +susceptible to control-flow subversion. Unconditional direct jumps using `JAL` +transfer control to a target that is in a +/- 1 MiB range from the current `pc`. +Unconditional indirect jumps using the `JALR` obtain their branch target by +adding the sign extended 12-bit immediate encoded in the instruction to the +`rs1` register. + +The RV32I/RV64I does not have a dedicated instruction for calling a procedure or +returning from a procedure. A `JAL` or `JALR` may be used to perform a procedure +call and `JALR` to return from a procedure. The RISC-V ABI however defines the +convention that a `JAL`/`JALR` where `rd` (i.e. the link register) is `x1` or +`x5` is a procedure call, and a `JALR` where `rs1` is the conventional +link register (i.e. `x1` or `x5`) is a return from procedure. The architecture +allows for using these hints and conventions to support return address +prediction (See <>). + +The RVC standard extension for compressed instructions provides unconditional +jump and conditional branch instructions. The `C.J` and `C.JAL` instructions +encode an offset in the immediate field of the instruction and thus are not +susceptible to control-flow subversion. The `C.JR` and `C.JALR` RVC instructions +perform an unconditional control transfer to the address in register `rs1`. The +`C.JALR` additionally writes the address of the instruction following the jump +(`pc+2`) to the link register `x1` and is a procedure call. The `C.JR` is a +return from procedure if `rs1` is a conventional link register (i.e. `x1` or +`x5`); else it is an indirect jump. + +The term _call_ is used to refer to a `JAL` or `JALR` instruction with a link +register as destination, i.e., `rd != x0`. Conventionally, the link register is +`x1` or `x5`. A _call_ using `JAL` or `C.JAL` is termed a direct call. A +`C.JALR` expands to `JALR x1, 0(rs1)` and is a _call_. A _call_ using `JALR` or +`C.JALR` is termed an _indirect-call_. + +The term _return_ is used to refer to a `JALR` instruction with `rd == x0` and +with `rs1 == x1` or `rs1 == x5` and `rd == x0`. A `C.JR` instruction expands to +`JALR x0, 0(rs1)` and is a _return_ if `rs1 == x1` or `rs1 == x5`. + +The term _indirect-jump_ is used to refer to a `JALR` instruction with `rd == x0` +and where the `rs1` is not `x1` or `x5` (i.e., not a return). A `C.JR` +instruction where `rs1` is not `x1` or `x5` (i.e., not a return) is an +_indirect-jump_. + +The Zicfiss and Zicfilp extensions build on these conventions and hints and +provide backward-edge and forward-edge control flow integrity respectively. + +The Unprivileged ISA for Zicfilp extension is specified in <> +and for the Unprivileged ISA for Zicfiss extension is specified in +<>. The Privileged ISA for these extensions is specified in the +Privileged ISA specification. + +[[unpriv-forward]] +=== Landing Pad (Zicfilp) + +To enforce forward-edge control-flow integrity, the Zicfilp extension introduces +a landing pad (`LPAD`) instruction. The `LPAD` instruction must be placed at the +program locations that are valid targets of indirect jumps or calls. The `LPAD` +instruction (See <>) is encoded using the `AUIPC` major opcode with +`rd=x0`. + +Compilers emit a landing pad instruction as the first instruction of an +address-taken function, as well as at any indirect jump targets. A landing pad +instruction is not required in functions that are only reached using a direct +call or direct jump. + +The landing pad is designed to provide integrity to control transfers performed +using indirect calls and jumps, and this is referred to as forward-edge +protection. When the Zicfilp is active, the hart tracks an expected landing pad +(`ELP`) state that is updated by an _indirect_call_ or _indirect_jump_ to +require a landing pad instruction at the target of the branch. If the +instruction at the target is not a landing pad, then a software-check exception +is raised. + +A landing pad may be optionally associated with a 20-bit label. With labeling +enabled, the number of landing pads that can be reached from an indirect call +or jump sites can be defined using programming language-based policies. Labeling +of the landing pads enables software to achieve greater precision in pairing up +indirect call/jump sites with valid targets. When labeling of landing pads +is used, indirect call or indirect jump site can specify the expected label of +the landing pad and thereby constrain the set of landing pads that may be +reached from each indirect call or indirect jump site in the program. + +In the simplest form, a program can be built with a single label value to +implement a coarse-grained version of forward-edge control-flow integrity. By +constraining gadgets to be preceded by a landing pad instruction that marks +the start of indirect callable functions, the program can significantly reduce +the available gadget space. A second form of label generation may generate a +signature, such as a MAC, using the prototype of the function. Programs that use +this approach would further constrain the gadgets accessible from a call site to +only indirectly callable functions that match the prototype of the called +functions. Another approach to label generation involves analyzing the +control-flow-graph (CFG) of the program, which can lead to even more stringent +constraints on the set of reachable gadgets. Such programs may further use +multiple labels per function, which means that if a function is called from two +or more call sites, the functions can be labeled as being reachable from each of +the call sites. For instance, consider two call sites A and B, where A calls the +functions X and Y, and B calls the functions Y and Z. In a single label scheme, +functions X, Y, and Z would need to be assigned the same label so that both call +sites A and B can invoke the common function Y. This scheme would allow call +site A to also call function Z and call site B to also call function X. However, +if function Y was assigned two labels - one corresponding to call site A and the +other to call site B, then Y can be invoked by both call sites, but X can only be +invoked by call site A and Z can only be invoked by call site B. To support +multiple labels, the compiler could generate a call-site-specific entry point +for shared functions, with each entry point having its own landing pad +instruction followed by a direct branch to the start of the function. This would +allow the function to be labeled with multiple labels, each corresponding to a +specific call site. A portion of the label space may be dedicated to labeled +landing pads that are only valid targets of an indirect jump (and not an +indirect call). + +The `LPAD` instruction uses the code points defined as HINTs for the `AUIPC` +opcode. When Zicfilp is not active at a privilege level or when the extension +is not implemented, the landing pad instruction executes as a no-op. A program +that is built with `LPAD` instructions can thus continue to operate correctly, +but without forward-edge control-flow integrity, on processors that do not +support the Zicfilp extension or if the Zicfilp extension is not active. + +Compilers and linkers should provide an attribute flag to indicate if the +program has been compiled with the Zicfilp extension and use that to determine +if the Zicfilp extension should be activated. The dynamic loader should activate +the use of Zicfilp extension for an application only if all executables (the +application and the dependent dynamically linked libraries) used by that +application use the Zicfilp extension. + +When Zicfilp extension is not active or not implemented, the hart does not +require landing pad instructions at the targets of indirect calls/jumps, and the +landing instructions revert to being no-ops. This allows a program compiled +with landing pad instructions to operate correctly but without forward-edge +control-flow integrity. + +The Zicfilp extensions may be activated for use individually and independently +for each privilege mode. + +The Zicfilp extension depends on the Zicsr extension. + +==== Landing Pad Enforcement + +To enforce that the target of an indirect call or indirect jump must be a valid +landing pad instruction, the hart maintains an expected landing pad (`ELP`) state +to determine if a landing pad instruction is required at the target of an +indirect call or an indirect jump. The `ELP` state can be one of: + +* 0 - `NO_LP_EXPECTED` +* 1 - `LP_EXPECTED` + +The `ELP` state is initialized to `NO_LP_EXPECTED` by the hart upon reset. + +The Zicfilp extension, when enabled, determines if an indirect call or an +indirect jump must land on a landing pad, as specified in <>. If +`is_lp_expected` is 1, then the hart updates the `ELP` to `LP_EXPECTED`. + +[[IND_CALL_JMP]] +.Landing pad expected determination +[listing] +---- + is_lp_expected = ( (JALR || C.JR || C.JALR) && + (rs1 != x1) && (rs1 != x5) && (rs1 != x7) ) ? 1 : 0; +---- + +An indirect branch using `JALR`, `C.JALR`, or `C.JR` with `rs1` as `x7` is +termed a software guarded branch. Such branches do not need to land on a +`LPAD` instruction and thus do not set `ELP` to `LP_EXPECTED`. + +[NOTE] +==== +When the register source is a link register and the register destination is +`x0`, then it's a return from a procedure and does not require a landing pad at +the target. + +When the register source and register destination are both link registers, then +it is a semantically-direct-call. For example, the `call offset` +pseudoinstruction may expand to a two instruction sequence composed of a +`lui ra, imm20` or a `auipc ra, imm20` instruction followed by a +`jalr ra, imm12(ra)` instruction where `ra` is the link register (either `x1` or +`x5`). Since the address of the procedure was not explicitly taken and the +computed address is not obtained from mutable memory, such semantically-direct +calls do not require a landing pad to be placed at the target. Compilers and +JITers must use the semantically-direct calls only if the `rs1` was computed as +a PC-relative or an absolute offset to the symbol. + +The `tail offset` pseudoinstruction used to tail call a far-away procedure may +also be expanded to a two instruction sequence composed of a `lui x7, imm20` or +`auipc x7, imm20` followed by a `jalr x0, x7`. Since the address of the +procedure was not explicitly taken and the computed address is not obtained from +mutable memory, such semantically-direct tail-calls do not require a landing pad +to be placed at the target. + +Software guarded branches may also be used by compilers to generate code for +constructs like switch-cases. When using the software guarded branches, the +compiler is required to ensure it has full control on the possible jump +targets (e.g., by obtaining the targets from a read-only table in memory and +performing bounds checking on the index into the table, etc.). +==== + +The landing pad may be labeled. Zicfilp extension designates the register `x7` +for use as the landing pad label register. To support labeled landing pads, the +indirect call/jump sites establish an expected landing pad label (e.g., using +the `LUI` instruction) in the bits 31:12 of the `x7` register. The `LPAD` +instruction is encoded with a 20-bit immediate value called the landing-pad-label +(`LPL`) that is matched to the expected landing pad label. When `LPL` is encoded +as zero, the `LPAD` instruction does not perform the label check and in programs +built with this single label mode of operation the indirect call/jump sites do +not need to establish an expected landing pad label value in `x7`. + +When `ELP` is set to `LP_EXPECTED`, if the next instruction in the instruction +stream is not 4-byte aligned, or is not `LPAD`, or if the landing pad label +encoded in `LPAD` is not zero and does not match the expected landing pad label +in bits 31:12 of the `x7` register, then a software-check exception (cause=18) +with `__x__tval` set to "landing pad fault (code=2)" is raised else the `ELP` is +updated to `NO_LP_EXPECTED`. + +[NOTE] +==== +The tracking of `ELP` and the requirement for a landing pad instruction +at the target of indirect call and jump enables a processor implementation to +significantly reduce or to prevent speculation to non-landing-pad instructions. +Constraining speculation using this technique, greatly reduces the gadget space +and increases the difficulty of using techniques such as branch-target-injection, +also known as Spectre variant 2, which use speculative execution to leak data +through side channels. + +The `LPAD` requires a 4-byte alignment to address the concatenation of two +instructions `A` and `B` accidentally forming an unintended landing pad in the +program. For example, consider a 32-bit instruction where the bytes 3 and 2 have +a pattern of `?017h` (for example, the immediate fields of a `LUI`, `AUIPC`, or +a `JAL` instruction), followed by a 16-bit or a 32-bit instruction. When +patterns that can accidentally form a valid landing pad are detected, the +assembler or linker can force instruction `A` to be aligned to a 4-byte +boundary to force the unintended `LPAD` pattern to become misaligned, and thus +not a valid landing pad, or may use an alternate register allocation to prevent +the accidental landing pad. +==== + +<<< + +[[LP_INST]] +==== Landing Pad Instruction + +When Zicfilp is enabled, `LPAD` is the only instruction allowed to execute when +the `ELP` state is `LP_EXPECTED`. If Zicfilp is not enabled then the instruction +is a no-op. If Zicfilp is enabled, the `LPAD` instruction causes a +software-check exception with `__x__tval` set to "landing pad fault (code=2)" if +any of the following conditions are true: + +* The `pc` is not 4-byte aligned and `ELP` is `LP_EXPECTED`. +* The `ELP` is `LP_EXPECTED` and the `LPL` is not zero and the `LPL` does not + match the expected landing pad label in bits 31:12 of the `x7` register. + +If a software-check exception is not caused then the `ELP` is updated to +`NO_LP_EXPECTED`. + +[wavedrom, ,svg] +.... +{reg: [ + {bits: 7, name: 'opcode', attr:'AUIPC'}, + {bits: 5, name: 'rd', attr:'00000'}, + {bits: 20, name: 'LPL'}, +], config:{lanes: 1, hspace:1024}} +.... + +The operation of the `LPAD` instruction is as follows: + +.`LPAD` operation +[listing] +---- +if (xLPE == 1 && ELP == LP_EXPECTED) + // If PC not 4-byte aligned then software-check exception + if pc[1:0] != 0 + raise software-check exception + // If landing pad label not matched -> software-check exception + else if (inst.LPL != x7[31:12] && inst.LPL != 0) + raise software-check exception + else + ELP = NO_LP_EXPECTED +else + no-op +endif +---- + +<<< + +[[unpriv-backward]] +=== Shadow Stack (Zicfiss) + +The Zicfiss extension introduces a shadow stack to enforce backward-edge +control-flow integrity. A shadow stack is a second stack used to store a +shadow copy of the return address in the link register if it needs to be +spilled. + +The shadow stack is designed to provide integrity to control transfers performed +using a _return_, where the return may be from a procedure invoked using an +indirect call or a direct call, and this is referred to as backward-edge +protection. + +A program using backward-edge control-flow integrity has two stacks: a regular +stack and a shadow stack. The shadow stack is used to spill the link register, +if required, by non-leaf functions. An additional register, shadow-stack-pointer +(`ssp`), is introduced in the architecture to hold the address of the top of the +active shadow stack. + +The shadow stack, similar to the regular stack, grows downwards, from +higher addresses to lower addresses. Each entry on the shadow stack is `XLEN` +wide and holds the link register value. The `ssp` points to the top of the +shadow stack, which is the address of the last element stored on the shadow +stack. + +The shadow stack is architecturally protected from inadvertent corruptions and +modifications, as detailed in the Privileged specification. + +The Zicfiss extension provides instructions to store and load the link register +to/from the shadow stack and to check the integrity of the return address. The +extension provides instructions to support common stack maintenance operations +such as stack unwinding and stack switching. + +When Zicfiss is enabled, each function that needs to spill the link register, +typically non-leaf functions, store the link register value to the regular stack +and a shadow copy of the link register value to the shadow stack when the +function is entered (the prologue). When such a function returns (the +epilogue), the function loads the link register from the regular stack and +the shadow copy of the link register from the shadow stack. Then, the link +register value from the regular stack and the shadow link register value from +the shadow stack are compared. A mismatch of the two values is indicative of a +subversion of the return address control variable and causes a software-check +exception. + +The Zicfiss instructions are encoded using a subset of May-Be-Operation +instructions defined by the Zimop and Zcmop extensions. This subset +of instructions revert to their Zimop/Zcmop defined behavior when the Zicfiss +extension is not implemented or if the extension has not been activated. A +program that is built with Zicfiss instructions can thus continue to operate +correctly, but without backward-edge control-flow integrity, on processors that +do not support the Zicfiss extension or if the Zicfiss extension is not active. +The Zicfiss extension may be activated for use individually and independently +for each privilege mode. + +Compilers should flag each object file (for example, using flags in the ELF +attributes) to indicate if the object file has been compiled with the Zicfiss +instructions. The linker should flag (for example, using flags in the ELF +attributes) the binary/executable generated by linking objects as being +compiled with the Zicfiss instructions only if all the object files that are +linked have the same Zicfiss attributes. + +The dynamic loader should activate the use of Zicfiss extension for an +application only if all executables (the application and the dependent +dynamically-linked libraries) used by that application use the Zicfiss +extension. + +<<< + +An application that has the Zicfiss extension active may request the dynamic +loader at runtime to load a new dynamic shared object (using dlopen() for +example). If the requested object does not have the Zicfiss attribute then +the dynamic loader, based on its policy (e.g., established by the operating +system or the administrator) configuration, could either deny the request or +deactivate the Zicfiss extension for the application. It is strongly recommended +that the policy enforces a strict security posture and denies the request. + +The Zicfiss extension depends on the Zicsr and Zimop extensions. Furthermore, +if the Zcmop extension is implemented, the Zicfiss extension also provides the +`C.SSPUSH` and `C.SSPOPCHK` instructions. Moreover, use of Zicfiss in U-mode +requires S-mode to be implemented. Use of Zicfiss in M-mode is not supported. + +==== Zicfiss Instructions Summary + +The Zicfiss extension introduces the following instructions: + +* Push to the shadow stack (See <>) +** `SSPUSH x1` and `SSPUSH x5` - encoded using `MOP.RR.7` +** `C.SSPUSH x1` - encoded using `C.MOP.1` + +* Pop from the shadow stack (See <>) +** `SSPOPCHK x1` and `SSPOPCHK x5` - encoded using `MOP.R.28` +** `C.SSPOPCHK x5` - encoded using `C.MOP.5` + +* Read the value of `ssp` into a register (See <>) +** `SSRDP` - encoded using `MOP.R.28` + +* Perform an atomic swap from a shadow stack location (See <>) +** `SSAMOSWAP.W` and `SSAMOSWAP.D` + +Zicfiss does not use all encodings of `MOP.RR.7` or `MOP.R.28`. When a +`MOP.RR.7` or `MOP.R.28` encoding is not used by the Zicfiss extension, the +corresponding instruction adheres to its Zimop-defined behavior, unless +redefined by another extension. + +==== Shadow Stack Pointer (`ssp`) + +The `ssp` CSR is an unprivileged read-write (URW) CSR that reads and writes +`XLEN` low order bits of the shadow stack pointer (`ssp`). The CSR address is +0x011. There is no high CSR defined as the `ssp` is always as wide as the `XLEN` +of the current privilege mode. The bits 1:0 of `ssp` are read-only zero. If the +UXLEN or SXLEN may never be 32, then the bit 2 is also read-only zero. + +<<< + +==== Zicfiss Instructions + +[[SS_PUSH]] +==== Push to the Shadow Stack +A shadow stack push operation is defined as decrement of the `ssp` by `XLEN/8` +followed by a store of the value in the link register to memory at the new top +of the shadow stack. + +[wavedrom, ,svg] +.... +{reg: [ + {bits: 7, name: 'opcode', attr:'SYSTEM'}, + {bits: 5, name: 'rd', attr:['00000']}, + {bits: 3, name: 'funct3', attr:['100']}, + {bits: 5, name: 'rs1', attr:['00000']}, + {bits: 5, name: 'rs2', attr:['00001', '00101']}, + {bits: 7, name: '1100111', attr:['SSPUSH x1','SSPUSH x5']}, +], config:{lanes: 1, hspace:1024}} +.... + +[wavedrom, ,svg] +.... +{reg: [ + {bits: 2, name: 'op', attr:'C1'}, + {bits: 5, name: '00000'}, + {bits: 1, name: '1'}, + {bits: 3, name: 'n[3:1]', attr:['000']}, + {bits: 1, name: '0'}, + {bits: 1, name: '0'}, + {bits: 3, name: '011', attr:['C.SSPUSH x1']}, +], config:{lanes: 1, hspace:1024}} +.... + +Only `x1` and `x5` registers are supported as `rs2` for `SSPUSH`. Zicfiss +provides a 16-bit version of the `SSPUSH x1` instruction using the Zcmop +defined `C.MOP.1` encoding. The `C.SSPUSH x1` expands to `SSPUSH x1`. + +The `SSPUSH` instruction and its compressed form `C.SSPUSH` can be used to push +a link register on the shadow stack. The `SSPUSH` and `C.SSPUSH` instructions +perform a store identically to the existing store instructions, with the +difference that the base is implicitly `ssp` and the width is implicitly `XLEN`. + +The operation of the `SSPUSH` and `C.SSPUSH` instructions is as follows: + +.`SSPUSH` and `C.SSPUSH` operation +[listing] +---- +if (xSSE == 1) + mem[ssp - (XLEN/8)] = X(src) # Store src value to ssp - XLEN/8 + ssp = ssp - (XLEN/8) # decrement ssp by XLEN/8 +endif +---- + +The `ssp` is decremented by `SSPUSH` and `C.SSPUSH` only if the store to the +shadow stack completes successfully. + +<<< + +[[SS_POP]] +==== Pop from the Shadow Stack + +A shadow stack pop operation is defined as an `XLEN` wide read from the +current top of the shadow stack followed by an increment of the `ssp` by +`XLEN/8`. + +[wavedrom, ,svg] +.... +{reg: [ + {bits: 7, name: 'opcode', attr:'SYSTEM'}, + {bits: 5, name: 'rd', attr:['00000','00000']}, + {bits: 3, name: 'funct3', attr:['100']}, + {bits: 5, name: 'rs1', attr:['00001','00101']}, + {bits: 12, name: '110011011100', attr:['SSPOPCHK x1','SSPOPCHK x5']}, +], config:{lanes: 1, hspace:1024}} +.... + +[wavedrom, ,svg] +.... +{reg: [ + {bits: 2, name: 'op', attr:'C1'}, + {bits: 5, name: '00000'}, + {bits: 1, name: '1'}, + {bits: 3, name: 'n[3:1]', attr:['010']}, + {bits: 1, name: '0'}, + {bits: 1, name: '0'}, + {bits: 3, name: '011', attr:['C.SSPOPCHK x5']}, +], config:{lanes: 1, hspace:1024}} +.... + +Only `x1` and `x5` registers are supported as `rs1` for `SSPOPCHK`. Zicfiss +provides a 16-bit version of the `SSPOPCHK x5` using the Zcmop defined `C.MOP.5` +encoding. The `C.SSPOPCHK x5` expands to `SSPOPCHK x5`. + +Programs with a shadow stack push the return address onto the regular stack as +well as the shadow stack in the prologue of non-leaf functions. When returning +from these non-leaf functions, such programs pop the link register from the +regular stack and pop a shadow copy of the link register from the shadow stack. +The two values are then compared. If the values do not match, it is indicative +of a corruption of the return address variable on the regular stack. + +The `SSPOPCHK` instruction, and its compressed form `C.SSPOPCHK`, can be used to +pop the shadow return address value from the shadow stack and check that the +value matches the contents of the link register, and if not cause a +software-check exception with `__x__tval` set to "shadow stack fault (code=3)". + +While any register may be used as link register, conventionally the `x1` or `x5` +registers are used. The shadow stack instructions are designed to be most +efficient when the `x1` and `x5` registers are used as the link register. + +[NOTE] +==== +Return-address prediction stacks are a common feature of high-performance +instruction-fetch units, but they require accurate detection of instructions +used for procedure calls and returns to be effective. For RISC-V, hints as to +the instructions' usage are encoded implicitly via the register numbers used. +The return-address stack (RAS) actions to pop and/or push onto the RAS are +specified in <>. + +Using `x1` or `x5` as the link register allows a program to benefit from the +return-address prediction stacks. Additionally, since the shadow stack +instructions are designed around the use of `x1` or `x5` as the link register, +using any other register as a link register would incur the cost of additional +register movements. + +Compilers, when generating code with backward-edge CFI, must protect the link +register, e.g., `x1` and/or `x5`, from arbitrary modification by not emitting +unsafe code sequences. +==== + +<<< + +[NOTE] +==== +Storing the return address on both stacks preserves the call stack layout and +the ABI, while also allowing for the detection of corruption of the return +address on the regular stack. The prologue and epilogue of a non-leaf function +that uses shadow stacks is as follows: + +[listing] +---- + function_entry: + addi sp,sp,-8 # push link register x1 + sd x1,(sp) # on regular stack + sspush x1 # push link register x1 on shadow stack + : + ld x1,(sp) # pop link register x1 from regular stack + addi sp,sp,8 + sspopchk x1 # fault if x1 not equal to shadow return address + ret +---- + +This example illustrates the use of `x1` register as the link register. +Alternatively, the `x5` register may also be used as the link register. + +A leaf function, a function that does not itself make function calls, does +not need to spill the link register. Consequently, the return value may be held +in the link register itself for the duration of the leaf function's execution. +==== + +The `C.SSPOPCHK`, and `SSPOPCHK` instructions perform a load identically to the +existing load instructions, with the difference that the base is implicitly +`ssp` and the width is implicitly `XLEN`. + +The operation of the `SSPOPCHK` and `C.SSPOPCHK` instructions is as follows: + +.`SSPOPCHK` and `C.SSPOPCHK` operation +[listing] +---- +if (xSSE == 1) + temp = mem[ssp] # Load temp from address in ssp and + if temp != X(src) # Compare temp to value in src and + # cause an software-check exception + # if they are not bitwise equal. + # Only x1 and x5 may be used as src + raise software-check exception + else + ssp = ssp + (XLEN/8) # increment ssp by XLEN/8. + endif +endif +---- + +If the value loaded from the address in `ssp` does not match the value in `rs1`, +a software-check exception (cause=18) is raised with `__x__tval` set to "shadow +stack fault (code=3)". The software-check exception caused by `SSPOPCHK`/ +`C.SSPOPCHK` is lower in priority than a load/store/AMO access-fault exception. + +The `ssp` is incremented by `SSPOPCHK` and `C.SSPOPCHK` only if the load from +the shadow stack completes successfully and no software-check exception is +raised. + +<<< + +[NOTE] +==== +The use of the compressed instruction `C.SSPUSH x1` to push on the shadow stack +is most efficient when the ABI uses `x1` as the link register, as the link +register may then be pushed without needing a register-to-register move in the +function prologue. To use the compressed instruction `C.SSPOPCHK x5`, the +function should pop the return address from regular stack into the alternate +link register `x5` and use the `C.SSPOPCHK x5` to compare the return address to +the shadow copy stored on the shadow stack. The function then uses `C.JR x5` to +jump to the return address. + +[listing] +---- + function_entry: + c.addi sp,sp,-8 # push link register x1 + c.sd x1,(sp) # on regular stack + c.sspush x1 # push link register x1 on shadow stack + : + c.ld x5,(sp) # pop link register x5 from regular stack + c.addi sp,sp,8 + c.sspopchk x5 # fault if x5 not equal to shadow return address + c.jr x5 +---- + +==== + +[NOTE] +==== +Store-to-load forwarding is a common technique employed by high-performance +processor implementations. Zicfiss implementations may prevent forwarding from +a non-shadow-stack store to the `SSPOPCHK` or the `C.SSPOPCHK` instructions. A +non-shadow-stack store causes a fault if done to a page mapped as a shadow +stack. However, such determination may be delayed till the PTE has been examined +and thus may be used to transiently forward the data from such stores to +`SSPOPCHK` or to `C.SSPOPCHK`. +==== + +<<< + +[[SSP_READ]] +==== Read `ssp` into a Register + +The `SSRDP` instruction is provided to move the contents of `ssp` to a destination +register. + +[wavedrom, ,svg] +.... +{reg: [ + {bits: 7, name: 'opcode', attr:'SYSTEM'}, + {bits: 5, name: 'rd', attr:['dst']}, + {bits: 3, name: 'funct3', attr:['100']}, + {bits: 5, name: '00000'}, + {bits: 12, name: '110011011100', attr:['SSRDP']}, +], config:{lanes: 1, hspace:1024}} +.... + +Encoding `rd` as `x0` is not supported for `SSRDP`. + +The operation of the `SSRDP` instructions is as follows: + +.`SSRDP` operation +[listing] +---- +if (xSSE == 1) + X(dst) = ssp +else + X(dst) = 0 +endif +---- + +[NOTE] +==== +The property of Zimop writing 0 to the `rd` when the extension using Zimop is +not implemented or not active may be used by to determine if Zicfiss extension +is active. For example, functions that unwind shadow stacks may skip over the +unwind actions by dynamically detecting if the Zicfiss extension is active. + +An example sequence such as the following may be used: + +[listing] + ssrdp t0 # mv ssp to t0 + beqz t0, zicfiss_not_active # zero is not a valid shadow stack + # pointer by convention + # Zicfiss is active + : + : +zicfiss_not_active: + +To assist with the use of such code sequences, operating systems and runtimes +must not locate shadow stacks at address 0. +==== + +<<< + +[NOTE] +==== +A common operation performed on stacks is to unwind them to support constructs +like `setjmp`/`longjmp`, C++ exception handling, etc. A program that uses shadow +stacks must unwind the shadow stack in addition to the stack used to store data. +The unwind function must verify that it does not accidentally unwind past the +bounds of the shadow stack. Shadow stacks are expected to be bounded on each end +using guard pages. A guard page for a stack is a page that is not accessible by +the process that owns the stack. To detect if the unwind occurs past the bounds +of the shadow stack, the unwind may be done in maximal increments of 4 KiB, +testing whether the `ssp` is still pointing to a shadow stack page or has +unwound into the guard page. The following examples illustrate the use of shadow +stack instructions to unwind a shadow stack. This example assumes that the +`setjmp` function itself does not push on to the shadow stack (being a leaf +function, it is not required to). + +[listing] +setjmp() { + : + : + // read and save the shadow stack pointer to jmp_buf + asm("ssrdp %0" : "=r"(cur_ssp):); + jmp_buf->saved_ssp = cur_ssp; + : + : +} +longjmp() { + : + // Read current shadow stack pointer and + // compute number of call frames to unwind + asm("ssrdp %0" : "=r"(cur_ssp):); + // Skip the unwind if backward-edge CFI not active + asm("beqz %0, back_cfi_not_active" : "=r"(cur_ssp):); + // Unwind the frames in a loop + while ( jmp_buf->saved_ssp > cur_ssp ) { + // advance by a maximum of 4K at a time to avoid + // unwinding past bounds of the shadow stack + cur_ssp = ( (jmp_buf->saved_ssp - cur_ssp) >= 4096 ) ? + (cur_ssp + 4096) : jmp_buf->saved_ssp; + asm("csrw ssp, %0" : : "r" (cur_ssp)); + // Test if unwound past the shadow stack bounds + asm("sspush x5"); + asm("sspopchk x5"); + } +back_cfi_not_active: + : +} +==== + +<<< + +[[SSAMOSWAP]] +==== Atomic Swap from a Shadow Stack Location + +[wavedrom, ,svg] +.... +{reg: [ + {bits: 7, name: 'opcode', attr:'AMO'}, + {bits: 5, name: 'rd', attr:'dest'}, + {bits: 3, name: 'funct3', attr:['010', '011']}, + {bits: 5, name: 'rs1', attr:'addr'}, + {bits: 5, name: 'rs2', attr:'src'}, + {bits: 1, name: 'rl'}, + {bits: 1, name: 'aq'}, + {bits: 5, name: '01001', attr:['SSAMOSWAP.W', 'SSAMOSWAP.D']}, +], config:{lanes: 1, hspace:1024}} +.... + +For RV32, `SSAMOSWAP.W` atomically loads a 32-bit data value from address of a +shadow stack location in `rs1`, puts the loaded value into register `rd`, and +stores the 32-bit value held in `rs2` to the original address in `rs1`. +`SSAMOSWAP.D` (RV64 only) is similar to `SSAMOSWAP.W` but operates on 64-bit +data values. + +.`SSAMOSWAP.W` for RV32 and `SSAMOSWAP.D` (RV64 only) operation +[listing] +---- + if privilege_mode != M && menvcfg.SSE == 0 + raise illegal-instruction exception + if S-mode not implemented + raise illegal-instruction exception + else if privilege_mode == U && senvcfg.SSE == 0 + raise illegal-instruction exception + else if privilege_mode == VS && henvcfg.SSE == 0 + raise virtual instruction exception + else if privilege_mode == VU && senvcfg.SSE == 0 + raise virtual instruction exception + else + X(rd) = mem[X(rs1)] + mem[X(rs1)] = X(rs2) + endif +---- + +For RV64, `SSAMOSWAP.W` atomically loads a 32-bit data value from address of a +shadow stack location in `rs1`, sign-extends the loaded value and puts it in +`rd`, and stores the lower 32 bits of the value held in `rs2` to the original +address in `rs1`. + +.`SSAMOSWAP.W` for RV64 +[listing] +---- + if privilege_mode != M && menvcfg.SSE == 0 + raise illegal-instruction exception + if S-mode not implemented + raise illegal-instruction exception + else if privilege_mode == U && senvcfg.SSE == 0 + raise illegal-instruction exception + else if privilege_mode == VS && henvcfg.SSE == 0 + raise virtual instruction exception + else if privilege_mode == VU && senvcfg.SSE == 0 + raise virtual instruction exception + else + temp[31:0] = mem[X(rs1)] + X(rd) = SignExtend(temp[31:0]) + mem[X(rs1)] = X(rs2)[31:0] + endif +---- + +Just as for AMOs in the A extension, `SSAMOSWAP.W/D` requires that the address +held in `rs1` be naturally aligned to the size of the operand (i.e., eight-byte +aligned for __doublewords__, and four-byte aligned for __words__). The same +exception options apply if the address is not naturally aligned. + +Just as for AMOs in the A extension, `SSAMOSWAP.W/D` optionally provides release +consistency semantics, using the `aq` and `rl` bits, to help implement +multiprocessor synchronization. An `SSAMOSWAP.W/D` operation has acquire +semantics if `aq=1` and release semantics if `rl=1`. + +[NOTE] +==== +Stack switching is a common operation in user programs as well as supervisor +programs. When a stack switch is performed the stack pointer of the currently +active stack is saved into a context data structure and the new stack is made +active by loading a new stack pointer from a context data structure. + +When shadow stacks are active for a program, the program needs to additionally +switch the shadow stack pointer. If the pointer to the top of the deactivated +shadow stack is held in a context data structure, then it may be susceptible to +memory corruption vulnerabilities. To protect the pointer value, the program may +store it at the top of the deactivated shadow stack itself and thereby create a +checkpoint. A legal checkpoint is defined as one that holds a value of `X`, +where `X` is the address at which the checkpoint is positioned on the shadow +stack. +==== + +[NOTE] +==== +An example sequence to restore the shadow stack pointer from the new shadow +stack and save the old shadow stack pointer on the old shadow stack is as +follows: + +[listing] +---- +# a0 hold pointer to top of new shadow stack to switch to +stack_switch: + ssrdp ra + beqz ra, 2f # skip if Zicfiss not active + ssamoswap.d ra, x0, (a0) # ra=*[a0] and *[a0]=0 + beq ra, a0, 1f # [a0] must be == [ra] + unimp # else crash +1: addi ra, ra, XLEN/8 # pop the checkpoint + csrrw ra, ssp, ra # swap ssp: ra=ssp, ssp=ra + addi ra, ra, -(XLEN/8) # checkpoint = "old ssp - XLEN/8" + ssamoswap.d x0, ra, (ra) # Save checkpoint at "old ssp - XLEN/8" +2: +---- + +This sequence uses the `ra` register. If the privilege mode at which this +sequence is executed can be interrupted, then the trap handler should save the +`ra` on the shadow stack itself. There it is guarded against tampering and +can be restored prior to returning from the trap. + +When a new shadow stack is created by the supervisor, it needs to store a +checkpoint at the highest address on that stack. This enables the shadow stack +pointer to be switched using the process outlined in this note. The +`SSAMOSWAP.W/D` instruction can be used to store this checkpoint. When the old +value at the memory location operated on by `SSAMOSWAP.W/D` is not required, +`rd` can be set to `x0`. +====