/*
 * poc_pccard_cis2.c -- userland model of sys/bus/pccard/pccard_cis.c (pass 2)
 *
 * Mirrors, statement-by-statement:
 *   - pccard_scan_cis()            (pccard_cis.c:100-444)  -- walk + link
 *                                                            state machine
 *   - pccard_parse_cis_tuple()     (pccard_cis.c:612-1260) -- END/MFC reset,
 *                                                            VERS_1, MANFID,
 *                                                            FUNCID, FUNCE,
 *                                                            CONFIG, CFTABLE
 *   - decode_funce()               (pccard_cis.c:1262-1294)
 *   - the io/mem consumer loops of pccard_function_init (pccard.c:429-474)
 *
 * "bus space" = two 16KB buffers (attribute / common); only the first
 * PCCARD_CIS_SIZE=4096 bytes (pccard_cis.c:62,130-131) are the mapping the
 * kernel allocates.  Reads at bus offsets >= 4096 are counted and reported
 * (the model returns a byte anyway so the parse continues, like a bridge
 * that answers garbage instead of faulting).
 *
 * Scenarios (argv[1]): livelock leakloop mfcleak checksum poison
 *                       kmallocfail negative all
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <stddef.h>
#include <sys/queue.h>

#define	PCCARD_CIS_SIZE		4096		/* pccard_cis.c:62 */
#define	MODEL_BUF_SIZE		16384
#define	WALK_CAP		1000000

/* ---------------- bus space model ---------------- */
static uint8_t attr_mem[MODEL_BUF_SIZE];
static uint8_t common_mem[MODEL_BUF_SIZE];

static unsigned long oob_read_cnt, oob_read_max;

static unsigned
bs_read(uint8_t *base, unsigned long off)
{
	if (off >= MODEL_BUF_SIZE) {
		fprintf(stderr, "MODEL-HARD-OOB off=%lu\n", off);
		off = MODEL_BUF_SIZE - 1;
	}
	if (off >= PCCARD_CIS_SIZE) {
		oob_read_cnt++;
		if (off > oob_read_max)
			oob_read_max = off;
	}
	return base[off];
}

/* pccardvar.h:213-220 + read macros :255-280, verbatim */
struct pccard_tuple {
	unsigned int	code;
	unsigned int	length;
	unsigned long	mult;
	uint64_t	ptr;
	uint8_t	       *memh;
};

#define	pccard_cis_read_1(tuple, idx0)					\
	(bs_read((tuple)->memh, (tuple)->mult*(idx0)))
#define	pccard_tuple_read_1(tuple, idx1)				\
	(pccard_cis_read_1((tuple), ((tuple)->ptr+(2+(idx1)))))
#define	pccard_tuple_read_2(tuple, idx2)				\
	(pccard_tuple_read_1((tuple), (idx2)) |				\
	 (pccard_tuple_read_1((tuple), (idx2)+1)<<8))
#define	pccard_tuple_read_4(tuple, idx4)				\
	(pccard_tuple_read_1((tuple), (idx4)) |				\
	 (pccard_tuple_read_1((tuple), (idx4)+1)<<8) |			\
	 (pccard_tuple_read_1((tuple), (idx4)+2)<<16) |			\
	 (pccard_tuple_read_1((tuple), (idx4)+3)<<24))
#define	pccard_tuple_read_n(tuple, n, idxn)				\
	(((n)==1)?pccard_tuple_read_1((tuple), (idxn)) :		\
	 (((n)==2)?pccard_tuple_read_2((tuple), (idxn)) :		\
	  (((n)==3)?0 : pccard_tuple_read_4((tuple), (idxn)))))

/* ---------------- tuple codes / masks (pccard_cis.h) ---------------- */
#define	CISTPL_NULL			0x00
#define	CISTPL_CHECKSUM			0x10
#define	CISTPL_LONGLINK_A		0x11
#define	CISTPL_LONGLINK_C		0x12
#define	CISTPL_LINKTARGET		0x13
#define	CISTPL_LONGLINK_MFC		0x06
#define	CISTPL_NO_LINK			0x14
#define	CISTPL_VERS_1			0x15
#define	CISTPL_CONFIG			0x1A
#define	CISTPL_CFTABLE_ENTRY		0x1B
#define	CISTPL_MANFID			0x20
#define	CISTPL_FUNCID			0x21
#define	CISTPL_FUNCE			0x22
#define	CISTPL_END			0xFF
#define	PCCARD_MFC_MEM_ATTR		0x00
#define	PCCARD_MFC_MEM_COMMON		0x01
#define	PCCARD_TPCC_RASZ_MASK		0x03
#define	PCCARD_TPCC_RMSZ_MASK		0x3C
#define	PCCARD_TPCC_RFSZ_MASK		0xC0
#define	PCCARD_TPCE_INDX_INTFACE	0x80
#define	PCCARD_TPCE_INDX_DEFAULT	0x40
#define	PCCARD_TPCE_INDX_NUM_MASK	0x3F
#define	PCCARD_TPCE_FS_POWER_MASK	0x03
#define	PCCARD_TPCE_FS_TIMING		0x04
#define	PCCARD_TPCE_FS_IOSPACE		0x08
#define	PCCARD_TPCE_FS_IRQ		0x10
#define	PCCARD_TPCE_FS_MEMSPACE_MASK	0x60
#define	PCCARD_TPCE_FS_MISC		0x80
#define	PCCARD_TPCE_TD_RESERVED_MASK	0xE0
#define	PCCARD_TPCE_TD_RDYBSY_MASK	0x1C
#define	PCCARD_TPCE_TD_WAIT_MASK		0x03
#define	PCCARD_TPCE_IO_HASRANGE		0x80
#define	PCCARD_TPCE_IO_IOADDRLINES_MASK	0x1F
#define	PCCARD_TPCE_IO_RANGE_COUNT	0x0F
#define	PCCARD_TPCE_IO_RANGE_ADDRSIZE_MASK	0x30
#define	PCCARD_TPCE_IO_RANGE_ADDRSIZE_ONE	0x10
#define	PCCARD_TPCE_IO_RANGE_ADDRSIZE_TWO	0x20
#define	PCCARD_TPCE_IO_RANGE_ADDRSIZE_FOUR	0x30
#define	PCCARD_TPCE_IO_RANGE_LENGTHSIZE_MASK	0xC0
#define	PCCARD_TPCE_IO_RANGE_LENGTHSIZE_ONE	0x40
#define	PCCARD_TPCE_IO_RANGE_LENGTHSIZE_TWO	0x80
#define	PCCARD_TPCE_IO_RANGE_LENGTHSIZE_FOUR	0xC0
#define	PCCARD_TPCE_IR_SHARE		0x80
#define	PCCARD_TPCE_IR_PULSE		0x40
#define	PCCARD_TPCE_IR_LEVEL		0x20
#define	PCCARD_TPCE_IR_HASMASK		0x10
#define	PCCARD_TPCE_IR_IRQ		0x0F
#define	PCCARD_TPCE_MS_HOSTADDR		0x80
#define	PCCARD_TPCE_MS_CARDADDR_SIZE_MASK	0x60
#define	PCCARD_TPCE_MS_CARDADDR_SIZE_SHIFT	5
#define	PCCARD_TPCE_MS_LENGTH_SIZE_MASK	0x18
#define	PCCARD_TPCE_MS_LENGTH_SIZE_SHIFT	3
#define	PCCARD_TPCE_MS_COUNT		0x07
#define	PCCARD_TPCE_MI_EXT		0x80
#define	PCCARD_CFE_MWAIT_REQUIRED	0x0001
#define	PCCARD_CFE_RDYBSY_ACTIVE	0x0002
#define	PCCARD_CFE_WP_ACTIVE		0x0004
#define	PCCARD_CFE_BVD_ACTIVE		0x0008
#define	PCCARD_CFE_IO8			0x0010
#define	PCCARD_CFE_IO16			0x0020
#define	PCCARD_CFE_IRQSHARE		0x0040
#define	PCCARD_CFE_IRQPULSE		0x0080
#define	PCCARD_CFE_IRQLEVEL		0x0100
#define	PCCARD_CFE_POWERDOWN		0x0200
#define	PCCARD_CFE_READONLY		0x0400
#define	PCCARD_CFE_AUDIO		0x0800
#define	PCCARD_IFTYPE_MEMORY		0
#define	PCCARD_FUNCTION_UNSPEC		-1
#define	PCCARD_FUNCTION_SERIAL		2
#define	PCCARD_FUNCTION_DISK		4
#define	PCCARD_FUNCTION_NETWORK		6
#define	PCCARD_TPLFE_TYPE_LAN_NID	0x04
#define	PCCARD_TPLFE_TYPE_DISK_DEVICE_INTERFACE	0x01

