# DF-5620 — rejected `_CST` MWAIT descriptors still mutate the global C3
# preamble (`cpu_mwait_cx_no_bmsts/bmarb` on the error path)

## Root cause

`acpi_cst_cx_mwait_setup()` (sys/platform/pc64/acpica/acpi_cstate_machdep.c)
jumps to its `done:` block on the **invalid-mwait-hint** rejection path
(:161-165) and the `done:` block unconditionally executes the descriptor's
global side effects:

```c
	if (!cpu_mwait_hint_valid(eax_hint)) {
		kprintf("C%d: invalid mwait hint 0x%08x\n", ...);
		error = EINVAL;
		goto done;                    /* <-- side effects still run */
	}
	...
done:
	if ((cx->gas.AccessWidth & ACPI_GAS_INTEL_ARG1_BM_STS) == 0 && ...) {
		cpu_mwait_cx_no_bmsts();     /* clears cpu_mwait_c3_preamble BM_STS */
		...
	}
	if (cx->type < ACPI_STATE_C3 && MWAIT_EAX_TO_CX(eax_hint) >= 3) {
		cpu_mwait_cx_no_bmsts();     /* again */
		cpu_mwait_cx_no_bmarb();     /* clears BM_ARB  (deep-mapping) */
	}
```

`cpu_mwait_c3_preamble` governs the C3 entry discipline of the
machine-dependent MWAIT idle loop on **every** CPU
(sys/platform/pc64/x86_64/machdep.c:3400-3409).  A descriptor the kernel
itself rejected as invalid therefore globally disables the C3 BM_STS check
and bus-master-arbitration avoidance.  The EOPNOTSUPP paths (no MONITOR /
no MWAIT extensions, :149-153) already return without side effects — only
the hint-rejection path is inconsistent.

## Live proof (differential, single variable = the _CST content)

Planted `_CST` = valid C1 + C2 FixedHW/native (Intel FFH encoding,
BitOffset=2) with **invalid** mwait hint `0x99999971`
(`MWAIT_EAX_TO_CX(0x71) = 8 >= CPU_MWAIT_CX_MAX`).  QEMU 7.2 never
advertises CPUID.05H.ECX.EXT, so the `mwaitforce.ko` helper substitutes
only the missing CPUID advertisement (DF-5155 "honest gap" methodology,
same as DF-5483's estforce.ko); every subsequent instruction is the stock
driver.

| boot                                            | console                                | machdep.mwait.CX.c3_preamble |
|-------------------------------------------------|----------------------------------------|------------------------------|
| control _CST (C1 only)      + mwaitforce        | (no mwait lines)                       | **3**                        |
| attack  _CST (C1 + badhint) + mwaitforce        | `C2: invalid mwait hint 0x99999971` x6 | **0**                        |
| attack  _CST, **fixed kernel** (fix.diff)       | `C2: invalid mwait hint 0x99999971` x6 | **3**  (side effects gone)   |

Guest stays healthy in all three (the valid C1 is used: `cx_supported: C1/1`)
— the rejected C2 never enters the idle table, yet on the stock kernel it
rewired the global C3 preamble for the whole system.

## Build

    # module (guest): /tmp/mwf with KMOD= mwaitforce SRCS= mwaitforce.c
    make SYSDIR=/usr/src/sys && cp mwaitforce.ko /boot/kernel/
    echo mwaitforce_load=YES >> /boot/loader.conf
    # blob: see DF-5619 pack (mkdsdt.py badhint + gen_cst.py -> cst_badhint.bin)

## Run

    vm.sh down
    QEMU_EXTRA="-machine accel=tcg -cpu SandyBridge \
      -device loader,file=/tmp/opencode/cst_badhint.bin,addr=0x40000000,force-raw=on" vm.sh up 240
    vm.sh run_root 'sysctl machdep.mwait.CX.c3_preamble; dmesg | grep -a "invalid mwait"'

## Expected decisive evidence

* run.attack_sysctl.txt — `c3_preamble: 0` + the "invalid mwait hint"
  rejection lines (state rejected) on the stock kernel.
* run.control_sysctl.txt — `c3_preamble: 3` with the same module/CPU/boot.
* run.fixedkernel_badhint.txt — `c3_preamble: 3`, rejection lines unchanged,
  on the kernel rebuilt with fix.diff.
