Home | History | Annotate | Line # | Download | only in efiboot
      1 /*	$NetBSD: efinet.c,v 1.9 2024/01/01 13:38:57 rin Exp $	*/
      2 
      3 /*-
      4  * Copyright (c) 2001 Doug Rabson
      5  * Copyright (c) 2002, 2006 Marcel Moolenaar
      6  * All rights reserved.
      7  *
      8  * Redistribution and use in source and binary forms, with or without
      9  * modification, are permitted provided that the following conditions
     10  * are met:
     11  * 1. Redistributions of source code must retain the above copyright
     12  *    notice, this list of conditions and the following disclaimer.
     13  * 2. Redistributions in binary form must reproduce the above copyright
     14  *    notice, this list of conditions and the following disclaimer in the
     15  *    documentation and/or other materials provided with the distribution.
     16  *
     17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
     18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
     21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     27  * SUCH DAMAGE.
     28  */
     29 
     30 #include <sys/cdefs.h>
     31 #include <sys/param.h>
     32 
     33 #include "efiboot.h"
     34 
     35 #include <lib/libsa/net.h>
     36 #include <lib/libsa/netif.h>
     37 #include <lib/libsa/dev_net.h>
     38 
     39 #include "devopen.h"
     40 
     41 #define ETHER_EXT_LEN	(ETHER_HDR_LEN + ETHER_CRC_LEN + ETHER_ALIGN)
     42 
     43 #if defined(DEBUG) || defined(ARP_DEBUG) || defined(BOOTP_DEBUG) || \
     44     defined(NET_DEBUG) || defined(NETIF_DEBUG) || defined(NFS_DEBUG) || \
     45     defined(RARP_DEBUG) || defined(RPC_DEBUG)
     46 int debug = 1;
     47 #else
     48 int debug = 0;
     49 #endif
     50 
     51 extern bool kernel_loaded;
     52 
     53 struct efinetinfo {
     54 	EFI_SIMPLE_NETWORK *net;
     55 	bool bootdev;
     56 	size_t pktbufsz;
     57 	UINT8 *pktbuf;
     58 	struct {
     59 		int type;
     60 		u_int tag;
     61 	} bus;
     62 };
     63 #if notyet
     64 static struct btinfo_netif bi_netif;
     65 #endif
     66 
     67 static int	efinet_match(struct netif *, void *);
     68 static int	efinet_probe(struct netif *, void *);
     69 static void	efinet_init(struct iodesc *, void *);
     70 static int	efinet_get(struct iodesc *, void *, size_t, saseconds_t);
     71 static int	efinet_put(struct iodesc *, void *, size_t);
     72 static void	efinet_end(struct netif *);
     73 
     74 struct netif_driver efinetif = {
     75 	.netif_bname = "net",
     76 	.netif_match = efinet_match,
     77 	.netif_probe = efinet_probe,
     78 	.netif_init = efinet_init,
     79 	.netif_get = efinet_get,
     80 	.netif_put = efinet_put,
     81 	.netif_end = efinet_end,
     82 	.netif_ifs = NULL,
     83 	.netif_nifs = 0
     84 };
     85 
     86 #ifdef EFINET_DEBUG
     87 static void
     88 dump_mode(EFI_SIMPLE_NETWORK_MODE *mode)
     89 {
     90 	int i;
     91 
     92 	printf("State                 = %x\n", mode->State);
     93 	printf("HwAddressSize         = %u\n", mode->HwAddressSize);
     94 	printf("MediaHeaderSize       = %u\n", mode->MediaHeaderSize);
     95 	printf("MaxPacketSize         = %u\n", mode->MaxPacketSize);
     96 	printf("NvRamSize             = %u\n", mode->NvRamSize);
     97 	printf("NvRamAccessSize       = %u\n", mode->NvRamAccessSize);
     98 	printf("ReceiveFilterMask     = %x\n", mode->ReceiveFilterMask);
     99 	printf("ReceiveFilterSetting  = %u\n", mode->ReceiveFilterSetting);
    100 	printf("MaxMCastFilterCount   = %u\n", mode->MaxMCastFilterCount);
    101 	printf("MCastFilterCount      = %u\n", mode->MCastFilterCount);
    102 	printf("MCastFilter           = {");
    103 	for (i = 0; i < mode->MCastFilterCount; i++)
    104 		printf(" %s", ether_sprintf(mode->MCastFilter[i].Addr));
    105 	printf(" }\n");
    106 	printf("CurrentAddress        = %s\n",
    107 	    ether_sprintf(mode->CurrentAddress.Addr));
    108 	printf("BroadcastAddress      = %s\n",
    109 	    ether_sprintf(mode->BroadcastAddress.Addr));
    110 	printf("PermanentAddress      = %s\n",
    111 	    ether_sprintf(mode->PermanentAddress.Addr));
    112 	printf("IfType                = %u\n", mode->IfType);
    113 	printf("MacAddressChangeable  = %d\n", mode->MacAddressChangeable);
    114 	printf("MultipleTxSupported   = %d\n", mode->MultipleTxSupported);
    115 	printf("MediaPresentSupported = %d\n", mode->MediaPresentSupported);
    116 	printf("MediaPresent          = %d\n", mode->MediaPresent);
    117 }
    118 #endif
    119 
    120 static const EFI_MAC_ADDRESS *
    121 efinet_hwaddr(const EFI_SIMPLE_NETWORK_MODE *mode)
    122 {
    123 	int valid, n;
    124 
    125 	for (valid = 0, n = 0; n < mode->HwAddressSize; n++)
    126 		if (mode->CurrentAddress.Addr[n] != 0x00) {
    127 			valid = true;
    128 			break;
    129 		}
    130 	if (!valid)
    131 		goto use_permanent;
    132 
    133 	for (valid = 0, n = 0; n < mode->HwAddressSize; n++)
    134 		if (mode->CurrentAddress.Addr[n] != 0xff) {
    135 			valid = true;
    136 			break;
    137 		}
    138 	if (!valid)
    139 		goto use_permanent;
    140 
    141 	return &mode->CurrentAddress;
    142 
    143 use_permanent:
    144 	return &mode->PermanentAddress;
    145 }
    146 
    147 static int
    148 efinet_match(struct netif *nif, void *machdep_hint)
    149 {
    150 	struct devdesc *dev = machdep_hint;
    151 
    152 	if (dev->d_unit != nif->nif_unit)
    153 		return 0;
    154 
    155 	return 1;
    156 }
    157 
    158 static int
    159 efinet_probe(struct netif *nif, void *machdep_hint)
    160 {
    161 
    162 	return 0;
    163 }
    164 
    165 static int
    166 efinet_put(struct iodesc *desc, void *pkt, size_t len)
    167 {
    168 	struct netif *nif = desc->io_netif;
    169 	struct efinetinfo *eni = nif->nif_devdata;
    170 	EFI_SIMPLE_NETWORK *net;
    171 	EFI_STATUS status;
    172 	void *buf;
    173 	char *ptr;
    174 
    175 	if (eni == NULL)
    176 		return -1;
    177 	net = eni->net;
    178 
    179 	ptr = eni->pktbuf;
    180 
    181 	memcpy(ptr, pkt, len);
    182 	status = uefi_call_wrapper(net->Transmit, 7, net, 0, (UINTN)len, ptr, NULL,
    183 	    NULL, NULL);
    184 	if (EFI_ERROR(status))
    185 		return -1;
    186 
    187 	/* Wait for the buffer to be transmitted */
    188 	do {
    189 		buf = NULL;	/* XXX Is this needed? */
    190 		status = uefi_call_wrapper(net->GetStatus, 3, net, NULL, &buf);
    191 		/*
    192 		 * XXX EFI1.1 and the E1000 card returns a different
    193 		 * address than we gave.  Sigh.
    194 		 */
    195 	} while (!EFI_ERROR(status) && buf == NULL);
    196 
    197 	/* XXX How do we deal with status != EFI_SUCCESS now? */
    198 	return EFI_ERROR(status) ? -1 : len;
    199 }
    200 
    201 static int
    202 efinet_get(struct iodesc *desc, void *pkt, size_t len, saseconds_t timeout)
    203 {
    204 	struct netif *nif = desc->io_netif;
    205 	struct efinetinfo *eni = nif->nif_devdata;
    206 	EFI_SIMPLE_NETWORK *net;
    207 	EFI_STATUS status;
    208 	UINTN bufsz, rsz;
    209 	time_t t;
    210 	char *buf, *ptr;
    211 	int ret = -1;
    212 
    213 	if (eni == NULL)
    214 		return -1;
    215 	net = eni->net;
    216 
    217 	if (eni->pktbufsz < net->Mode->MaxPacketSize + ETHER_EXT_LEN) {
    218 		bufsz = net->Mode->MaxPacketSize + ETHER_EXT_LEN;
    219 		buf = alloc(bufsz);
    220 		if (buf == NULL)
    221 			return -1;
    222 		dealloc(eni->pktbuf, eni->pktbufsz);
    223 		eni->pktbufsz = bufsz;
    224 		eni->pktbuf = buf;
    225 	}
    226 	ptr = eni->pktbuf + ETHER_ALIGN;
    227 
    228 	t = getsecs();
    229 	while ((getsecs() - t) < timeout) {
    230 		rsz = eni->pktbufsz;
    231 		status = uefi_call_wrapper(net->Receive, 7, net, NULL, &rsz, ptr,
    232 		    NULL, NULL, NULL);
    233 		if (!EFI_ERROR(status)) {
    234 			rsz = uimin(rsz, len);
    235 			memcpy(pkt, ptr, rsz);
    236 
    237 			ret = (int)rsz;
    238 			break;
    239 		}
    240 		if (status != EFI_NOT_READY)
    241 			break;
    242 	}
    243 
    244 	return ret;
    245 }
    246 
    247 static void
    248 efinet_init(struct iodesc *desc, void *machdep_hint)
    249 {
    250 	struct netif *nif = desc->io_netif;
    251 	struct efinetinfo *eni;
    252 	EFI_SIMPLE_NETWORK *net;
    253 	EFI_STATUS status;
    254 	UINT32 mask;
    255 
    256 	if (nif->nif_driver->netif_ifs[nif->nif_unit].dif_unit < 0) {
    257 		printf("Invalid network interface %d\n", nif->nif_unit);
    258 		return;
    259 	}
    260 
    261 	eni = nif->nif_driver->netif_ifs[nif->nif_unit].dif_private;
    262 	nif->nif_devdata = eni;
    263 	net = eni->net;
    264 	if (net->Mode->State == EfiSimpleNetworkStopped) {
    265 		status = uefi_call_wrapper(net->Start, 1, net);
    266 		if (EFI_ERROR(status)) {
    267 			printf("net%d: cannot start interface (status=%"
    268 			    PRIxMAX ")\n", nif->nif_unit, (uintmax_t)status);
    269 			return;
    270 		}
    271 	}
    272 
    273 	if (net->Mode->State != EfiSimpleNetworkInitialized) {
    274 		status = uefi_call_wrapper(net->Initialize, 3, net, 0, 0);
    275 		if (EFI_ERROR(status)) {
    276 			printf("net%d: cannot init. interface (status=%"
    277 			    PRIxMAX ")\n", nif->nif_unit, (uintmax_t)status);
    278 			return;
    279 		}
    280 	}
    281 
    282 	mask = EFI_SIMPLE_NETWORK_RECEIVE_UNICAST |
    283 	    EFI_SIMPLE_NETWORK_RECEIVE_BROADCAST;
    284 
    285 	status = uefi_call_wrapper(net->ReceiveFilters, 6, net, mask, 0, FALSE,
    286 	    0, NULL);
    287 	if (EFI_ERROR(status) && status != EFI_INVALID_PARAMETER && status != EFI_UNSUPPORTED) {
    288 		printf("net%d: cannot set rx. filters (status=%" PRIxMAX ")\n",
    289 		    nif->nif_unit, (uintmax_t)status);
    290 		return;
    291 	}
    292 
    293 #if notyet
    294 	if (!kernel_loaded) {
    295 		bi_netif.bus = eni->bus.type;
    296 		bi_netif.addr.tag = eni->bus.tag;
    297 		snprintf(bi_netif.ifname, sizeof(bi_netif.ifname), "net%d",
    298 		    nif->nif_unit);
    299 		BI_ADD(&bi_netif, BTINFO_NETIF, sizeof(bi_netif));
    300 	}
    301 #endif
    302 
    303 #ifdef EFINET_DEBUG
    304 	dump_mode(net->Mode);
    305 #endif
    306 
    307 	memcpy(desc->myea, efinet_hwaddr(net->Mode)->Addr, 6);
    308 	desc->xid = 1;
    309 }
    310 
    311 static void
    312 efinet_end(struct netif *nif)
    313 {
    314 	struct efinetinfo *eni = nif->nif_devdata;
    315 	EFI_SIMPLE_NETWORK *net;
    316 
    317 	if (eni == NULL)
    318 		return;
    319 	net = eni->net;
    320 
    321 	uefi_call_wrapper(net->Shutdown, 1, net);
    322 }
    323 
    324 void
    325 efi_net_probe(void)
    326 {
    327 	struct efinetinfo *enis;
    328 	struct netif_dif *dif;
    329 	struct netif_stats *stats;
    330 	EFI_DEVICE_PATH *dp0, *dp;
    331 	EFI_SIMPLE_NETWORK *net;
    332 	EFI_HANDLE *handles;
    333 	EFI_STATUS status;
    334 	UINTN i, nhandles;
    335 	int nifs, depth = -1;
    336 	bool found;
    337 
    338 	status = LibLocateHandle(ByProtocol, &SimpleNetworkProtocol, NULL,
    339 	    &nhandles, &handles);
    340 	if (EFI_ERROR(status) || nhandles == 0)
    341 		return;
    342 
    343 	enis = alloc(nhandles * sizeof(*enis));
    344 	if (enis == NULL)
    345 		return;
    346 	memset(enis, 0, nhandles * sizeof(*enis));
    347 
    348 	if (efi_bootdp) {
    349 		/*
    350 		 * Either Hardware or Messaging Device Paths can be used
    351 		 * here, see Sec 10.4.4 of UEFI Spec 2.10. Try both.
    352 		 */
    353 		depth = efi_device_path_depth(efi_bootdp, HARDWARE_DEVICE_PATH);
    354 		if (depth == -1) {
    355 			depth = efi_device_path_depth(efi_bootdp,
    356 			    MESSAGING_DEVICE_PATH);
    357 		}
    358 		if (depth == 0)
    359 			depth = 1;
    360 	}
    361 
    362 	nifs = 0;
    363 	for (i = 0; i < nhandles; i++) {
    364 		status = uefi_call_wrapper(BS->HandleProtocol, 3, handles[i],
    365 		    &DevicePathProtocol, (void **)&dp0);
    366 		if (EFI_ERROR(status))
    367 			continue;
    368 
    369 		found = false;
    370 		for (dp = dp0; !IsDevicePathEnd(dp); dp = NextDevicePathNode(dp)) {
    371 			if (DevicePathType(dp) == MESSAGING_DEVICE_PATH &&
    372 			    DevicePathSubType(dp) == MSG_MAC_ADDR_DP) {
    373 				found = true;
    374 				break;
    375 			}
    376 		}
    377 		if (!found)
    378 			continue;
    379 
    380 		status = uefi_call_wrapper(BS->OpenProtocol, 6, handles[i],
    381 		    &SimpleNetworkProtocol, (void **)&net, IH, NULL,
    382 		    EFI_OPEN_PROTOCOL_EXCLUSIVE);
    383 		if (EFI_ERROR(status)) {
    384 			printf("Unable to open network interface %" PRIuMAX
    385 			    " for exclusive access: %" PRIxMAX "\n",
    386 			    (uintmax_t)i, (uintmax_t)status);
    387 			continue;
    388 		}
    389 
    390 		enis[nifs].net = net;
    391 		enis[nifs].bootdev = efi_pxe_match_booted_interface(
    392 		    efinet_hwaddr(net->Mode), net->Mode->HwAddressSize);
    393 		enis[nifs].pktbufsz = net->Mode->MaxPacketSize +
    394 		    ETHER_EXT_LEN;
    395 		enis[nifs].pktbuf = alloc(enis[nifs].pktbufsz);
    396 		if (enis[nifs].pktbuf == NULL) {
    397 			while (i-- > 0) {
    398 				dealloc(enis[i].pktbuf, enis[i].pktbufsz);
    399 				if (i == 0)
    400 					break;
    401 			}
    402 			dealloc(enis, nhandles * sizeof(*enis));
    403 			FreePool(handles);
    404 			return;
    405 		}
    406 
    407 		if (depth > 0 && efi_device_path_ncmp(efi_bootdp, dp0, depth) == 0) {
    408 			char devname[9];
    409 			snprintf(devname, sizeof(devname), "net%u", nifs);
    410 			set_default_device(devname);
    411 		}
    412 
    413 		nifs++;
    414 	}
    415 
    416 	FreePool(handles);
    417 
    418 	if (nifs == 0)
    419 		return;
    420 
    421 	efinetif.netif_ifs = alloc(nifs * sizeof(*dif));
    422 	stats = alloc(nifs * sizeof(*stats));
    423 	if (efinetif.netif_ifs == NULL || stats == NULL) {
    424 		if (efinetif.netif_ifs != NULL) {
    425 			dealloc(efinetif.netif_ifs, nifs * sizeof(*dif));
    426 			efinetif.netif_ifs = NULL;
    427 		}
    428 		if (stats != NULL)
    429 			dealloc(stats, nifs * sizeof(*stats));
    430 		for (i = 0; i < nifs; i++)
    431 			dealloc(enis[i].pktbuf, enis[i].pktbufsz);
    432 		dealloc(enis, nhandles * sizeof(*enis));
    433 		return;
    434 	}
    435 	memset(efinetif.netif_ifs, 0, nifs * sizeof(*dif));
    436 	memset(stats, 0, nifs * sizeof(*stats));
    437 	efinetif.netif_nifs = nifs;
    438 
    439 	for (i = 0; i < nifs; i++) {
    440 		dif = &efinetif.netif_ifs[i];
    441 		dif->dif_unit = i;
    442 		dif->dif_nsel = 1;
    443 		dif->dif_stats = &stats[i];
    444 		dif->dif_private = &enis[i];
    445 	}
    446 }
    447 
    448 void
    449 efi_net_show(void)
    450 {
    451 	const struct netif_dif *dif;
    452 	const struct efinetinfo *eni;
    453 	EFI_SIMPLE_NETWORK *net;
    454 	int i;
    455 
    456 	for (i = 0; i < efinetif.netif_nifs; i++) {
    457 		dif = &efinetif.netif_ifs[i];
    458 		eni = dif->dif_private;
    459 		net = eni->net;
    460 
    461 		printf("net%d", dif->dif_unit);
    462 		if (net->Mode != NULL) {
    463 			const EFI_MAC_ADDRESS *mac = efinet_hwaddr(net->Mode);
    464 			for (UINT32 x = 0; x < net->Mode->HwAddressSize; x++) {
    465 				printf("%c%02x", x == 0 ? ' ' : ':',
    466 				    mac->Addr[x]);
    467 			}
    468 		}
    469 		if (eni->bootdev)
    470 			printf(" pxeboot");
    471 		printf("\n");
    472 	}
    473 }
    474 
    475 int
    476 efi_net_get_booted_interface_unit(void)
    477 {
    478 	const struct netif_dif *dif;
    479 	const struct efinetinfo *eni;
    480 	int i;
    481 
    482 	for (i = 0; i < efinetif.netif_nifs; i++) {
    483 		dif = &efinetif.netif_ifs[i];
    484 		eni = dif->dif_private;
    485 		if (eni->bootdev)
    486 			return dif->dif_unit;
    487 	}
    488 	return -1;
    489 }
    490 
    491 int
    492 efi_net_get_booted_macaddr(uint8_t *mac)
    493 {
    494 	const struct netif_dif *dif;
    495 	const struct efinetinfo *eni;
    496 	EFI_SIMPLE_NETWORK *net;
    497 	int i;
    498 
    499 	for (i = 0; i < efinetif.netif_nifs; i++) {
    500 		dif = &efinetif.netif_ifs[i];
    501 		eni = dif->dif_private;
    502 		net = eni->net;
    503 		if (eni->bootdev && net->Mode != NULL && net->Mode->HwAddressSize == 6) {
    504 			memcpy(mac, net->Mode->PermanentAddress.Addr, 6);
    505 			return 0;
    506 		}
    507 	}
    508 
    509 	return -1;
    510 }
    511 
    512 int
    513 efi_net_open(struct open_file *f, ...)
    514 {
    515 	char **file, pathbuf[PATH_MAX], *default_device, *path, *ep;
    516 	const char *fname, *full_path;
    517 	struct devdesc desc;
    518 	intmax_t dev;
    519 	va_list ap;
    520 	int n, error;
    521 
    522 	va_start(ap, f);
    523 	fname = va_arg(ap, const char *);
    524 	file = va_arg(ap, char **);
    525 	va_end(ap);
    526 
    527 	default_device = get_default_device();
    528 	if (strchr(fname, ':') == NULL) {
    529 		if (strlen(default_device) > 0) {
    530 			snprintf(pathbuf, sizeof(pathbuf), "%s:%s", default_device, fname);
    531 			full_path = pathbuf;
    532 			path = __UNCONST(fname);
    533 		} else {
    534 			return EINVAL;
    535 		}
    536 	} else {
    537 		full_path = fname;
    538 		path = strchr(fname, ':') + 1;
    539 	}
    540 
    541 	if (strncmp(full_path, "net", 3) != 0)
    542 		return EINVAL;
    543         dev = strtoimax(full_path + 3, &ep, 10);
    544         if (dev < 0 || dev >= efinetif.netif_nifs)
    545                 return ENXIO;
    546 
    547         for (n = 0; n < ndevs; n++)
    548                 if (strcmp(DEV_NAME(&devsw[n]), "net") == 0) {
    549                         f->f_dev = &devsw[n];
    550                         break;
    551                 }
    552         if (n == ndevs)
    553                 return ENXIO;
    554 
    555 	*file = path;
    556 
    557 	//try_bootp = 1;
    558 
    559 	memset(&desc, 0, sizeof(desc));
    560 	strlcpy(desc.d_name, "net", sizeof(desc.d_name));
    561 	desc.d_unit = dev;
    562 
    563 	error = DEV_OPEN(f->f_dev)(f, &desc);
    564 	if (error)
    565 		return error;
    566 
    567 	return 0;
    568 }
    569