uboot/lib/efi_loader/efi_net.c
<<
>>
Prefs
   1/*
   2 *  EFI application network access support
   3 *
   4 *  Copyright (c) 2016 Alexander Graf
   5 *
   6 *  SPDX-License-Identifier:     GPL-2.0+
   7 */
   8
   9#include <common.h>
  10#include <efi_loader.h>
  11#include <inttypes.h>
  12#include <lcd.h>
  13#include <malloc.h>
  14
  15DECLARE_GLOBAL_DATA_PTR;
  16
  17static const efi_guid_t efi_net_guid = EFI_SIMPLE_NETWORK_GUID;
  18static const efi_guid_t efi_pxe_guid = EFI_PXE_GUID;
  19static struct efi_pxe_packet *dhcp_ack;
  20static bool new_rx_packet;
  21static void *new_tx_packet;
  22/*
  23 * The notification function of this event is called in every timer cycle
  24 * to check if a new network packet has been received.
  25 */
  26static struct efi_event *network_timer_event;
  27/*
  28 * This event is signaled when a packet has been received.
  29 */
  30static struct efi_event *wait_for_packet;
  31
  32struct efi_net_obj {
  33        /* Generic EFI object parent class data */
  34        struct efi_object parent;
  35        /* EFI Interface callback struct for network */
  36        struct efi_simple_network net;
  37        struct efi_simple_network_mode net_mode;
  38        /* PXE struct to transmit dhcp data */
  39        struct efi_pxe pxe;
  40        struct efi_pxe_mode pxe_mode;
  41};
  42
  43static efi_status_t EFIAPI efi_net_start(struct efi_simple_network *this)
  44{
  45        EFI_ENTRY("%p", this);
  46
  47        return EFI_EXIT(EFI_SUCCESS);
  48}
  49
  50static efi_status_t EFIAPI efi_net_stop(struct efi_simple_network *this)
  51{
  52        EFI_ENTRY("%p", this);
  53
  54        return EFI_EXIT(EFI_SUCCESS);
  55}
  56
  57static efi_status_t EFIAPI efi_net_initialize(struct efi_simple_network *this,
  58                                              ulong extra_rx, ulong extra_tx)
  59{
  60        EFI_ENTRY("%p, %lx, %lx", this, extra_rx, extra_tx);
  61
  62        eth_init();
  63
  64        return EFI_EXIT(EFI_SUCCESS);
  65}
  66
  67static efi_status_t EFIAPI efi_net_reset(struct efi_simple_network *this,
  68                                         int extended_verification)
  69{
  70        EFI_ENTRY("%p, %x", this, extended_verification);
  71
  72        return EFI_EXIT(EFI_SUCCESS);
  73}
  74
  75static efi_status_t EFIAPI efi_net_shutdown(struct efi_simple_network *this)
  76{
  77        EFI_ENTRY("%p", this);
  78
  79        return EFI_EXIT(EFI_SUCCESS);
  80}
  81
  82static efi_status_t EFIAPI efi_net_receive_filters(
  83                struct efi_simple_network *this, u32 enable, u32 disable,
  84                int reset_mcast_filter, ulong mcast_filter_count,
  85                struct efi_mac_address *mcast_filter)
  86{
  87        EFI_ENTRY("%p, %x, %x, %x, %lx, %p", this, enable, disable,
  88                  reset_mcast_filter, mcast_filter_count, mcast_filter);
  89
  90        return EFI_EXIT(EFI_UNSUPPORTED);
  91}
  92
  93static efi_status_t EFIAPI efi_net_station_address(
  94                struct efi_simple_network *this, int reset,
  95                struct efi_mac_address *new_mac)
  96{
  97        EFI_ENTRY("%p, %x, %p", this, reset, new_mac);
  98
  99        return EFI_EXIT(EFI_UNSUPPORTED);
 100}
 101
 102static efi_status_t EFIAPI efi_net_statistics(struct efi_simple_network *this,
 103                                              int reset, ulong *stat_size,
 104                                              void *stat_table)
 105{
 106        EFI_ENTRY("%p, %x, %p, %p", this, reset, stat_size, stat_table);
 107
 108        return EFI_EXIT(EFI_UNSUPPORTED);
 109}
 110
 111static efi_status_t EFIAPI efi_net_mcastiptomac(struct efi_simple_network *this,
 112                                                int ipv6,
 113                                                struct efi_ip_address *ip,
 114                                                struct efi_mac_address *mac)
 115{
 116        EFI_ENTRY("%p, %x, %p, %p", this, ipv6, ip, mac);
 117
 118        return EFI_EXIT(EFI_INVALID_PARAMETER);
 119}
 120
 121static efi_status_t EFIAPI efi_net_nvdata(struct efi_simple_network *this,
 122                                          int read_write, ulong offset,
 123                                          ulong buffer_size, char *buffer)
 124{
 125        EFI_ENTRY("%p, %x, %lx, %lx, %p", this, read_write, offset, buffer_size,
 126                  buffer);
 127
 128        return EFI_EXIT(EFI_UNSUPPORTED);
 129}
 130
 131static efi_status_t EFIAPI efi_net_get_status(struct efi_simple_network *this,
 132                                              u32 *int_status, void **txbuf)
 133{
 134        EFI_ENTRY("%p, %p, %p", this, int_status, txbuf);
 135
 136        efi_timer_check();
 137
 138        if (int_status) {
 139                /* We send packets synchronously, so nothing is outstanding */
 140                *int_status = EFI_SIMPLE_NETWORK_TRANSMIT_INTERRUPT;
 141                if (new_rx_packet)
 142                        *int_status |= EFI_SIMPLE_NETWORK_RECEIVE_INTERRUPT;
 143        }
 144        if (txbuf)
 145                *txbuf = new_tx_packet;
 146
 147        new_tx_packet = NULL;
 148
 149        return EFI_EXIT(EFI_SUCCESS);
 150}
 151
 152static efi_status_t EFIAPI efi_net_transmit(struct efi_simple_network *this,
 153                size_t header_size, size_t buffer_size, void *buffer,
 154                struct efi_mac_address *src_addr,
 155                struct efi_mac_address *dest_addr, u16 *protocol)
 156{
 157        EFI_ENTRY("%p, %lu, %lu, %p, %p, %p, %p", this,
 158                  (unsigned long)header_size, (unsigned long)buffer_size,
 159                  buffer, src_addr, dest_addr, protocol);
 160
 161        efi_timer_check();
 162
 163        if (header_size) {
 164                /* We would need to create the header if header_size != 0 */
 165                return EFI_EXIT(EFI_INVALID_PARAMETER);
 166        }
 167
 168#ifdef CONFIG_EFI_LOADER_BOUNCE_BUFFER
 169        /* Ethernet packets always fit, just bounce */
 170        memcpy(efi_bounce_buffer, buffer, buffer_size);
 171        net_send_packet(efi_bounce_buffer, buffer_size);
 172#else
 173        net_send_packet(buffer, buffer_size);
 174#endif
 175
 176        new_tx_packet = buffer;
 177
 178        return EFI_EXIT(EFI_SUCCESS);
 179}
 180
 181static void efi_net_push(void *pkt, int len)
 182{
 183        new_rx_packet = true;
 184        wait_for_packet->is_signaled = true;
 185}
 186
 187/*
 188 * Receive a packet from a network interface.
 189 *
 190 * This function implements the Receive service of the Simple Network Protocol.
 191 * See the UEFI spec for details.
 192 *
 193 * @this        the instance of the Simple Network Protocol
 194 * @header_size size of the media header
 195 * @buffer_size size of the buffer to receive the packet
 196 * @buffer      buffer to receive the packet
 197 * @src_addr    source MAC address
 198 * @dest_addr   destination MAC address
 199 * @protocol    protocol
 200 * @return      status code
 201 */
 202static efi_status_t EFIAPI efi_net_receive(struct efi_simple_network *this,
 203                size_t *header_size, size_t *buffer_size, void *buffer,
 204                struct efi_mac_address *src_addr,
 205                struct efi_mac_address *dest_addr, u16 *protocol)
 206{
 207        struct ethernet_hdr *eth_hdr;
 208        size_t hdr_size = sizeof(struct ethernet_hdr);
 209        u16 protlen;
 210
 211        EFI_ENTRY("%p, %p, %p, %p, %p, %p, %p", this, header_size,
 212                  buffer_size, buffer, src_addr, dest_addr, protocol);
 213
 214        efi_timer_check();
 215
 216        if (!new_rx_packet)
 217                return EFI_EXIT(EFI_NOT_READY);
 218        /* Check that we at least received an Ethernet header */
 219        if (net_rx_packet_len < sizeof(struct ethernet_hdr)) {
 220                new_rx_packet = false;
 221                return EFI_EXIT(EFI_NOT_READY);
 222        }
 223        /* Fill export parameters */
 224        eth_hdr = (struct ethernet_hdr *)net_rx_packet;
 225        protlen = ntohs(eth_hdr->et_protlen);
 226        if (protlen == 0x8100) {
 227                hdr_size += 4;
 228                protlen = ntohs(*(u16 *)&net_rx_packet[hdr_size - 2]);
 229        }
 230        if (header_size)
 231                *header_size = hdr_size;
 232        if (dest_addr)
 233                memcpy(dest_addr, eth_hdr->et_dest, ARP_HLEN);
 234        if (src_addr)
 235                memcpy(src_addr, eth_hdr->et_src, ARP_HLEN);
 236        if (protocol)
 237                *protocol = protlen;
 238        if (*buffer_size < net_rx_packet_len) {
 239                /* Packet doesn't fit, try again with bigger buf */
 240                *buffer_size = net_rx_packet_len;
 241                return EFI_EXIT(EFI_BUFFER_TOO_SMALL);
 242        }
 243        /* Copy packet */
 244        memcpy(buffer, net_rx_packet, net_rx_packet_len);
 245        *buffer_size = net_rx_packet_len;
 246        new_rx_packet = false;
 247
 248        return EFI_EXIT(EFI_SUCCESS);
 249}
 250
 251void efi_net_set_dhcp_ack(void *pkt, int len)
 252{
 253        int maxsize = sizeof(*dhcp_ack);
 254
 255        if (!dhcp_ack)
 256                dhcp_ack = malloc(maxsize);
 257
 258        memcpy(dhcp_ack, pkt, min(len, maxsize));
 259}
 260
 261/*
 262 * Check if a new network packet has been received.
 263 *
 264 * This notification function is called in every timer cycle.
 265 *
 266 * @event       the event for which this notification function is registered
 267 * @context     event context - not used in this function
 268 */
 269static void EFIAPI efi_network_timer_notify(struct efi_event *event,
 270                                            void *context)
 271{
 272        EFI_ENTRY("%p, %p", event, context);
 273
 274        if (!new_rx_packet) {
 275                push_packet = efi_net_push;
 276                eth_rx();
 277                push_packet = NULL;
 278        }
 279        EFI_EXIT(EFI_SUCCESS);
 280}
 281
 282/* This gets called from do_bootefi_exec(). */
 283int efi_net_register(void)
 284{
 285        struct efi_net_obj *netobj;
 286        efi_status_t r;
 287
 288        if (!eth_get_dev()) {
 289                /* No eth device active, don't expose any */
 290                return 0;
 291        }
 292
 293        /* We only expose the "active" eth device, so one is enough */
 294        netobj = calloc(1, sizeof(*netobj));
 295
 296        /* Fill in object data */
 297        netobj->parent.protocols[0].guid = &efi_net_guid;
 298        netobj->parent.protocols[0].protocol_interface = &netobj->net;
 299        netobj->parent.protocols[1].guid = &efi_guid_device_path;
 300        netobj->parent.protocols[1].protocol_interface =
 301                efi_dp_from_eth();
 302        netobj->parent.protocols[2].guid = &efi_pxe_guid;
 303        netobj->parent.protocols[2].protocol_interface = &netobj->pxe;
 304        netobj->parent.handle = &netobj->net;
 305        netobj->net.revision = EFI_SIMPLE_NETWORK_PROTOCOL_REVISION;
 306        netobj->net.start = efi_net_start;
 307        netobj->net.stop = efi_net_stop;
 308        netobj->net.initialize = efi_net_initialize;
 309        netobj->net.reset = efi_net_reset;
 310        netobj->net.shutdown = efi_net_shutdown;
 311        netobj->net.receive_filters = efi_net_receive_filters;
 312        netobj->net.station_address = efi_net_station_address;
 313        netobj->net.statistics = efi_net_statistics;
 314        netobj->net.mcastiptomac = efi_net_mcastiptomac;
 315        netobj->net.nvdata = efi_net_nvdata;
 316        netobj->net.get_status = efi_net_get_status;
 317        netobj->net.transmit = efi_net_transmit;
 318        netobj->net.receive = efi_net_receive;
 319        netobj->net.mode = &netobj->net_mode;
 320        netobj->net_mode.state = EFI_NETWORK_STARTED;
 321        memcpy(netobj->net_mode.current_address.mac_addr, eth_get_ethaddr(), 6);
 322        netobj->net_mode.hwaddr_size = ARP_HLEN;
 323        netobj->net_mode.max_packet_size = PKTSIZE;
 324
 325        netobj->pxe.mode = &netobj->pxe_mode;
 326        if (dhcp_ack)
 327                netobj->pxe_mode.dhcp_ack = *dhcp_ack;
 328
 329        /* Hook net up to the device list */
 330        list_add_tail(&netobj->parent.link, &efi_obj_list);
 331
 332        /*
 333         * Create WaitForPacket event.
 334         */
 335        r = efi_create_event(EVT_NOTIFY_WAIT, TPL_CALLBACK,
 336                             efi_network_timer_notify, NULL,
 337                             &wait_for_packet);
 338        if (r != EFI_SUCCESS) {
 339                printf("ERROR: Failed to register network event\n");
 340                return r;
 341        }
 342        netobj->net.wait_for_packet = wait_for_packet;
 343        /*
 344         * Create a timer event.
 345         *
 346         * The notification function is used to check if a new network packet
 347         * has been received.
 348         */
 349        r = efi_create_event(EVT_TIMER | EVT_NOTIFY_SIGNAL, TPL_CALLBACK,
 350                             efi_network_timer_notify, NULL,
 351                             &network_timer_event);
 352        if (r != EFI_SUCCESS) {
 353                printf("ERROR: Failed to register network event\n");
 354                return r;
 355        }
 356        /* Network is time critical, create event in every timer cyle */
 357        r = efi_set_timer(network_timer_event, EFI_TIMER_PERIODIC, 0);
 358        if (r != EFI_SUCCESS) {
 359                printf("ERROR: Failed to set network timer\n");
 360                return r;
 361        }
 362
 363        return 0;
 364}
 365