1 /*-
   2  * Copyright (c) 2001 Doug Rabson
   3  * Copyright (c) 2002, 2006 Marcel Moolenaar
   4  * All rights reserved.
   5  *
   6  * Redistribution and use in source and binary forms, with or without
   7  * modification, are permitted provided that the following conditions
   8  * are met:
   9  * 1. Redistributions of source code must retain the above copyright
  10  *    notice, this list of conditions and the following disclaimer.
  11  * 2. Redistributions in binary form must reproduce the above copyright
  12  *    notice, this list of conditions and the following disclaimer in the
  13  *    documentation and/or other materials provided with the distribution.
  14  *
  15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
  16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
  19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  25  * SUCH DAMAGE.
  26  */
  27 
  28 #include <sys/cdefs.h>
  29 
  30 #include <sys/param.h>
  31 #include <net/ethernet.h>
  32 #include <netinet/in.h>
  33 #include <netinet/in_systm.h>
  34 
  35 #include <stand.h>
  36 #include <net.h>
  37 #include <netif.h>
  38 
  39 #include <efi.h>
  40 #include <efilib.h>
  41 
  42 static EFI_GUID sn_guid = EFI_SIMPLE_NETWORK_PROTOCOL;
  43 
  44 static void efinet_end(struct netif *);
  45 static ssize_t efinet_get(struct iodesc *, void **, time_t);
  46 static void efinet_init(struct iodesc *, void *);
  47 static int efinet_match(struct netif *, void *);
  48 static int efinet_probe(struct netif *, void *);
  49 static ssize_t efinet_put(struct iodesc *, void *, size_t);
  50 
  51 struct netif_driver efinetif = {   
  52         .netif_bname = "efinet",
  53         .netif_match = efinet_match,
  54         .netif_probe = efinet_probe,
  55         .netif_init = efinet_init,
  56         .netif_get = efinet_get,
  57         .netif_put = efinet_put,
  58         .netif_end = efinet_end,
  59         .netif_ifs = NULL,
  60         .netif_nifs = 0
  61 };
  62 
  63 #ifdef EFINET_DEBUG
  64 static void
  65 dump_mode(EFI_SIMPLE_NETWORK_MODE *mode)
  66 {
  67         int i;
  68 
  69         printf("State                 = %x\n", mode->State);
  70         printf("HwAddressSize         = %u\n", mode->HwAddressSize);
  71         printf("MediaHeaderSize       = %u\n", mode->MediaHeaderSize);
  72         printf("MaxPacketSize         = %u\n", mode->MaxPacketSize);
  73         printf("NvRamSize             = %u\n", mode->NvRamSize);
  74         printf("NvRamAccessSize       = %u\n", mode->NvRamAccessSize);
  75         printf("ReceiveFilterMask     = %x\n", mode->ReceiveFilterMask);
  76         printf("ReceiveFilterSetting  = %u\n", mode->ReceiveFilterSetting);
  77         printf("MaxMCastFilterCount   = %u\n", mode->MaxMCastFilterCount);
  78         printf("MCastFilterCount      = %u\n", mode->MCastFilterCount);
  79         printf("MCastFilter           = {");
  80         for (i = 0; i < mode->MCastFilterCount; i++)
  81                 printf(" %s", ether_sprintf(mode->MCastFilter[i].Addr));
  82         printf(" }\n");
  83         printf("CurrentAddress        = %s\n",
  84             ether_sprintf(mode->CurrentAddress.Addr));
  85         printf("BroadcastAddress      = %s\n",
  86             ether_sprintf(mode->BroadcastAddress.Addr));
  87         printf("PermanentAddress      = %s\n",
  88             ether_sprintf(mode->PermanentAddress.Addr));
  89         printf("IfType                = %u\n", mode->IfType);
  90         printf("MacAddressChangeable  = %d\n", mode->MacAddressChangeable);
  91         printf("MultipleTxSupported   = %d\n", mode->MultipleTxSupported);
  92         printf("MediaPresentSupported = %d\n", mode->MediaPresentSupported);
  93         printf("MediaPresent          = %d\n", mode->MediaPresent);
  94 }
  95 #endif
  96 
  97 static int
  98 efinet_match(struct netif *nif, void *machdep_hint)
  99 {
 100         struct devdesc *dev = machdep_hint;
 101 
 102         if (dev->d_unit == nif->nif_unit)
 103                 return (1);
 104         return(0);
 105 }
 106 
 107 static int
 108 efinet_probe(struct netif *nif, void *machdep_hint)
 109 {
 110 
 111         return (0);
 112 }
 113 
 114 static ssize_t
 115 efinet_put(struct iodesc *desc, void *pkt, size_t len)
 116 {
 117         struct netif *nif = desc->io_netif;
 118         EFI_SIMPLE_NETWORK *net;
 119         EFI_STATUS status;
 120         void *buf;
 121 
 122         net = nif->nif_devdata;
 123         if (net == NULL)
 124                 return (-1);
 125 
 126         status = net->Transmit(net, 0, len, pkt, NULL, NULL, NULL);
 127         if (status != EFI_SUCCESS)
 128                 return (-1);
 129 
 130         /* Wait for the buffer to be transmitted */
 131         do {
 132                 buf = NULL;     /* XXX Is this needed? */
 133                 status = net->GetStatus(net, NULL, &buf);
 134                 /*
 135                  * XXX EFI1.1 and the E1000 card returns a different 
 136                  * address than we gave.  Sigh.
 137                  */
 138         } while (status == EFI_SUCCESS && buf == NULL);
 139 
 140         /* XXX How do we deal with status != EFI_SUCCESS now? */
 141         return ((status == EFI_SUCCESS) ? len : -1);
 142 }
 143 
 144 static ssize_t
 145 efinet_get(struct iodesc *desc, void **pkt, time_t timeout)
 146 {
 147         struct netif *nif = desc->io_netif;
 148         EFI_SIMPLE_NETWORK *net;
 149         EFI_STATUS status;
 150         UINTN bufsz;
 151         time_t t;
 152         char *buf, *ptr;
 153         ssize_t ret = -1;
 154 
 155         net = nif->nif_devdata;
 156         if (net == NULL)
 157                 return (ret);
 158 
 159         bufsz = net->Mode->MaxPacketSize + ETHER_HDR_LEN + ETHER_CRC_LEN;
 160         buf = malloc(bufsz + ETHER_ALIGN);
 161         if (buf == NULL)
 162                 return (ret);
 163         ptr = buf + ETHER_ALIGN;
 164 
 165         t = getsecs();
 166         while ((getsecs() - t) < timeout) {
 167                 status = net->Receive(net, NULL, &bufsz, ptr, NULL, NULL, NULL);
 168                 if (status == EFI_SUCCESS) {
 169                         *pkt = buf;
 170                         ret = (ssize_t)bufsz;
 171                         break;
 172                 }
 173                 if (status != EFI_NOT_READY)
 174                         break;
 175         }
 176 
 177         if (ret == -1)
 178                 free(buf);
 179         return (ret);
 180 }
 181 
 182 static void
 183 efinet_init(struct iodesc *desc, void *machdep_hint)
 184 {
 185         struct netif *nif = desc->io_netif;
 186         EFI_SIMPLE_NETWORK *net;
 187         EFI_HANDLE h;
 188         EFI_STATUS status;
 189         UINT32 mask;
 190 
 191         if (nif->nif_driver->netif_ifs[nif->nif_unit].dif_unit < 0) {
 192                 printf("Invalid network interface %d\n", nif->nif_unit);
 193                 return;
 194         }
 195 
 196         h = nif->nif_driver->netif_ifs[nif->nif_unit].dif_private;
 197         status = BS->HandleProtocol(h, &sn_guid, (VOID **)&nif->nif_devdata);
 198         if (status != EFI_SUCCESS) {
 199                 printf("net%d: cannot fetch interface data (status=%lu)\n",
 200                     nif->nif_unit, EFI_ERROR_CODE(status));
 201                 return;
 202         }
 203 
 204         net = nif->nif_devdata;
 205         if (net->Mode->State == EfiSimpleNetworkStopped) {
 206                 status = net->Start(net);
 207                 if (status != EFI_SUCCESS) {
 208                         printf("net%d: cannot start interface (status=%lu)\n",
 209                             nif->nif_unit, EFI_ERROR_CODE(status));
 210                         return;
 211                 }
 212         }
 213 
 214         if (net->Mode->State != EfiSimpleNetworkInitialized) {
 215                 status = net->Initialize(net, 0, 0);
 216                 if (status != EFI_SUCCESS) {
 217                         printf("net%d: cannot init. interface (status=%lu)\n",
 218                             nif->nif_unit, EFI_ERROR_CODE(status));
 219                         return;
 220                 }
 221         }
 222 
 223         mask = EFI_SIMPLE_NETWORK_RECEIVE_UNICAST |
 224             EFI_SIMPLE_NETWORK_RECEIVE_BROADCAST;
 225 
 226         status = net->ReceiveFilters(net, mask, 0, FALSE, 0, NULL);
 227         if (status != EFI_SUCCESS) {
 228                 printf("net%d: cannot set rx. filters (status=%lu)\n",
 229                     nif->nif_unit, EFI_ERROR_CODE(status));
 230                 return;
 231         }
 232 
 233 #ifdef EFINET_DEBUG
 234         dump_mode(net->Mode);
 235 #endif
 236 
 237         bcopy(net->Mode->CurrentAddress.Addr, desc->myea, 6);
 238         desc->xid = 1;
 239 }
 240 
 241 static void
 242 efinet_end(struct netif *nif)
 243 {
 244         EFI_SIMPLE_NETWORK *net = nif->nif_devdata; 
 245 
 246         if (net == NULL)
 247                 return;
 248 
 249         net->Shutdown(net);
 250 }
 251 
 252 static int efinet_dev_init(void);
 253 static int efinet_dev_print(int);
 254 
 255 struct devsw efinet_dev = {
 256         .dv_name = "net",
 257         .dv_type = DEVT_NET,
 258         .dv_init = efinet_dev_init,
 259         .dv_strategy = NULL,            /* Will be set in efinet_dev_init */
 260         .dv_open = NULL,                /* Will be set in efinet_dev_init */
 261         .dv_close = NULL,               /* Will be set in efinet_dev_init */
 262         .dv_ioctl = noioctl,
 263         .dv_print = efinet_dev_print,
 264         .dv_cleanup = NULL
 265 };
 266 
 267 static int
 268 efinet_dev_init()
 269 {
 270         struct netif_dif *dif;
 271         struct netif_stats *stats;
 272         EFI_DEVICE_PATH *devpath, *node;
 273         EFI_SIMPLE_NETWORK *net;
 274         EFI_HANDLE *handles, *handles2;
 275         EFI_STATUS status;
 276         UINTN sz;
 277         int err, i, nifs;
 278         extern struct devsw netdev;
 279 
 280         sz = 0;
 281         handles = NULL;
 282         status = BS->LocateHandle(ByProtocol, &sn_guid, NULL, &sz, NULL);
 283         if (status == EFI_BUFFER_TOO_SMALL) {
 284                 handles = (EFI_HANDLE *)malloc(sz);
 285                 status = BS->LocateHandle(ByProtocol, &sn_guid, NULL, &sz,
 286                     handles);
 287                 if (EFI_ERROR(status))
 288                         free(handles);
 289         }
 290         if (EFI_ERROR(status))
 291                 return (efi_status_to_errno(status));
 292         handles2 = (EFI_HANDLE *)malloc(sz);
 293         if (handles2 == NULL) {
 294                 free(handles);
 295                 return (ENOMEM);
 296         }
 297         nifs = 0;
 298         for (i = 0; i < sz / sizeof(EFI_HANDLE); i++) {
 299                 devpath = efi_lookup_devpath(handles[i]);
 300                 if (devpath == NULL)
 301                         continue;
 302                 if ((node = efi_devpath_last_node(devpath)) == NULL)
 303                         continue;
 304 
 305                 if (DevicePathType(node) != MESSAGING_DEVICE_PATH ||
 306                     DevicePathSubType(node) != MSG_MAC_ADDR_DP)
 307                         continue;
 308 
 309                 /*
 310                  * Open the network device in exclusive mode. Without this
 311                  * we will be racing with the UEFI network stack. It will
 312                  * pull packets off the network leading to lost packets.
 313                  */
 314                 status = BS->OpenProtocol(handles[i], &sn_guid, (void **)&net,
 315                     IH, NULL, EFI_OPEN_PROTOCOL_EXCLUSIVE);
 316                 if (status != EFI_SUCCESS) {
 317                         printf("Unable to open network interface %d for "
 318                             "exclusive access: %lu\n", i,
 319                             EFI_ERROR_CODE(status));
 320                 }
 321 
 322                 handles2[nifs] = handles[i];
 323                 nifs++;
 324         }
 325         free(handles);
 326         if (nifs == 0) {
 327                 err = ENOENT;
 328                 goto done;
 329         }
 330 
 331         err = efi_register_handles(&efinet_dev, handles2, NULL, nifs);
 332         if (err != 0)
 333                 goto done;
 334 
 335         efinetif.netif_ifs = calloc(nifs, sizeof(struct netif_dif));
 336         stats = calloc(nifs, sizeof(struct netif_stats));
 337         if (efinetif.netif_ifs == NULL || stats == NULL) {
 338                 free(efinetif.netif_ifs);
 339                 free(stats);
 340                 efinetif.netif_ifs = NULL;
 341                 err = ENOMEM;
 342                 goto done;
 343         }
 344         efinetif.netif_nifs = nifs;
 345 
 346         for (i = 0; i < nifs; i++) {
 347 
 348                 dif = &efinetif.netif_ifs[i];
 349                 dif->dif_unit = i;
 350                 dif->dif_nsel = 1;
 351                 dif->dif_stats = &stats[i];
 352                 dif->dif_private = handles2[i];
 353         }
 354 
 355         efinet_dev.dv_open = netdev.dv_open;
 356         efinet_dev.dv_close = netdev.dv_close;
 357         efinet_dev.dv_strategy = netdev.dv_strategy;
 358 
 359 done:
 360         free(handles2);
 361         return (err);
 362 }
 363 
 364 static int
 365 efinet_dev_print(int verbose)
 366 {
 367         CHAR16 *text;
 368         EFI_HANDLE h;
 369         int unit, ret = 0;
 370 
 371         printf("%s devices:", efinet_dev.dv_name);
 372         if ((ret = pager_output("\n")) != 0)
 373                 return (ret);
 374 
 375         for (unit = 0, h = efi_find_handle(&efinet_dev, 0);
 376             h != NULL; h = efi_find_handle(&efinet_dev, ++unit)) {
 377                 printf("    %s%d:", efinet_dev.dv_name, unit);
 378                 if (verbose) {
 379                         text = efi_devpath_name(efi_lookup_devpath(h));
 380                         if (text != NULL) {
 381                                 printf("    %S", text);
 382                                 efi_free_devpath_name(text);
 383                         }
 384                 }
 385                 if ((ret = pager_output("\n")) != 0)
 386                         break;
 387         }
 388         return (ret);
 389 }