1// Copyright 2016 The Fuchsia Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include <efi/protocol/simple-network.h> 6 7#include <inttypes.h> 8#include <stdio.h> 9#include <string.h> 10 11#include <xefi.h> 12 13#include "inet6.h" 14#include "netifc.h" 15#include "osboot.h" 16 17static efi_simple_network_protocol* snp; 18 19#define MAX_FILTER 8 20static efi_mac_addr mcast_filters[MAX_FILTER]; 21static unsigned mcast_filter_count = 0; 22 23// if nonzero, drop 1 in DROP_PACKETS packets at random 24#define DROP_PACKETS 0 25 26#if DROP_PACKETS > 0 27 28//TODO: use libc random() once it's actually random 29 30// Xorshift32 prng 31typedef struct { 32 uint32_t n; 33} rand32_t; 34 35static inline uint32_t rand32(rand32_t* state) { 36 uint32_t n = state->n; 37 n ^= (n << 13); 38 n ^= (n >> 17); 39 n ^= (n << 5); 40 return (state->n = n); 41} 42 43rand32_t rstate = {.n = 0x8716253}; 44#define random() rand32(&rstate) 45 46static int txc; 47static int rxc; 48#endif 49 50#define NUM_BUFFER_PAGES 8 51#define ETH_BUFFER_SIZE 1516 52#define ETH_HEADER_SIZE 16 53#define ETH_BUFFER_MAGIC 0x424201020304A7A7UL 54 55typedef struct eth_buffer_t eth_buffer; 56struct eth_buffer_t { 57 uint64_t magic; 58 eth_buffer* next; 59 uint8_t data[0]; 60}; 61 62static efi_physical_addr eth_buffers_base = 0; 63static eth_buffer* eth_buffers = NULL; 64static int num_eth_buffers = 0; 65static int eth_buffers_avail = 0; 66 67void* eth_get_buffer(size_t sz) { 68 eth_buffer* buf; 69 if (sz > ETH_BUFFER_SIZE) { 70 return NULL; 71 } 72 if (eth_buffers == NULL) { 73 return NULL; 74 } 75 buf = eth_buffers; 76 eth_buffers = buf->next; 77 buf->next = NULL; 78 eth_buffers_avail--; 79 return buf->data; 80} 81 82void eth_put_buffer(void* data) { 83 efi_physical_addr buf_paddr = (efi_physical_addr)data; 84 if ((buf_paddr < eth_buffers_base) 85 || (buf_paddr >= (eth_buffers_base + (NUM_BUFFER_PAGES * PAGE_SIZE)))) { 86 printf("fatal: attempt to use buffer outside of allocated range\n"); 87 for (;;) 88 ; 89 } 90 91 eth_buffer* buf = (void*)(buf_paddr & (~2047)); 92 if (buf->magic != ETH_BUFFER_MAGIC) { 93 printf("fatal: eth buffer %p (from %p) bad magic %" PRIx64 "\n", 94 buf, data, buf->magic); 95 for (;;) 96 ; 97 } 98 99 buf->next = eth_buffers; 100 eth_buffers_avail++; 101 eth_buffers = buf; 102} 103 104int eth_send(void* data, size_t len) { 105#if DROP_PACKETS 106 txc++; 107 if ((random() % DROP_PACKETS) == 0) { 108 printf("tx drop %d\n", txc); 109 eth_put_buffer(data); 110 return 0; 111 } 112#endif 113 efi_status r; 114 if ((r = snp->Transmit(snp, 0, len, (void*)data, NULL, NULL, NULL))) { 115 eth_put_buffer(data); 116 return -1; 117 } else { 118 return 0; 119 } 120} 121 122void eth_dump_status(void) { 123#ifdef VERBOSE 124 printf("State/HwAdSz/HdrSz/MaxSz %d %d %d %d\n", 125 snp->Mode->State, snp->Mode->HwAddressSize, 126 snp->Mode->MediaHeaderSize, snp->Mode->MaxPacketSize); 127 printf("RcvMask/RcvCfg/MaxMcast/NumMcast %d %d %d %d\n", 128 snp->Mode->ReceiveFilterMask, snp->Mode->ReceiveFilterSetting, 129 snp->Mode->MaxMCastFilterCount, snp->Mode->MCastFilterCount); 130 uint8_t* x = snp->Mode->CurrentAddress.addr; 131 printf("MacAddr %02x:%02x:%02x:%02x:%02x:%02x\n", 132 x[0], x[1], x[2], x[3], x[4], x[5]); 133 printf("SetMac/MultiTx/LinkDetect/Link %d %d %d %d\n", 134 snp->Mode->MacAddressChangeable, snp->Mode->MultipleTxSupported, 135 snp->Mode->MediaPresentSupported, snp->Mode->MediaPresent); 136#endif 137} 138 139int eth_add_mcast_filter(const mac_addr* addr) { 140 if (mcast_filter_count >= MAX_FILTER) 141 return -1; 142 if (mcast_filter_count >= snp->Mode->MaxMCastFilterCount) 143 return -1; 144 memcpy(mcast_filters + mcast_filter_count, addr, ETH_ADDR_LEN); 145 mcast_filter_count++; 146 return 0; 147} 148 149static efi_event net_timer = NULL; 150 151#define TIMER_MS(n) (((uint64_t)(n)) * 10000UL) 152 153void netifc_set_timer(uint32_t ms) { 154 if (net_timer == 0) { 155 return; 156 } 157 gBS->SetTimer(net_timer, TimerRelative, TIMER_MS(ms)); 158} 159 160int netifc_timer_expired(void) { 161 if (net_timer == 0) { 162 return 0; 163 } 164 if (gBS->CheckEvent(net_timer) == EFI_SUCCESS) { 165 return 1; 166 } 167 return 0; 168} 169 170/* Search the available network interfaces via SimpleNetworkProtocol handles 171 * and find the first valid one with a Link detected */ 172efi_simple_network_protocol* netifc_find_available(void) { 173 efi_boot_services* bs = gSys->BootServices; 174 efi_status ret; 175 efi_simple_network_protocol* cur_snp = NULL; 176 efi_handle handles[32]; 177 char16_t *paths[32]; 178 size_t nic_cnt = 0; 179 size_t sz = sizeof(handles); 180 uint32_t last_parent = 0; 181 uint32_t int_sts; 182 void *tx_buf; 183 184 /* Get the handles of all devices that provide SimpleNetworkProtocol interfaces */ 185 ret = bs->LocateHandle(ByProtocol, &SimpleNetworkProtocol, NULL, &sz, handles); 186 if (ret != EFI_SUCCESS) { 187 printf("Failed to locate network interfaces (%s)\n", xefi_strerror(ret)); 188 return NULL; 189 } 190 191 nic_cnt = sz / sizeof(efi_handle); 192 for (size_t i = 0; i < nic_cnt; i++) { 193 paths[i] = xefi_handle_to_str(handles[i]); 194 } 195 196 /* Iterate over our SNP list until we find one with an established link */ 197 for (size_t i = 0; i < nic_cnt; i++) { 198 /* Check each interface once, but ignore any additional device paths a given interface 199 * may provide. e1000 tends to add a path for ipv4 and ipv6 configuration information 200 * for instance */ 201 if (i != last_parent) { 202 if (memcmp(paths[i], paths[last_parent], strlen_16(paths[last_parent])) == 0) { 203 continue; 204 } else { 205 last_parent = i; 206 } 207 } 208 209 puts16(paths[i]); 210 printf(": "); 211 ret = bs->HandleProtocol(handles[i], &SimpleNetworkProtocol, (void**)&cur_snp); 212 if (ret) { 213 printf("Failed to open (%s)\n", xefi_strerror(ret)); 214 continue; 215 } 216 217 /* If a driver is provided by the firmware then it should be started already, but check 218 * to make sure. This also covers the case where we're providing the AX88772 driver in-line 219 * during this boot itself */ 220 ret = cur_snp->Start(cur_snp); 221 if (EFI_ERROR(ret) && ret != EFI_ALREADY_STARTED) { 222 printf("Failed to start (%s)", xefi_strerror(ret)); 223 goto link_fail; 224 } 225 226 if (ret != EFI_ALREADY_STARTED) { 227 ret = cur_snp->Initialize(cur_snp, 0, 0); 228 if (EFI_ERROR(ret)) { 229 printf("Failed to initialize (%s)\n", xefi_strerror(ret)); 230 goto link_fail; 231 } 232 } 233 234 /* Prod the driver to cache its current status. We don't need the status or buffer, 235 * but some drivers appear to require the OPTIONAL parameters. */ 236 ret = cur_snp->GetStatus(cur_snp, &int_sts, &tx_buf); 237 if (EFI_ERROR(ret)) { 238 printf("Failed to read status (%s)\n", xefi_strerror(ret)); 239 goto link_fail; 240 } 241 242 /* With status cached, do we have a Link detected on the netifc? */ 243 if (!cur_snp->Mode->MediaPresent) { 244 printf("No link detected\n"); 245 goto link_fail; 246 } 247 248 printf("Link detected!\n"); 249 return cur_snp; 250 251link_fail: 252 bs->CloseProtocol(handles[i], &SimpleNetworkProtocol, gImg, NULL); 253 cur_snp = NULL; 254 } 255 256 return NULL; 257} 258 259int netifc_open(void) { 260 efi_boot_services* bs = gSys->BootServices; 261 efi_status ret; 262 int j; 263 264 bs->CreateEvent(EVT_TIMER, TPL_CALLBACK, NULL, NULL, &net_timer); 265 266 snp = netifc_find_available(); 267 if (!snp) { 268 printf("Failed to find a usable network interface\n"); 269 return -1; 270 } 271 272 if (bs->AllocatePages(AllocateAnyPages, EfiLoaderData, NUM_BUFFER_PAGES, ð_buffers_base)) { 273 printf("Failed to allocate net buffers\n"); 274 return -1; 275 } 276 277 num_eth_buffers = NUM_BUFFER_PAGES * 2; 278 uint8_t* ptr = (void*)eth_buffers_base; 279 for (ret = 0; ret < num_eth_buffers; ret++) { 280 eth_buffer* buf = (void*)ptr; 281 buf->magic = ETH_BUFFER_MAGIC; 282 eth_put_buffer(buf); 283 ptr += 2048; 284 } 285 286 ip6_init(snp->Mode->CurrentAddress.addr); 287 288 ret = snp->ReceiveFilters(snp, 289 EFI_SIMPLE_NETWORK_RECEIVE_UNICAST | 290 EFI_SIMPLE_NETWORK_RECEIVE_MULTICAST, 291 0, 0, mcast_filter_count, (void*)mcast_filters); 292 if (ret) { 293 printf("Failed to install multicast filters %s\n", xefi_strerror(ret)); 294 return -1; 295 } 296 297 eth_dump_status(); 298 299 if (snp->Mode->MCastFilterCount != mcast_filter_count) { 300 printf("OOPS: expected %d filters, found %d\n", 301 mcast_filter_count, snp->Mode->MCastFilterCount); 302 goto force_promisc; 303 } 304 for (size_t i = 0; i < mcast_filter_count; i++) { 305 //uint8_t *m = (void*) &mcast_filters[i]; 306 //printf("i=%d %02x %02x %02x %02x %02x %02x\n", i, m[0], m[1], m[2], m[3], m[4], m[5]); 307 for (j = 0; j < mcast_filter_count; j++) { 308 //m = (void*) &snp->Mode->MCastFilter[j]; 309 //printf("j=%d %02x %02x %02x %02x %02x %02x\n", j, m[0], m[1], m[2], m[3], m[4], m[5]); 310 if (!memcmp(mcast_filters + i, &snp->Mode->MCastFilter[j], 6)) { 311 goto found_it; 312 } 313 } 314 printf("OOPS: filter #%zu missing\n", i); 315 goto force_promisc; 316 found_it:; 317 } 318 319 return 0; 320 321force_promisc: 322 ret = snp->ReceiveFilters(snp, 323 EFI_SIMPLE_NETWORK_RECEIVE_UNICAST | 324 EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS | 325 EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS_MULTICAST, 326 0, 0, 0, NULL); 327 if (ret) { 328 printf("Failed to set promiscuous mode (%s)\n", xefi_strerror(ret)); 329 return -1; 330 } 331 return 0; 332} 333 334void netifc_close(void) { 335 gBS->SetTimer(net_timer, TimerCancel, 0); 336 gBS->CloseEvent(net_timer); 337 snp->Shutdown(snp); 338 snp->Stop(snp); 339} 340 341int netifc_active(void) { 342 return (snp != 0); 343} 344 345void netifc_poll(void) { 346 uint8_t data[1514]; 347 efi_status r; 348 size_t hsz, bsz; 349 uint32_t irq; 350 void* txdone; 351 352 if (eth_buffers_avail < num_eth_buffers) { 353 // Only check for completion if we have operations in progress. 354 // Otherwise, the result of GetStatus is unreliable. See ZX-759. 355 if ((r = snp->GetStatus(snp, &irq, &txdone))) { 356 return; 357 } 358 if (txdone) { 359 // Check to make sure this is one of our buffers (see ZX-1516) 360 efi_physical_addr buf_paddr = (efi_physical_addr)txdone; 361 if ((buf_paddr >= eth_buffers_base) 362 && (buf_paddr < (eth_buffers_base + (NUM_BUFFER_PAGES * PAGE_SIZE)))) { 363 eth_put_buffer(txdone); 364 } 365 } 366 } 367 368 hsz = 0; 369 bsz = sizeof(data); 370 r = snp->Receive(snp, &hsz, &bsz, data, NULL, NULL, NULL); 371 if (r != EFI_SUCCESS) { 372 return; 373 } 374 375#if DROP_PACKETS 376 rxc++; 377 if ((random() % DROP_PACKETS) == 0) { 378 printf("rx drop %d\n", rxc); 379 return; 380 } 381#endif 382 383#if TRACE 384 printf("RX %02x:%02x:%02x:%02x:%02x:%02x < %02x:%02x:%02x:%02x:%02x:%02x %02x%02x %d\n", 385 data[0], data[1], data[2], data[3], data[4], data[5], 386 data[6], data[7], data[8], data[9], data[10], data[11], 387 data[12], data[13], (int)(bsz - hsz)); 388#endif 389 eth_recv(data, bsz); 390} 391