/*
 * DF-5340 — (base_memory - 1) * 1024 underflow in mptable_search()
 * (mptable.c:284), the mptable.c sibling of the DF-5326 mp_bootaddress
 * class.
 *
 * base_memory (u_int, KB) = physmap[1]/1024 (machdep.c:2270), straight
 * from the loader-supplied SMAP/EFI memory map with no lower bound.
 * When the EBDA pointer (BDA 0x40e, machdep.c:2275-2276) is 0, the
 * "last 1K of base memory" scan target is computed as
 *
 *     target = (u_int32_t)((base_memory - 1) * 1024);   mptable.c:284
 *
 *   base_memory == 1  ->  target == 0       -> KKASSERT(target != 0)
 *                                           (mptable.c:368) PANIC
 *   base_memory == 0  ->  target == 0xFFFFFC00 -> pmap_mapdev() maps
 *                           the top-of-4GB MMIO hole (pa+0x400 crosses
 *                           the 4GB wrap) and scans garbage; the
 *                           intended base-memory scan never happens.
 *
 * mptable_search_sig()'s mapdev/scan body is elided (no device access
 * in userland); the KKASSERT and the arithmetic are verbatim.
 */
#include <stdio.h>
#include <stdlib.h>
#include <setjmp.h>

typedef unsigned int u_int;
typedef unsigned int u_int32_t;

static sigjmp_buf panic_jmp;
static int in_panic_scope;
static char panic_msg[256];

static void kpanic(void)
{
	snprintf(panic_msg, sizeof(panic_msg),
	    "assertion \"target != 0\" failed in mptable_search_sig "
	    "at mptable.c:368");
	if (in_panic_scope) {
		in_panic_scope = 0;
		siglongjmp(panic_jmp, 1);
	}
	fprintf(stderr, "KERNEL PANIC: %s\n", panic_msg);
	exit(86);
}
#define KKASSERT(x) do { if (!(x)) kpanic(); } while (0)

/* verbatim head of mptable_search_sig (mptable.c:361-371) */
static long
mptable_search_sig(u_int32_t target, int count)
{
	(void)count;
	KKASSERT(target != 0);		/* mptable.c:368 */
	/* pmap_mapdev(target, count*4) + _MP_ scan elided */
	return 0;
}

/* verbatim else-branch of mptable_search (mptable.c:282-287) */
static int
search_base_memory_scan(u_int base_memory)
{
	u_int32_t target;
	long x;

	target = (u_int32_t)((base_memory - 1) * 1024);	/* mptable.c:284 */
	in_panic_scope = 1;
	if (sigsetjmp(panic_jmp, 1) != 0) {
		printf("  base_memory=%-4u -> target=0x%08x  KERNEL PANIC -> %s\n",
		    base_memory, target, panic_msg);
		return 1;
	}
	x = mptable_search_sig(target, 1024 / 4);
	in_panic_scope = 0;
	if (target + 1024 < target)
		printf("  base_memory=%-4u -> target=0x%08x  scan [%08x..%08x]: "
		    "WRAPPED past 4GB, top-of-4GB MMIO hole, not base memory\n",
		    base_memory, target, target, target + 1024);
	else if (target >= 0xF00000)
		printf("  base_memory=%-4u -> target=0x%08x  scan [%08x..%08x]: "
		    "high MMIO hole, not the intended base-memory scan\n",
		    base_memory, target, target, target + 1024);
	else
		printf("  base_memory=%-4u -> target=0x%08x  scan [%08x..%08x]: "
		    "%s (sig not found: 0)\n",
		    base_memory, target, target, target + 1024,
		    (x > 0) ? "signature found" : "sane region");
	return 0;
}

int
main(void)
{
	u_int bm[] = { 0, 1, 2, 16, 511, 638, 639 };
	unsigned i;
	int panics = 0;

	printf("mptable_search() base-memory branch, ebda_addr == 0 "
	    "(verbatim arithmetic, mptable.c:284/368):\n");
	for (i = 0; i < sizeof(bm) / sizeof(bm[0]); i++)
		panics += search_base_memory_scan(bm[i]);
	printf("\nresult: base_memory==1 -> deterministic boot-time KKASSERT panic; "
	    "base_memory==0 -> wrong-region scan at 0xFFFFFC00 "
	    "(crosses the 4GB wrap); panics=%d\n", panics);
	return 0;
}
