/*
 * DF-5452 PoC — nexus_alloc_resource() indexes irq_rman[cpuid] with a
 * debug-only (INVARIANTS-gated) assertion; on production kernels the plain
 * documented API feeds it cpuid = -1.
 *
 *   subr_bus.c:3293-3300  bus_alloc_resource() -> BUS_ALLOC_RESOURCE(...,-1)
 *   nexus.c:376-379        case SYS_RES_IRQ:
 *                           KASSERT(cpuid >= 0 && cpuid < ncpus)   <- NO-OP
 *                           rm = &irq_rman[cpuid];                 <- [-1]
 *   systm.h:94-112         KASSERT/KKASSERT are empty without INVARIANTS
 *
 * This guest kernel is X86_64_GENERIC with INVARIANTS disabled, so the
 * negative index reaches rman_reserve_resource() on the static memory that
 * precedes irq_rman[] — an out-of-bounds struct-rman interpretation whose
 * outcome (silent NULL vs wild list corruption) depends on unrelated statics.
 *
 * Demonstrated contrast on the same free vector:
 *   bus_alloc_legacy_irq_resource() (valid cpuid)  -> succeeds
 *   bus_alloc_resource()           (default -1)    -> silently operates on
 *                                                     &irq_rman[-1] and fails
 */
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/bus.h>
#include <sys/rman.h>
#include <sys/systm.h>

static int	nxirq_probe(device_t);
static int	nxirq_attach(device_t);
static void	nxirq_identify(driver_t *, device_t);

static device_method_t nxirq_methods[] = {
	DEVMETHOD(device_identify,	nxirq_identify),
	DEVMETHOD(device_probe,		nxirq_probe),
	DEVMETHOD(device_attach,	nxirq_attach),
	DEVMETHOD_END
};

static driver_t nxirq_driver = { "nxirq", nxirq_methods, 0 };
static devclass_t nxirq_devclass;
DRIVER_MODULE(nxirq, nexus, nxirq_driver, nxirq_devclass, NULL, NULL);

#define	POC_VEC	5			/* legacy IRQ 5 (free on this box) */

static void
nxirq_identify(driver_t *drv, device_t parent)
{
	if (device_find_child(parent, "nxirq", 0) == NULL)
		BUS_ADD_CHILD(parent, parent, 0, "nxirq", 0);
}

static int
nxirq_probe(device_t dev)
{
	device_set_desc(dev, "nexus irq cpuid PoC");
	return (0);
}

static int
nxirq_attach(device_t dev)
{
	struct resource *r;
	int rid;

	/* leg 1: the helper that supplies a valid cpuid (subr_bus.c:3302) */
	rid = 0;
	r = bus_alloc_legacy_irq_resource(dev, &rid, POC_VEC, RF_SHAREABLE);
	kprintf("DF-5452: legacy helper (valid cpuid):   res=%p rid=%d\n",
	    r, rid);
	if (r != NULL)
		bus_release_resource(dev, SYS_RES_IRQ, rid, r);

	/* leg 2: plain bus_alloc_resource -> cpuid defaults to -1 */
	rid = 1;
	r = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, POC_VEC, POC_VEC, 1,
	    RF_SHAREABLE);
	kprintf("DF-5452: plain API (cpuid -1 default):  res=%p\n", r);
	if (r != NULL)
		bus_release_resource(dev, SYS_RES_IRQ, rid, r);

	kprintf("DF-5452: sizeof(struct rman)=%zu; nexus indexed "
	    "irq_rman[-1] == base-%zu bytes with the KASSERT compiled out\n",
	    sizeof(struct rman), sizeof(struct rman));
	return (0);
}