/* ---------------- struct mirrors (pccardvar.h:87-184) ---------------- */
struct pccard_config_entry {
	int		number;
	uint32_t	flags;
	int		iftype;
	int		num_iospace;
	unsigned long	iomask;
	struct {
		unsigned long	length;
		unsigned long	start;
	} iospace[4];			/* pccardvar.h:102 */
	uint16_t	irqmask;
	int		num_memspace;
	struct {
		unsigned long	length;
		unsigned long	cardaddr;
		unsigned long	hostaddr;
	} memspace[2];			/* pccardvar.h:109 */
	int		maxtwins;
	STAILQ_ENTRY(pccard_config_entry) cfe_list;
};

struct pccard_function {
	int		number;
	int		function;
	int		last_config_index;
	uint32_t	ccr_base;
	uint32_t	ccr_mask;
	STAILQ_HEAD(, pccard_config_entry) cfe_head;
	STAILQ_ENTRY(pccard_function) pf_list;
	int		pf_funce_disk_interface;
	uint8_t		pf_funce_lan_nid[8];
	int		pf_funce_lan_nidlen;
};

struct pccard_card {
	int		cis1_major;
	int		cis1_minor;
	char		cis1_info_buf[256];
	char		*cis1_info[4];
	int32_t		manufacturer;
	int32_t		product;
	uint16_t	error;
	STAILQ_HEAD(, pccard_function) pf_head;
};

struct cis_state {				/* pccard_cis.c:64-71 */
	int	count;
	int	gotmfc;
	struct pccard_config_entry temp_cfe;
	struct pccard_config_entry *default_cfe;
	struct pccard_card *card;
	struct pccard_function *pf;
};

/* ---------------- allocation accounting ---------------- */
static unsigned long alloc_cnt, free_cnt, alloc_bytes;
static long		 fail_next_alloc;
static int		 df5364_hit;

static void *
m_kmalloc(size_t sz)
{
	void *p;

	if (fail_next_alloc > 0) {
		fail_next_alloc--;
		return NULL;
	}
	p = malloc(sz);
	if (p != NULL) {
		alloc_cnt++;
		alloc_bytes += sz;
	}
	return p;
}

#define	PF_SZ	sizeof(struct pccard_function)
#define	CFE_SZ	sizeof(struct pccard_config_entry)

/* ---------------- instrumentation ---------------- */
static unsigned long link_follows;
static int		 poison_iospace_seen, poison_memspace_seen;

struct sig_ent {
	uint64_t ptr; int space_attr; int ll_present; uint64_t ll_addr;
	int ll_common; int mfc_cnt; int mfc_idx; int gotmfc;
};
static struct sig_ent sigs[4096];
static int sig_cnt;

static int
sig_seen(uint64_t ptr, int space_attr, int ll_present, uint64_t ll_addr,
	 int ll_common, int mfc_cnt, int mfc_idx, int gotmfc)
{
	int i;

	for (i = 0; i < sig_cnt; i++)
		if (sigs[i].ptr == ptr && sigs[i].space_attr == space_attr &&
		    sigs[i].ll_present == ll_present &&
		    sigs[i].ll_addr == ll_addr && sigs[i].ll_common == ll_common &&
		    sigs[i].mfc_cnt == mfc_cnt && sigs[i].mfc_idx == mfc_idx &&
		    sigs[i].gotmfc == gotmfc)
			return 1;
	if (sig_cnt < (int)(sizeof(sigs) / sizeof(sigs[0]))) {
		sigs[sig_cnt].ptr = ptr;
		sigs[sig_cnt].space_attr = space_attr;
		sigs[sig_cnt].ll_present = ll_present;
		sigs[sig_cnt].ll_addr = ll_addr;
		sigs[sig_cnt].ll_common = ll_common;
		sigs[sig_cnt].mfc_cnt = mfc_cnt;
		sigs[sig_cnt].mfc_idx = mfc_idx;
		sigs[sig_cnt].gotmfc = gotmfc;
		sig_cnt++;
	}
	return 0;
}

/* ---------------- parse handler (pccard_cis.c:612-1260) ---------------- */
static struct cis_state *state_g;	/* the one live cis_state */

