|
@@ -19,6 +19,15 @@ static const efi_guid_t efi_pxe_guid = EFI_PXE_GUID;
|
|
static struct efi_pxe_packet *dhcp_ack;
|
|
static struct efi_pxe_packet *dhcp_ack;
|
|
static bool new_rx_packet;
|
|
static bool new_rx_packet;
|
|
static void *new_tx_packet;
|
|
static void *new_tx_packet;
|
|
|
|
+/*
|
|
|
|
+ * The notification function of this event is called in every timer cycle
|
|
|
|
+ * to check if a new network packet has been received.
|
|
|
|
+ */
|
|
|
|
+static struct efi_event *network_timer_event;
|
|
|
|
+/*
|
|
|
|
+ * This event is signaled when a packet has been received.
|
|
|
|
+ */
|
|
|
|
+static struct efi_event *wait_for_packet;
|
|
|
|
|
|
struct efi_net_obj {
|
|
struct efi_net_obj {
|
|
/* Generic EFI object parent class data */
|
|
/* Generic EFI object parent class data */
|
|
@@ -78,9 +87,7 @@ static efi_status_t EFIAPI efi_net_receive_filters(
|
|
EFI_ENTRY("%p, %x, %x, %x, %lx, %p", this, enable, disable,
|
|
EFI_ENTRY("%p, %x, %x, %x, %lx, %p", this, enable, disable,
|
|
reset_mcast_filter, mcast_filter_count, mcast_filter);
|
|
reset_mcast_filter, mcast_filter_count, mcast_filter);
|
|
|
|
|
|
- /* XXX Do we care? */
|
|
|
|
-
|
|
|
|
- return EFI_EXIT(EFI_SUCCESS);
|
|
|
|
|
|
+ return EFI_EXIT(EFI_UNSUPPORTED);
|
|
}
|
|
}
|
|
|
|
|
|
static efi_status_t EFIAPI efi_net_station_address(
|
|
static efi_status_t EFIAPI efi_net_station_address(
|
|
@@ -89,7 +96,7 @@ static efi_status_t EFIAPI efi_net_station_address(
|
|
{
|
|
{
|
|
EFI_ENTRY("%p, %x, %p", this, reset, new_mac);
|
|
EFI_ENTRY("%p, %x, %p", this, reset, new_mac);
|
|
|
|
|
|
- return EFI_EXIT(EFI_INVALID_PARAMETER);
|
|
|
|
|
|
+ return EFI_EXIT(EFI_UNSUPPORTED);
|
|
}
|
|
}
|
|
|
|
|
|
static efi_status_t EFIAPI efi_net_statistics(struct efi_simple_network *this,
|
|
static efi_status_t EFIAPI efi_net_statistics(struct efi_simple_network *this,
|
|
@@ -98,7 +105,7 @@ static efi_status_t EFIAPI efi_net_statistics(struct efi_simple_network *this,
|
|
{
|
|
{
|
|
EFI_ENTRY("%p, %x, %p, %p", this, reset, stat_size, stat_table);
|
|
EFI_ENTRY("%p, %x, %p, %p", this, reset, stat_size, stat_table);
|
|
|
|
|
|
- return EFI_EXIT(EFI_INVALID_PARAMETER);
|
|
|
|
|
|
+ return EFI_EXIT(EFI_UNSUPPORTED);
|
|
}
|
|
}
|
|
|
|
|
|
static efi_status_t EFIAPI efi_net_mcastiptomac(struct efi_simple_network *this,
|
|
static efi_status_t EFIAPI efi_net_mcastiptomac(struct efi_simple_network *this,
|
|
@@ -118,7 +125,7 @@ static efi_status_t EFIAPI efi_net_nvdata(struct efi_simple_network *this,
|
|
EFI_ENTRY("%p, %x, %lx, %lx, %p", this, read_write, offset, buffer_size,
|
|
EFI_ENTRY("%p, %x, %lx, %lx, %p", this, read_write, offset, buffer_size,
|
|
buffer);
|
|
buffer);
|
|
|
|
|
|
- return EFI_EXIT(EFI_INVALID_PARAMETER);
|
|
|
|
|
|
+ return EFI_EXIT(EFI_UNSUPPORTED);
|
|
}
|
|
}
|
|
|
|
|
|
static efi_status_t EFIAPI efi_net_get_status(struct efi_simple_network *this,
|
|
static efi_status_t EFIAPI efi_net_get_status(struct efi_simple_network *this,
|
|
@@ -126,9 +133,14 @@ static efi_status_t EFIAPI efi_net_get_status(struct efi_simple_network *this,
|
|
{
|
|
{
|
|
EFI_ENTRY("%p, %p, %p", this, int_status, txbuf);
|
|
EFI_ENTRY("%p, %p, %p", this, int_status, txbuf);
|
|
|
|
|
|
- /* We send packets synchronously, so nothing is outstanding */
|
|
|
|
- if (int_status)
|
|
|
|
- *int_status = 0;
|
|
|
|
|
|
+ efi_timer_check();
|
|
|
|
+
|
|
|
|
+ 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;
|
|
|
|
+ }
|
|
if (txbuf)
|
|
if (txbuf)
|
|
*txbuf = new_tx_packet;
|
|
*txbuf = new_tx_packet;
|
|
|
|
|
|
@@ -138,12 +150,15 @@ static efi_status_t EFIAPI efi_net_get_status(struct efi_simple_network *this,
|
|
}
|
|
}
|
|
|
|
|
|
static efi_status_t EFIAPI efi_net_transmit(struct efi_simple_network *this,
|
|
static efi_status_t EFIAPI efi_net_transmit(struct efi_simple_network *this,
|
|
- ulong header_size, ulong buffer_size, void *buffer,
|
|
|
|
|
|
+ size_t header_size, size_t buffer_size, void *buffer,
|
|
struct efi_mac_address *src_addr,
|
|
struct efi_mac_address *src_addr,
|
|
struct efi_mac_address *dest_addr, u16 *protocol)
|
|
struct efi_mac_address *dest_addr, u16 *protocol)
|
|
{
|
|
{
|
|
- EFI_ENTRY("%p, %lx, %lx, %p, %p, %p, %p", this, header_size,
|
|
|
|
- buffer_size, buffer, src_addr, dest_addr, protocol);
|
|
|
|
|
|
+ EFI_ENTRY("%p, %lu, %lu, %p, %p, %p, %p", this,
|
|
|
|
+ (unsigned long)header_size, (unsigned long)buffer_size,
|
|
|
|
+ buffer, src_addr, dest_addr, protocol);
|
|
|
|
+
|
|
|
|
+ efi_timer_check();
|
|
|
|
|
|
if (header_size) {
|
|
if (header_size) {
|
|
/* We would need to create the header if header_size != 0 */
|
|
/* We would need to create the header if header_size != 0 */
|
|
@@ -166,29 +181,66 @@ static efi_status_t EFIAPI efi_net_transmit(struct efi_simple_network *this,
|
|
static void efi_net_push(void *pkt, int len)
|
|
static void efi_net_push(void *pkt, int len)
|
|
{
|
|
{
|
|
new_rx_packet = true;
|
|
new_rx_packet = true;
|
|
|
|
+ wait_for_packet->is_signaled = true;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+/*
|
|
|
|
+ * Receive a packet from a network interface.
|
|
|
|
+ *
|
|
|
|
+ * This function implements the Receive service of the Simple Network Protocol.
|
|
|
|
+ * See the UEFI spec for details.
|
|
|
|
+ *
|
|
|
|
+ * @this the instance of the Simple Network Protocol
|
|
|
|
+ * @header_size size of the media header
|
|
|
|
+ * @buffer_size size of the buffer to receive the packet
|
|
|
|
+ * @buffer buffer to receive the packet
|
|
|
|
+ * @src_addr source MAC address
|
|
|
|
+ * @dest_addr destination MAC address
|
|
|
|
+ * @protocol protocol
|
|
|
|
+ * @return status code
|
|
|
|
+ */
|
|
static efi_status_t EFIAPI efi_net_receive(struct efi_simple_network *this,
|
|
static efi_status_t EFIAPI efi_net_receive(struct efi_simple_network *this,
|
|
- ulong *header_size, ulong *buffer_size, void *buffer,
|
|
|
|
|
|
+ size_t *header_size, size_t *buffer_size, void *buffer,
|
|
struct efi_mac_address *src_addr,
|
|
struct efi_mac_address *src_addr,
|
|
struct efi_mac_address *dest_addr, u16 *protocol)
|
|
struct efi_mac_address *dest_addr, u16 *protocol)
|
|
{
|
|
{
|
|
|
|
+ struct ethernet_hdr *eth_hdr;
|
|
|
|
+ size_t hdr_size = sizeof(struct ethernet_hdr);
|
|
|
|
+ u16 protlen;
|
|
|
|
+
|
|
EFI_ENTRY("%p, %p, %p, %p, %p, %p, %p", this, header_size,
|
|
EFI_ENTRY("%p, %p, %p, %p, %p, %p, %p", this, header_size,
|
|
buffer_size, buffer, src_addr, dest_addr, protocol);
|
|
buffer_size, buffer, src_addr, dest_addr, protocol);
|
|
|
|
|
|
- push_packet = efi_net_push;
|
|
|
|
- eth_rx();
|
|
|
|
- push_packet = NULL;
|
|
|
|
|
|
+ efi_timer_check();
|
|
|
|
|
|
if (!new_rx_packet)
|
|
if (!new_rx_packet)
|
|
return EFI_EXIT(EFI_NOT_READY);
|
|
return EFI_EXIT(EFI_NOT_READY);
|
|
-
|
|
|
|
|
|
+ /* Check that we at least received an Ethernet header */
|
|
|
|
+ if (net_rx_packet_len < sizeof(struct ethernet_hdr)) {
|
|
|
|
+ new_rx_packet = false;
|
|
|
|
+ return EFI_EXIT(EFI_NOT_READY);
|
|
|
|
+ }
|
|
|
|
+ /* Fill export parameters */
|
|
|
|
+ eth_hdr = (struct ethernet_hdr *)net_rx_packet;
|
|
|
|
+ protlen = ntohs(eth_hdr->et_protlen);
|
|
|
|
+ if (protlen == 0x8100) {
|
|
|
|
+ hdr_size += 4;
|
|
|
|
+ protlen = ntohs(*(u16 *)&net_rx_packet[hdr_size - 2]);
|
|
|
|
+ }
|
|
|
|
+ if (header_size)
|
|
|
|
+ *header_size = hdr_size;
|
|
|
|
+ if (dest_addr)
|
|
|
|
+ memcpy(dest_addr, eth_hdr->et_dest, ARP_HLEN);
|
|
|
|
+ if (src_addr)
|
|
|
|
+ memcpy(src_addr, eth_hdr->et_src, ARP_HLEN);
|
|
|
|
+ if (protocol)
|
|
|
|
+ *protocol = protlen;
|
|
if (*buffer_size < net_rx_packet_len) {
|
|
if (*buffer_size < net_rx_packet_len) {
|
|
/* Packet doesn't fit, try again with bigger buf */
|
|
/* Packet doesn't fit, try again with bigger buf */
|
|
*buffer_size = net_rx_packet_len;
|
|
*buffer_size = net_rx_packet_len;
|
|
return EFI_EXIT(EFI_BUFFER_TOO_SMALL);
|
|
return EFI_EXIT(EFI_BUFFER_TOO_SMALL);
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+ /* Copy packet */
|
|
memcpy(buffer, net_rx_packet, net_rx_packet_len);
|
|
memcpy(buffer, net_rx_packet, net_rx_packet_len);
|
|
*buffer_size = net_rx_packet_len;
|
|
*buffer_size = net_rx_packet_len;
|
|
new_rx_packet = false;
|
|
new_rx_packet = false;
|
|
@@ -206,10 +258,32 @@ void efi_net_set_dhcp_ack(void *pkt, int len)
|
|
memcpy(dhcp_ack, pkt, min(len, maxsize));
|
|
memcpy(dhcp_ack, pkt, min(len, maxsize));
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+/*
|
|
|
|
+ * Check if a new network packet has been received.
|
|
|
|
+ *
|
|
|
|
+ * 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
|
|
|
|
+ */
|
|
|
|
+static void EFIAPI efi_network_timer_notify(struct efi_event *event,
|
|
|
|
+ void *context)
|
|
|
|
+{
|
|
|
|
+ EFI_ENTRY("%p, %p", event, context);
|
|
|
|
+
|
|
|
|
+ if (!new_rx_packet) {
|
|
|
|
+ push_packet = efi_net_push;
|
|
|
|
+ eth_rx();
|
|
|
|
+ push_packet = NULL;
|
|
|
|
+ }
|
|
|
|
+ EFI_EXIT(EFI_SUCCESS);
|
|
|
|
+}
|
|
|
|
+
|
|
/* This gets called from do_bootefi_exec(). */
|
|
/* This gets called from do_bootefi_exec(). */
|
|
int efi_net_register(void)
|
|
int efi_net_register(void)
|
|
{
|
|
{
|
|
struct efi_net_obj *netobj;
|
|
struct efi_net_obj *netobj;
|
|
|
|
+ efi_status_t r;
|
|
|
|
|
|
if (!eth_get_dev()) {
|
|
if (!eth_get_dev()) {
|
|
/* No eth device active, don't expose any */
|
|
/* No eth device active, don't expose any */
|
|
@@ -228,6 +302,7 @@ int efi_net_register(void)
|
|
netobj->parent.protocols[2].guid = &efi_pxe_guid;
|
|
netobj->parent.protocols[2].guid = &efi_pxe_guid;
|
|
netobj->parent.protocols[2].protocol_interface = &netobj->pxe;
|
|
netobj->parent.protocols[2].protocol_interface = &netobj->pxe;
|
|
netobj->parent.handle = &netobj->net;
|
|
netobj->parent.handle = &netobj->net;
|
|
|
|
+ netobj->net.revision = EFI_SIMPLE_NETWORK_PROTOCOL_REVISION;
|
|
netobj->net.start = efi_net_start;
|
|
netobj->net.start = efi_net_start;
|
|
netobj->net.stop = efi_net_stop;
|
|
netobj->net.stop = efi_net_stop;
|
|
netobj->net.initialize = efi_net_initialize;
|
|
netobj->net.initialize = efi_net_initialize;
|
|
@@ -244,6 +319,7 @@ int efi_net_register(void)
|
|
netobj->net.mode = &netobj->net_mode;
|
|
netobj->net.mode = &netobj->net_mode;
|
|
netobj->net_mode.state = EFI_NETWORK_STARTED;
|
|
netobj->net_mode.state = EFI_NETWORK_STARTED;
|
|
memcpy(netobj->net_mode.current_address.mac_addr, eth_get_ethaddr(), 6);
|
|
memcpy(netobj->net_mode.current_address.mac_addr, eth_get_ethaddr(), 6);
|
|
|
|
+ netobj->net_mode.hwaddr_size = ARP_HLEN;
|
|
netobj->net_mode.max_packet_size = PKTSIZE;
|
|
netobj->net_mode.max_packet_size = PKTSIZE;
|
|
|
|
|
|
netobj->pxe.mode = &netobj->pxe_mode;
|
|
netobj->pxe.mode = &netobj->pxe_mode;
|
|
@@ -253,5 +329,36 @@ int efi_net_register(void)
|
|
/* Hook net up to the device list */
|
|
/* Hook net up to the device list */
|
|
list_add_tail(&netobj->parent.link, &efi_obj_list);
|
|
list_add_tail(&netobj->parent.link, &efi_obj_list);
|
|
|
|
|
|
|
|
+ /*
|
|
|
|
+ * Create WaitForPacket event.
|
|
|
|
+ */
|
|
|
|
+ r = efi_create_event(EVT_NOTIFY_WAIT, TPL_CALLBACK,
|
|
|
|
+ efi_network_timer_notify, NULL,
|
|
|
|
+ &wait_for_packet);
|
|
|
|
+ if (r != EFI_SUCCESS) {
|
|
|
|
+ printf("ERROR: Failed to register network event\n");
|
|
|
|
+ return r;
|
|
|
|
+ }
|
|
|
|
+ netobj->net.wait_for_packet = wait_for_packet;
|
|
|
|
+ /*
|
|
|
|
+ * Create a timer event.
|
|
|
|
+ *
|
|
|
|
+ * The notification function is used to check if a new network packet
|
|
|
|
+ * has been received.
|
|
|
|
+ */
|
|
|
|
+ r = efi_create_event(EVT_TIMER | EVT_NOTIFY_SIGNAL, TPL_CALLBACK,
|
|
|
|
+ efi_network_timer_notify, NULL,
|
|
|
|
+ &network_timer_event);
|
|
|
|
+ if (r != EFI_SUCCESS) {
|
|
|
|
+ printf("ERROR: Failed to register network event\n");
|
|
|
|
+ return r;
|
|
|
|
+ }
|
|
|
|
+ /* Network is time critical, create event in every timer cyle */
|
|
|
|
+ r = efi_set_timer(network_timer_event, EFI_TIMER_PERIODIC, 0);
|
|
|
|
+ if (r != EFI_SUCCESS) {
|
|
|
|
+ printf("ERROR: Failed to set network timer\n");
|
|
|
|
+ return r;
|
|
|
|
+ }
|
|
|
|
+
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|