Merge tag 'ti-v2020.07-rc3' of https://gitlab.denx.de/u-boot/custodians/u-boot-ti
[oweals/u-boot.git] / lib / efi_loader / efi_net.c
index d71c663068258be8ec909b8d81a4d8d5ba088815..22f0123eca4790753d62af98ca67a2667488375f 100644 (file)
@@ -1,16 +1,28 @@
 // SPDX-License-Identifier: GPL-2.0+
 /*
- *  EFI application network access support
+ * Simple network protocol
+ * PXE base code protocol
  *
- *  Copyright (c) 2016 Alexander Graf
+ * Copyright (c) 2016 Alexander Graf
+ *
+ * The simple network protocol has the following statuses and services
+ * to move between them:
+ *
+ * Start():     EfiSimpleNetworkStopped     -> EfiSimpleNetworkStarted
+ * Initialize(): EfiSimpleNetworkStarted     -> EfiSimpleNetworkInitialized
+ * Shutdown():  EfiSimpleNetworkInitialized -> EfiSimpleNetworkStarted
+ * Stop():      EfiSimpleNetworkStarted     -> EfiSimpleNetworkStopped
+ * Reset():     EfiSimpleNetworkInitialized -> EfiSimpleNetworkInitialized
  */
 
 #include <common.h>
 #include <efi_loader.h>
 #include <malloc.h>
+#include <net.h>
 
 static const efi_guid_t efi_net_guid = EFI_SIMPLE_NETWORK_PROTOCOL_GUID;
-static const efi_guid_t efi_pxe_guid = EFI_PXE_BASE_CODE_PROTOCOL_GUID;
+static const efi_guid_t efi_pxe_base_code_protocol_guid =
+                                       EFI_PXE_BASE_CODE_PROTOCOL_GUID;
 static struct efi_pxe_packet *dhcp_ack;
 static bool new_rx_packet;
 static void *new_tx_packet;
@@ -39,7 +51,7 @@ struct efi_net_obj {
        struct efi_object header;
        struct efi_simple_network net;
        struct efi_simple_network_mode net_mode;
-       struct efi_pxe pxe;
+       struct efi_pxe_base_code_protocol pxe;
        struct efi_pxe_mode pxe_mode;
 };
 
@@ -65,10 +77,13 @@ static efi_status_t EFIAPI efi_net_start(struct efi_simple_network *this)
                goto out;
        }
 
-       if (this->mode->state != EFI_NETWORK_STOPPED)
+       if (this->mode->state != EFI_NETWORK_STOPPED) {
                ret = EFI_ALREADY_STARTED;
-       else
+       } else {
+               this->int_status = 0;
+               wait_for_packet->is_signaled = false;
                this->mode->state = EFI_NETWORK_STARTED;
+       }
 out:
        return EFI_EXIT(ret);
 }
@@ -95,10 +110,13 @@ static efi_status_t EFIAPI efi_net_stop(struct efi_simple_network *this)
                goto out;
        }
 
-       if (this->mode->state == EFI_NETWORK_STOPPED)
+       if (this->mode->state == EFI_NETWORK_STOPPED) {
                ret = EFI_NOT_STARTED;
-       else
+       } else {
+               /* Disable hardware and put it into the reset state */
+               eth_halt();
                this->mode->state = EFI_NETWORK_STOPPED;
+       }
 out:
        return EFI_EXIT(ret);
 }
@@ -129,6 +147,15 @@ static efi_status_t EFIAPI efi_net_initialize(struct efi_simple_network *this,
                goto out;
        }
 
+       switch (this->mode->state) {
+       case EFI_NETWORK_INITIALIZED:
+       case EFI_NETWORK_STARTED:
+               break;
+       default:
+               r = EFI_NOT_STARTED;
+               goto out;
+       }
+
        /* Setup packet buffers */
        net_init();
        /* Disable hardware and put it into the reset state */
@@ -143,6 +170,8 @@ static efi_status_t EFIAPI efi_net_initialize(struct efi_simple_network *this,
                r = EFI_DEVICE_ERROR;
                goto out;
        } else {
+               this->int_status = 0;
+               wait_for_packet->is_signaled = false;
                this->mode->state = EFI_NETWORK_INITIALIZED;
        }
 out:
