# DF-4785 — psp_v11_0_init_microcode never loads ASD firmware for Vega20

## What

`psp_v11_0_init_microcode()` (sys/dev/drm/amd/amdgpu/psp_v11_0.c:95-143)
requests and parses ONLY the SOS firmware. It never touches
`adev->psp.asd_fw` / `asd_start_addr` / `asd_ucode_size`, unlike both
siblings in the same tree — `psp_v3_1_init_microcode()`
(psp_v3_1.c:126-143, "amdgpufw_%s_asd") and `psp_v10_0_init_microcode()`
(psp_v10_0.c:126-140) — and unlike upstream Linux, whose psp_v11_0 loads
`vega20_asd.bin` (verified in mainline v5.2's psp_v11_0.c; upstream also
loads vega20_ta.bin later).

The generic PSP start path is ASIC-agnostic:
`psp_hw_start()` (amdgpu_psp.c:268-296) unconditionally calls
`psp_asd_load()` (amdgpu_psp.c:238-266), which does:

```c
memset(psp->fw_pri_buf, 0, PSP_1_MEG);
memcpy(psp->fw_pri_buf, psp->asd_start_addr, psp->asd_ucode_size);  /* NULL, 0 */
psp_prep_asd_cmd_buf(cmd, psp->fw_pri_mc_addr, psp->asd_shared_mc_addr,
                     0 /* asd_ucode_size */, PSP_ASD_SHARED_MEM_SIZE);
ret = psp_cmd_submit_buf(psp, NULL, cmd, psp->fence_buf_mc_addr, 2);
```

For Vega20 (CH_VEGA20 → psp_v11_0_set_psp_funcs, amdgpu_psp.c:61-62) this
executes on every PSP-mode boot with `asd_start_addr == NULL` and
`asd_ucode_size == 0` (adev is zero-allocated):

1. `memcpy(dst, NULL, 0)` — undefined behavior per ISO C; benign on
   DragonFly's current x86_64 memcpy (zero-length is a no-op) but a latent
   portability/panic hazard;
2. a `GFX_CMD_ID_LOAD_ASD` command with `app_len == 0` and a NULL-derived
   payload is submitted to the PSP. When the PSP rejects it, the rejection
   is INVISIBLE: `psp_cmd_submit_buf()` checks `resp.status` only when
   `ucode != NULL` (amdgpu_psp.c:137-142 — the DF-4768 flaw). The driver
   then proceeds through `psp_np_fw_load()` believing ASD was loaded,
   with the PSP's ASD application never actually running.

## Threat model

Not attacker-triggered: this fires deterministically at driver
initialization on Vega20 (AMD Radeon VII / MI50 / MI60 class) hardware
with `amdgpu` in PSP firmware-load mode. There is no user-controlled
input. Impact is robustness + silent-security-state divergence of the
secure-processor driver chain:

- UB memcpy with NULL source (latent panic on any memcpy implementation
  that pre-touches src, e.g. vectorized copies doing aligned prefetch —
  gcc-8-era DF is safe, this is defense-in-depth),
- swallowed PSP error → the "secure OS driver loaded" assumption of all
  later PSP commands is wrong on this ASIC (the DF-4768 silent-success
  class hitting v11 through the sysdrv/ASD path),
- interaction with the unbounded fence wait
  (`while (*fence_buf != index) msleep(1)`, amdgpu_psp.c:133-135): if the
  PSP FW did not write the fence for the rejected command, boot would hang
  forever in-kernel; on real Vega20 PSP FW the fence is written, which is
  why this has gone unnoticed.

Severity Low (no attacker control, affected hardware barely supported on
DF, memory-safety manifestation benign on current toolchain).

## Fix

`fix.diff` (this pack): mirror `psp_v3_1_init_microcode()` — load
"amdgpufw_vega20_asd", validate, and populate `asd_fw_version`,
`asd_feature_version`, `asd_ucode_size`, `asd_start_addr`, plus release on
the error path and the MODULE_FIRMWARE declaration. Equivalent alternative
(one line, in amdgpu_psp.c): skip `psp_asd_load()` when `psp->asd_fw ==
NULL` for ASICs without ASD. The upstream-identical choice is loading ASD.

## Verification status

**untested — not testable on this guest**: the audit QEMU guest has no AMD
GPU at all (pciconf shows only Intel/QEMU virtual PCI: hostb, isab, atapci,
vgapci0 chip=0x11111234, virtio; no AMD display device), so the amdgpu/PSP
probe never runs and `psp_v11_0_*` is unreachable. The divergence itself is
fully proven from source (siblings + upstream comparison); no PoC can
execute the path without Vega20 hardware.
