summaryrefslogtreecommitdiff
path: root/lib/efi_loader/efi_net.c
diff options
context:
space:
mode:
authorTom Rini <trini@konsulko.com>2019-09-06 08:04:08 -0400
committerTom Rini <trini@konsulko.com>2019-09-06 08:04:08 -0400
commit6128e61429a5d989cbb1e1a92b02791f69615c42 (patch)
treef463d4e65bc5bec09c7cf6ee8a01991e441841e1 /lib/efi_loader/efi_net.c
parentece9834f7d223097cec92e3d3c70cd37b3768482 (diff)
parentfe1a81c1a47737d3ce6b6855a05468b7546d4982 (diff)
Merge tag 'efi-2019-10-rc4-3' of https://gitlab.denx.de/u-boot/custodians/u-boot-efi
Pull request for UEFI sub-system for v2019.10-rc4 (3) This includes the patches from Pull request for UEFI sub-system for v2019.10-rc4 (2) Fix UEFI specification compliance issues in the simple network protocol: * Correctly set and reset the interrupt status. * Support filling the header in the Transmit() service. * Correct the checking and setting of the network state. * Implement the MCastIPtoMAC() service. * Adjust the simple network protocol unit test. Fix UEFI specification compliance issues in the protocol. Fix UEFI specification compliance issues in the simple text output protocol: * Avoid out of bounds cursor position. * Do not set illegal screen mode. Fix UEFI specification compliance issues in the block IO protocol: * Check parameters. * Return correct status code if buffer is unaligned. Refactor initialization of EFI memory in preparation of support for > 3GB memory on x86.
Diffstat (limited to 'lib/efi_loader/efi_net.c')
-rw-r--r--lib/efi_loader/efi_net.c193
1 files changed, 160 insertions, 33 deletions
diff --git a/lib/efi_loader/efi_net.c b/lib/efi_loader/efi_net.c
index 825e064f9a..82d2595847 100644
--- a/lib/efi_loader/efi_net.c
+++ b/lib/efi_loader/efi_net.c
@@ -1,8 +1,18 @@
// 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>
@@ -66,10 +76,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);
}
@@ -96,10 +109,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);
}
@@ -130,6 +146,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 */
@@ -144,6 +169,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:
@@ -164,9 +191,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);
}
/*
@@ -191,8 +240,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);
@@ -270,7 +332,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.
*
@@ -285,9 +347,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);
}
/**
@@ -297,7 +399,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
@@ -350,10 +452,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;
@@ -404,15 +504,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;
@@ -429,7 +547,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);
}
@@ -487,12 +605,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);
@@ -517,7 +629,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);
}
@@ -526,6 +639,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)
{
@@ -548,7 +664,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;
}
/**
@@ -556,8 +671,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)
@@ -577,6 +692,17 @@ 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);
@@ -751,9 +877,10 @@ 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;