@@ -163,9 +192,31 @@ out:
 static efi_status_t EFIAPI efi_net_reset(struct efi_simple_network *this,
                                         int extended_verification)
 {
+       efi_status_t ret;
+
        EFI_ENTRY("%p, %x", this, extended_verification);
 
-       return EFI_EXIT(EFI_CALL(efi_net_initialize(this, 0, 0)));
+       /* Check parameters */
+       if (!this) {
+               ret = EFI_INVALID_PARAMETER;
+               goto out;
+       }
+
+       switch (this->mode->state) {
+       case EFI_NETWORK_INITIALIZED:
+               break;
+       case EFI_NETWORK_STOPPED:
+               ret = EFI_NOT_STARTED;
+               goto out;
+       default:
+               ret = EFI_DEVICE_ERROR;
+               goto out;
+       }
+
+       this->mode->state = EFI_NETWORK_STARTED;
+       ret = EFI_CALL(efi_net_initialize(this, 0, 0));
+out:
+       return EFI_EXIT(ret);
 }
 
 /*
@@ -190,8 +241,21 @@ static efi_status_t EFIAPI efi_net_shutdown(struct efi_simple_network *this)
                goto out;
        }
 
+       switch (this->mode->state) {
+       case EFI_NETWORK_INITIALIZED:
+               break;
+       case EFI_NETWORK_STOPPED:
+               ret = EFI_NOT_STARTED;
+               goto out;
+       default:
+               ret = EFI_DEVICE_ERROR;
+               goto out;
+       }
+
        eth_halt();
-       this->mode->state = EFI_NETWORK_STOPPED;
+       this->int_status = 0;
+       wait_for_packet->is_signaled = false;
+       this->mode->state = EFI_NETWORK_STARTED;
 
 out:
        return EFI_EXIT(ret);
@@ -269,7 +333,7 @@ static efi_status_t EFIAPI efi_net_statistics(struct efi_simple_network *this,
 /*
  * efi_net_mcastiptomac() - translate multicast IP address to MAC address
  *
- * This function implements the Statistics service of the
+ * This function implements the MCastIPtoMAC service of the
  * EFI_SIMPLE_NETWORK_PROTOCOL. See the Unified Extensible Firmware Interface
  * (UEFI) specification for details.
  *
@@ -284,9 +348,49 @@ static efi_status_t EFIAPI efi_net_mcastiptomac(struct efi_simple_network *this,
                                                struct efi_ip_address *ip,
                                                struct efi_mac_address *mac)
 {
+       efi_status_t ret = EFI_SUCCESS;
+
        EFI_ENTRY("%p, %x, %p, %p", this, ipv6, ip, mac);
 
-       return EFI_EXIT(EFI_INVALID_PARAMETER);
+       if (!this || !ip || !mac) {
+               ret = EFI_INVALID_PARAMETER;
+               goto out;
+       }
+
+       if (ipv6) {
+               ret = EFI_UNSUPPORTED;
+               goto out;
+       }
+
+       /* Multi-cast addresses are in the range 224.0.0.0 - 239.255.255.255 */
+       if ((ip->ip_addr[0] & 0xf0) != 0xe0) {
+               ret = EFI_INVALID_PARAMETER;
+               goto out;
+       };
+
+       switch (this->mode->state) {
+       case EFI_NETWORK_INITIALIZED:
+       case EFI_NETWORK_STARTED:
+               break;
+       default:
+               ret = EFI_NOT_STARTED;
+               goto out;
+       }
+
+       memset(mac, 0, sizeof(struct efi_mac_address));
+
+       /*
+        * Copy lower 23 bits of IPv4 multi-cast address
+        * RFC 1112, RFC 7042 2.1.1.
+        */
+       mac->mac_addr[0] = 0x01;
+       mac->mac_addr[1] = 0x00;
+       mac->mac_addr[2] = 0x5E;
+       mac->mac_addr[3] = ip->ip_addr[1] & 0x7F;
+       mac->mac_addr[4] = ip->ip_addr[2];
+       mac->mac_addr[5] = ip->ip_addr[3];
+out:
+       return EFI_EXIT(ret);
 }
 
 /**
@@ -296,7 +400,7 @@ static efi_status_t EFIAPI efi_net_mcastiptomac(struct efi_simple_network *this,
  * Protocol. See the UEFI spec for details.
  *
  * @this:              the instance of the Simple Network Protocol
- * @readwrite:         true for read, false for write
+ * @read_write:                true for read, false for write
  * @offset:            offset in NVRAM
  * @buffer_size:       size of buffer
  * @buffer:            buffer
@@ -349,10 +453,8 @@ static efi_status_t EFIAPI efi_net_get_status(struct efi_simple_network *this,
        }
 
        if (int_status) {
-               /* We send packets synchronously, so nothing is outstanding */
-               *int_status = EFI_SIMPLE_NETWORK_TRANSMIT_INTERRUPT;
-               if (new_rx_packet)
-                       *int_status |= EFI_SIMPLE_NETWORK_RECEIVE_INTERRUPT;
+               *int_status = this->int_status;
+               this->int_status = 0;
        }
        if (txbuf)
                *txbuf = new_tx_packet;
