# DF-5506 PoC — amdsmb poll state machine: all three timeout branches are dead code; wedged EC/controller is reported as SUCCESS with stale data

## Root cause

Every polling loop in `sys/bus/smbus/amdsmb/amdsmb.c` detects its timeout
with `if (x == 0)` after a loop whose exit-on-exhaustion leaves `x == -1`
(post-decrement idiom `while (x-- && cond)`):

- `amdsmb_ec_wait_write` (amdsmb.c:212-221): `while (timeout-- && ...IBF)`
  then `if (timeout == 0)` — when the 500-iteration budget is exhausted,
  `timeout == -1`, the branch never fires, the function returns **0
  ("success")** and the caller *writes the next EC byte anyway* against a
  wedged input path.
- `amdsmb_ec_wait_read` (amdsmb.c:224-235): identical, OBF side — after
  500 polls the caller reads `EC_DATA` and **accepts the stale byte from
  the previous EC data read as the requested register's value**.
- `amdsmb_wait` (amdsmb.c:277-294): `do { DELAY(500); ... } while (temp !=
  0 && count--)` then `if (count == 0) return (SMB_ETIMEOUT)` — `count`
  exits as **-1**, so **`SMB_ETIMEOUT` is unreachable dead code**. A
  controller whose `SMB_PRTCL` never returns to 0 (wedged transaction)
  makes the driver busy-spin **10001 × 500 µs ≈ 5.45 s while holding the
  exclusive `sc->lock`**, then read `SMB_STS`, see 0x00 (OK) and return
  **`SMB_ENOERR` success** — whereupon `amdsmb_bread` returns the *stale
  data and byte count of the previous transaction* as the result of the
  new one.

Compounding it, every `amdsmb_ec_read`/`amdsmb_ec_write` return value in
the request engine is ignored (amdsmb.c:350-351, 368-370, 388-389, 408-411,
429-431, 450-453, 473-475, 501-506, 528-530) — but since the timeout
branches are dead code those functions can never report failure anyway:
the error plumbing of the whole driver is unreachable.

Hardware-gated (AMD-8111, PCI 1022:746a) and reached through `/dev/smbN`
(root-only on stock installs), same evidence class as the ichsmb/ig4
batches.

## Evidence method (production-code KLD harness)

No QEMU machine model contains an AMD-8111 SMBus controller, so the rig
compiles the **real, unmodified production source** (`amdsmb.c`, asserted
byte-identical to `/usr/src` in build.log:1) into a KLD and redirects
`bus_read_1`/`bus_write_1` (`#undef` + re-`#define` before the
`#include`) into a stateful emulation of the AMD-8111 ACPI-EC / SMBus 2.0
register interface (full EC_CMD_RD/WR handshake, IBF/OBF status flags,
SMB register bank 0x00-0x27). The file's own `AMDSMB_ECINB/ECOUTB` macros
expand at their use sites, so the production code executes verbatim,
in-kernel, against the emulated controller.

## Observed (run.log, baseline)

- `T1 wedge_prtcl`: `amdsmb_bread` on a controller whose PRTCL never
  clears → **rc=0 (SMB_ENOERR)** after **5440 ms** / **10002 polls**,
  returning the *previous* transaction's data (0xB0..) and count (32)
  as the new transaction's result. `SMB_ETIMEOUT` (0x20) never returned.
- `T2 wedge_ibf`: `amdsmb_ec_read` after 502 EC_SC polls (budget 500)
  → **rc=0 "success"**; the write proceeded against the wedged EC.
- `T2b wedge_obf`: `amdsmb_ec_read` of SMB_BCNT after 503 polls →
  **rc=0 with the stale EC data byte 0x5C accepted as the register
  value**.

## Observed (run_fixed.log, fix.diff applied to the harness copy only)

- `T1`: **rc=32 (SMB_ETIMEOUT)**, no data copied (buffer untouched).
  The 5.4 s busy-wait remains (the EC interface offers no abort/KILL
  register; the fix detects, it cannot cancel) — noted as residual.
- `T2/T2b`: **rc=1** — the previously-dead `device_printf` timeout
  messages now actually fire ("timeout waiting for IBF to clear" /
  "for OBF to set").
- Sanity `T0` (healthy path) unchanged: rc=0, correct data.

## Build & run

```
guest# cd /root/amdh && sh build.sh          # asserts amdsmb.c == /usr/src copy, builds amdh.ko (-Werror clean)
guest# sh run.sh                             # kldload ./amdh.ko; tests run at MOD_LOAD; output via dmesg
guest# sh build.sh fixed && sh run.sh        # fix.diff applied to the local copy, same tests
```

## Fix

fix.diff: make all three poll loops detect exhaustion (`x-- > 0` +
`if (x < 0)`), and propagate `amdsmb_ec_read` failures in `amdsmb_wait`
as `SMB_EBUSERR` (plus the DF-5507/DF-1091-family clamp in
`amdsmb_bread`). Authored against the read-only `sys/` tree; validated in
the fixed harness build above; never applied to `sys/`.
