xref: /netbsd-src/sys/stand/efiboot/efinet.c (revision 71d224f4ab9b1138f061928df297156f39c5052c)
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
dump_mode(EFI_SIMPLE_NETWORK_MODE * mode)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 *
efinet_hwaddr(const EFI_SIMPLE_NETWORK_MODE * mode)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
efinet_match(struct netif * nif,void * machdep_hint)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
efinet_probe(struct netif * nif,void * machdep_hint)159 efinet_probe(struct netif *nif, void *machdep_hint)
160 {
161 
162 	return 0;
163 }
164 
165 static int
efinet_put(struct iodesc * desc,void * pkt,size_t len)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
efinet_get(struct iodesc * desc,void * pkt,size_t len,saseconds_t timeout)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
efinet_init(struct iodesc * desc,void * machdep_hint)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
efinet_end(struct netif * nif)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
efi_net_probe(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
efi_net_show(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
efi_net_get_booted_interface_unit(void)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
efi_net_get_booted_macaddr(uint8_t * mac)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
efi_net_open(struct open_file * f,...)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