@@ -403,15 +505,33 @@ static efi_status_t EFIAPI efi_net_transmit
                goto out;
        }
 
-       if (header_size) {
-               /*
-                * TODO: We would need to create the header
-                * if header_size != 0
-                */
-               ret = EFI_UNSUPPORTED;
+       /* At least the IP header has to fit into the buffer */
+       if (buffer_size < this->mode->media_header_size) {
+               ret = EFI_BUFFER_TOO_SMALL;
                goto out;
        }
 
+       /*
+        * TODO:
+        * Support VLANs. Use net_set_ether() for copying the header. Use a
+        * U_BOOT_ENV_CALLBACK to update the media header size.
+        */
+       if (header_size) {
+               struct ethernet_hdr *header = buffer;
+
+               if (!dest_addr || !protocol ||
+                   header_size != this->mode->media_header_size) {
+                       ret = EFI_INVALID_PARAMETER;
+                       goto out;
+               }
+               if (!src_addr)
+                       src_addr = &this->mode->current_address;
+
+               memcpy(header->et_dest, dest_addr, ARP_HLEN);
+               memcpy(header->et_src, src_addr, ARP_HLEN);
+               header->et_protlen = htons(*protocol);
+       }
+
        switch (this->mode->state) {
        case EFI_NETWORK_STOPPED:
                ret = EFI_NOT_STARTED;
@@ -428,7 +548,7 @@ static efi_status_t EFIAPI efi_net_transmit
        net_send_packet(transmit_buffer, buffer_size);
 
        new_tx_packet = buffer;
-
+       this->int_status |= EFI_SIMPLE_NETWORK_TRANSMIT_INTERRUPT;
 out:
        return EFI_EXIT(ret);
 }
@@ -486,12 +606,6 @@ static efi_status_t EFIAPI efi_net_receive
                ret = EFI_NOT_READY;
                goto out;
        }
-       /* Check that we at least received an Ethernet header */
-       if (net_rx_packet_len < sizeof(struct ethernet_hdr)) {
-               new_rx_packet = false;
-               ret = EFI_NOT_READY;
-               goto out;
-       }
        /* Fill export parameters */
        eth_hdr = (struct ethernet_hdr *)net_rx_packet;
        protlen = ntohs(eth_hdr->et_protlen);
@@ -516,7 +630,8 @@ static efi_status_t EFIAPI efi_net_receive
        /* Copy packet */
        memcpy(buffer, net_rx_packet, net_rx_packet_len);
        *buffer_size = net_rx_packet_len;
-       new_rx_packet = false;
+       new_rx_packet = 0;
+       this->int_status &= ~EFI_SIMPLE_NETWORK_RECEIVE_INTERRUPT;
 out:
        return EFI_EXIT(ret);
 }
@@ -525,6 +640,9 @@ out:
  * efi_net_set_dhcp_ack() - take note of a selected DHCP IP address
  *
  * This function is called by dhcp_handler().
