diff options
Diffstat (limited to 'lib/efi_loader/efi_net.c')
-rw-r--r-- | lib/efi_loader/efi_net.c | 165 |
1 files changed, 127 insertions, 38 deletions
diff --git a/lib/efi_loader/efi_net.c b/lib/efi_loader/efi_net.c index 0b949d86e8..432d9a99a2 100644 --- a/lib/efi_loader/efi_net.c +++ b/lib/efi_loader/efi_net.c @@ -19,6 +19,15 @@ static const efi_guid_t efi_pxe_guid = EFI_PXE_GUID; static struct efi_pxe_packet *dhcp_ack; static bool new_rx_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 { /* Generic EFI object parent class data */ @@ -26,9 +35,6 @@ struct efi_net_obj { /* EFI Interface callback struct for network */ struct efi_simple_network net; struct efi_simple_network_mode net_mode; - /* Device path to the network adapter */ - struct efi_device_path_mac_addr dp_mac; - struct efi_device_path_file_path dp_end; /* PXE struct to transmit dhcp data */ struct efi_pxe pxe; struct efi_pxe_mode pxe_mode; @@ -81,9 +87,7 @@ static efi_status_t EFIAPI efi_net_receive_filters( EFI_ENTRY("%p, %x, %x, %x, %lx, %p", this, enable, disable, 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( @@ -92,7 +96,7 @@ static efi_status_t EFIAPI efi_net_station_address( { 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, @@ -101,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); - return EFI_EXIT(EFI_INVALID_PARAMETER); + return EFI_EXIT(EFI_UNSUPPORTED); } static efi_status_t EFIAPI efi_net_mcastiptomac(struct efi_simple_network *this, @@ -121,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, 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, @@ -129,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); - /* 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) *txbuf = new_tx_packet; @@ -141,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, - 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 *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) { /* We would need to create the header if header_size != 0 */ @@ -169,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) { 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, - 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 *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, 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) 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) { /* Packet doesn't fit, try again with bigger buf */ *buffer_size = net_rx_packet_len; return EFI_EXIT(EFI_BUFFER_TOO_SMALL); } - + /* Copy packet */ memcpy(buffer, net_rx_packet, net_rx_packet_len); *buffer_size = net_rx_packet_len; new_rx_packet = false; @@ -209,20 +258,32 @@ void efi_net_set_dhcp_ack(void *pkt, int len) 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(). */ -int efi_net_register(void **handle) +int efi_net_register(void) { struct efi_net_obj *netobj; - struct efi_device_path_mac_addr dp_net = { - .dp.type = DEVICE_PATH_TYPE_MESSAGING_DEVICE, - .dp.sub_type = DEVICE_PATH_SUB_TYPE_MSG_MAC_ADDR, - .dp.length = sizeof(dp_net), - }; - struct efi_device_path_file_path dp_end = { - .dp.type = DEVICE_PATH_TYPE_END, - .dp.sub_type = DEVICE_PATH_SUB_TYPE_END, - .dp.length = sizeof(dp_end), - }; + efi_status_t r; if (!eth_get_dev()) { /* No eth device active, don't expose any */ @@ -236,10 +297,12 @@ int efi_net_register(void **handle) netobj->parent.protocols[0].guid = &efi_net_guid; netobj->parent.protocols[0].protocol_interface = &netobj->net; netobj->parent.protocols[1].guid = &efi_guid_device_path; - netobj->parent.protocols[1].protocol_interface = &netobj->dp_mac; + netobj->parent.protocols[1].protocol_interface = + efi_dp_from_eth(); netobj->parent.protocols[2].guid = &efi_pxe_guid; netobj->parent.protocols[2].protocol_interface = &netobj->pxe; netobj->parent.handle = &netobj->net; + netobj->net.revision = EFI_SIMPLE_NETWORK_PROTOCOL_REVISION; netobj->net.start = efi_net_start; netobj->net.stop = efi_net_stop; netobj->net.initialize = efi_net_initialize; @@ -255,10 +318,8 @@ int efi_net_register(void **handle) netobj->net.receive = efi_net_receive; netobj->net.mode = &netobj->net_mode; netobj->net_mode.state = EFI_NETWORK_STARTED; - netobj->dp_mac = dp_net; - netobj->dp_end = dp_end; - memcpy(netobj->dp_mac.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->pxe.mode = &netobj->pxe_mode; @@ -268,8 +329,36 @@ int efi_net_register(void **handle) /* Hook net up to the device list */ list_add_tail(&netobj->parent.link, &efi_obj_list); - if (handle) - *handle = &netobj->net; + /* + * 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; } |