static int
pccard_parse_cis_tuple(const struct pccard_tuple *tuple, void *arg)
{
	/* pccard_cis.c:616-619 */
	static struct pccard_config_entry init_cfe = {
		-1, PCCARD_CFE_RDYBSY_ACTIVE | PCCARD_CFE_WP_ACTIVE |
		PCCARD_CFE_BVD_ACTIVE, PCCARD_IFTYPE_MEMORY,
	};
	struct cis_state *state = arg;

	switch (tuple->code) {
	case CISTPL_END:				/* :624-649 */
		if (state->gotmfc == 1) {
			struct pccard_function *pf, *pfnext;

			for (pf = STAILQ_FIRST(&state->card->pf_head);
			     pf != NULL; pf = pfnext) {
				pfnext = STAILQ_NEXT(pf, pf_list);
				/*
				 * KERNEL: kfree(pf) only (:641).  The cfe
				 * chain hanging off pf is NOT drained ->
				 * DF-5361.  Model frees pf only, exactly.
				 */
				free_cnt++;
				free(pf);
			}
			STAILQ_INIT(&state->card->pf_head);
			state->count = 0;
			state->gotmfc = 2;
			state->pf = NULL;			/* :648 */
		}
		break;
	case CISTPL_LONGLINK_MFC:			/* :651-659 */
		state->gotmfc = 1;
		break;
	case CISTPL_VERS_1:				/* :735-761 */
		if (tuple->length < 6)
			break;
		{
			int start, i, ch, count;

			state->card->cis1_major = pccard_tuple_read_1(tuple, 0);
			state->card->cis1_minor = pccard_tuple_read_1(tuple, 1);
			for (count = 0, start = 0, i = 0;
			    (count < 4) && ((i + 4) < 256); i++) {
				ch = pccard_tuple_read_1(tuple, 2 + i);
				if (ch == 0xff)
					break;
				state->card->cis1_info_buf[i] = ch;
				if (ch == 0) {
					state->card->cis1_info[count] =
					    state->card->cis1_info_buf + start;
					start = i + 1;
					count++;
				}
			}
		}
		break;
	case CISTPL_MANFID:				/* :762-782 */
		if (tuple->length < 4)
			break;
		state->card->manufacturer = pccard_tuple_read_2(tuple, 0);
		state->card->product = pccard_tuple_read_2(tuple, 2);
		break;
	case CISTPL_FUNCID:				/* :783-802 */
		if (tuple->length < 1)
			break;
		if ((state->pf == NULL) || (state->gotmfc == 2)) {
			state->pf = m_kmalloc(PF_SZ);		/* :790 */
			if (state->pf == NULL) {
				/*
				 * DF-5364: no NULL check in the kernel; the
				 * next statement writes through it:
				 *     state->pf->number = state->count++;  :792
				 */
				printf("RESULT: DF-5364 FUNCID kmalloc"
				    "(M_NOWAIT) returned NULL; next kernel"
				    " statement writes *(int*)(NULL+%zu)"
				    " = %d  -> NULL-page write, panic\n",
				    offsetof(struct pccard_function, number),
				    state->count);
				df5364_hit = 1;
				return 1;	/* unwind the model scan */
			}
			memset(state->pf, 0, PF_SZ);
			state->pf->number = state->count++;
			state->pf->last_config_index = -1;
			STAILQ_INIT(&state->pf->cfe_head);
			STAILQ_INSERT_TAIL(&state->card->pf_head, state->pf,
			    pf_list);
		}
		state->pf->function = pccard_tuple_read_1(tuple, 0);
		break;
	case CISTPL_FUNCE:				/* :803-812 */
		if (state->pf == NULL || state->pf->function <= 0)
			break;
		if (tuple->length >= 2) {
			/* decode_funce, pccard_cis.c:1262-1294, verbatim */
			int i, len;
			int type = pccard_tuple_read_1(tuple, 0);

			switch (state->pf->function) {
			case PCCARD_FUNCTION_DISK:
				if (type == PCCARD_TPLFE_TYPE_DISK_DEVICE_INTERFACE)
					state->pf->pf_funce_disk_interface =
					    pccard_tuple_read_1(tuple, 1);
				break;
			case PCCARD_FUNCTION_NETWORK:
				if (type == PCCARD_TPLFE_TYPE_LAN_NID) {
					len = pccard_tuple_read_1(tuple, 1);
					if (tuple->length < 2 + len || len > 8)
						break;		/* :1279 */
					for (i = 0; i < len; i++)
						state->pf->pf_funce_lan_nid[i] =
						    pccard_tuple_read_1(tuple, i + 2);
					state->pf->pf_funce_lan_nidlen = len;
				}
				break;
			default:
				break;
			}
		}
		break;
	case CISTPL_CONFIG:				/* :813-870 */
		if (tuple->length < 3)
			break;
		{
			unsigned int reg, rasz, rmsz, rfsz;
			int i;

			reg = pccard_tuple_read_1(tuple, 0);
			rasz = 1 + (reg & PCCARD_TPCC_RASZ_MASK);
			rmsz = 1 + ((reg & PCCARD_TPCC_RMSZ_MASK) >> 2);
			rfsz = ((reg & PCCARD_TPCC_RFSZ_MASK) >> 6);

			if (tuple->length < (rasz + rmsz + rfsz))
				break;
			if (state->pf == NULL) {
				state->pf = m_kmalloc(PF_SZ);	/* :837 */
				if (state->pf == NULL) {
					printf("RESULT: DF-5364 CONFIG kmalloc"
					    "(M_NOWAIT) returned NULL; next"
					    " kernel statement writes"
					    " *(int*)(NULL+%zu) = %d"
					    " -> NULL-page write, panic\n",
					    offsetof(struct pccard_function,
					    number), state->count);
					df5364_hit = 1;
					return 1;
				}
				memset(state->pf, 0, PF_SZ);
				state->pf->number = state->count++;
				state->pf->last_config_index = -1;
				STAILQ_INIT(&state->pf->cfe_head);
				STAILQ_INSERT_TAIL(&state->card->pf_head,
				    state->pf, pf_list);
				state->pf->function = PCCARD_FUNCTION_UNSPEC;
			}
			state->pf->last_config_index =
			    pccard_tuple_read_1(tuple, 1);
			state->pf->ccr_base = 0;
			for (i = 0; i < (int)rasz; i++)
				state->pf->ccr_base |=
				    (pccard_tuple_read_1(tuple, 2 + i) << (i * 8));
			state->pf->ccr_mask = 0;
			for (i = 0; i < (int)rmsz; i++)
				state->pf->ccr_mask |=
				    (pccard_tuple_read_1(tuple, 2 + rasz + i) <<
				    (i * 8));
			state->temp_cfe = init_cfe;
			state->default_cfe = &state->temp_cfe;
		}
		break;
	case CISTPL_CFTABLE_ENTRY: {			/* :871-1253 */
		int idx, i, j;
		unsigned int reg, reg2;
		unsigned int intface, def, num;
		unsigned int power, timing, iospace, irq, memspace, misc;
		struct pccard_config_entry *cfe;

		idx = 0;
		reg = pccard_tuple_read_1(tuple, idx);
		idx++;
		intface = reg & PCCARD_TPCE_INDX_INTFACE;
		def = reg & PCCARD_TPCE_INDX_DEFAULT;
		num = reg & PCCARD_TPCE_INDX_NUM_MASK;

		if (num != state->default_cfe->number) {		/* :907 */
			cfe = m_kmalloc(CFE_SZ);			/* :908 */
			if (cfe == NULL)				/* :910 */
				goto abort_cfe;
			memset(cfe, 0, CFE_SZ);
			*cfe = *state->default_cfe;			/* :914 */
			if (state->pf == NULL) {
				/* DF-2319 second case (KNOWN, not re-reported):
				 * kernel STAILQ_INSERT_TAIL through NULL pf. */
				printf("NOTE: known DF-2319 path reached"
				    " (pf==NULL at :916); not re-reported.\n");
				goto abort_cfe;
			}
			STAILQ_INSERT_TAIL(&state->pf->cfe_head, cfe, cfe_list);
			cfe->number = num;
			if (def)
				state->default_cfe = cfe;
		} else {
			cfe = state->default_cfe;
			if (!def) {
				state->temp_cfe = *state->default_cfe;
				state->default_cfe = &state->temp_cfe;
			}
		}

		if (intface) {
			reg = pccard_tuple_read_1(tuple, idx);
			idx++;
			cfe->flags &= ~(PCCARD_CFE_MWAIT_REQUIRED |
			    PCCARD_CFE_RDYBSY_ACTIVE | PCCARD_CFE_WP_ACTIVE |
			    PCCARD_CFE_BVD_ACTIVE);
			cfe->iftype = reg & 0x0F;
		}
		reg = pccard_tuple_read_1(tuple, idx);
		idx++;

		power = reg & PCCARD_TPCE_FS_POWER_MASK;
		timing = reg & PCCARD_TPCE_FS_TIMING;
		iospace = reg & PCCARD_TPCE_FS_IOSPACE;
		irq = reg & PCCARD_TPCE_FS_IRQ;
		memspace = reg & PCCARD_TPCE_FS_MEMSPACE_MASK;
		misc = reg & PCCARD_TPCE_FS_MISC;

		if (power) {					/* :987-1006 */
			for (i = 0; i < (int)power; i++) {
				reg = pccard_tuple_read_1(tuple, idx);
				idx++;
				for (j = 0; j < 7; j++) {
					if ((reg >> j) & 0x01) {
						do {
							reg2 = pccard_tuple_read_1(tuple, idx);
							idx++;
						} while (reg2 & 0x80);
					}
				}
			}
		}
		if (timing) {					/* :1008-1022 */
			reg = pccard_tuple_read_1(tuple, idx);
			idx++;
			if ((reg & PCCARD_TPCE_TD_RESERVED_MASK) !=
			    PCCARD_TPCE_TD_RESERVED_MASK)
				idx++;
			if ((reg & PCCARD_TPCE_TD_RDYBSY_MASK) !=
			    PCCARD_TPCE_TD_RDYBSY_MASK)
				idx++;
			if ((reg & PCCARD_TPCE_TD_WAIT_MASK) !=
			    PCCARD_TPCE_TD_WAIT_MASK)
				idx++;
		}
		if (iospace) {
			if (tuple->length <= idx)			/* :1024 */
				goto abort_cfe;
			reg = pccard_tuple_read_1(tuple, idx);
			idx++;
			cfe->flags &= ~(PCCARD_CFE_IO8 | PCCARD_CFE_IO16);
			cfe->iomask = reg & PCCARD_TPCE_IO_IOADDRLINES_MASK;

			if (reg & PCCARD_TPCE_IO_HASRANGE) {
				reg = pccard_tuple_read_1(tuple, idx);
				idx++;
				cfe->num_iospace = 1 + (reg &		/* :1044 */
				    PCCARD_TPCE_IO_RANGE_COUNT);
				if (cfe->num_iospace >
				    (sizeof(cfe->iospace) /
				    sizeof(cfe->iospace[0]))) {
					/* :1047-1055 -- DF-5360: count already
					 * stored & cfe already on the queue */
					poison_iospace_seen = cfe->num_iospace;
					state->card->error++;
					break;				/* :1054 */
				}
				for (i = 0; i < cfe->num_iospace; i++) {
					switch (reg & PCCARD_TPCE_IO_RANGE_ADDRSIZE_MASK) {
					case PCCARD_TPCE_IO_RANGE_ADDRSIZE_ONE:
						cfe->iospace[i].start =
							pccard_tuple_read_1(tuple, idx);
						idx++;
						break;
					case PCCARD_TPCE_IO_RANGE_ADDRSIZE_TWO:
						cfe->iospace[i].start =
							pccard_tuple_read_2(tuple, idx);
						idx += 2;
						break;
					case PCCARD_TPCE_IO_RANGE_ADDRSIZE_FOUR:
						cfe->iospace[i].start =
							pccard_tuple_read_4(tuple, idx);
						idx += 4;
						break;
					}
					switch (reg & PCCARD_TPCE_IO_RANGE_LENGTHSIZE_MASK) {
					case PCCARD_TPCE_IO_RANGE_LENGTHSIZE_ONE:
						cfe->iospace[i].length =
							pccard_tuple_read_1(tuple, idx);
						idx++;
						break;
					case PCCARD_TPCE_IO_RANGE_LENGTHSIZE_TWO:
						cfe->iospace[i].length =
							pccard_tuple_read_2(tuple, idx);
						idx += 2;
						break;
					case PCCARD_TPCE_IO_RANGE_LENGTHSIZE_FOUR:
						cfe->iospace[i].length =
							pccard_tuple_read_4(tuple, idx);
						idx += 4;
						break;
					}
					cfe->iospace[i].length++;
				}
			} else {
				cfe->num_iospace = 1;
				cfe->iospace[0].start = 0;
				cfe->iospace[0].length = (1 << cfe->iomask);
			}
		}
		if (irq) {
			if (tuple->length <= idx)			/* :1102 */
				goto abort_cfe;
			reg = pccard_tuple_read_1(tuple, idx);
			idx++;
			if (reg & PCCARD_TPCE_IR_SHARE)
				cfe->flags |= PCCARD_CFE_IRQSHARE;
			if (reg & PCCARD_TPCE_IR_PULSE)
				cfe->flags |= PCCARD_CFE_IRQPULSE;
			if (reg & PCCARD_TPCE_IR_LEVEL)
				cfe->flags |= PCCARD_CFE_IRQLEVEL;
			if (reg & PCCARD_TPCE_IR_HASMASK) {
				cfe->irqmask = pccard_tuple_read_2(tuple, idx);
				idx += 2;
			} else {
				cfe->irqmask = (1 << (reg & PCCARD_TPCE_IR_IRQ));
			}
		}
		if (memspace) {
			if (tuple->length <= idx)			/* :1134 */
				goto abort_cfe;
			if (memspace == 0x20) {
				cfe->num_memspace = 1;
				cfe->memspace[0].length =
				    256 * pccard_tuple_read_2(tuple, idx);
				idx += 2;
				cfe->memspace[0].cardaddr = 0;
				cfe->memspace[0].hostaddr = 0;
			} else if (memspace == 0x40) {
				cfe->num_memspace = 1;
				cfe->memspace[0].length =
				    256 * pccard_tuple_read_2(tuple, idx);
				idx += 2;
				cfe->memspace[0].cardaddr =
				    256 * pccard_tuple_read_2(tuple, idx);
				idx += 2;
				cfe->memspace[0].hostaddr =
				    cfe->memspace[0].cardaddr;
			} else {
				int lengthsize, cardaddrsize, hostaddrsize;

				reg = pccard_tuple_read_1(tuple, idx);
				idx++;
				cfe->num_memspace = (reg &		/* :1164 */
				    PCCARD_TPCE_MS_COUNT) + 1;
				if (cfe->num_memspace >
				    (sizeof(cfe->memspace) /
				    sizeof(cfe->memspace[0]))) {
					/* :1167-1175 -- DF-5360 */
					poison_memspace_seen = cfe->num_memspace;
					state->card->error++;
					break;				/* :1174 */
				}
				lengthsize = (reg &
				    PCCARD_TPCE_MS_LENGTH_SIZE_MASK) >> 3;
				cardaddrsize = (reg &
				    PCCARD_TPCE_MS_CARDADDR_SIZE_MASK) >> 5;
				hostaddrsize = (reg & PCCARD_TPCE_MS_HOSTADDR) ?
				    cardaddrsize : 0;
				if (lengthsize == 0)
					state->card->error++;
				for (i = 0; i < cfe->num_memspace; i++) {
					if (lengthsize) {
						cfe->memspace[i].length = 256 *
						    pccard_tuple_read_n(tuple,
						    lengthsize, idx);
						idx += lengthsize;
					} else
						cfe->memspace[i].length = 0;
					if (cfe->memspace[i].length == 0)
						state->card->error++;
					if (cardaddrsize) {
						cfe->memspace[i].cardaddr = 256 *
						    pccard_tuple_read_n(tuple,
						    cardaddrsize, idx);
						idx += cardaddrsize;
					} else
						cfe->memspace[i].cardaddr = 0;
					if (hostaddrsize) {
						cfe->memspace[i].hostaddr = 256 *
						    pccard_tuple_read_n(tuple,
						    hostaddrsize, idx);
						idx += hostaddrsize;
					} else
						cfe->memspace[i].hostaddr = 0;
				}
			}
		} else
			cfe->num_memspace = 0;
		if (misc) {
			if (tuple->length <= idx)			/* :1225 */
				goto abort_cfe;
			reg = pccard_tuple_read_1(tuple, idx);
			idx++;
			cfe->flags &= ~(PCCARD_CFE_POWERDOWN | PCCARD_CFE_READONLY |
			    PCCARD_CFE_AUDIO);
			cfe->maxtwins = reg & 0x07;
			while (reg & PCCARD_TPCE_MI_EXT) {	/* :1243 (DF-1041) */
				reg = pccard_tuple_read_1(tuple, idx);
				idx++;
			}
		}
	}
abort_cfe:
		break;
	default:
		break;
	}

	return (0);
}