+ *
+ * @pkt:       packet received by dhcp_handler()
+ * @len:       length of the packet received
  */
 void efi_net_set_dhcp_ack(void *pkt, int len)
 {
@@ -547,7 +665,6 @@ void efi_net_set_dhcp_ack(void *pkt, int len)
 static void efi_net_push(void *pkt, int len)
 {
        new_rx_packet = true;
-       wait_for_packet->is_signaled = true;
 }
 
 /**
@@ -555,8 +672,8 @@ static void efi_net_push(void *pkt, int len)
  *
  * This notification function is called in every timer cycle.
  *
- * @event      the event for which this notification function is registered
- * @context    event context - not used in this function
+ * @event:     the event for which this notification function is registered
+ * @context:   event context - not used in this function
  */
 static void EFIAPI efi_network_timer_notify(struct efi_event *event,
                                            void *context)
@@ -576,11 +693,134 @@ static void EFIAPI efi_network_timer_notify(struct efi_event *event,
                push_packet = efi_net_push;
                eth_rx();
                push_packet = NULL;
+               if (new_rx_packet) {
+                       /* Check that we at least received an Ethernet header */
+                       if (net_rx_packet_len >=
+                           sizeof(struct ethernet_hdr)) {
+                               this->int_status |=
+                                       EFI_SIMPLE_NETWORK_RECEIVE_INTERRUPT;
+                               wait_for_packet->is_signaled = true;
+                       } else {
+                               new_rx_packet = 0;
+                       }
+               }
        }
 out:
        EFI_EXIT(EFI_SUCCESS);
 }
 
