⬢ DragonFlyBSD Kernel Audit
DF-4267 / wb_logic.c
← back to finding ↓ download raw
/*
 * DF-4266 / DF-4267 logic harness (userland, host-runnable)
 *
 * Transcribes verbatim the loop-termination and ring-linking code from
 *   sys/dev/netif/wb/if_wb.c   (DragonFly, audited tree)
 *   sys/pci/if_wb.c            (FreeBSD 4.11 original, for comparison)
 * and drives it against a faithful model of the W89C840F descriptor
 * ownership protocol (OWN=1 -> chip owns/free-to-fill; chip clears OWN
 * and sets RXCMP when a frame is written back).
 *
 * Build:  cc -O2 -o wb_logic wb_logic.c
 * Run:    ./wb_logic
 */
#include <stdio.h>
#include <stdint.h>
#include <string.h>

#define WB_RX_LIST_CNT	64
#define WB_TX_LIST_CNT	128
#define WB_RXSTAT_RXLEN	0x3FFF0000u
#define WB_RXSTAT_LASTFRAG 0x00000100u
#define WB_RXSTAT_FIRSTFRAG 0x00000200u
#define WB_RXSTAT_RXCMP	0x40000000u
#define WB_RXSTAT_OWN	0x80000000u
#define WB_RXBYTES(x)	(((x) & WB_RXSTAT_RXLEN) >> 16)
#define WB_RXSTAT (WB_RXSTAT_FIRSTFRAG|WB_RXSTAT_LASTFRAG|WB_RXSTAT_OWN)
#define WB_MIN_FRAMELEN	60
#define ETHER_CRC_LEN	4

struct wb_desc { uint32_t wb_status, wb_ctl, wb_ptr1, wb_ptr2; };

/* ---- DragonFly wb_list_rx_init() linking loop (if_wb.c:904-912) ---- */
static int dfly_nexti(int i)
{
	int nexti;
	nexti = (WB_RX_LIST_CNT - 1) ? 0 : i + 1;	/* constant condition! */
	return nexti;
}

/* ---- FreeBSD 4.11 wb_list_rx_init() linking loop (reference) ---- */
static int fbsd_nextdesc_idx(int i)
{
	if (i == (WB_RX_LIST_CNT - 1))
		return 0;
	return i + 1;
}

static void ring_report(const char *tag, int (*nextfn)(int))
{
	int next[WB_RX_LIST_CNT], i, visited = 1, cur = 0, seen[WB_RX_LIST_CNT];

	for (i = 0; i < WB_RX_LIST_CNT; i++)
		next[i] = nextfn(i);

	memset(seen, 0, sizeof(seen));
	seen[0] = 1;
	for (;;) {
		cur = next[cur];
		if (seen[cur]) break;
		seen[cur] = 1;
		visited++;
	}
	printf("%-12s ring: next[0]=%d next[1]=%d next[63]=%d | distinct descriptors "
	       "reachable from head: %d / %d\n", tag, next[0], next[1],
	       next[WB_RX_LIST_CNT - 1], visited, WB_RX_LIST_CNT);
}

/*
 * RX processing loop simulation.
 * chip: -1 = chip mid-write on the head descriptor (OWN still set, no RXCMP)
 *        0 = idle
 *        1 = chip completed head descriptor (OWN cleared, RXCMP set, len 1518)
 */
static int run_rx_loop(const char *tag, int chip_state, int *babbling_out, int *frames_out)
{
	struct wb_desc descs[WB_RX_LIST_CNT];
	int i, processed = 0, babbling = 0, head = 0;

	for (i = 0; i < WB_RX_LIST_CNT; i++)
		descs[i].wb_status = WB_RXSTAT;	/* wb_newbuf(): armed, chip-owned */

	if (chip_state == 1)
		descs[0].wb_status = (WB_RXSTAT & ~WB_RXSTAT_OWN) | WB_RXSTAT_RXCMP
				     | (1518 << 16);	/* completed frame, OWN=0 */
	else if (chip_state == -1)
		descs[0].wb_status = WB_RXSTAT | (37 << 16); /* mid-write, no RXCMP */

	/* DragonFly wb_rxeof() loop (if_wb.c:972-1001), verbatim condition: */
	for (;;) {
		uint32_t rxstat = descs[head].wb_status;

		if ((rxstat & WB_RXSTAT_OWN) == 0)
			break;
		/* body: length/validity checks */
		if ((rxstat & WB_RXSTAT_RXCMP) == 0 ||
		    WB_RXBYTES(rxstat) < WB_MIN_FRAMELEN ||
		    WB_RXBYTES(rxstat) > 1536) {
			babbling = 1;	/* "receiver babbling ... forcing reset" */
			break;
		}
		processed++;	/* deliver frame */
		break;		/* harness: single iteration decision */
	}

	/* FreeBSD 4.11 wb_rxeof() loop condition, verbatim: process while !OWN */
	{
		uint32_t rxstat = descs[head].wb_status;
		int fprocessed = ((rxstat & WB_RXSTAT_OWN) == 0) &&
				 (rxstat & WB_RXSTAT_RXCMP);
		printf("%-28s chip_state=%-9s DFLY: processed=%d babbling-reset=%d | "
		       "FBSD411-COND: processed=%d\n",
		       tag, chip_state == 1 ? "completed" :
			    chip_state == -1 ? "mid-write" : "idle-none",
		       processed, babbling, fprocessed);
	}
	*babbling_out = babbling;
	*frames_out = processed;
	return 0;
}

int main(void)
{
	printf("== DF-4267: RX ring construction ==\n");
	ring_report("DragonFly", dfly_nexti);
	ring_report("FreeBSD411", fbsd_nextdesc_idx);

	printf("\n== DF-4266: wb_rxeof loop direction vs chip ownership ==\n");
	{
		int b, f;
		run_rx_loop("frame completed by chip", 1, &b, &f);
		run_rx_loop("chip still writing desc", -1, &b, &f);
		run_rx_loop("no rx activity", 0, &b, &f);
	}
	printf("\nExpected (correct driver): completed -> processed, mid-write/idle -> wait.\n");
	return 0;
}