/* ---------------- the walk (pccard_cis.c:100-444) ---------------- */
static int
pccard_scan_cis_model(struct pccard_tuple *tuple, int *space_attr,
		      long *iters)
{
	int longlink_present = 1;			/* :147-149 */
	int longlink_common = 1;
	uint64_t longlink_addr = 0;
	int mfc_count = 0, mfc_index = 0;
	struct {
		int	common;
		uint64_t addr;
	} mfc[256 / 5];
	int ret = 0;
	long n = 0;

	tuple->mult = 2;				/* :145 */
	tuple->ptr = 0;
	tuple->memh = attr_mem;
	*space_attr = 1;

	while (1) {
		while (1) {
			if (++n > WALK_CAP) {
				*iters = n;
				return 3;		/* cap exceeded */
			}
			if (tuple->mult * tuple->ptr >=		/* :164 */
			    PCCARD_CIS_SIZE - 1 - 32) {
				tuple->code = CISTPL_END;
			} else {
				tuple->code = pccard_cis_read_1(tuple, tuple->ptr);
			}

			if (tuple->code == CISTPL_NULL) {	/* :175 */
				tuple->ptr++;
				continue;
			} else if (tuple->code == CISTPL_END) {	/* :185 */
				if (pccard_parse_cis_tuple(tuple, state_g)) {
					ret = 1;
					goto done;
				}
				tuple->ptr++;
				break;
			}

			tuple->length = pccard_cis_read_1(tuple,	/* :198 */
			    tuple->ptr + 1);
			switch (tuple->code) {
			case CISTPL_LONGLINK_A:			/* :200-216 */
			case CISTPL_LONGLINK_C:
				if (tuple->length < 4)
					break;
				longlink_present = 1;
				longlink_common = (tuple->code ==
				    CISTPL_LONGLINK_C) ? 1 : 0;
				longlink_addr = pccard_tuple_read_4(tuple, 0);
				break;
			case CISTPL_NO_LINK:			/* :217 */
				longlink_present = 0;
				break;
			case CISTPL_CHECKSUM: {			/* :221-276 */
				int16_t offset;
				uint64_t addr, length;
				unsigned cksum, sum;
				int i;

				if (tuple->length < 5)
					break;
				offset = (uint16_t)pccard_tuple_read_2(tuple, 0);
				length = pccard_tuple_read_2(tuple, 2);
				cksum = pccard_tuple_read_1(tuple, 4);
				addr = tuple->ptr + offset;		/* :237 */

				if ((addr >= PCCARD_CIS_SIZE) ||	/* :247 */
				    ((addr + length) >= PCCARD_CIS_SIZE))
					break;
				sum = 0;
				for (i = 0; i < (int)length; i++)	/* :255 */
					/*
					 * :255-259 verbatim: RAW
					 * bus_space_read_1 at
					 * addr + tuple.mult * i  -- the
					 * mult is NOT in the :247 check.
					 */
					sum += bs_read(tuple->memh,
					    addr + tuple->mult * i);
				(void)cksum;
				break;
			}
			case CISTPL_LONGLINK_MFC: {		/* :277-346 */
				int i, tmp_count;

				if (tuple->length < 1)
					break;
				if (((tuple->length - 1) % 5) != 0)
					break;
				tmp_count = pccard_tuple_read_1(tuple, 0);
				if (tuple->length != (tmp_count * 5 + 1))
					break;
				if (tmp_count >
				    (int)(sizeof mfc / sizeof mfc[0]))
					abort();	/* :324 -- proven
							 * unreachable, see
							 * 'negative' */
				mfc_count = tmp_count;
				for (i = 0; i < mfc_count; i++) {
					mfc[i].common =
					    (pccard_tuple_read_1(tuple,
					    1 + 5 * i) == PCCARD_MFC_MEM_COMMON)
					    ? 1 : 0;
					mfc[i].addr =
					    pccard_tuple_read_4(tuple,
					    1 + 5 * i + 1);
				}
				goto call_fct;		/* fall through :347 */
			}
			default:
call_fct:
				if (pccard_parse_cis_tuple(tuple, state_g)) {
					ret = 1;
					goto done;
				}
				break;
			}
			tuple->ptr += 2 + tuple->length;	/* :376 */
		}

		/* chain done; follow links -- :386-437 */
		while (1) {
			link_follows++;
			if (sig_seen(tuple->ptr, *space_attr, longlink_present,
			    longlink_addr, longlink_common, mfc_count,
			    mfc_index, state_g->gotmfc)) {
				*iters = n;
				return 2;			/* CYCLE */
			}
			if (longlink_present) {				/* :387 */
				*space_attr = longlink_common ? 0 : 1;
				tuple->memh = longlink_common ? common_mem :
				    attr_mem;
				tuple->mult = longlink_common ? 1 : 2;
				tuple->ptr = longlink_addr;		/* :394 */
				longlink_present = 0;
				longlink_common = 1;
				longlink_addr = 0;
			} else if (mfc_count && (mfc_index < mfc_count)) { /* :398 */
				*space_attr = mfc[mfc_index].common ? 0 : 1;
				tuple->memh = mfc[mfc_index].common ?
				    common_mem : attr_mem;
				tuple->mult = mfc[mfc_index].common ? 1 : 2;
				tuple->ptr = mfc[mfc_index].addr;	/* :406 */
				mfc_index++;
			} else {
				goto done;					/* :409 */
			}

			tuple->code = pccard_cis_read_1(tuple, tuple->ptr); /* :413 */
			if (tuple->code != CISTPL_LINKTARGET)
				continue;
			tuple->length = pccard_cis_read_1(tuple, tuple->ptr + 1);
			if (tuple->length < 3)
				continue;
			if ((pccard_tuple_read_1(tuple, 0) != 'C') ||
			    (pccard_tuple_read_1(tuple, 1) != 'I') ||
			    (pccard_tuple_read_1(tuple, 2) != 'S'))
				continue;
			tuple->ptr += 2 + tuple->length;		/* :435 */
			break;
		}
	}

done:
	*iters = n;
	return ret;
}