+static efi_status_t EFIAPI efi_pxe_base_code_start(
+                               struct efi_pxe_base_code_protocol *this,
+                               u8 use_ipv6)
+{
+       return EFI_UNSUPPORTED;
+}
+
+static efi_status_t EFIAPI efi_pxe_base_code_stop(
+                               struct efi_pxe_base_code_protocol *this)
+{
+       return EFI_UNSUPPORTED;
+}
+
+static efi_status_t EFIAPI efi_pxe_base_code_dhcp(
+                               struct efi_pxe_base_code_protocol *this,
+                               u8 sort_offers)
+{
+       return EFI_UNSUPPORTED;
+}
+
+static efi_status_t EFIAPI efi_pxe_base_code_discover(
+                               struct efi_pxe_base_code_protocol *this,
+                               u16 type, u16 *layer, u8 bis,
+                               struct efi_pxe_base_code_discover_info *info)
+{
+       return EFI_UNSUPPORTED;
+}
+
+static efi_status_t EFIAPI efi_pxe_base_code_mtftp(
+                               struct efi_pxe_base_code_protocol *this,
+                               u32 operation, void *buffer_ptr,
+                               u8 overwrite, efi_uintn_t *buffer_size,
+                               struct efi_ip_address server_ip, char *filename,
+                               struct efi_pxe_base_code_mtftp_info *info,
+                               u8 dont_use_buffer)
+{
+       return EFI_UNSUPPORTED;
+}
+
+static efi_status_t EFIAPI efi_pxe_base_code_udp_write(
+                               struct efi_pxe_base_code_protocol *this,
+                               u16 op_flags, struct efi_ip_address *dest_ip,
+                               u16 *dest_port,
+                               struct efi_ip_address *gateway_ip,
+                               struct efi_ip_address *src_ip, u16 *src_port,
+                               efi_uintn_t *header_size, void *header_ptr,
+                               efi_uintn_t *buffer_size, void *buffer_ptr)
+{
+       return EFI_UNSUPPORTED;
+}
+
+static efi_status_t EFIAPI efi_pxe_base_code_udp_read(
+                               struct efi_pxe_base_code_protocol *this,
+                               u16 op_flags, struct efi_ip_address *dest_ip,
+                               u16 *dest_port, struct efi_ip_address *src_ip,
+                               u16 *src_port, efi_uintn_t *header_size,
+                               void *header_ptr, efi_uintn_t *buffer_size,
+                               void *buffer_ptr)
+{
+       return EFI_UNSUPPORTED;
+}
+
+static efi_status_t EFIAPI efi_pxe_base_code_set_ip_filter(
+                               struct efi_pxe_base_code_protocol *this,
+                               struct efi_pxe_base_code_filter *new_filter)
+{
+       return EFI_UNSUPPORTED;
+}
+
+static efi_status_t EFIAPI efi_pxe_base_code_arp(
+                               struct efi_pxe_base_code_protocol *this,
+                               struct efi_ip_address *ip_addr,
+                               struct efi_mac_address *mac_addr)
+{
+       return EFI_UNSUPPORTED;
+}
+
+static efi_status_t EFIAPI efi_pxe_base_code_set_parameters(
+                               struct efi_pxe_base_code_protocol *this,
+                               u8 *new_auto_arp, u8 *new_send_guid,
+                               u8 *new_ttl, u8 *new_tos,
+                               u8 *new_make_callback)
+{
+       return EFI_UNSUPPORTED;
+}
+
+static efi_status_t EFIAPI efi_pxe_base_code_set_station_ip(
+                               struct efi_pxe_base_code_protocol *this,
+                               struct efi_ip_address *new_station_ip,
+                               struct efi_ip_address *new_subnet_mask)
+{
+       return EFI_UNSUPPORTED;
+}
+
+static efi_status_t EFIAPI efi_pxe_base_code_set_packets(
+                               struct efi_pxe_base_code_protocol *this,
+                               u8 *new_dhcp_discover_valid,
+                               u8 *new_dhcp_ack_received,
+                               u8 *new_proxy_offer_received,
+                               u8 *new_pxe_discover_valid,
+                               u8 *new_pxe_reply_received,
+                               u8 *new_pxe_bis_reply_received,
+                               EFI_PXE_BASE_CODE_PACKET *new_dchp_discover,
+                               EFI_PXE_BASE_CODE_PACKET *new_dhcp_acc,
+                               EFI_PXE_BASE_CODE_PACKET *new_proxy_offer,
+                               EFI_PXE_BASE_CODE_PACKET *new_pxe_discover,
+                               EFI_PXE_BASE_CODE_PACKET *new_pxe_reply,
+                               EFI_PXE_BASE_CODE_PACKET *new_pxe_bis_reply)
+{
+       return EFI_UNSUPPORTED;
+}
+
 /**
  * efi_net_register() - register the simple network protocol
  *
@@ -619,7 +859,7 @@ efi_status_t efi_net_register(void)
                             efi_dp_from_eth());
        if (r != EFI_SUCCESS)
                goto failure_to_add_protocol;
-       r = efi_add_protocol(&netobj->header, &efi_pxe_guid,
+       r = efi_add_protocol(&netobj->header, &efi_pxe_base_code_protocol_guid,
                             &netobj->pxe);
        if (r != EFI_SUCCESS)
                goto failure_to_add_protocol;
@@ -638,12 +878,26 @@ efi_status_t efi_net_register(void)
        netobj->net.transmit = efi_net_transmit;
        netobj->net.receive = efi_net_receive;
        netobj->net.mode = &netobj->net_mode;
-       netobj->net_mode.state = EFI_NETWORK_STARTED;
+       netobj->net_mode.state = EFI_NETWORK_STOPPED;
        memcpy(netobj->net_mode.current_address.mac_addr, eth_get_ethaddr(), 6);
        netobj->net_mode.hwaddr_size = ARP_HLEN;
+       netobj->net_mode.media_header_size = ETHER_HDR_SIZE;
        netobj->net_mode.max_packet_size = PKTSIZE;
        netobj->net_mode.if_type = ARP_ETHER;
 
+       netobj->pxe.revision = EFI_PXE_BASE_CODE_PROTOCOL_REVISION;
+       netobj->pxe.start = efi_pxe_base_code_start;
+       netobj->pxe.stop = efi_pxe_base_code_stop;
+       netobj->pxe.dhcp = efi_pxe_base_code_dhcp;
+       netobj->pxe.discover = efi_pxe_base_code_discover;
+       netobj->pxe.mtftp = efi_pxe_base_code_mtftp;
+       netobj->pxe.udp_write = efi_pxe_base_code_udp_write;
+       netobj->pxe.udp_read = efi_pxe_base_code_udp_read;
+       netobj->pxe.set_ip_filter = efi_pxe_base_code_set_ip_filter;
+       netobj->pxe.arp = efi_pxe_base_code_arp;
+       netobj->pxe.set_parameters = efi_pxe_base_code_set_parameters;
+       netobj->pxe.set_station_ip = efi_pxe_base_code_set_station_ip;
+       netobj->pxe.set_packets = efi_pxe_base_code_set_packets;
        netobj->pxe.mode = &netobj->pxe_mode;
        if (dhcp_ack)
                netobj->pxe_mode.dhcp_ack = *dhcp_ack;