/*
 * DF-5450 PoC — nexus_activate_resource() is not idempotent.
 *
 * sys/platform/pc64/x86_64/nexus.c:420-451 maps a SYS_RES_MEMORY resource
 * with pmap_mapdev() BEFORE calling the (idempotent) rman_activate_resource()
 * and overwrites r_virtual with the new mapping.  Activating an already
 * RF_ACTIVE resource therefore allocates a SECOND kernel_map range and fresh
 * RW PTEs for the same physical MMIO window; the first range is orphaned
 * forever (its only handle, r_virtual, is overwritten).  On failure of
 * rman_activate_resource() the fresh mapping is likewise never unmapped.
 *
 * This module (a nexus child driver, the API surface nexus exposes):
 *   1. allocates + activates a 1-page MMIO resource    -> vaddr1
 *   2. calls the documented bus_activate_resource() API again on the same
 *      active resource                                  -> vaddr2 != vaddr1
 *   3. releases the resource (unmaps ONLY vaddr2)
 *   4. reads through vaddr1 afterwards — still mapped (stale RW PTE) and
 *      kernel_map->size stays elevated by one page      => leaked KVA+PTEs.
 */
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/bus.h>
#include <sys/rman.h>
#include <sys/systm.h>
#include <vm/vm_kern.h>

static int	nxmap_probe(device_t);
static int	nxmap_attach(device_t);
static void	nxmap_identify(driver_t *, device_t);

static device_method_t nxmap_methods[] = {
	DEVMETHOD(device_identify,	nxmap_identify),
	DEVMETHOD(device_probe,		nxmap_probe),
	DEVMETHOD(device_attach,	nxmap_attach),
	DEVMETHOD_END
};

static driver_t nxmap_driver = { "nxmap", nxmap_methods, 0 };
static devclass_t nxmap_devclass;
DRIVER_MODULE(nxmap, nexus, nxmap_driver, nxmap_devclass, NULL, NULL);

/* physical MMIO window in the QEMU PCI hole; PTE-backed read is safe */
#define	POC_PA		0xE0000000UL
#define	POC_SZ		0x1000UL

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

static int
nxmap_probe(device_t dev)
{
	device_set_desc(dev, "nexus activate idempotence PoC");
	return (0);
}

static int
nxmap_attach(device_t dev)
{
	struct resource *r;
	vm_offset_t v1, v2, first_v1;
	uint32_t d1, d2, d1_after;
	const char *stale;
	int rid, err, i;
	vm_offset_t ksize0, ksize1, ksize2;

	rid = 100;
	if (bus_set_resource(dev, SYS_RES_MEMORY, rid, POC_PA, POC_SZ, -1)) {
		kprintf("nxmap: set_resource failed\n");
		return (ENXIO);
	}

	ksize0 = kernel_map->size;

	/*
	 * Cycle alloc -> activate -> release 16 times: each cycle maps the
	 * MMIO window twice but unmaps only the second mapping, so one page
	 * of kernel_map KVA (+ its RW PTEs) is stranded per cycle.
	 */
	for (i = 0; i < 16; i++) {
		r = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE);
		if (r == NULL) {
			kprintf("nxmap: alloc failed at cycle %d\n", i);
			return (ENXIO);
		}
		v1 = (vm_offset_t)rman_get_virtual(r);
		if (i == 0) {
			d1 = *(volatile uint32_t *)v1;
			first_v1 = v1;
		}
		err = bus_activate_resource(dev, SYS_RES_MEMORY, rid, r);
		v2 = (vm_offset_t)rman_get_virtual(r);
		d2 = *(volatile uint32_t *)v2;
		if (i == 0) {
			kprintf("DF-5450: err2ndact=%d vaddr1=%p vaddr2=%p "
			    "same_phys=%s\n", err, (void *)v1, (void *)v2,
			    d1 == d2 ? "YES" : "no");
		}
		bus_release_resource(dev, SYS_RES_MEMORY, rid, r);
	}
	v1 = first_v1;

	ksize1 = kernel_map->size;
	ksize2 = kernel_map->size;

	/*
	 * The orphaned first mapping: still alive after full release.
	 * On a fixed (idempotent) nexus, v1 == v2 was properly unmapped by
	 * the release, so dereferencing would fault -- guard the probe.
	 */
	if (v1 != v2) {
		d1_after = *(volatile uint32_t *)v1;
		stale = d1_after == d1 ? "YES" : "no";
	} else {
		d1_after = 0;
		stale = "N/A (v1 == v2, single mapping)";
	}

	kprintf("DF-5450: err2ndact=%d vaddr1=%p vaddr2=%p same_phys=%s\n",
	    err, (void *)v1, (void *)v2, d1 == d2 ? "YES" : "no");
	kprintf("DF-5450: kernel_map size before=%ju after2nd=%ju "
	    "afterrelease=%ju (leaked pages: %ju)\n",
	    (uintmax_t)ksize0, (uintmax_t)ksize1, (uintmax_t)ksize2,
	    (uintmax_t)((ksize2 - ksize0) / PAGE_SIZE));
	kprintf("DF-5450: orphan vaddr1 read after full release: "
	    "%#x (stale RW PTE still present: %s)\n",
	    d1_after, stale);

	return (0);
}
