⬢ DragonFlyBSD Kernel Audit
DF-4304 / bfe_walk_harness.c
← back to finding ↓ download raw
/*
 * bfe_walk_harness.c - index-space + loop-termination proof for the
 * bfe_rxeof()/bfe_txeof() DMA-status walks (if_bfe.c:1064-1083, :1105-1147).
 *
 * Constants from sys/dev/netif/bfe/if_bfereg.h:
 *   BFE_STAT_CDMASK  0x00000fff   (:69, 12-bit "current descriptor" offset)
 *   BFE_DESC (size)  8 bytes      (:434-437, two uint32_t)
 *   BFE_RX_LIST_CNT  511          (:406)   valid indices 0..510
 *   BFE_TX_LIST_CNT  511          (:405)
 *   BFE_INC(x,y)     x = (x == y-1) ? 0 : x+1   (:426)
 *
 * Driver arithmetic under test:
 *   bfe_txeof:  chipidx = (CSR_READ_4(BFE_DMATX_STAT) & CDMASK) / 8;  :1064-1065
 *               while (i != chipidx) { ...; BFE_INC(i, 511); }        :1070-1083
 *   bfe_rxeof:  current = (CSR_READ_4(BFE_DMARX_STAT) & CDMASK) / 8;  :1106-1107
 *               while (current != cons) { ...; BFE_INC(cons, 511); }  :1109-1147
 *
 * The 12-bit field encodes byte offsets 0..4095 -> indices 0..511.
 * 511 is NOT a valid ring index (ring is 0..510) and BFE_INC can never
 * produce it, so any hardware-reported offset in [4088..4095] makes both
 * loops non-terminating (interrupt context, serializer held).
 * The driver itself programs 511*8 = 4088 into BFE_DMARX_PTR
 * (if_bfe.c:549), so the off-ring encoding is an architecturally used
 * value on this register interface, not a purely theoretical one.
 */
#include <stdio.h>

#define BFE_STAT_CDMASK	0x00000fffu
#define DESC_SZ		8u
#define LIST_CNT	511u

int main(void)
{
	unsigned max_idx = BFE_STAT_CDMASK / DESC_SZ;
	unsigned cons, current;
	long steps;
	int terminated = 0;

	printf("bfe walk harness: CDMASK=0x%03x desc_sz=%u ring=%u entries "
	    "(valid 0..%u)\n", BFE_STAT_CDMASK, DESC_SZ, LIST_CNT, LIST_CNT - 1);
	printf("encodable descriptor indices: 0..%u  -> index %u is OUTSIDE "
	    "the ring (ring max %u)\n", max_idx, max_idx, LIST_CNT - 1);
	printf("offsets encoding the off-ring index: [%u..%u]  "
	    "(driver itself writes %u (511*8) to BFE_DMARX_PTR at if_bfe.c:549)\n",
	    max_idx * DESC_SZ, BFE_STAT_CDMASK, LIST_CNT * DESC_SZ);

	/* simulate bfe_rxeof's walk: cons starts at 0, BFE_INC cycles 0..510 */
	for (current = 0; current <= max_idx; current++) {
		cons = 0;
		steps = 0;
		while (current != cons) {
			cons = (cons == LIST_CNT - 1) ? 0 : cons + 1; /* BFE_INC */
			if (++steps > 1000000L) {
				printf("current=%3u: walk NEVER TERMINATES "
				    "(no cons in 0..%u equals it; >1e6 steps)\n",
				    current, LIST_CNT - 1);
				if (current == max_idx)
					terminated = -1;
				break;
			}
		}
		if (steps <= 1000000L && current == max_idx)
			printf("current=%3u: terminated after %ld steps "
			    "(UNEXPECTED)\n", current, steps);
	}
	printf("RESULT: off-ring index %u => %s\n", max_idx,
	    terminated ? "bfe_rxeof/bfe_txeof walk cannot terminate "
	    "(interrupt-context livelock holding the serializer)"
	    : "walk terminated (analysis wrong)");
	return terminated ? 0 : 1;
}