โฌข DragonFlyBSD Kernel Audit
DF-5617 / df5617.c
โ† back to finding โ†“ download raw
/*
 * DF-5617 harness โ€” ig4 ACPI attach-error unwinder re-enters the serializer
 * leaked by ig4_iic.c's bus_setup_intr error path (ACPI twin of DF-5601).
 *
 * Target chain (all production code except the failure injection):
 *   ig4_acpi.c:104   lwkt_serialize_init(&sc->slz)
 *   ig4_acpi.c:108   sc->version = IG4_ATOM
 *   ig4_acpi.c:127   sc->pci_attached = 1
 *   ig4_acpi.c:132   error = ig4iic_attach(sc)      (REAL, stock ig4.ko)
 *     ig4_iic.c:648    lwkt_serialize_enter(&sc->slz)
 *     ig4_iic.c:649    error = bus_setup_intr(...)   <- injected EINVAL
 *     ig4_iic.c:651-655 if (error) goto done;        <-- returns with the
 *                        serializer STILL HELD (no lwkt_serialize_exit)
 *   ig4_acpi.c:133-134 if (error) ig4iic_acpi_detach(dev)
 *     ig4_acpi.c:146-147  pci_attached -> ig4iic_detach(sc)   (REAL)
 *       ig4_iic.c:678       lwkt_serialize_enter(&sc->slz)    <-- re-entry!
 *                           sys/serialize.h:41-42: the serializer is
 *                           "not safe from deadlocks nor is it recursive"
 *
 * On X86_64_GENERIC (options INVARIANTS) lwkt_serialize_enter's
 * ASSERT_NOT_SERIALIZED KKASSERT-panics; without INVARIANTS the same path
 * self-deadlocks in atomic_intr_cond_enter().
 *
 * The bus_setup_intr failure is the only synthetic input: on real ACPI
 * hardware it is any rman_activate_resource()/register_int() failure
 * (nexus.c:519-532) โ€” e.g. an exclusive-owner conflict on the GSI.  The
 * planted-ACPI runs in this evidence pack (ssdt_ig4p.aml, dance logs) drove
 * the REAL ig4iic_acpi_probe/_CRS-translation/ig4iic_acpi_attach path end to
 * end and show every other step is reachable and live on this guest.
 *
 * Run: kldload ig4; kldload df56.ko; kldload df5617.ko
 * Success criterion (baseline): panic in lwkt_serialize_enter <-
 * ig4iic_detach <- df5617_attach, with the dmesg trail showing
 * "Unable to setup irq" then "serializer is HELD".
 * With the fixed ig4.ko (ig4_iic.c error path releases the serializer):
 * ig4iic_detach() returns 0, guest stays alive.
 */
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/systm.h>
#include <sys/bus.h>
#include <sys/malloc.h>
#include <sys/serialize.h>
#include <sys/kthread.h>

#include "ig4_reg.h"
#include "ig4_var.h"

static volatile int df5617_mirror_run = 1;
static struct thread *df5617_mirror_td;

/*
 * Mirror kthread: emulates the DW ENABLE_STATUS latch by reflecting the
 * IG4_I2C_ENABLE bit written to I2C_EN (0x6c) into ENABLE_STATUS (0x9c).
 * set_controller() polls with 1-tick tsleeps and 100 retries, so a 1-tick
 * mirror cadence always keeps up.
 */
static void
df5617_mirror(void *arg)
{
	volatile uint32_t *f = arg;

	kprintf("DF5617: mirror thread up (emulates ENABLE_STATUS latch)\n");
	while (df5617_mirror_run) {
		f[IG4_REG_ENABLE_STATUS / 4] =
		    f[IG4_REG_I2C_EN / 4] & IG4_I2C_ENABLE;
		tsleep(&df5617_mirror_run, 0, "df56mir", 1);
	}
	kprintf("DF5617: mirror thread exiting\n");
	lwkt_exit();
	/* NOT REACHED */
}

static int
df5617_probe(device_t dev)
{
	device_set_desc(dev, "DF-5617 ig4 ACPI attach-error unwinder scenario");
	return (0);
}

/*
 * Replay of ig4_acpi.c's unwinder body (ig4iic_acpi_detach, lines 141-169),
 * which is static in ig4_acpi.c so it cannot be called directly.  Everything
 * up to ig4iic_detach()'s lwkt_serialize_enter is identical; the panic (or,
 * on a fixed kernel, the clean return) happens INSIDE the production
 * ig4iic_detach() before any of the later release lines matter, so the
 * harness skips the bus_release_resource()/D3 tail to stay
 * allocation-neutral on a fixed kernel.
 */
static int
df5617_acpi_detach_replay(device_t dev, ig4iic_softc_t *sc)
{
	int error;

	if (sc->pci_attached) {			/* ig4_acpi.c:146 */
		error = ig4iic_detach(sc);	/* ig4_acpi.c:147 (REAL) */
		if (error)
			return error;
		sc->pci_attached = 0;
	}
	/* ig4_acpi.c:153-166: release intr_res/regs_res, D3 โ€” after the
	 * decision point; intentionally not replayed (fake resources). */
	return (0);
}

