ref: 567b1b912a91bf56bebacc67b6a467986d5bed6c
dir: /sys/src/boot/zynq/net.c/
#include <u.h> #include "dat.h" #include "fns.h" #include "mem.h" enum { ETHLEN = 1600, UDPLEN = 576, NRX = 64, RXBASE = 128 * 1024 * 1024, ETHHEAD = 14, IPHEAD = 20, UDPHEAD = 8, BOOTREQ = 1, DHCPDISCOVER = 1, DHCPOFFER, DHCPREQUEST, DHCPDECLINE, }; enum { NET_CTRL, NET_CFG, NET_STATUS, DMA_CFG = 4, TX_STATUS, RX_QBAR, TX_QBAR, RX_STATUS, INTR_STATUS, INTR_EN, INTR_DIS, INTR_MASK, PHY_MAINT, RX_PAUSEQ, TX_PAUSEQ, HASH_BOT = 32, HASH_TOP, SPEC_ADDR1_BOT, SPEC_ADDR1_TOP, }; enum { MDCTRL, MDSTATUS, MDID1, MDID2, MDAUTOADV, MDAUTOPART, MDAUTOEX, MDAUTONEXT, MDAUTOLINK, MDGCTRL, MDGSTATUS, MDPHYCTRL = 0x1f, }; enum { /* NET_CTRL */ RXEN = 1<<2, TXEN = 1<<3, MDEN = 1<<4, STARTTX = 1<<9, /* NET_CFG */ SPEED = 1<<0, FDEN = 1<<1, RX1536EN = 1<<8, GIGE_EN = 1<<10, RXCHKSUMEN = 1<<24, /* NET_STATUS */ PHY_IDLE = 1<<2, /* DMA_CFG */ TXCHKSUMEN = 1<<11, /* TX_STATUS */ TXCOMPL = 1<<5, /* MDCTRL */ MDRESET = 1<<15, AUTONEG = 1<<12, FULLDUP = 1<<8, /* MDSTATUS */ LINK = 1<<2, /* MDGSTATUS */ RECVOK = 3<<12, }; typedef struct { uchar edest[6]; uchar esrc[6]; ulong idest; ulong isrc; ushort dport, sport; ushort len; uchar data[UDPLEN]; } udp; static ulong *eth0 = (ulong *) 0xe000b000; static int phyaddr = 7; static u32int myip, dhcpip, tftpip, xid; static uchar mac[6] = {0x0E, 0xA7, 0xDE, 0xAD, 0xBE, 0xEF}; static uchar tmac[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; static char file[128]; static udp ubuf, urbuf; static uchar txbuf[ETHLEN]; static ulong txdesc[4], *txact, *rxact; static ulong rxdesc[NRX*2]; void mdwrite(ulong *r, int reg, u16int val) { while((r[NET_STATUS] & PHY_IDLE) == 0) ; r[PHY_MAINT] = 1<<30 | 1<<28 | 1<<17 | phyaddr << 23 | reg << 18 | val; while((r[NET_STATUS] & PHY_IDLE) == 0) ; } u16int mdread(ulong *r, int reg) { while((r[NET_STATUS] & PHY_IDLE) == 0) ; r[PHY_MAINT] = 1<<30 | 1<<29 | 1<<17 | phyaddr << 23 | reg << 18; while((r[NET_STATUS] & PHY_IDLE) == 0) ; return r[PHY_MAINT]; } void ethinit(ulong *r) { int v; ulong *p; ulong d; r[NET_CTRL] = 0; r[RX_STATUS] = 0xf; r[TX_STATUS] = 0xff; r[INTR_DIS] = 0x7FFFEFF; r[RX_QBAR] = r[TX_QBAR] = 0; r[NET_CFG] = MDC_DIV << 18 | FDEN | SPEED | RX1536EN | GIGE_EN | RXCHKSUMEN; r[SPEC_ADDR1_BOT] = mac[0] | mac[1] << 8 | mac[2] << 16 | mac[3] << 24; r[SPEC_ADDR1_TOP] = mac[4] | mac[5] << 8; r[DMA_CFG] = TXCHKSUMEN | 0x18 << 16 | 1 << 10 | 3 << 8 | 0x10; txdesc[0] = 0; txdesc[1] = 1<<31; txdesc[2] = 0; txdesc[3] = 1<<31 | 1<<30; txact = txdesc; r[TX_QBAR] = (ulong) txdesc; for(p = rxdesc, d = RXBASE; p < rxdesc + nelem(rxdesc); d += ETHLEN){ *p++ = d; *p++ = 0; } p[-2] |= 2; rxact = rxdesc; r[RX_QBAR] = (ulong) rxdesc; r[NET_CTRL] = MDEN; // mdwrite(r, MDCTRL, MDRESET); mdwrite(r, MDCTRL, AUTONEG); if((mdread(r, MDSTATUS) & LINK) == 0){ puts("Waiting for Link ...\n"); while((mdread(r, MDSTATUS) & LINK) == 0) ; } *(u32int*)(SLCR_BASE + SLCR_UNLOCK) = UNLOCK_KEY; v = mdread(r, MDPHYCTRL); if((v & 0x40) != 0){ puts("1000BASE-T"); while((mdread(r, MDGSTATUS) & RECVOK) != RECVOK) ; r[NET_CFG] |= GIGE_EN; *(u32int*)(SLCR_BASE + GEM0_CLK_CTRL) = 1 << 20 | 8 << 8 | 1; }else if((v & 0x20) != 0){ puts("100BASE-TX"); r[NET_CFG] = r[NET_CFG] & ~GIGE_EN | SPEED; *(u32int*)(SLCR_BASE + GEM0_CLK_CTRL) = 5 << 20 | 8 << 8 | 1; }else if((v & 0x10) != 0){ puts("10BASE-T"); r[NET_CFG] = r[NET_CFG] & ~(GIGE_EN | SPEED); *(u32int*)(SLCR_BASE + GEM0_CLK_CTRL) = 20 << 20 | 20 << 8 | 1; }else puts("???"); *(u32int*)(SLCR_BASE + SLCR_UNLOCK) = LOCK_KEY; if((v & 0x08) != 0) puts(" Full Duplex\n"); else{ puts(" Half Duplex\n"); r[NET_CFG] &= ~FDEN; } r[NET_CTRL] |= TXEN | RXEN; } void ethtx(ulong *r, uchar *buf, int len) { txact[0] = (ulong) buf; txact[1] = 1<<15 | len; if(txact == txdesc + nelem(txdesc) - 2){ txact[1] |= 1<<30; txact = txdesc; }else txact += 2; r[TX_STATUS] = -1; r[NET_CTRL] |= STARTTX; while((r[TX_STATUS] & TXCOMPL) == 0) ; } void udptx(ulong *r, udp *u) { uchar *p, *q; int n; p = q = txbuf; memcpy(p, u->edest, 6); memcpy(p + 6, u->esrc, 6); q += 12; *q++ = 8; *q++ = 0; *q++ = 5 | 4 << 4; *q++ = 0; n = IPHEAD + UDPHEAD + u->len; *q++ = n >> 8; *q++ = n; *q++ = 0x13; *q++ = 0x37; *q++ = 1<<6; *q++ = 0; *q++ = 1; *q++ = 0x11; *q++ = 0; *q++ = 0; q = u32put(q, u->isrc); q = u32put(q, u->idest); *q++ = u->sport >> 8; *q++ = u->sport; *q++ = u->dport >> 8; *q++ = u->dport; n = UDPHEAD + u->len; *q++ = n >> 8; *q++ = n; *q++ = 0; *q++ = 0; memcpy(q, u->data, u->len); ethtx(r, p, ETHHEAD + IPHEAD + UDPHEAD + u->len); } void dhcppkg(ulong *r, int t) { uchar *p; udp *u; u = &ubuf; p = u->data; *p++ = BOOTREQ; *p++ = 1; *p++ = 6; *p++ = 0; p = u32put(p, xid); p = u32put(p, 0x8000); memset(p, 0, 16); u32put(p + 8, dhcpip); p += 16; memcpy(p, mac, 6); p += 6; memset(p, 0, 202); p += 202; *p++ = 99; *p++ = 130; *p++ = 83; *p++ = 99; *p++ = 53; *p++ = 1; *p++ = t; if(t == DHCPREQUEST){ *p++ = 50; *p++ = 4; p = u32put(p, myip); *p++ = 54; *p++ = 4; p = u32put(p, dhcpip); } *p++ = 0xff; memset(u->edest, 0xff, 6); memcpy(u->esrc, mac, 6); u->sport = 68; u->dport = 67; u->idest = -1; u->isrc = 0; u->len = p - u->data; udptx(r, u); } uchar * ethrx(void) { while((*rxact & 1) == 0) if(timertrig()) return nil; return (uchar *) (*rxact & ~3); } void ethnext(void) { *rxact &= ~1; if((*rxact & 2) != 0) rxact = rxdesc; else rxact += 2; } void arp(int op, uchar *edest, ulong idest) { uchar *p; static uchar broad[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; p = txbuf; if(edest == nil) edest = broad; memcpy(p, edest, 6); memcpy(p + 6, mac, 6); p[12] = 8; p[13] = 6; p += 14; p = u32put(p, 0x00010800); p = u32put(p, 0x06040000 | op); memcpy(p, mac, 6); p = u32put(p + 6, myip); memcpy(p, edest, 6); p = u32put(p + 6, idest); ethtx(eth0, txbuf, p - txbuf); } void arpc(uchar *p) { p += 14; if(u32get(p) != 0x00010800 || p[4] != 6 || p[5] != 4 || p[6] != 0) return; switch(p[7]){ case 1: if(myip != 0 && u32get(p + 24) == myip) arp(2, p + 8, u32get(p + 14)); break; case 2: if(tftpip != 0 && u32get(p + 14) == tftpip) memcpy(tmac, p + 8, 6); break; } } udp * udprx(void) { uchar *p; ulong v; udp *u; u = &urbuf; for(;; ethnext()){ p = ethrx(); if(p == nil) return nil; if(p[12] != 8) continue; if(p[13] == 6){ arpc(p); continue; } if(p[13] != 0) continue; p += ETHHEAD; if((p[0] >> 4) != 4 || p[9] != 0x11) continue; v = u32get(p + 16); if(v != (ulong) -1 && v != myip) continue; u->idest = v; u->isrc = u32get(p + 12); p += (p[0] & 0xf) << 2; u->sport = p[0] << 8 | p[1]; u->dport = p[2] << 8 | p[3]; u->len = p[4] << 8 | p[5]; if(u->len < 8) continue; u->len -= 8; if(u->len >= sizeof(u->data)) u->len = sizeof(u->data); memcpy(u->data, p + 8, u->len); ethnext(); return u; } } void arpreq(void) { uchar *p; arp(1, nil, tftpip); timeren(ARPTIMEOUT); for(;; ethnext()){ p = ethrx(); if(p == nil){ print("ARP timeout\n"); timeren(ARPTIMEOUT); arp(1, nil, tftpip); } if(p[12] != 8 || p[13] != 6) continue; arpc(p); if(tmac[0] != 0xff) break; } timeren(-1); } void dhcp(ulong *r) { udp *u; uchar *p; uchar type; xid = 0xdeadbeef; tftpip = 0; dhcppkg(r, DHCPDISCOVER); timeren(DHCPTIMEOUT); for(;;){ u = udprx(); if(u == nil){ timeren(DHCPTIMEOUT); dhcppkg(r, DHCPDISCOVER); print("DHCP timeout\n"); } p = u->data; if(u->dport != 68 || p[0] != 2 || u32get(p + 4) != xid || u32get(p + 236) != 0x63825363) continue; p += 240; type = 0; dhcpip = 0; for(; p < u->data + u->len && *p != 0xff; p += 2 + p[1]) switch(*p){ case 53: type = p[2]; break; case 54: dhcpip = u32get(p + 2); break; } if(type != DHCPOFFER) continue; p = u->data; if(p[108] == 0){ print("Offer from %I for %I with no boot file\n", dhcpip, u32get(p + 16)); continue; } myip = u32get(p + 16); tftpip = u32get(p + 20); memcpy(file, p + 108, 128); print("Offer from %I for %I with boot file '%s' at %I\n", dhcpip, myip, file, tftpip); break; } timeren(-1); dhcppkg(r, DHCPREQUEST); } udp * tftppkg(void) { udp *u; u = &ubuf; memcpy(u->edest, tmac, 6); memcpy(u->esrc, mac, 6); u->idest = tftpip; u->isrc = myip; u->sport = 69; u->dport = 69; return u; } void tftp(ulong *r, char *q, uintptr base) { udp *u, *v; uchar *p; int bn, len; restart: u = tftppkg(); p = u->data; *p++ = 0; *p++ = 1; do *p++ = *q; while(*q++ != 0); memcpy(p, "octet", 6); p += 6; u->len = p - u->data; udptx(r, u); timeren(TFTPTIMEOUT); for(;;){ v = udprx(); if(v == nil){ print("TFTP timeout"); goto restart; } if(v->dport != 69 || v->isrc != tftpip || v->idest != myip) continue; if(v->data[0] != 0) continue; switch(v->data[1]){ case 3: bn = v->data[2] << 8 | v->data[3]; len = v->len - 4; if(len < 0) continue; if(len > 512) len = 512; memcpy((char*)base + ((bn - 1) << 9), v->data + 4, len); if((bn & 127) == 0) putc('.'); p = u->data; *p++ = 0; *p++ = 4; *p++ = bn >> 8; *p = bn; u->len = 4; udptx(r, u); if(len < 512){ putc(10); timeren(-1); return; } timeren(TFTPTIMEOUT); break; case 5: v->data[v->len - 1] = 0; print("TFTP error: %s\n", v->data + 4); timeren(-1); return; } } } int netboot(void) { ethinit(eth0); myip = 0; dhcp(eth0); arpreq(); tftp(eth0, file, TZERO); memset((void *) CONF, 0, CONFSIZE); tftp(eth0, "/cfg/pxe/0ea7deadbeef", CONF); return 1; }