1*9dc70af8SWarner Losh /*- 2*9dc70af8SWarner Losh * Copyright (c) 2000-2001 Benno Rice 3*9dc70af8SWarner Losh * Copyright (c) 2007 Semihalf, Rafal Jaworowski <raj@semihalf.com> 4*9dc70af8SWarner Losh * All rights reserved. 5*9dc70af8SWarner Losh * 6*9dc70af8SWarner Losh * Redistribution and use in source and binary forms, with or without 7*9dc70af8SWarner Losh * modification, are permitted provided that the following conditions 8*9dc70af8SWarner Losh * are met: 9*9dc70af8SWarner Losh * 1. Redistributions of source code must retain the above copyright 10*9dc70af8SWarner Losh * notice, this list of conditions and the following disclaimer. 11*9dc70af8SWarner Losh * 2. Redistributions in binary form must reproduce the above copyright 12*9dc70af8SWarner Losh * notice, this list of conditions and the following disclaimer in the 13*9dc70af8SWarner Losh * documentation and/or other materials provided with the distribution. 14*9dc70af8SWarner Losh * 15*9dc70af8SWarner Losh * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16*9dc70af8SWarner Losh * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17*9dc70af8SWarner Losh * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18*9dc70af8SWarner Losh * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19*9dc70af8SWarner Losh * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20*9dc70af8SWarner Losh * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21*9dc70af8SWarner Losh * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22*9dc70af8SWarner Losh * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23*9dc70af8SWarner Losh * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24*9dc70af8SWarner Losh * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25*9dc70af8SWarner Losh * SUCH DAMAGE. 26*9dc70af8SWarner Losh */ 27*9dc70af8SWarner Losh 28*9dc70af8SWarner Losh #include <sys/cdefs.h> 29*9dc70af8SWarner Losh __FBSDID("$FreeBSD$"); 30*9dc70af8SWarner Losh 31*9dc70af8SWarner Losh #include <sys/param.h> 32*9dc70af8SWarner Losh #include <sys/types.h> 33*9dc70af8SWarner Losh #include <sys/socket.h> 34*9dc70af8SWarner Losh 35*9dc70af8SWarner Losh #include <net/if.h> 36*9dc70af8SWarner Losh #include <netinet/in.h> 37*9dc70af8SWarner Losh #include <netinet/in_systm.h> 38*9dc70af8SWarner Losh #include <netinet/if_ether.h> 39*9dc70af8SWarner Losh #include <netinet/ip.h> 40*9dc70af8SWarner Losh 41*9dc70af8SWarner Losh #include <stand.h> 42*9dc70af8SWarner Losh #include <net.h> 43*9dc70af8SWarner Losh #include <netif.h> 44*9dc70af8SWarner Losh 45*9dc70af8SWarner Losh #include "api_public.h" 46*9dc70af8SWarner Losh #include "glue.h" 47*9dc70af8SWarner Losh #include "libuboot.h" 48*9dc70af8SWarner Losh #include "dev_net.h" 49*9dc70af8SWarner Losh 50*9dc70af8SWarner Losh static int net_probe(struct netif *, void *); 51*9dc70af8SWarner Losh static int net_match(struct netif *, void *); 52*9dc70af8SWarner Losh static void net_init(struct iodesc *, void *); 53*9dc70af8SWarner Losh static ssize_t net_get(struct iodesc *, void **, time_t); 54*9dc70af8SWarner Losh static ssize_t net_put(struct iodesc *, void *, size_t); 55*9dc70af8SWarner Losh static void net_end(struct netif *); 56*9dc70af8SWarner Losh 57*9dc70af8SWarner Losh extern struct netif_stats net_stats[]; 58*9dc70af8SWarner Losh 59*9dc70af8SWarner Losh struct netif_dif net_ifs[] = { 60*9dc70af8SWarner Losh /* dif_unit dif_nsel dif_stats dif_private */ 61*9dc70af8SWarner Losh { 0, 1, &net_stats[0], 0, }, 62*9dc70af8SWarner Losh }; 63*9dc70af8SWarner Losh 64*9dc70af8SWarner Losh struct netif_stats net_stats[nitems(net_ifs)]; 65*9dc70af8SWarner Losh 66*9dc70af8SWarner Losh struct netif_driver uboot_net = { 67*9dc70af8SWarner Losh "uboot_eth", /* netif_bname */ 68*9dc70af8SWarner Losh net_match, /* netif_match */ 69*9dc70af8SWarner Losh net_probe, /* netif_probe */ 70*9dc70af8SWarner Losh net_init, /* netif_init */ 71*9dc70af8SWarner Losh net_get, /* netif_get */ 72*9dc70af8SWarner Losh net_put, /* netif_put */ 73*9dc70af8SWarner Losh net_end, /* netif_end */ 74*9dc70af8SWarner Losh net_ifs, /* netif_ifs */ 75*9dc70af8SWarner Losh nitems(net_ifs) /* netif_nifs */ 76*9dc70af8SWarner Losh }; 77*9dc70af8SWarner Losh 78*9dc70af8SWarner Losh struct uboot_softc { 79*9dc70af8SWarner Losh uint32_t sc_pad; 80*9dc70af8SWarner Losh uint8_t sc_rxbuf[ETHER_MAX_LEN]; 81*9dc70af8SWarner Losh uint8_t sc_txbuf[ETHER_MAX_LEN + PKTALIGN]; 82*9dc70af8SWarner Losh uint8_t *sc_txbufp; 83*9dc70af8SWarner Losh int sc_handle; /* device handle for ub_dev_xxx */ 84*9dc70af8SWarner Losh }; 85*9dc70af8SWarner Losh 86*9dc70af8SWarner Losh static struct uboot_softc uboot_softc; 87*9dc70af8SWarner Losh 88*9dc70af8SWarner Losh /* 89*9dc70af8SWarner Losh * get_env_net_params() 90*9dc70af8SWarner Losh * 91*9dc70af8SWarner Losh * Attempt to obtain all the parms we need for netbooting from the U-Boot 92*9dc70af8SWarner Losh * environment. If we fail to obtain the values it may still be possible to 93*9dc70af8SWarner Losh * netboot; the net_dev code will attempt to get the values from bootp, rarp, 94*9dc70af8SWarner Losh * and other such sources. 95*9dc70af8SWarner Losh * 96*9dc70af8SWarner Losh * If rootip.s_addr is non-zero net_dev assumes the required global variables 97*9dc70af8SWarner Losh * are set and skips the bootp inquiry. For that reason, we don't set rootip 98*9dc70af8SWarner Losh * until we've verified that we have at least the minimum required info. 99*9dc70af8SWarner Losh * 100*9dc70af8SWarner Losh * This is called from netif_init() which can result in it getting called 101*9dc70af8SWarner Losh * multiple times, by design. The network code at higher layers zeroes out 102*9dc70af8SWarner Losh * rootip when it closes a network interface, so if it gets opened again we have 103*9dc70af8SWarner Losh * to obtain all this info again. 104*9dc70af8SWarner Losh */ 105*9dc70af8SWarner Losh static void 106*9dc70af8SWarner Losh get_env_net_params() 107*9dc70af8SWarner Losh { 108*9dc70af8SWarner Losh char *envstr; 109*9dc70af8SWarner Losh in_addr_t rootaddr, serveraddr; 110*9dc70af8SWarner Losh 111*9dc70af8SWarner Losh /* 112*9dc70af8SWarner Losh * Silently get out right away if we don't have rootpath, because none 113*9dc70af8SWarner Losh * of the other info we obtain below is sufficient to boot without it. 114*9dc70af8SWarner Losh * 115*9dc70af8SWarner Losh * If we do have rootpath, copy it into the global var and also set 116*9dc70af8SWarner Losh * dhcp.root-path in the env. If we don't get all the other info from 117*9dc70af8SWarner Losh * the u-boot env below, we will still try dhcp/bootp, but the server- 118*9dc70af8SWarner Losh * provided path will not replace the user-provided value we set here. 119*9dc70af8SWarner Losh */ 120*9dc70af8SWarner Losh if ((envstr = ub_env_get("rootpath")) == NULL) 121*9dc70af8SWarner Losh return; 122*9dc70af8SWarner Losh strlcpy(rootpath, envstr, sizeof(rootpath)); 123*9dc70af8SWarner Losh setenv("dhcp.root-path", rootpath, 0); 124*9dc70af8SWarner Losh 125*9dc70af8SWarner Losh /* 126*9dc70af8SWarner Losh * Our own IP address must be valid. Silently get out if it's not set, 127*9dc70af8SWarner Losh * but whine if it's there and we can't parse it. 128*9dc70af8SWarner Losh */ 129*9dc70af8SWarner Losh if ((envstr = ub_env_get("ipaddr")) == NULL) 130*9dc70af8SWarner Losh return; 131*9dc70af8SWarner Losh if ((myip.s_addr = inet_addr(envstr)) == INADDR_NONE) { 132*9dc70af8SWarner Losh printf("Could not parse ipaddr '%s'\n", envstr); 133*9dc70af8SWarner Losh return; 134*9dc70af8SWarner Losh } 135*9dc70af8SWarner Losh 136*9dc70af8SWarner Losh /* 137*9dc70af8SWarner Losh * Netmask is optional, default to the "natural" netmask for our IP, but 138*9dc70af8SWarner Losh * whine if it was provided and we couldn't parse it. 139*9dc70af8SWarner Losh */ 140*9dc70af8SWarner Losh if ((envstr = ub_env_get("netmask")) != NULL && 141*9dc70af8SWarner Losh (netmask = inet_addr(envstr)) == INADDR_NONE) { 142*9dc70af8SWarner Losh printf("Could not parse netmask '%s'\n", envstr); 143*9dc70af8SWarner Losh } 144*9dc70af8SWarner Losh if (netmask == INADDR_NONE) { 145*9dc70af8SWarner Losh if (IN_CLASSA(myip.s_addr)) 146*9dc70af8SWarner Losh netmask = IN_CLASSA_NET; 147*9dc70af8SWarner Losh else if (IN_CLASSB(myip.s_addr)) 148*9dc70af8SWarner Losh netmask = IN_CLASSB_NET; 149*9dc70af8SWarner Losh else 150*9dc70af8SWarner Losh netmask = IN_CLASSC_NET; 151*9dc70af8SWarner Losh } 152*9dc70af8SWarner Losh 153*9dc70af8SWarner Losh /* 154*9dc70af8SWarner Losh * Get optional serverip before rootpath; the latter can override it. 155*9dc70af8SWarner Losh * Whine only if it's present but can't be parsed. 156*9dc70af8SWarner Losh */ 157*9dc70af8SWarner Losh serveraddr = INADDR_NONE; 158*9dc70af8SWarner Losh if ((envstr = ub_env_get("serverip")) != NULL) { 159*9dc70af8SWarner Losh if ((serveraddr = inet_addr(envstr)) == INADDR_NONE) 160*9dc70af8SWarner Losh printf("Could not parse serverip '%s'\n", envstr); 161*9dc70af8SWarner Losh } 162*9dc70af8SWarner Losh 163*9dc70af8SWarner Losh /* 164*9dc70af8SWarner Losh * There must be a rootpath. It may be ip:/path or it may be just the 165*9dc70af8SWarner Losh * path in which case the ip needs to be in serverip. 166*9dc70af8SWarner Losh */ 167*9dc70af8SWarner Losh rootaddr = net_parse_rootpath(); 168*9dc70af8SWarner Losh if (rootaddr == INADDR_NONE) 169*9dc70af8SWarner Losh rootaddr = serveraddr; 170*9dc70af8SWarner Losh if (rootaddr == INADDR_NONE) { 171*9dc70af8SWarner Losh printf("No server address for rootpath '%s'\n", envstr); 172*9dc70af8SWarner Losh return; 173*9dc70af8SWarner Losh } 174*9dc70af8SWarner Losh rootip.s_addr = rootaddr; 175*9dc70af8SWarner Losh 176*9dc70af8SWarner Losh /* 177*9dc70af8SWarner Losh * Gateway IP is optional unless rootip is on a different net in which 178*9dc70af8SWarner Losh * case whine if it's missing or we can't parse it, and set rootip addr 179*9dc70af8SWarner Losh * to zero, which signals to other network code that network params 180*9dc70af8SWarner Losh * aren't set (so it will try dhcp, bootp, etc). 181*9dc70af8SWarner Losh */ 182*9dc70af8SWarner Losh envstr = ub_env_get("gatewayip"); 183*9dc70af8SWarner Losh if (!SAMENET(myip, rootip, netmask)) { 184*9dc70af8SWarner Losh if (envstr == NULL) { 185*9dc70af8SWarner Losh printf("Need gatewayip for a root server on a " 186*9dc70af8SWarner Losh "different network.\n"); 187*9dc70af8SWarner Losh rootip.s_addr = 0; 188*9dc70af8SWarner Losh return; 189*9dc70af8SWarner Losh } 190*9dc70af8SWarner Losh if ((gateip.s_addr = inet_addr(envstr)) == INADDR_NONE) { 191*9dc70af8SWarner Losh printf("Could not parse gatewayip '%s'\n", envstr); 192*9dc70af8SWarner Losh rootip.s_addr = 0; 193*9dc70af8SWarner Losh return; 194*9dc70af8SWarner Losh } 195*9dc70af8SWarner Losh } 196*9dc70af8SWarner Losh } 197*9dc70af8SWarner Losh 198*9dc70af8SWarner Losh static int 199*9dc70af8SWarner Losh net_match(struct netif *nif, void *machdep_hint) 200*9dc70af8SWarner Losh { 201*9dc70af8SWarner Losh char **a = (char **)machdep_hint; 202*9dc70af8SWarner Losh 203*9dc70af8SWarner Losh if (memcmp("net", *a, 3) == 0) 204*9dc70af8SWarner Losh return (1); 205*9dc70af8SWarner Losh 206*9dc70af8SWarner Losh printf("net_match: could not match network device\n"); 207*9dc70af8SWarner Losh return (0); 208*9dc70af8SWarner Losh } 209*9dc70af8SWarner Losh 210*9dc70af8SWarner Losh static int 211*9dc70af8SWarner Losh net_probe(struct netif *nif, void *machdep_hint) 212*9dc70af8SWarner Losh { 213*9dc70af8SWarner Losh struct device_info *di; 214*9dc70af8SWarner Losh int i; 215*9dc70af8SWarner Losh 216*9dc70af8SWarner Losh for (i = 0; i < devs_no; i++) 217*9dc70af8SWarner Losh if ((di = ub_dev_get(i)) != NULL) 218*9dc70af8SWarner Losh if (di->type == DEV_TYP_NET) 219*9dc70af8SWarner Losh break; 220*9dc70af8SWarner Losh 221*9dc70af8SWarner Losh if (i == devs_no) { 222*9dc70af8SWarner Losh printf("net_probe: no network devices found, maybe not" 223*9dc70af8SWarner Losh " enumerated yet..?\n"); 224*9dc70af8SWarner Losh return (-1); 225*9dc70af8SWarner Losh } 226*9dc70af8SWarner Losh 227*9dc70af8SWarner Losh #if defined(NETIF_DEBUG) 228*9dc70af8SWarner Losh printf("net_probe: network device found: %d\n", i); 229*9dc70af8SWarner Losh #endif 230*9dc70af8SWarner Losh uboot_softc.sc_handle = i; 231*9dc70af8SWarner Losh 232*9dc70af8SWarner Losh return (0); 233*9dc70af8SWarner Losh } 234*9dc70af8SWarner Losh 235*9dc70af8SWarner Losh static ssize_t 236*9dc70af8SWarner Losh net_put(struct iodesc *desc, void *pkt, size_t len) 237*9dc70af8SWarner Losh { 238*9dc70af8SWarner Losh struct netif *nif = desc->io_netif; 239*9dc70af8SWarner Losh struct uboot_softc *sc = nif->nif_devdata; 240*9dc70af8SWarner Losh size_t sendlen; 241*9dc70af8SWarner Losh ssize_t rv; 242*9dc70af8SWarner Losh 243*9dc70af8SWarner Losh #if defined(NETIF_DEBUG) 244*9dc70af8SWarner Losh struct ether_header *eh; 245*9dc70af8SWarner Losh 246*9dc70af8SWarner Losh printf("net_put: desc %p, pkt %p, len %d\n", desc, pkt, len); 247*9dc70af8SWarner Losh eh = pkt; 248*9dc70af8SWarner Losh printf("dst: %s ", ether_sprintf(eh->ether_dhost)); 249*9dc70af8SWarner Losh printf("src: %s ", ether_sprintf(eh->ether_shost)); 250*9dc70af8SWarner Losh printf("type: 0x%x\n", eh->ether_type & 0xffff); 251*9dc70af8SWarner Losh #endif 252*9dc70af8SWarner Losh 253*9dc70af8SWarner Losh if (len < ETHER_MIN_LEN - ETHER_CRC_LEN) { 254*9dc70af8SWarner Losh sendlen = ETHER_MIN_LEN - ETHER_CRC_LEN; 255*9dc70af8SWarner Losh bzero(sc->sc_txbufp, sendlen); 256*9dc70af8SWarner Losh } else 257*9dc70af8SWarner Losh sendlen = len; 258*9dc70af8SWarner Losh 259*9dc70af8SWarner Losh memcpy(sc->sc_txbufp, pkt, len); 260*9dc70af8SWarner Losh 261*9dc70af8SWarner Losh rv = ub_dev_send(sc->sc_handle, sc->sc_txbufp, sendlen); 262*9dc70af8SWarner Losh 263*9dc70af8SWarner Losh #if defined(NETIF_DEBUG) 264*9dc70af8SWarner Losh printf("net_put: ub_send returned %d\n", rv); 265*9dc70af8SWarner Losh #endif 266*9dc70af8SWarner Losh if (rv == 0) 267*9dc70af8SWarner Losh rv = len; 268*9dc70af8SWarner Losh else 269*9dc70af8SWarner Losh rv = -1; 270*9dc70af8SWarner Losh 271*9dc70af8SWarner Losh return (rv); 272*9dc70af8SWarner Losh } 273*9dc70af8SWarner Losh 274*9dc70af8SWarner Losh static ssize_t 275*9dc70af8SWarner Losh net_get(struct iodesc *desc, void **pkt, time_t timeout) 276*9dc70af8SWarner Losh { 277*9dc70af8SWarner Losh struct netif *nif = desc->io_netif; 278*9dc70af8SWarner Losh struct uboot_softc *sc = nif->nif_devdata; 279*9dc70af8SWarner Losh time_t t; 280*9dc70af8SWarner Losh int err, rlen; 281*9dc70af8SWarner Losh size_t len; 282*9dc70af8SWarner Losh char *buf; 283*9dc70af8SWarner Losh 284*9dc70af8SWarner Losh #if defined(NETIF_DEBUG) 285*9dc70af8SWarner Losh printf("net_get: pkt %p, timeout %d\n", pkt, timeout); 286*9dc70af8SWarner Losh #endif 287*9dc70af8SWarner Losh t = getsecs(); 288*9dc70af8SWarner Losh len = sizeof(sc->sc_rxbuf); 289*9dc70af8SWarner Losh do { 290*9dc70af8SWarner Losh err = ub_dev_recv(sc->sc_handle, sc->sc_rxbuf, len, &rlen); 291*9dc70af8SWarner Losh 292*9dc70af8SWarner Losh if (err != 0) { 293*9dc70af8SWarner Losh printf("net_get: ub_dev_recv() failed, error=%d\n", 294*9dc70af8SWarner Losh err); 295*9dc70af8SWarner Losh rlen = 0; 296*9dc70af8SWarner Losh break; 297*9dc70af8SWarner Losh } 298*9dc70af8SWarner Losh } while ((rlen == -1 || rlen == 0) && (getsecs() - t < timeout)); 299*9dc70af8SWarner Losh 300*9dc70af8SWarner Losh #if defined(NETIF_DEBUG) 301*9dc70af8SWarner Losh printf("net_get: received len %d (%x)\n", rlen, rlen); 302*9dc70af8SWarner Losh #endif 303*9dc70af8SWarner Losh 304*9dc70af8SWarner Losh if (rlen > 0) { 305*9dc70af8SWarner Losh buf = malloc(rlen + ETHER_ALIGN); 306*9dc70af8SWarner Losh if (buf == NULL) 307*9dc70af8SWarner Losh return (-1); 308*9dc70af8SWarner Losh memcpy(buf + ETHER_ALIGN, sc->sc_rxbuf, rlen); 309*9dc70af8SWarner Losh *pkt = buf; 310*9dc70af8SWarner Losh return ((ssize_t)rlen); 311*9dc70af8SWarner Losh } 312*9dc70af8SWarner Losh 313*9dc70af8SWarner Losh return (-1); 314*9dc70af8SWarner Losh } 315*9dc70af8SWarner Losh 316*9dc70af8SWarner Losh static void 317*9dc70af8SWarner Losh net_init(struct iodesc *desc, void *machdep_hint) 318*9dc70af8SWarner Losh { 319*9dc70af8SWarner Losh struct netif *nif = desc->io_netif; 320*9dc70af8SWarner Losh struct uboot_softc *sc; 321*9dc70af8SWarner Losh struct device_info *di; 322*9dc70af8SWarner Losh int err; 323*9dc70af8SWarner Losh 324*9dc70af8SWarner Losh sc = nif->nif_devdata = &uboot_softc; 325*9dc70af8SWarner Losh 326*9dc70af8SWarner Losh if ((err = ub_dev_open(sc->sc_handle)) != 0) 327*9dc70af8SWarner Losh panic("%s%d: initialisation failed with error %d", 328*9dc70af8SWarner Losh nif->nif_driver->netif_bname, nif->nif_unit, err); 329*9dc70af8SWarner Losh 330*9dc70af8SWarner Losh /* Get MAC address */ 331*9dc70af8SWarner Losh di = ub_dev_get(sc->sc_handle); 332*9dc70af8SWarner Losh memcpy(desc->myea, di->di_net.hwaddr, 6); 333*9dc70af8SWarner Losh if (memcmp (desc->myea, "\0\0\0\0\0\0", 6) == 0) { 334*9dc70af8SWarner Losh panic("%s%d: empty ethernet address!", 335*9dc70af8SWarner Losh nif->nif_driver->netif_bname, nif->nif_unit); 336*9dc70af8SWarner Losh } 337*9dc70af8SWarner Losh 338*9dc70af8SWarner Losh /* Attempt to get netboot params from the u-boot env. */ 339*9dc70af8SWarner Losh get_env_net_params(); 340*9dc70af8SWarner Losh if (myip.s_addr != 0) 341*9dc70af8SWarner Losh desc->myip = myip; 342*9dc70af8SWarner Losh 343*9dc70af8SWarner Losh #if defined(NETIF_DEBUG) 344*9dc70af8SWarner Losh printf("network: %s%d attached to %s\n", nif->nif_driver->netif_bname, 345*9dc70af8SWarner Losh nif->nif_unit, ether_sprintf(desc->myea)); 346*9dc70af8SWarner Losh #endif 347*9dc70af8SWarner Losh 348*9dc70af8SWarner Losh /* Set correct alignment for TX packets */ 349*9dc70af8SWarner Losh sc->sc_txbufp = sc->sc_txbuf; 350*9dc70af8SWarner Losh if ((unsigned long)sc->sc_txbufp % PKTALIGN) 351*9dc70af8SWarner Losh sc->sc_txbufp += PKTALIGN - 352*9dc70af8SWarner Losh (unsigned long)sc->sc_txbufp % PKTALIGN; 353*9dc70af8SWarner Losh } 354*9dc70af8SWarner Losh 355*9dc70af8SWarner Losh static void 356*9dc70af8SWarner Losh net_end(struct netif *nif) 357*9dc70af8SWarner Losh { 358*9dc70af8SWarner Losh struct uboot_softc *sc = nif->nif_devdata; 359*9dc70af8SWarner Losh int err; 360*9dc70af8SWarner Losh 361*9dc70af8SWarner Losh if ((err = ub_dev_close(sc->sc_handle)) != 0) 362*9dc70af8SWarner Losh panic("%s%d: net_end failed with error %d", 363*9dc70af8SWarner Losh nif->nif_driver->netif_bname, nif->nif_unit, err); 364*9dc70af8SWarner Losh } 365