static int
df5617_attach(device_t dev)
{
	ig4iic_softc_t *sc;
	volatile uint32_t *f;
	int error;

	f = kmalloc(0x1000, M_DEVBUF, M_WAITOK | M_ZERO);
	sc = kmalloc(sizeof(*sc), M_DEVBUF, M_WAITOK | M_ZERO);
	if (f == NULL || sc == NULL)
		return (ENOMEM);

	f[0x0F4 / 4] = 0x0000003F;		/* COMP_PARAM1 */
	f[0x0F8 / 4] = 0x7FFFFFFF;		/* COMP_VER (>= 0x3131352A) */
	f[0x0FC / 4] = 0x7FFFFFFF;		/* COMP_TYPE */

	/* ---- ig4iic_acpi_attach() prologue replay (ig4_acpi.c:99-127) ---- */
	/* ig4_acpi.c: no bzero in the ACPI glue (softc is M_ZERO-allocated
	 * by newbus, subr_bus.c:1953) โ€” the DF-5600 twin-check */
	lwkt_serialize_init(&sc->slz);		/* ig4_acpi.c:104 */
	sc->dev = dev;				/* ig4_acpi.c:106 */
	sc->version = IG4_ATOM;			/* ig4_acpi.c:108 */
	sc->regs_rid = 0;			/* ig4_acpi.c:109 */
	/* ig4_acpi.c:110-116: bus_alloc_resource_any(SYS_RES_MEMORY) */
	sc->regs_t = 1;				/* mem space; tag unused */
	sc->regs_h = (bus_space_handle_t)f;
	sc->intr_rid = 0;			/* ig4_acpi.c:117 */
	sc->intr_res = (struct resource *)dev;	/* ig4_acpi.c:118; never
						 * dereferenced: the
						 * pseudo-bus setup_intr
						 * fails before touching it */
	sc->regs_t = 1;				/* ig4_acpi.c:125 */
	sc->regs_h = (bus_space_handle_t)f;	/* ig4_acpi.c:126 */
	sc->pci_attached = 1;			/* ig4_acpi.c:127 */

	kthread_create(df5617_mirror, __DEVOLATILE(void *, f),
	    &df5617_mirror_td, "df5617mir");

	kprintf("DF5617: replaying ig4_acpi.c:132 ig4iic_attach() ...\n");
	error = ig4iic_attach(sc);		/* ig4_acpi.c:132 (REAL) */
	kprintf("DF5617: ig4iic_attach() = %d (EINVAL from bus_setup_intr)\n",
	    error);
	kprintf("DF5617: serializer is %s by the ig4_iic.c:648-655 error path "
	    "(IS_SERIALIZED: %d)\n",
	    IS_SERIALIZED(&sc->slz) ? "HELD" : "released",
	    IS_SERIALIZED(&sc->slz));
	kprintf("DF5617: replaying ig4_acpi.c:133-134 unwinder -> "
	    "ig4iic_acpi_detach() -> ig4_acpi.c:146-147 ig4iic_detach().  "
	    "If the serializer is held, lwkt_serialize_enter at ig4_iic.c:678 "
	    "self-deadlocks/panics NOW.\n");

	error = df5617_acpi_detach_replay(dev, sc);	/* ig4_acpi.c:134 */
	kprintf("DF5617: ig4iic_acpi_detach replay = %d -- returned cleanly, "
	    "NO PANIC (fix effective)\n", error);

	df5617_mirror_run = 0;
	wakeup(&df5617_mirror_run);
	return (0);
}

static device_method_t df5617_methods[] = {
	DEVMETHOD(device_probe,		df5617_probe),
	DEVMETHOD(device_attach,	df5617_attach),
	DEVMETHOD_END
};

static driver_t df5617_driver = { "df5617ig4", df5617_methods, 0 };
static devclass_t df5617_devclass;

DRIVER_MODULE(df5617ig4, df56bus, df5617_driver, df5617_devclass, NULL, NULL);

static int
df5617_load(struct module *m, int what, void *arg)
{
	device_t bus;
	device_t dev;
	int error;

	switch (what) {
	case MOD_LOAD:
		bus = devclass_get_device(devclass_find("df56bus"), 0);
		if (bus == NULL) {
			kprintf("DF5617: df56bus0 not found; load df56.ko "
			    "first\n");
			return (ENXIO);
		}
		kprintf("DF5617: scenario armed under %s\n",
		    device_get_nameunit(bus));
		dev = device_add_child(bus, "df5617ig4", -1);
		if (dev == NULL)
			return (ENOMEM);
		error = device_probe_and_attach(dev);
		kprintf("DF5617: scenario device attach = %d\n", error);
		break;
	default:
		break;
	}
	return (0);
}

static moduledata_t df5617_mod = { "df5617", df5617_load, NULL };
DECLARE_MODULE(df5617, df5617_mod, SI_SUB_DRIVERS, SI_ORDER_ANY);