/*
 * DF-5230 synthetic trigger: NULL rdev->uvd_fw dereference in
 * uvd_v4_2_resume() (sys/dev/drm/radeon/uvd_v4_2.c:51).
 *
 * Production trigger chain (all links verified path:line in VERDICT.md):
 *
 *   suspend / dpm-disable / pm_fini
 *     -> radeon_pm_suspend_dpm()            radeon_pm.c:1257
 *     -> radeon_dpm_disable(rdev)           radeon.h:2797
 *     -> kv_dpm_disable()                   kv_dpm.c:1305,1316  (bound at
 *                                          radeon_asic.c:2285)
 *     -> kv_dpm_powergate_uvd(rdev,false)   kv_dpm.c:1608
 *        [caps_uvd_pg==true unconditionally kv_dpm.c:2773;
 *         uvd_power_gated was set true at boot by kv_dpm_late_enable()
 *         kv_dpm.c:1300]
 *     -> uvd_v4_2_resume(rdev)              kv_dpm.c:1628   <-- NO has_uvd /
 *                                                          uvd_fw guard here
 *     -> rdev->uvd_fw->datasize             uvd_v4_2.c:51   uvd_fw == NULL
 *
 * This harness reproduces the exact device state a CIK APU has when
 * radeon_uvd_init() failed for lack of the radeonkmsfw_*_uvd firmware kmod
 * (radeon_uvd.c:137 fw_header_present=false; :141/:176 request_firmware
 * failed -> uvd_fw stays NULL; cik.c:8150-8157 has_uvd=0 -- which guards
 * the cik.c:8175 caller but NOT the kv_dpm.c:1628 caller) while DPM was
 * enabled from the SMC firmware, and calls the production
 * uvd_v4_2_resume() compiled into radeon.ko.
 *
 * Expected: immediate kernel panic -- page fault reading
 * offsetof(struct firmware, datasize) == 0x10 from NULL at uvd_v4_2.c:51,
 * BEFORE the first WREG32 (uvd_v4_2.c:52), so no MMIO is touched.
 */
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/systm.h>
#include <sys/malloc.h>

#include <linux/firmware.h>
#include <drm/drmP.h>
#include "radeon.h"
#include "radeon_asic.h"

static int
df5230_modevent(module_t mod, int type, void *data)
{
	struct radeon_device *fake;
	int r;

	switch (type) {
	case MOD_LOAD:
		/* zeroed == post-failed-radeon_uvd_init() state:
		 *   uvd.fw_header_present == false  (radeon_uvd.c:137)
		 *   uvd.gpu_addr         == 0       (pin never happened)
		 *   uvd_fw               == NULL    (request_firmware failed)
		 *   has_uvd              == 0       (cik.c:8156; guards only
		 *                                    the cik.c:8175 caller)
		 */
		fake = kmalloc(sizeof(*fake), M_DRM, M_WAITOK | M_ZERO);
		fake->uvd.max_handles = 10;	/* radeon_uvd.c:138 */

		kprintf("DF5230: pre   uvd_fw=%p fw_header_present=%d "
		    "gpu_addr=0x%jx max_handles=%u\n",
		    fake->uvd_fw, fake->uvd.fw_header_present,
		    (uintmax_t)fake->uvd.gpu_addr, fake->uvd.max_handles);
		kprintf("DF5230: calling production uvd_v4_2_resume() "
		    "(radeon.ko) -- expect panic at uvd_v4_2.c:51\n");

		r = uvd_v4_2_resume(fake);	/* <-- uvd_v4_2.c:51 */

		/* only reachable with the DF-5230 guard applied */
		kprintf("DF5230: survived, uvd_v4_2_resume returned %d\n", r);
		/* fake deliberately leaked: one-shot test module */
		return (0);
	default:
		return (ENOTSUP);
	}
}

static moduledata_t df5230_mod = { "df5230", df5230_modevent, NULL };
DECLARE_MODULE(df5230, df5230_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE);
MODULE_VERSION(df5230, 1);
/* no MODULE_DEPEND: radeon.ko declares no MODULE_VERSION, so a versioned
 * dependency always fails; drm.ko + radeon.ko are pre-loaded manually and
 * DFBSD's linker resolves uvd_v4_2_resume against the loaded files. */
