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