/* ---------------- CIS authoring ---------------- */
static void
put_tuple(uint8_t *buf, unsigned addr, unsigned code, unsigned len,
	  const uint8_t *data)
{
	unsigned i;

	/* tuple index n lives at bus offset mult*n (attr space: mult=2);
	 * kernel reads code=pccard_cis_read_1(ptr), len=...(ptr+1),
	 * body[i]=...(ptr+2+i)  (pccardvar.h:255-259) */
	buf[2 * addr] = code;
	buf[2 * (addr + 1)] = len;
	for (i = 0; i < len; i++)
		buf[2 * (addr + 2 + i)] = data[i];
}

static struct cis_state	g_state;
static struct pccard_tuple	g_tuple;
static struct pccard_card	g_card;

static void
reset_all(void)
{
	memset(&g_state, 0, sizeof(g_state));
	memset(&g_card, 0, sizeof(g_card));
	g_state.card = &g_card;
	g_card.cis1_major = -1;
	g_card.cis1_minor = -1;
	g_card.manufacturer = -1;
	g_card.product = -1;
	STAILQ_INIT(&g_card.pf_head);
	state_g = &g_state;
	alloc_cnt = free_cnt = alloc_bytes = 0;
	oob_read_cnt = oob_read_max = 0;
	link_follows = 0;
	poison_iospace_seen = poison_memspace_seen = 0;
	df5364_hit = 0;
	sig_cnt = 0;
	fail_next_alloc = 0;
	memset(attr_mem, 0, sizeof(attr_mem));
	memset(common_mem, 0, sizeof(common_mem));
}

