Summary
The 48-bit I3C Provisioned ID (PID) registers in the standby-controller CSR block are plain read/write storage flops with no hardware lock, no fuse/OTP source, and no write-enable qualifier. Any agent able to write the CSR/AXI interface (i.e. MCU firmware) can overwrite the PID to an arbitrary value at any time. Combined with hardcoded FUTUREFIX test reset values, a device's I3C identity is today neither unique-per-device nor protected against modification by local firmware.
Raising this to understand the intended design: is the PID meant to be fuse-fed, provisioned-then-locked, or is identity integrity expected to be enforced by the integrator outside i3c-core?
Affected registers
| Register |
Offset |
Target |
I3C_EC.StdbyCtrlMode.STBY_CR_DEVICE_PID_LO.PID_LO |
0x19c |
main |
I3C_EC.StdbyCtrlMode.STBY_CR_VIRTUAL_DEVICE_PID_LO.PID_LO |
0x1a4 |
virtual |
STBY_CR_DEVICE_CHAR.PID_HI |
0x198 |
main (PID_HI) |
STBY_CR_VIRTUAL_DEVICE_CHAR.PID_HI |
0x190 |
virtual (PID_HI) |
Evidence (RTL)
RDL field — hw = r; sw = rw, reset = test constant, flagged FUTUREFIX (src/rdl/standby_controller_mode.rdl):
field { name = "PID_LO"; hw = r; sw = rw;
reset = 32'h005A00A5; // FUTUREFIX: was `pid_lo_reset`
} PID_LO[31:0];
Generated CSR write logic — the only gate on the write is "register addressed + is-write"; no lock/enable term (src/csr/I3CCSR.sv:7363-7385):
if(decoded_reg_strb...STBY_CR_DEVICE_PID_LO && decoded_req_is_wr) begin // SW write
next_c = (old & ~wr_biten) | (wr_data & wr_biten);
load_next_c = '1;
end
// ...
if(~rst_ni) field_storage...PID_LO.value <= 32'h5a00a5; // power-on default only
else if(load_next) field_storage...PID_LO.value <= next_c; // accepts ANY sw write
Confirmed absent across the entire i3c-core RTL for these fields:
- no
swwe / swwel / .wel software-write-enable or write-lock
- no lifecycle / LC-state gate on the write
- no
hwif_in load path (no fuse/OTP autoload — hw=r only consumes the value for ENTDAA)
- no
lock / otp / fuse signal touching PID or device-char fields
The virtual-target field (src/csr/I3CCSR.sv:7724-7746) is identical (reset 0x5a10a5).
ROM does not program these either — rom/src/i3c.rs I3c::configure() sets static address, ENTDAA/SETDASA enable, timing, queues, recovery FIFO, and PHY enable, but never writes PID_LO / PID_HI / device-char. Shipping parts therefore retain the RTL reset constant.
Observations
- Mutable by local firmware. The reset value is a power-on default, not an immutability guarantee; compromised/rogue MCU firmware can rewrite the PID (e.g. to clone or spoof an identity). The external I3C bus side is read-only toward these fields, so the exposure is specifically local CSR/AXI writes.
- Not unique per device. With no fuse source wired in, every device defaults to the same
0x005A00A5 (main) / 0x005A10A5 (virtual) PID.
- Applies to both targets the core exposes (main + virtual).
Possible directions (for discussion)
- Fuse-sourced: drive
PID_LO / PID_HI from OTP via a hwif_in autoload and make the field sw = r.
- Sticky write-once lock: add a
swwel-gated lock bit that clears only on cold reset (program-once, then RO).
- Lifecycle-gated write: allow writes only in a manufacturing/provisioning LC phase, disabled in production.
- Replace/zero the
FUTUREFIX reset constants so an unprovisioned part is detectable rather than shipping a fixed value.
Questions
- What is the intended provisioning model for the I3C PID — fuse-fed, provisioned-then-locked, or ROM-programmed each boot from a trusted source? The current RTL supports none of these as a protected path.
- Is identity immutability expected to be enforced inside
i3c-core, or is the integrator expected to add it externally (e.g. a wrapper/AXI access filter gating the CSR region)?
Summary
The 48-bit I3C Provisioned ID (PID) registers in the standby-controller CSR block are plain read/write storage flops with no hardware lock, no fuse/OTP source, and no write-enable qualifier. Any agent able to write the CSR/AXI interface (i.e. MCU firmware) can overwrite the PID to an arbitrary value at any time. Combined with hardcoded
FUTUREFIXtest reset values, a device's I3C identity is today neither unique-per-device nor protected against modification by local firmware.Raising this to understand the intended design: is the PID meant to be fuse-fed, provisioned-then-locked, or is identity integrity expected to be enforced by the integrator outside
i3c-core?Affected registers
I3C_EC.StdbyCtrlMode.STBY_CR_DEVICE_PID_LO.PID_LO0x19cI3C_EC.StdbyCtrlMode.STBY_CR_VIRTUAL_DEVICE_PID_LO.PID_LO0x1a4STBY_CR_DEVICE_CHAR.PID_HI0x198STBY_CR_VIRTUAL_DEVICE_CHAR.PID_HI0x190Evidence (RTL)
RDL field —
hw = r; sw = rw, reset = test constant, flaggedFUTUREFIX(src/rdl/standby_controller_mode.rdl):Generated CSR write logic — the only gate on the write is "register addressed + is-write"; no lock/enable term (
src/csr/I3CCSR.sv:7363-7385):Confirmed absent across the entire
i3c-coreRTL for these fields:swwe/swwel/.welsoftware-write-enable or write-lockhwif_inload path (no fuse/OTP autoload —hw=ronly consumes the value for ENTDAA)lock/otp/fusesignal touching PID or device-char fieldsThe virtual-target field (
src/csr/I3CCSR.sv:7724-7746) is identical (reset0x5a10a5).ROM does not program these either —
rom/src/i3c.rsI3c::configure()sets static address, ENTDAA/SETDASA enable, timing, queues, recovery FIFO, and PHY enable, but never writesPID_LO/PID_HI/ device-char. Shipping parts therefore retain the RTL reset constant.Observations
0x005A00A5(main) /0x005A10A5(virtual) PID.Possible directions (for discussion)
PID_LO/PID_HIfrom OTP via ahwif_inautoload and make the fieldsw = r.swwel-gated lock bit that clears only on cold reset (program-once, then RO).FUTUREFIXreset constants so an unprovisioned part is detectable rather than shipping a fixed value.Questions
i3c-core, or is the integrator expected to add it externally (e.g. a wrapper/AXI access filter gating the CSR region)?