/*
 * DF-4068 trigger/termination model (runs on the audit guest; userland).
 *
 * Bug modeled (source-certain, hardware-gated):
 *   All three ti_intr() completion walks terminate on raw equality between
 *   a driver-side consumer index (uint16_t, kept inside the ring modulus by
 *   TI_INC(x,y) = (x+1)%y, if_tireg.h:965) and a 32-bit index word the
 *   Tigon firmware DMA-writes into host memory (struct ti_producer.ti_idx,
 *   if_tireg.h:485-488; the NIC owns ti_return_prodidx_r / ti_tx_considx_r
 *   / ti_ev_prodidx_r inside the contigmalloc'd ti_ring_data):
 *
 *     ti_rxeof:         while (sc->ti_rx_saved_considx !=                  +
 *                          sc->ti_return_prodidx.ti_idx);  if_ti.c:1629
 *     ti_txeof:         while (sc->ti_tx_saved_considx !=
 *                          sc->ti_tx_considx.ti_idx);      if_ti.c:1729
 *     ti_handle_events: while (sc->ti_ev_saved_considx !=
 *                          sc->ti_ev_prodidx.ti_idx);      if_ti.c:511
 *
 *   The consumer only ever takes values in [0, ring_cnt): 2048 / 512 / 256.
 *   A DMA'd producer/consumer value OUTSIDE that range (e.g. 0x8000, or a
 *   high bit set by faulty firmware / DMA tampering) makes the equality
 *   unsatisfiable -> the loop never exits.  It runs inside the interrupt
 *   handler AFTER host interrupts were masked (if_ti.c:1778
 *   CSR_WRITE_4(sc, TI_MB_HOSTINTR, 1)) while holding ifp->if_serializer:
 *   hard livelock (soft lockup; the CPU never returns from the IRQ).
 *   txeof additionally decrements sc->ti_txcnt (int, if_ti.c:1755) each
 *   spin -> signed underflow once it exits (it cannot while spinning).
 *
 *   This program simulates both the benign and the poisoned loop with a
 *   bounded iteration budget and reports termination.  It does not touch
 *   the kernel: the real path needs a Tigon NIC writing an out-of-range
 *   index word.
 */

#include <stdio.h>

struct loop_spec {
	const char   *name;
	unsigned      ring_cnt;
	unsigned      cons_start;
	unsigned long prod_raw;
};

static void simulate(const struct loop_spec *s)
{
	unsigned long cons = s->cons_start;
	unsigned long iters = 0, budget = 8UL * s->ring_cnt + 16;
	int done = 0;

	while (iters < budget) {
		if (cons == s->prod_raw) { done = 1; break; }
		cons = (cons + 1) % s->ring_cnt;   /* TI_INC */
		iters++;
	}
	printf("%-14s ring=%-4u prod_raw=0x%08lx -> %s after %lu iterations"
	       " (consumer range [0,%u])\n",
	       s->name, s->ring_cnt, s->prod_raw,
	       done ? "TERMINATES" : "NEVER TERMINATES (equality unsatisfiable)",
	       iters, s->ring_cnt - 1);
}

int main(void)
{
	struct loop_spec cases[] = {
		/* benign: producer inside modular range */
		{ "rxeof/benign",  2048, 0, 37 },
		{ "txeof/benign",   512, 0, 511 },
		{ "events/benign",  256, 0, 3 },
		/* poisoned: 32-bit DMA'd word outside consumer's range */
		{ "rxeof/poison",  2048, 0, 0x00008000UL },
		{ "rxeof/poison2", 2048, 0, 4096UL },          /* 2x ring size */
		{ "txeof/poison",   512, 0, 0x00010000UL },
		{ "events/poison",  256, 0, 0xFFFFFFFFUL },
	};
	unsigned i;

	printf("DF-4068 model: ti completion walks keyed on raw equality with "
	       "NIC-DMA'd u32 index (if_ti.c:511,1629,1729)\n\n");
	for (i = 0; i < sizeof(cases)/sizeof(cases[0]); i++)
		simulate(&cases[i]);
	printf("\nInterrupt handler masks host interrupts before walking "
	       "(if_ti.c:1778) and holds ifp->if_serializer: an unsatisfiable "
	       "loop = hard CPU livelock.\n");
	printf("\nRC_DF4068_MODEL=0\n");
	return 0;
}