/* pccard.c:429-474 consumer loops, OOB-instrumented */
static void
consumer_io_loops(const struct pccard_config_entry *cfe)
{
	const char *objend = (const char *)cfe + CFE_SZ;
	unsigned long oob = 0, maxpast = 0;
	int i;

	for (i = 0; i < cfe->num_iospace; i++) {			/* pccard.c:429 */
		const char *e = (const char *)&cfe->iospace[i];
		long past = (e + sizeof(cfe->iospace[0])) - objend;
		if (past > 0) {
			oob++;
			if ((unsigned long)past > maxpast)
				maxpast = past;
		}
	}
	printf("  consumer pccard.c:429  i<num_iospace=%d -> %lu OOB"
	    " element reads, deepest %lu bytes past the %zu-byte"
	    " kmalloc chunk\n", cfe->num_iospace, oob, maxpast, CFE_SZ);

	oob = maxpast = 0;
	for (i = 0; i < cfe->num_memspace; i++) {		/* pccard.c:452 */
		const char *e = (const char *)&cfe->memspace[i];
		long past = (e + sizeof(cfe->memspace[0])) - objend;
		if (past > 0) {
			oob++;
			if ((unsigned long)past > maxpast)
				maxpast = past;
		}
	}
	printf("  consumer pccard.c:452  i<num_memspace=%d -> %lu OOB"
	    " element reads, deepest %lu bytes past the chunk\n",
	    cfe->num_memspace, oob, maxpast);
}

/* ---------------- scenarios ---------------- */
static int
sc_livelock(void)
{
	int sp, r;
	long it;

	printf("=== S1 livelock: LONGLINK_A cycle (DF-5362) ===\n");
	reset_all();
	put_tuple(attr_mem, 0x00, 0x13, 3, (const uint8_t *)"CIS");
	put_tuple(attr_mem, 0x05, 0x11, 4, (const uint8_t *)"\0\0\0\0");
	put_tuple(attr_mem, 0x0B, 0xFF, 0, NULL);

	r = pccard_scan_cis_model(&g_tuple, &sp, &it);
	if (r == 2) {
		printf("RESULT: DF-5362 scan NEVER terminates: identical walker"
		    " state (space,ptr=%llu,link state) revisited after %ld"
		    " tuple steps / %lu link follows; model cap %d would be"
		    " exceeded.\n", (unsigned long long)sigs[0].ptr, it,
		    link_follows, WALK_CAP);
		printf("  kernel: pccard_attach_card (pccard.c:195) spins"
		    " forever on the socket event thread; the CIS window"
		    " resource (pccard_cis.c:129-131) is never released"
		    " (pccard_cis.c:441 never reached).\n");
		return 0;
	}
	printf("RESULT: S1 UNEXPECTED r=%d iters=%ld\n", r, it);
	return 1;
}

