11.5-6"Smctr": CTR Behavior — Qualification, Types, RASEMU

Part II Linux boot: optional Vol. II (Privileged) pp. 104–111 · ~4 min read

A transfer is qualified — and becomes logical entry 0, pushing the rest down — when four things align: the current mode’s enable bit is set, its type isn’t filtered out, sctrstatus.FROZEN is clear, and the instruction actually retires. The subtleties are all at privilege boundaries.

Recording across privilege transitions

Table 31 — traps and xRET between modes
Recorded?Rationale
enabled → enabled (trap or xRET)Yes, full source + targetBoth sides already visible to the profiler.
disabled → enabled trapYes, ctrsource = 0The handler entry is profilable; where it came from is not.
enabled → disabled trap ("external trap")Only via the TE opt-in chain, with ctrtarget = 0A disabled mode’s handler address must not leak into a buffer readable below it; TE lets it volunteer the trap’s existence while still zeroing the address.
enabled → disabled xRETYes, ctrtarget = 0The return instruction itself ran in an enabled mode.
disabled → anything xRETNoNothing about a disabled mode’s execution is observable.
anything ↔ Debug ModeNoDebug is invisible by construction.
Dotted-underlined cells have explanations — click one.

Under virtualization the enables come from each world’s own register — sctrctl.S for HS, vsctrctl.S/U for VS/VU — while all other config (filters, RASEMU, freezes) follows vsctrctl whenever V=1. The spec directs hypervisors to clear the S enable before entering a guest: the buffer is shared physical state, and stale host qualification must not bleed into (or out of) the guest’s records.

The type taxonomy

ctrdata.TYPE uses the E-Trace itype encodings: 1 exception, 2 interrupt, 3 trap return, 4 not-taken branch (opt-IN via NTBREN — it doubles record volume), 5 taken branch, then the jump taxonomy 8–15 classified purely by whether rd/rs1 are link registers (x1/x5) — the same Table-3 comparators as Vol I’s RAS hints: indirect/direct call (rd = link), return (JALR x1/x5 with rs1 = link, rd ≠ link), co-routine swap (JALR x1,x5 / JALR x5,x1 / C.JALR x5 — both sides link), plain jumps (no linkage), and “other with linkage”. Zcmt’s cm.jalt counts as direct call, cm.jt as direct jump, Zcmp’s cm.popret as return. Filters are optional to implement, and the spec’s performance clause cuts one way: the default (record-everything) configuration must not cost performance — filtering may.

Cycle counting

CtrCycleCounter accumulates active cycles (stalls in low-power states don’t count) between qualified transfers, then compresses into the record: CC = 12-bit mantissa CCM + 4-bit exponent CCE, value = CCE ? (4096 + CCM) << (CCE−1) : CCM, saturating at the top. It’s a tiny float: undercount averages (2^(CCE−1) − 1)/2, so relative error stays bounded from 1-cycle hot loops to ~2²⁷-cycle stalls. CCV = 0 flags an untrustworthy count; the counter resets on any xctrctl write and on SCTRCLR, so the next record after reconfiguration carries CCV = 0.

RASEMU and freeze

With RASEMU = 1 the buffer inverts from history to shadow call stack: calls push, returns pop (clear entry 0’s V bit, decrement WRPTR), co-routine swaps overwrite entry 0, and every other type is inhibited regardless of filters. Unmodified binaries get hardware-collected call stacks for perf — with honest caveats: only the innermost depth frames survive, and setjmp/longjmp or exception unwinding desyncs the emulated stack.

Hardware sets FROZEN — halting all recording until software clears it — when a local-counter-overflow interrupt traps to M/S (if LCOFIFRZ) or a breakpoint exception targets M/S (if BPFRZ); the freezing trap itself is not recorded. That’s the sampling handshake: the PMU overflow that triggers a perf sample freezes the branch history at exactly that instant, and the handler reads a coherent snapshot.

Hardware Designer Notes

The whole behavioral layer is commit-stage combinational logic plus one saturating counter: type classification from decode bits you already have, a 3-term qualification AND, and the zero-muxes on the two PC fields. The expensive design review is the privilege firewall — walk Table 31 against your trap unit case by case.

Minimal Linux-boot hart MUST

  • Gate record-write at retirement on: mode enable ∧ type not inhibited ∧ !FROZEN — mispredicted-path transfers never record
  • Zero ctrsource on disabled→enabled traps and ctrtarget on enabled→disabled transitions — the privilege firewall lives in this mux
  • Implement the TE chain check as an AND across the TE bits of target and every intervening mode

MAY simplify / trap-and-emulate

  • Tie NTBREN, all filters, MISP, and cycle counting to 0 — a bare push-only recorder is conformant
  • Reuse the branch predictor’s existing rd/rs1 link-register comparators for the TYPE encoder — Table 34 is deliberately identical to Vol I Table 3
  • Report CCV = 0 whenever RASEMU’s pop/push accounting makes cycle attribution ambiguous

Check yourself — CTR behavior

1.A trap goes from CTR-enabled S-mode into CTR-disabled M-mode. When is it recorded?

2.Decode the transfer-type taxonomy: what distinguishes an 'indirect call' from a 'co-routine swap'?

3.The CC field is a 12-bit mantissa + 4-bit exponent 'float'. Why this shape for cycle counts?

4.In RASEMU mode, what happens on a function return?

5.Why must a hypervisor clear sctrctl's S-enable before VS-entry if the guest gets CTR access?

5 questions