// SPDX-License-Identifier: Apache-2.0 // // The lwIP network interface backed by the e1000 driver. // // Two directions and almost nothing else: a linkoutput that flattens a pbuf // chain into a frame and hands it to the driver, and a poll that takes frames // off the driver and hands them up. Everything about IP, ARP and TCP lives in // lwIP; everything about descriptors and registers lives in the driver; this // file is the seam. #include "types.h" #include "defs.h" #include "param.h" #include "lwip/opt.h" #include "lwip/netif.h" #include "lwip/pbuf.h" #include "lwip/etharp.h" #include "lwip/ethip6.h" #include "netif/ethernet.h" #define ETH_MTU 1500 #define ETH_FRAMESZ 1518 // 1500 payload + 14 header + 4 FCS allowance static struct netif e1000_netif; // One staging buffer, not one per call: a frame is 1518 bytes and the kernel // stack is 4096 in total, so a local of this size on a path that is several // frames deep is exactly the overflow the kstack canary exists to catch. // Safe as a single static because every caller holds the lwIP lock. static char txframe[ETH_FRAMESZ]; static char rxframe[ETH_FRAMESZ]; // Send one packet. lwIP hands over a pbuf chain; the driver wants one flat // buffer, so this walks the chain into txframe. static err_t e1000_linkoutput(struct netif *netif, struct pbuf *p) { (void)netif; if (p->tot_len > sizeof(txframe)) return ERR_IF; u16_t off = 0; for (struct pbuf *q = p; q != NULL; q = q->next) { if (off + q->len > sizeof(txframe)) return ERR_IF; memmove(txframe + off, q->payload, q->len); off += q->len; } // A full transmit ring is congestion, not failure. Reporting ERR_IF would // make lwIP drop the segment and wait for a retransmission timer; ERR_WOULDBLOCK // lets it try again, which for TCP is the difference between a stall of // milliseconds and one of seconds. if (e1000_transmit(txframe, off) != 0) return ERR_WOULDBLOCK; return ERR_OK; } // Bring the interface up. Called once, by the network thread. static err_t e1000_netif_init(struct netif *netif) { const unsigned char *mac = e1000_mac(); netif->name[0] = 'e'; netif->name[1] = 'n'; netif->output = etharp_output; // IP -> ARP -> linkoutput netif->linkoutput = e1000_linkoutput; netif->mtu = ETH_MTU; netif->hwaddr_len = 6; for (int i = 0; i < 6; i++) netif->hwaddr[i] = mac[i]; netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_LINK_UP; return ERR_OK; } struct netif * net_e1000_netif(void) { return &e1000_netif; } // Attach the interface with no address yet -- DHCP fills it in. int net_e1000_attach(void) { ip4_addr_t any; IP4_ADDR(&any, 0, 0, 0, 0); if (netif_add(&e1000_netif, &any, &any, &any, NULL, e1000_netif_init, netif_input) == NULL) return -1; netif_set_default(&e1000_netif); netif_set_up(&e1000_netif); netif_set_link_up(&e1000_netif); return 0; } // Take whatever the driver has and push it into lwIP. Returns how many frames // moved, so the caller can tell a busy poll from an idle one. // // Bounded rather than draining: a receive loop with no limit starves everything // else on a busy network, and the caller comes straight back anyway. int net_e1000_poll(void) { int moved = 0; for (int i = 0; i < 32; i++) { int len = e1000_receive(rxframe, sizeof(rxframe)); if (len <= 0) break; // PBUF_POOL, so this comes from lwIP's own pool rather than a heap it // does not have. A null return means the pool is exhausted, which is a // dropped packet -- entirely normal under load, and what the protocol // above is designed to survive. struct pbuf *p = pbuf_alloc(PBUF_RAW, (u16_t)len, PBUF_POOL); if (p == NULL) break; pbuf_take(p, rxframe, (u16_t)len); // netif->input is netif_input, set at netif_add: it dispatches on the // Ethernet type. Ownership of the pbuf passes with the call, including // when it fails, so there is nothing to free here. if (e1000_netif.input(p, &e1000_netif) != ERR_OK) pbuf_free(p); moved++; } return moved; }