static int
sc_leakloop(void)
{
	int sp, r;
	long it;

	printf("=== S2 leakloop: cycle + per-cycle cfe leak"
	    " (DF-5361 x DF-5362) ===\n");
	reset_all();
	put_tuple(attr_mem, 0x00, 0x13, 3, (const uint8_t *)"CIS");
	put_tuple(attr_mem, 0x05, 0x06, 6,
	    (const uint8_t *)"\x01\x00\x00\x00\x00\x00");
	put_tuple(attr_mem, 0x0D, 0x21, 1, (const uint8_t *)"\x02");
	put_tuple(attr_mem, 0x10, 0x1A, 3,
	    (const uint8_t *)"\x00\xff\x00");
	put_tuple(attr_mem, 0x15, 0x1B, 2, (const uint8_t *)"\x01\x00");
	put_tuple(attr_mem, 0x19, 0x11, 4, (const uint8_t *)"\0\0\0\0");
	put_tuple(attr_mem, 0x1F, 0xFF, 0, NULL);

	r = pccard_scan_cis_model(&g_tuple, &sp, &it);
	if (r == 2) {
		printf("RESULT: DF-5361+DF-5362 combined: cycle confirmed after"
		    " %ld tuple steps; allocs=%lu (%lu B) frees=%lu ->"
		    " %ld chunk(s) net-leaked when the cycle was cut; the"
		    " kernel loop has no cut.\n", it, alloc_cnt, alloc_bytes,
		    free_cnt, (long)(alloc_cnt - free_cnt));
		printf("  every cycle: FUNCID allocs pf + CFTABLE allocs cfe"
		    " (%zu B); the MFC reset at END (pccard_cis.c:638-644)"
		    " frees ONLY pf -> unbounded kmalloc leak inside one"
		    " insertion.\n", CFE_SZ);
		return 0;
	}
	printf("RESULT: S2 UNEXPECTED r=%d iters=%ld\n", r, it);
	return 1;
}

static int
sc_mfcleak(void)
{
	int sp, r;
	long it;

	printf("=== S3 mfcleak: pre-MFC cfe chain leaked (DF-5361) ===\n");
	reset_all();
	put_tuple(attr_mem, 0x00, 0x1A, 3, (const uint8_t *)"\x00\xff\x00");
	put_tuple(attr_mem, 0x05, 0x1B, 2, (const uint8_t *)"\x01\x00");
	put_tuple(attr_mem, 0x08, 0x1B, 2, (const uint8_t *)"\x02\x00");
	put_tuple(attr_mem, 0x0B, 0x06, 6,
	    (const uint8_t *)"\x01\x00\x00\x00\x00\x00");
	put_tuple(attr_mem, 0x13, 0xFF, 0, NULL);

	r = pccard_scan_cis_model(&g_tuple, &sp, &it);
	printf("  scan r=%d iters=%ld allocs=%lu frees=%lu\n", r, it,
	    alloc_cnt, free_cnt);
	if (r == 0 && alloc_cnt > free_cnt) {
		printf("RESULT: DF-5361 a TERMINATING (well-formed-ish) MFC"
		    " card leaks %ld cfe chunk(s) (%zu B each): the reset at"
		    " pccard_cis.c:638-644 kfree()s the pf structs but never"
		    " drains pf->cfe_head; pccard_detach_card"
		    " (pccard.c:317-324) only frees what is still linked, so"
		    " the leak is permanent per insertion cycle.\n",
		    (long)(alloc_cnt - free_cnt), CFE_SZ);
		return 0;
	}
	printf("RESULT: S3 UNEXPECTED\n");
	return 1;
}

static int
sc_checksum(void)
{
	int sp, r;
	long it;

	printf("=== S4 checksum: bound check ignores tuple.mult (DF-5363) ===\n");
	reset_all();
	/* CHECKSUM len=5: offset=0x0004, length=0x0FFB (4091), cksum=0 */
	put_tuple(attr_mem, 0x00, 0x10, 5,
	    (const uint8_t *)"\x04\x00\xfb\x0f\x00");
	put_tuple(attr_mem, 0x07, 0xFF, 0, NULL);

	r = pccard_scan_cis_model(&g_tuple, &sp, &it);
	printf("  scan r=%d iters=%ld\n", r, it);
	if (oob_read_cnt > 0) {
		printf("RESULT: DF-5363 %lu bus_space_read_1 calls at offsets"
		    " >= %d (mapping size); max offset %lu = %.2fx the"
		    " mapping.  addr=4, length=4091 passes the check at"
		    " pccard_cis.c:247-249 (4+4091=4095 < 4096) but the"
		    " reads at :255-259 run to addr+2*(length-1)=8184 in"
		    " attribute space (mult=2).\n", oob_read_cnt,
		    PCCARD_CIS_SIZE, oob_read_max,
		    (double)oob_read_max / PCCARD_CIS_SIZE);
		return 0;
	}
	printf("RESULT: S4 UNEXPECTED (no OOB reads)\n");
	return 1;
}

