diff --git a/gc_bba_test/source/netboot.c b/gc_bba_test/source/netboot.c index 63bf8c6..bc7a346 100644 --- a/gc_bba_test/source/netboot.c +++ b/gc_bba_test/source/netboot.c @@ -29,6 +29,7 @@ static u8 s_peer_mac[6]; /* Last descriptor ring_next() looked at, kept for the error path: when a * length is rejected, the four raw bytes are the whole diagnosis. */ static u8 s_last_desc[4]; +static u8 s_last_dump[32]; static u32 s_last_page; static u16 rd16(const u8 *p) { return ((u16)p[0] << 8) | p[1]; } @@ -54,6 +55,12 @@ static int ring_next(u8 *buf, u32 cap) if (bba_stream((u16)addr, hdr, 4) < 0) return -1; memcpy(s_last_desc, hdr, 4); s_last_page = rpage; + /* Keep the head of the page for the error path. Guessing at a 4-byte + * descriptor has already cost several round trips; 32 bytes shows + * whether an ethernet header is present and AT WHAT OFFSET, which + * settles "bad length" versus "reading the wrong place" outright. */ + if (bba_stream((u16)addr, s_last_dump, sizeof(s_last_dump)) < 0) + return -1; dlen = ((u32)hdr[2] << 8) | hdr[3]; if (dlen <= 4 || dlen > 1518 + 4) return -3; /* descriptor implausible */ flen = dlen - 4; @@ -104,7 +111,7 @@ int netboot_receive(void *dst, u32 maxlen, void (*status)(const char *msg)) { static u8 frame[1536]; u8 *out = (u8 *)dst; - u32 got = 0, nblocks = 0; + u32 got = 0, nblocks = 0, bad = 0, seen = 0; u16 expect = 0; char line[80]; u64 t0; @@ -177,6 +184,46 @@ int netboot_receive(void *dst, u32 maxlen, void (*status)(const char *msg)) for (;;) { int n = ring_next(frame, sizeof(frame)); + if (n == -3) { + /* A descriptor we cannot make sense of must NOT kill the + * transfer. The ring collects EVERYTHING on the wire -- + * mDNS, IPv6 RAs, ARP from the host -- so pressing Z on a + * live network hits stray frames long before the sender + * starts. Log the first few, resync, and carry on. */ + bad++; + if (bad <= 3 && status) { + int i; + snprintf(line, sizeof(line), + "skip bad descriptor at page %u: %02X %02X %02X %02X", + (unsigned)s_last_page, s_last_desc[0], + s_last_desc[1], s_last_desc[2], s_last_desc[3]); + status(line); + for (i = 0; i < 32; i += 16) { + snprintf(line, sizeof(line), + " +%02X %02X %02X %02X %02X %02X %02X %02X %02X " + "%02X %02X %02X %02X %02X %02X %02X %02X", i, + s_last_dump[i+0], s_last_dump[i+1], s_last_dump[i+2], + s_last_dump[i+3], s_last_dump[i+4], s_last_dump[i+5], + s_last_dump[i+6], s_last_dump[i+7], s_last_dump[i+8], + s_last_dump[i+9], s_last_dump[i+10], s_last_dump[i+11], + s_last_dump[i+12], s_last_dump[i+13], s_last_dump[i+14], + s_last_dump[i+15]); + status(line); + } + } + if (bad > 200) { + if (status) status("too many bad descriptors - giving up"); + return -2; + } + /* Resync to the gateware's own pointer and keep waiting. */ + { + u8 wp[2]; + if (bba_read(REG_RWP_LO, wp, 2) < 0) return -1; + if (wp[0] >= 1 && wp[0] <= 15) + bba_w8(REG_RRP_LO, wp[0]); + } + continue; + } if (n < 0) { /* Report the pointers, not just the code: -1 is an EXI access * failure, -2 an RRP outside the ring, -3 a descriptor whose @@ -206,13 +253,34 @@ int netboot_receive(void *dst, u32 maxlen, void (*status)(const char *msg)) * console power cycle. */ u32 idle = (u32)ticks_to_millisecs(diff_ticks(t0, gettime())); if (idle > (got ? 5000u : 30000u)) { - if (status) status(got ? "timeout mid-transfer" : "timeout: no frames"); + if (status) { + snprintf(line, sizeof(line), + "%s (%u frames seen, %u bad descriptors)", + got ? "timeout mid-transfer" : "timeout: no match", + (unsigned)seen, (unsigned)bad); + status(line); + } return -3; } continue; } + seen++; if ((u32)n < NB_HDR) continue; - if (rd16(frame + 12) != NB_ETHERTYPE) continue; + if (rd16(frame + 12) != NB_ETHERTYPE) { + /* Dump the first non-matching frame. If the sender is running + * and nothing matches, the bytes show whether the body is + * offset -- the ethertype would appear at 11 or 13 instead of + * 12. */ + if (seen == 1 && status) { + snprintf(line, sizeof(line), + "frame 1 (%d B) etype=%04X at 12; " + "%02X %02X %02X %02X %02X %02X %02X %02X", + n, rd16(frame + 12), frame[0], frame[1], frame[2], + frame[3], frame[4], frame[5], frame[6], frame[7]); + status(line); + } + continue; + } if (frame[14] != 'G' || frame[15] != 'C' || frame[16] != 'U' || frame[17] != 'P') continue;