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, &eth_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