static int
sc_poison(void)
{
	struct pccard_function *pf;
	struct pccard_config_entry *cfe;
	int sp, r, any = 0;
	long it;

	printf("=== S5 poison: counts>array persist after error-break"
	    " (DF-5360) ===\n");
	reset_all();
	put_tuple(attr_mem, 0x00, 0x1A, 3, (const uint8_t *)"\x00\xff\x00");
	/* num=1 def; FS=IOSPACE; IO=0x80 HASRANGE; range=0xFF ->
	 * LEN4|ADDR4|COUNT=15 -> num_iospace=16 -> error++, break */
	put_tuple(attr_mem, 0x05, 0x1B, 4,
	    (const uint8_t *)"\x41\x08\x80\xff");
	/* num=2; FS=MEMSPACE TABLE; table byte 0x07 -> num_memspace=8 ->
	 * error++, break */
	put_tuple(attr_mem, 0x0B, 0x1B, 3, (const uint8_t *)"\x02\x60\x07");
	put_tuple(attr_mem, 0x10, 0xFF, 0, NULL);

	r = pccard_scan_cis_model(&g_tuple, &sp, &it);
	printf("  scan r=%d iters=%ld card.error=%u poison_io=%d"
	    " poison_mem=%d\n", r, it, g_card.error, poison_iospace_seen,
	    poison_memspace_seen);
	STAILQ_FOREACH(pf, &g_card.pf_head, pf_list) {
		STAILQ_FOREACH(cfe, &pf->cfe_head, cfe_list) {
			printf("  live cfe number=%d num_iospace=%d"
			    " (array %zu) num_memspace=%d (array %zu)\n",
			    cfe->number, cfe->num_iospace,
			    sizeof(cfe->iospace) / sizeof(cfe->iospace[0]),
			    cfe->num_memspace,
			    sizeof(cfe->memspace) / sizeof(cfe->memspace[0]));
			if (cfe->num_iospace >
			    (int)(sizeof(cfe->iospace)/sizeof(cfe->iospace[0])) ||
			    cfe->num_memspace >
			    (int)(sizeof(cfe->memspace)/sizeof(cfe->memspace[0]))) {
				any = 1;
				if (g_card.error)
					printf("  GATE: card.error=%u ->"
					    " pccard_attach_card bails at"
					    " pccard.c:204-208 TODAY, so the"
					    " consumer loops below are"
					    " LATENT; any consumer that"
					    " trusts the stored counts reads"
					    " OOB:\n", g_card.error);
				consumer_io_loops(cfe);
			}
		}
	}
	if (any) {
		printf("RESULT: DF-5360 counts > array size are stored on the"
		    " queue-inserted cfe BEFORE validation (:1044/:1164) and"
		    " the error-break (:1054/:1174) leaves them; the"
		    " default-copy *cfe = *default_cfe (:914) then propagates"
		    " them to later entries.\n");
		return 0;
	}
	printf("RESULT: S5 UNEXPECTED\n");
	return 1;
}

static int
sc_kmallocfail(void)
{
	int sp, r;
	long it;

	printf("=== S6 kmallocfail: unchecked M_NOWAIT (DF-5364) ===\n");
	reset_all();
	put_tuple(attr_mem, 0x00, 0x21, 1, (const uint8_t *)"\x02");
	put_tuple(attr_mem, 0x03, 0xFF, 0, NULL);
	fail_next_alloc = 1;
	r = pccard_scan_cis_model(&g_tuple, &sp, &it);
	if (df5364_hit) {
		printf("  (scan unwound r=%d; kernel would have faulted"
		    " before this point)\n", r);
		return 0;
	}
	printf("RESULT: S6 UNEXPECTED r=%d\n", r);
	return 1;
}

static int
sc_negative(void)
{
	int sp, r, i, ok = 1;
	long it;

	printf("=== SN negatives ===\n");
	reset_all();
	/* VERS_1: major 4 minor 1, "AB\0CD\0EF\0GH", 0xFF terminator */
	{
		uint8_t v[13] = {4, 1, 'A', 'B', 0, 'C', 'D', 0, 'E', 'F', 0,
				 'G', 'H'};
		put_tuple(attr_mem, 0x00, 0x15, 13, v);
		attr_mem[2 * (0 + 2 + 13)] = 0xFF;	/* stop byte */
	}
	put_tuple(attr_mem, 0x0F, 0xFF, 0, NULL);
	r = pccard_scan_cis_model(&g_tuple, &sp, &it);
	printf("  scan r=%d iters=%ld\n", r, it);
	for (i = 0; i < 4; i++) {
		if (g_card.cis1_info[i] == NULL)
			continue;
		const char *p, *end = g_card.cis1_info_buf + 256;
		int term = 0;
		for (p = g_card.cis1_info[i]; p < end; p++)
			if (*p == 0) { term = 1; break; }
		printf("  cis1_info[%d] = \"%s\" %s\n", i, g_card.cis1_info[i],
		    term ? "NUL-terminated in-bounds" : "UNTERMINATED");
		if (!term)
			ok = 0;
	}
	if (g_card.cis1_info[3] != NULL) {
		printf("  cis1_info[3] unexpectedly set\n");
		ok = 0;
	}

	/* MFC :324 panic reachability */
	{
		int cnt, maxc = -1;
		for (cnt = 0; cnt <= 255; cnt++)
			if (cnt * 5 + 1 <= 255)
				maxc = cnt;
		printf("  MFC :312 (length==count*5+1, length<=255) admits"
		    " count<=%d < 51 stack slots -> :324 panic() dead code\n",
		    maxc);
	}

	/* FUNCE LAN NID bounds */
	reset_all();
	put_tuple(attr_mem, 0x00, 0x21, 1, (const uint8_t *)"\x06");
	put_tuple(attr_mem, 0x03, 0x22, 11,
	    (const uint8_t *)"\x04\x09\x01\x02\x03\x04\x05\x06\x07\x08\x09");
	put_tuple(attr_mem, 0x10, 0x22, 10,
	    (const uint8_t *)"\x04\x08\x01\x02\x03\x04\x05\x06\x07\x08");
	put_tuple(attr_mem, 0x1C, 0xFF, 0, NULL);
	r = pccard_scan_cis_model(&g_tuple, &sp, &it);
	{
		struct pccard_function *pf = STAILQ_FIRST(&g_card.pf_head);
		int nid = pf ? pf->pf_funce_lan_nidlen : -1;
		printf("  FUNCE NID: nidlen=%d (len=9 rejected by 'len > 8' at"
		    " :1279; len=8 accepted, pfl_nid[8] never overrun)\n", nid);
		if (nid != 8)
			ok = 0;
	}
	printf("RESULT: SN negatives %s\n",
	    ok ? "confirmed (checked, no new defect)" : "FAILED");
	return ok ? 0 : 1;
}

int
main(int argc, char **argv)
{
	const char *s = argc > 1 ? argv[1] : "all";
	int rc = 0;

	if (!strcmp(s, "livelock"))		rc = sc_livelock();
	else if (!strcmp(s, "leakloop"))	rc = sc_leakloop();
	else if (!strcmp(s, "mfcleak"))		rc = sc_mfcleak();
	else if (!strcmp(s, "checksum"))	rc = sc_checksum();
	else if (!strcmp(s, "poison"))		rc = sc_poison();
	else if (!strcmp(s, "kmallocfail"))	rc = sc_kmallocfail();
	else if (!strcmp(s, "negative"))	rc = sc_negative();
	else if (!strcmp(s, "all")) {
		rc |= sc_livelock();
		rc |= sc_leakloop();
		rc |= sc_mfcleak();
		rc |= sc_checksum();
		rc |= sc_poison();
		rc |= sc_negative();
		rc |= sc_kmallocfail();
	} else {
		fprintf(stderr, "unknown scenario %s\n", s);
		return 2;
	}
	return rc;
}
