efinet.c revision 1.6.32.1 1 /* $NetBSD: efinet.c,v 1.6.32.1 2023/12/30 19:33:25 martin 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, is_bootdp;
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 is_bootdp = depth > 0 &&
381 efi_device_path_ncmp(efi_bootdp, dp0, depth) == 0;
382
383 status = uefi_call_wrapper(BS->OpenProtocol, 6, handles[i],
384 &SimpleNetworkProtocol, (void **)&net, IH, NULL,
385 EFI_OPEN_PROTOCOL_EXCLUSIVE);
386 if (EFI_ERROR(status)) {
387 printf("Unable to open network interface %" PRIuMAX
388 " for exclusive access: %" PRIxMAX "\n",
389 (uintmax_t)i, (uintmax_t)status);
390 continue;
391 }
392
393 enis[nifs].net = net;
394 enis[nifs].bootdev = efi_pxe_match_booted_interface(
395 efinet_hwaddr(net->Mode), net->Mode->HwAddressSize);
396 enis[nifs].pktbufsz = net->Mode->MaxPacketSize +
397 ETHER_EXT_LEN;
398 enis[nifs].pktbuf = alloc(enis[nifs].pktbufsz);
399 if (enis[nifs].pktbuf == NULL) {
400 while (i-- > 0) {
401 dealloc(enis[i].pktbuf, enis[i].pktbufsz);
402 if (i == 0)
403 break;
404 }
405 dealloc(enis, nhandles * sizeof(*enis));
406 FreePool(handles);
407 return;
408 }
409
410 if (is_bootdp) {
411 /*
412 * This is boot device...
413 */
414 char devname[9];
415
416 snprintf(devname, sizeof(devname), "net%u", nifs);
417 set_default_device(devname);
418
419 /*
420 * and now opened for us excluively. Therefore,
421 * access via device path is illegal.
422 */
423 efi_bootdp = NULL;
424 depth = -1;
425 }
426
427 nifs++;
428 }
429
430 FreePool(handles);
431
432 if (nifs == 0)
433 return;
434
435 efinetif.netif_ifs = alloc(nifs * sizeof(*dif));
436 stats = alloc(nifs * sizeof(*stats));
437 if (efinetif.netif_ifs == NULL || stats == NULL) {
438 if (efinetif.netif_ifs != NULL) {
439 dealloc(efinetif.netif_ifs, nifs * sizeof(*dif));
440 efinetif.netif_ifs = NULL;
441 }
442 if (stats != NULL)
443 dealloc(stats, nifs * sizeof(*stats));
444 for (i = 0; i < nifs; i++)
445 dealloc(enis[i].pktbuf, enis[i].pktbufsz);
446 dealloc(enis, nhandles * sizeof(*enis));
447 return;
448 }
449 memset(efinetif.netif_ifs, 0, nifs * sizeof(*dif));
450 memset(stats, 0, nifs * sizeof(*stats));
451 efinetif.netif_nifs = nifs;
452
453 for (i = 0; i < nifs; i++) {
454 dif = &efinetif.netif_ifs[i];
455 dif->dif_unit = i;
456 dif->dif_nsel = 1;
457 dif->dif_stats = &stats[i];
458 dif->dif_private = &enis[i];
459 }
460 }
461
462 void
463 efi_net_show(void)
464 {
465 const struct netif_dif *dif;
466 const struct efinetinfo *eni;
467 EFI_SIMPLE_NETWORK *net;
468 int i;
469
470 for (i = 0; i < efinetif.netif_nifs; i++) {
471 dif = &efinetif.netif_ifs[i];
472 eni = dif->dif_private;
473 net = eni->net;
474
475 printf("net%d", dif->dif_unit);
476 if (net->Mode != NULL) {
477 const EFI_MAC_ADDRESS *mac = efinet_hwaddr(net->Mode);
478 for (UINT32 x = 0; x < net->Mode->HwAddressSize; x++) {
479 printf("%c%02x", x == 0 ? ' ' : ':',
480 mac->Addr[x]);
481 }
482 }
483 if (eni->bootdev)
484 printf(" pxeboot");
485 printf("\n");
486 }
487 }
488
489 int
490 efi_net_get_booted_interface_unit(void)
491 {
492 const struct netif_dif *dif;
493 const struct efinetinfo *eni;
494 int i;
495
496 for (i = 0; i < efinetif.netif_nifs; i++) {
497 dif = &efinetif.netif_ifs[i];
498 eni = dif->dif_private;
499 if (eni->bootdev)
500 return dif->dif_unit;
501 }
502 return -1;
503 }
504
505 int
506 efi_net_get_booted_macaddr(uint8_t *mac)
507 {
508 const struct netif_dif *dif;
509 const struct efinetinfo *eni;
510 EFI_SIMPLE_NETWORK *net;
511 int i;
512
513 for (i = 0; i < efinetif.netif_nifs; i++) {
514 dif = &efinetif.netif_ifs[i];
515 eni = dif->dif_private;
516 net = eni->net;
517 if (eni->bootdev && net->Mode != NULL && net->Mode->HwAddressSize == 6) {
518 memcpy(mac, net->Mode->PermanentAddress.Addr, 6);
519 return 0;
520 }
521 }
522
523 return -1;
524 }
525
526 int
527 efi_net_open(struct open_file *f, ...)
528 {
529 char **file, pathbuf[PATH_MAX], *default_device, *path, *ep;
530 const char *fname, *full_path;
531 struct devdesc desc;
532 intmax_t dev;
533 va_list ap;
534 int n, error;
535
536 va_start(ap, f);
537 fname = va_arg(ap, const char *);
538 file = va_arg(ap, char **);
539 va_end(ap);
540
541 default_device = get_default_device();
542 if (strchr(fname, ':') == NULL) {
543 if (strlen(default_device) > 0) {
544 snprintf(pathbuf, sizeof(pathbuf), "%s:%s", default_device, fname);
545 full_path = pathbuf;
546 path = __UNCONST(fname);
547 } else {
548 return EINVAL;
549 }
550 } else {
551 full_path = fname;
552 path = strchr(fname, ':') + 1;
553 }
554
555 if (strncmp(full_path, "net", 3) != 0)
556 return EINVAL;
557 dev = strtoimax(full_path + 3, &ep, 10);
558 if (dev < 0 || dev >= efinetif.netif_nifs)
559 return ENXIO;
560
561 for (n = 0; n < ndevs; n++)
562 if (strcmp(DEV_NAME(&devsw[n]), "net") == 0) {
563 f->f_dev = &devsw[n];
564 break;
565 }
566 if (n == ndevs)
567 return ENXIO;
568
569 *file = path;
570
571 //try_bootp = 1;
572
573 memset(&desc, 0, sizeof(desc));
574 strlcpy(desc.d_name, "net", sizeof(desc.d_name));
575 desc.d_unit = dev;
576
577 error = DEV_OPEN(f->f_dev)(f, &desc);
578 if (error)
579 return error;
580
581 return 0;
582 }
583