diff options
author | Geoff Lywood | 2010-05-28 05:08:28 +0200 |
---|---|---|
committer | Michael Brown | 2010-06-02 16:15:29 +0200 |
commit | 62149deb116516bb0d70b756bfc4f5b4669034da (patch) | |
tree | b8adc9a047202f1cebc15f244da2c36d75a05ba0 | |
parent | [qib7322] Fix whitespace errors (diff) | |
download | ipxe-62149deb116516bb0d70b756bfc4f5b4669034da.tar.gz ipxe-62149deb116516bb0d70b756bfc4f5b4669034da.tar.xz ipxe-62149deb116516bb0d70b756bfc4f5b4669034da.zip |
[efi] Add the "snpnet" driver
Add a new network driver that consumes the EFI Simple Network
Protocol. Also add a bus driver that can find the Simple Network
Protocol that iPXE was loaded from; the resulting behavior is similar
to the "undionly" driver for BIOS systems.
Signed-off-by: Michael Brown <mcb30@ipxe.org>
-rw-r--r-- | src/Makefile | 1 | ||||
-rw-r--r-- | src/drivers/net/efi/snp.h | 49 | ||||
-rw-r--r-- | src/drivers/net/efi/snpnet.c | 362 | ||||
-rw-r--r-- | src/drivers/net/efi/snpnet.h | 35 | ||||
-rw-r--r-- | src/drivers/net/efi/snponly.c | 129 | ||||
-rw-r--r-- | src/image/efi_image.c | 37 | ||||
-rwxr-xr-x | src/include/ipxe/efi/Protocol/LoadedImage.h | 88 | ||||
-rw-r--r-- | src/include/ipxe/efi/efi.h | 2 | ||||
-rw-r--r-- | src/include/ipxe/errfile.h | 2 | ||||
-rw-r--r-- | src/interface/efi/efi_init.c | 23 |
10 files changed, 724 insertions, 4 deletions
diff --git a/src/Makefile b/src/Makefile index 5079f937..f81c4331 100644 --- a/src/Makefile +++ b/src/Makefile @@ -67,6 +67,7 @@ SRCDIRS += drivers/net/phantom SRCDIRS += drivers/net/rtl818x SRCDIRS += drivers/net/ath5k SRCDIRS += drivers/net/vxge +SRCDIRS += drivers/net/efi SRCDIRS += drivers/block SRCDIRS += drivers/nvs SRCDIRS += drivers/bitbash diff --git a/src/drivers/net/efi/snp.h b/src/drivers/net/efi/snp.h new file mode 100644 index 00000000..4d6b1014 --- /dev/null +++ b/src/drivers/net/efi/snp.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2010 VMware, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef _SNP_H +#define _SNP_H + +/** @file + * + * SNP driver + * + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include <ipxe/device.h> +#include <ipxe/netdevice.h> +#include <ipxe/efi/Protocol/SimpleNetwork.h> + +/** A network device that consumes the EFI Simple Network Protocol */ +struct snp_device { + /** Underlying simple network protocol instance */ + EFI_SIMPLE_NETWORK_PROTOCOL *snp; + + /** Generic device */ + struct device dev; + + /** Network device */ + struct net_device *netdev; + + /** State to put the snp in when removing the device */ + uint32 removal_state; +}; + +#endif /* _SNP_H */ diff --git a/src/drivers/net/efi/snpnet.c b/src/drivers/net/efi/snpnet.c new file mode 100644 index 00000000..ba63a01d --- /dev/null +++ b/src/drivers/net/efi/snpnet.c @@ -0,0 +1,362 @@ +/* + * Copyright (C) 2010 VMware, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include <errno.h> +#include <string.h> +#include <ipxe/io.h> +#include <ipxe/iobuf.h> +#include <ipxe/netdevice.h> +#include <ipxe/if_ether.h> +#include <ipxe/ethernet.h> +#include <ipxe/efi/efi.h> +#include <ipxe/efi/Protocol/SimpleNetwork.h> +#include "snp.h" +#include "snpnet.h" + +/** @file + * + * SNP network device driver + * + */ + +/** SNP net device structure */ +struct snpnet_device { + /** The underlying simple network protocol */ + EFI_SIMPLE_NETWORK_PROTOCOL *snp; + + /** State that the SNP should be in after close */ + UINT32 close_state; +}; + +/** + * Transmit packet + * + * @v netdev Network device + * @v iobuf I/O buffer + * @ret rc Return status code + */ +static int snpnet_transmit ( struct net_device *netdev, + struct io_buffer *iobuf ) { + struct snpnet_device *snpnetdev = netdev->priv; + EFI_SIMPLE_NETWORK_PROTOCOL *snp = snpnetdev->snp; + EFI_STATUS efirc; + size_t len = iob_len ( iobuf ); + + efirc = snp->Transmit ( snp, 0, len, iobuf->data, NULL, NULL, NULL ); + return EFIRC_TO_RC ( efirc ); +} + +/** + * Find a I/O buffer on the list of outstanding Tx buffers and complete it. + * + * @v snpnetdev SNP network device + * @v txbuf Buffer address + */ +static void snpnet_complete ( struct net_device *netdev, void *txbuf ) { + struct io_buffer *tmp; + struct io_buffer *iobuf; + + list_for_each_entry_safe ( iobuf, tmp, &netdev->tx_queue, list ) { + if ( iobuf->data == txbuf ) { + netdev_tx_complete ( netdev, iobuf ); + break; + } + } +} + +/** + * Poll for received packets + * + * @v netdev Network device + */ +static void snpnet_poll ( struct net_device *netdev ) { + struct snpnet_device *snpnetdev = netdev->priv; + EFI_SIMPLE_NETWORK_PROTOCOL *snp = snpnetdev->snp; + EFI_STATUS efirc; + struct io_buffer *iobuf = NULL; + UINTN len; + void *txbuf; + + /* Process Tx completions */ + while ( 1 ) { + efirc = snp->GetStatus ( snp, NULL, &txbuf ); + if ( efirc ) { + DBGC ( snp, "SNP %p could not get status %s\n", snp, + efi_strerror ( efirc ) ); + break; + } + + if ( txbuf == NULL ) + break; + + snpnet_complete ( netdev, txbuf ); + } + + /* Process received packets */ + while ( 1 ) { + /* The spec is not clear if the max packet size refers to the + * payload or the entire packet including headers. The Receive + * function needs a buffer large enough to contain the headers, + * and potentially a 4-byte CRC and 4-byte VLAN tag (?), so add + * some breathing room. + */ + len = snp->Mode->MaxPacketSize + ETH_HLEN + 8; + iobuf = alloc_iob ( len ); + if ( iobuf == NULL ) { + netdev_rx_err ( netdev, NULL, -ENOMEM ); + break; + } + + efirc = snp->Receive ( snp, NULL, &len, iobuf->data, + NULL, NULL, NULL ); + + /* No packets left? */ + if ( efirc == EFI_NOT_READY ) { + free_iob ( iobuf ); + break; + } + + /* Other error? */ + if ( efirc ) { + DBGC ( snp, "SNP %p receive packet error: %s " + "(len was %zd, is now %zd)\n", + snp, efi_strerror ( efirc ), iob_len(iobuf), + (size_t)len ); + netdev_rx_err ( netdev, iobuf, efirc ); + break; + } + + /* Packet is valid, deliver it */ + iob_put ( iobuf, len ); + netdev_rx ( netdev, iob_disown ( iobuf ) ); + } +} + +/** + * Open NIC + * + * @v netdev Net device + * @ret rc Return status code + */ +static int snpnet_open ( struct net_device *netdev ) { + struct snpnet_device *snpnetdev = netdev->priv; + EFI_SIMPLE_NETWORK_PROTOCOL *snp = snpnetdev->snp; + EFI_STATUS efirc; + UINT32 enableFlags, disableFlags; + + snpnetdev->close_state = snp->Mode->State; + if ( snp->Mode->State != EfiSimpleNetworkInitialized ) { + efirc = snp->Initialize ( snp, 0, 0 ); + if ( efirc ) { + DBGC ( snp, "SNP %p could not initialize: %s\n", + snp, efi_strerror ( efirc ) ); + return EFIRC_TO_RC ( efirc ); + } + } + + /* Use the default MAC address */ + efirc = snp->StationAddress ( snp, FALSE, + (EFI_MAC_ADDRESS *)netdev->ll_addr ); + if ( efirc ) { + DBGC ( snp, "SNP %p could not reset station address: %s\n", + snp, efi_strerror ( efirc ) ); + } + + /* Set up receive filters to receive unicast and broadcast packets + * always. Also, enable either promiscuous multicast (if possible) or + * promiscuous operation, in order to catch all multicast packets. + */ + enableFlags = snp->Mode->ReceiveFilterMask & + ( EFI_SIMPLE_NETWORK_RECEIVE_UNICAST | + EFI_SIMPLE_NETWORK_RECEIVE_BROADCAST ); + disableFlags = snp->Mode->ReceiveFilterMask & + ( EFI_SIMPLE_NETWORK_RECEIVE_MULTICAST | + EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS | + EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS_MULTICAST ); + if ( snp->Mode->ReceiveFilterMask & + EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS_MULTICAST ) { + enableFlags |= EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS_MULTICAST; + } else if ( snp->Mode->ReceiveFilterMask & + EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS ) { + enableFlags |= EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS; + } + disableFlags &= ~enableFlags; + efirc = snp->ReceiveFilters ( snp, enableFlags, disableFlags, + FALSE, 0, NULL ); + if ( efirc ) { + DBGC ( snp, "SNP %p could not set receive filters: %s\n", + snp, efi_strerror ( efirc ) ); + } + + DBGC ( snp, "SNP %p opened\n", snp ); + return 0; +} + +/** + * Close NIC + * + * @v netdev Net device + */ +static void snpnet_close ( struct net_device *netdev ) { + struct snpnet_device *snpnetdev = netdev->priv; + EFI_SIMPLE_NETWORK_PROTOCOL *snp = snpnetdev->snp; + EFI_STATUS efirc; + + if ( snpnetdev->close_state != EfiSimpleNetworkInitialized ) { + efirc = snp->Shutdown ( snp ); + if ( efirc ) { + DBGC ( snp, "SNP %p could not shut down: %s\n", + snp, efi_strerror ( efirc ) ); + } + } +} + +/** + * Enable/disable interrupts + * + * @v netdev Net device + * @v enable Interrupts should be enabled + */ +static void snpnet_irq ( struct net_device *netdev, int enable ) { + struct snpnet_device *snpnetdev = netdev->priv; + EFI_SIMPLE_NETWORK_PROTOCOL *snp = snpnetdev->snp; + + /* On EFI, interrupts are never necessary. (This function is only + * required for BIOS PXE.) If interrupts were required, they could be + * simulated using a fast timer. + */ + DBGC ( snp, "SNP %p cannot %s interrupts\n", + snp, ( enable ? "enable" : "disable" ) ); +} + +/** SNP network device operations */ +static struct net_device_operations snpnet_operations = { + .open = snpnet_open, + .close = snpnet_close, + .transmit = snpnet_transmit, + .poll = snpnet_poll, + .irq = snpnet_irq, +}; + +/** + * Probe SNP device + * + * @v snpdev SNP device + * @ret rc Return status code + */ +int snpnet_probe ( struct snp_device *snpdev ) { + EFI_SIMPLE_NETWORK_PROTOCOL *snp = snpdev->snp; + EFI_STATUS efirc; + struct net_device *netdev; + struct snpnet_device *snpnetdev; + int rc; + + DBGC ( snp, "SNP %p probing...\n", snp ); + + /* Allocate net device */ + netdev = alloc_etherdev ( sizeof ( struct snpnet_device ) ); + if ( ! netdev ) + return -ENOMEM; + netdev_init ( netdev, &snpnet_operations ); + netdev->dev = &snpdev->dev; + snpdev->netdev = netdev; + snpnetdev = netdev->priv; + snpnetdev->snp = snp; + snpdev->removal_state = snp->Mode->State; + + /* Start the interface */ + if ( snp->Mode->State == EfiSimpleNetworkStopped ) { + efirc = snp->Start ( snp ); + if ( efirc ) { + DBGC ( snp, "SNP %p could not start: %s\n", snp, + efi_strerror ( efirc ) ); + rc = EFIRC_TO_RC ( efirc ); + goto err_start; + } + } + + if ( snp->Mode->HwAddressSize > sizeof ( netdev->hw_addr ) ) { + DBGC ( snp, "SNP %p hardware address is too large\n", snp ); + rc = -EINVAL; + goto err_hwaddr; + } + memcpy ( netdev->hw_addr, snp->Mode->PermanentAddress.Addr, + snp->Mode->HwAddressSize ); + + /* Mark as link up; we don't handle link state */ + netdev_link_up ( netdev ); + + /* Register network device */ + if ( ( rc = register_netdev ( netdev ) ) != 0 ) + goto err_register; + + DBGC ( snp, "SNP %p added\n", snp ); + return 0; + +err_register: +err_hwaddr: + if ( snpdev->removal_state == EfiSimpleNetworkStopped ) + snp->Stop ( snp ); + +err_start: + netdev_nullify ( netdev ); + netdev_put ( netdev ); + snpdev->netdev = NULL; + return rc; +} + +/** + * Remove SNP device + * + * @v snpdev SNP device + */ +void snpnet_remove ( struct snp_device *snpdev ) { + EFI_SIMPLE_NETWORK_PROTOCOL *snp = snpdev->snp; + EFI_STATUS efirc; + struct net_device *netdev = snpdev->netdev; + + if ( snp->Mode->State == EfiSimpleNetworkInitialized && + snpdev->removal_state != EfiSimpleNetworkInitialized ) { + DBGC ( snp, "SNP %p shutting down\n", snp ); + efirc = snp->Shutdown ( snp ); + if ( efirc ) { + DBGC ( snp, "SNP %p could not shut down: %s\n", + snp, efi_strerror ( efirc ) ); + } + } + + if ( snp->Mode->State == EfiSimpleNetworkStarted && + snpdev->removal_state == EfiSimpleNetworkStopped ) { + DBGC ( snp, "SNP %p stopping\n", snp ); + efirc = snp->Stop ( snp ); + if ( efirc ) { + DBGC ( snp, "SNP %p could not be stopped\n", snp ); + } + } + + /* Unregister net device */ + unregister_netdev ( netdev ); + + /* Free network device */ + netdev_nullify ( netdev ); + netdev_put ( netdev ); + + DBGC ( snp, "SNP %p removed\n", snp ); +} diff --git a/src/drivers/net/efi/snpnet.h b/src/drivers/net/efi/snpnet.h new file mode 100644 index 00000000..72b4a7d6 --- /dev/null +++ b/src/drivers/net/efi/snpnet.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2010 VMware, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef _SNPNET_H +#define _SNPNET_H + +/** @file + * + * EFI Simple Network Protocol network device driver + * + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +struct snp_device; + +extern int snpnet_probe ( struct snp_device *snpdev ); +extern void snpnet_remove ( struct snp_device *snpdev ); + +#endif /* _SNPNET_H */ diff --git a/src/drivers/net/efi/snponly.c b/src/drivers/net/efi/snponly.c new file mode 100644 index 00000000..435ed4fb --- /dev/null +++ b/src/drivers/net/efi/snponly.c @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2010 VMware, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include <string.h> +#include <errno.h> +#include <ipxe/device.h> +#include <ipxe/init.h> +#include <ipxe/efi/efi.h> +#include <ipxe/efi/Protocol/SimpleNetwork.h> +#include "snp.h" +#include "snpnet.h" + +/** @file + * + * Chain-loading Simple Network Protocol Bus Driver + * + * This bus driver allows iPXE to use the EFI Simple Network Protocol provided + * by the platform to transmit and receive packets. It attaches to only the + * device handle that iPXE was loaded from, that is, it will only use the + * Simple Network Protocol on the current loaded image's device handle. + * + * Eseentially, this driver provides the EFI equivalent of the "undionly" + * driver. + */ + +/** The one and only SNP network device */ +static struct snp_device snponly_dev; + +/** EFI simple network protocol GUID */ +static EFI_GUID efi_simple_network_protocol_guid + = EFI_SIMPLE_NETWORK_PROTOCOL_GUID; + +/** + * Probe SNP root bus + * + * @v rootdev SNP bus root device + * + * Look at the loaded image's device handle and see if the simple network + * protocol exists. If so, register a driver for it. + */ +static int snpbus_probe ( struct root_device *rootdev ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + EFI_STATUS efirc; + int rc; + void *snp; + + efirc = bs->OpenProtocol ( efi_loaded_image->DeviceHandle, + &efi_simple_network_protocol_guid, + &snp, efi_image_handle, NULL, + EFI_OPEN_PROTOCOL_GET_PROTOCOL ); + if ( efirc ) { + DBG ( "Could not find Simple Network Protocol!\n" ); + return -ENODEV; + } + snponly_dev.snp = snp; + + /* Add to device hierarchy */ + strncpy ( snponly_dev.dev.name, "EFI SNP", + ( sizeof ( snponly_dev.dev.name ) - 1 ) ); + snponly_dev.dev.parent = &rootdev->dev; + list_add ( &snponly_dev.dev.siblings, &rootdev->dev.children); + INIT_LIST_HEAD ( &snponly_dev.dev.children ); + + /* Create network device */ + if ( ( rc = snpnet_probe ( &snponly_dev ) ) != 0 ) + goto err; + + return 0; + +err: + list_del ( &snponly_dev.dev.siblings ); + return rc; +} + +/** + * Remove SNP root bus + * + * @v rootdev SNP bus root device + */ +static void snpbus_remove ( struct root_device *rootdev __unused ) { + snpnet_remove ( &snponly_dev ); + list_del ( &snponly_dev.dev.siblings ); +} + +/** SNP bus root device driver */ +static struct root_driver snp_root_driver = { + .probe = snpbus_probe, + .remove = snpbus_remove, +}; + +/** SNP bus root device */ +struct root_device snp_root_device __root_device = { + .dev = { .name = "EFI SNP" }, + .driver = &snp_root_driver, +}; + +/** + * Prepare for exit + * + * @v flags Shutdown flags + */ +static void snponly_shutdown ( int flags ) { + /* If we are shutting down to boot an OS, make sure the SNP does not + * stay active. + */ + if ( flags & SHUTDOWN_BOOT ) + snponly_dev.removal_state = EfiSimpleNetworkStopped; +} + +struct startup_fn startup_snponly __startup_fn ( STARTUP_LATE ) = { + .shutdown = snponly_shutdown, +}; diff --git a/src/image/efi_image.c b/src/image/efi_image.c index 6eb943b6..6b6600de 100644 --- a/src/image/efi_image.c +++ b/src/image/efi_image.c @@ -21,12 +21,27 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include <errno.h> #include <ipxe/efi/efi.h> #include <ipxe/image.h> +#include <ipxe/init.h> #include <ipxe/features.h> FEATURE ( FEATURE_IMAGE, "EFI", DHCP_EB_FEATURE_EFI, 1 ); struct image_type efi_image_type __image_type ( PROBE_NORMAL ); +/** Event used to signal shutdown */ +static EFI_EVENT efi_shutdown_event; + +/** + * Shut down in preparation for booting an OS. + * + * This hook gets called at ExitBootServices time in order to make sure that + * the network cards are properly shut down before the OS takes over. + */ +static EFIAPI void efi_shutdown_hook ( EFI_EVENT event __unused, + void *context __unused ) { + shutdown ( SHUTDOWN_BOOT ); +} + /** * Execute EFI image * @@ -39,6 +54,7 @@ static int efi_image_exec ( struct image *image ) { UINTN exit_data_size; CHAR16 *exit_data; EFI_STATUS efirc; + int rc; /* Attempt loading image */ if ( ( efirc = bs->LoadImage ( FALSE, efi_image_handle, NULL, @@ -50,21 +66,36 @@ static int efi_image_exec ( struct image *image ) { return -ENOEXEC; } + /* Be sure to shut down the NIC at ExitBootServices time, or else + * DMA from the card can corrupt the OS. + */ + efirc = bs->CreateEvent ( EVT_SIGNAL_EXIT_BOOT_SERVICES, + TPL_CALLBACK, efi_shutdown_hook, + NULL, &efi_shutdown_event ); + if ( efirc ) { + rc = EFIRC_TO_RC ( efirc ); + goto done; + } + /* Start the image */ if ( ( efirc = bs->StartImage ( handle, &exit_data_size, &exit_data ) ) != 0 ) { DBGC ( image, "EFIIMAGE %p returned with status %s\n", image, efi_strerror ( efirc ) ); - goto done; } - done: + rc = EFIRC_TO_RC ( efirc ); + + /* Remove the shutdown hook */ + bs->CloseEvent ( efi_shutdown_event ); + +done: /* Unload the image. We can't leave it loaded, because we * have no "unload" operation. */ bs->UnloadImage ( handle ); - return EFIRC_TO_RC ( efirc ); + return rc; } /** diff --git a/src/include/ipxe/efi/Protocol/LoadedImage.h b/src/include/ipxe/efi/Protocol/LoadedImage.h new file mode 100755 index 00000000..12e5e2d7 --- /dev/null +++ b/src/include/ipxe/efi/Protocol/LoadedImage.h @@ -0,0 +1,88 @@ +/** @file + UEFI 2.0 Loaded image protocol definition. + + Every EFI driver and application is passed an image handle when it is loaded. + This image handle will contain a Loaded Image Protocol. + + Copyright (c) 2006 - 2008, Intel Corporation. All rights reserved.<BR> + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef __LOADED_IMAGE_PROTOCOL_H__ +#define __LOADED_IMAGE_PROTOCOL_H__ + +#define EFI_LOADED_IMAGE_PROTOCOL_GUID \ + { \ + 0x5B1B31A1, 0x9562, 0x11d2, {0x8E, 0x3F, 0x00, 0xA0, 0xC9, 0x69, 0x72, 0x3B } \ + } + +#define EFI_LOADED_IMAGE_DEVICE_PATH_PROTOCOL_GUID \ + { \ + 0xbc62157e, 0x3e33, 0x4fec, {0x99, 0x20, 0x2d, 0x3b, 0x36, 0xd7, 0x50, 0xdf } \ + } + +/// +/// Protocol GUID defined in EFI1.1. +/// +#define LOADED_IMAGE_PROTOCOL EFI_LOADED_IMAGE_PROTOCOL_GUID + +/// +/// EFI_SYSTEM_TABLE & EFI_IMAGE_UNLOAD are defined in EfiApi.h +/// +#define EFI_LOADED_IMAGE_PROTOCOL_REVISION 0x1000 + +/// +/// Revision defined in EFI1.1. +/// +#define EFI_LOADED_IMAGE_INFORMATION_REVISION EFI_LOADED_IMAGE_PROTOCOL_REVISION + +/// +/// Can be used on any image handle to obtain information about the loaded image. +/// +typedef struct { + UINT32 Revision; ///< Defines the revision of the EFI_LOADED_IMAGE_PROTOCOL structure. + ///< All future revisions will be backward compatible to the current revision. + EFI_HANDLE ParentHandle; ///< Parent image's image handle. NULL if the image is loaded directly from + ///< the firmware's boot manager. + EFI_SYSTEM_TABLE *SystemTable; ///< the image's EFI system table pointer. + + // + // Source location of image + // + EFI_HANDLE DeviceHandle; ///< The device handle that the EFI Image was loaded from. + EFI_DEVICE_PATH_PROTOCOL *FilePath; ///< A pointer to the file path portion specific to DeviceHandle + ///< that the EFI Image was loaded from. + VOID *Reserved; ///< Reserved. DO NOT USE. + + // + // Images load options + // + UINT32 LoadOptionsSize;///< The size in bytes of LoadOptions. + VOID *LoadOptions; ///< A pointer to the image's binary load options. + + // + // Location of where image was loaded + // + VOID *ImageBase; ///< The base address at which the image was loaded. + UINT64 ImageSize; ///< The size in bytes of the loaded image. + EFI_MEMORY_TYPE ImageCodeType; ///< The memory type that the code sections were loaded as. + EFI_MEMORY_TYPE ImageDataType; ///< The memory type that the data sections were loaded as. + EFI_IMAGE_UNLOAD Unload; +} EFI_LOADED_IMAGE_PROTOCOL; + +// +// For backward-compatible with EFI1.1. +// +typedef EFI_LOADED_IMAGE_PROTOCOL EFI_LOADED_IMAGE; + +extern EFI_GUID gEfiLoadedImageProtocolGuid; +extern EFI_GUID gEfiLoadedImageDevicePathProtocolGuid; + +#endif diff --git a/src/include/ipxe/efi/efi.h b/src/include/ipxe/efi/efi.h index 639d7990..6dca1324 100644 --- a/src/include/ipxe/efi/efi.h +++ b/src/include/ipxe/efi/efi.h @@ -42,6 +42,7 @@ /* Include the top-level EFI header files */ #include <ipxe/efi/Uefi.h> #include <ipxe/efi/PiDxe.h> +#include <ipxe/efi/Protocol/LoadedImage.h> /* Reset any trailing #pragma pack directives */ #pragma pack(1) @@ -135,6 +136,7 @@ struct efi_config_table { #define EFIRC_TO_RC( efirc ) (efirc) extern EFI_HANDLE efi_image_handle; +extern EFI_LOADED_IMAGE_PROTOCOL *efi_loaded_image; extern EFI_SYSTEM_TABLE *efi_systab; extern const char * efi_strerror ( EFI_STATUS efirc ); diff --git a/src/include/ipxe/errfile.h b/src/include/ipxe/errfile.h index 2bb655fd..b8a8a8cc 100644 --- a/src/include/ipxe/errfile.h +++ b/src/include/ipxe/errfile.h @@ -123,6 +123,8 @@ FILE_LICENCE ( GPL2_OR_LATER ); #define ERRFILE_vxge_config ( ERRFILE_DRIVER | 0x00560000 ) #define ERRFILE_vxge_traffic ( ERRFILE_DRIVER | 0x00570000 ) #define ERRFILE_igb_main ( ERRFILE_DRIVER | 0x00580000 ) +#define ERRFILE_snpnet ( ERRFILE_DRIVER | 0x00590000 ) +#define ERRFILE_snponly ( ERRFILE_DRIVER | 0x005a0000 ) #define ERRFILE_scsi ( ERRFILE_DRIVER | 0x00700000 ) #define ERRFILE_arbel ( ERRFILE_DRIVER | 0x00710000 ) diff --git a/src/interface/efi/efi_init.c b/src/interface/efi/efi_init.c index ed659932..029bc06f 100644 --- a/src/interface/efi/efi_init.c +++ b/src/interface/efi/efi_init.c @@ -20,14 +20,22 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include <string.h> #include <ipxe/efi/efi.h> +#include <ipxe/efi/Protocol/LoadedImage.h> #include <ipxe/uuid.h> /** Image handle passed to entry point */ EFI_HANDLE efi_image_handle; +/** Loaded image protocol for this image */ +EFI_LOADED_IMAGE_PROTOCOL *efi_loaded_image; + /** System table passed to entry point */ EFI_SYSTEM_TABLE *efi_systab; +/** EFI loaded image protocol GUID */ +static EFI_GUID efi_loaded_image_protocol_guid + = EFI_LOADED_IMAGE_PROTOCOL_GUID; + /** * Look up EFI configuration table * @@ -59,6 +67,7 @@ EFI_STATUS efi_init ( EFI_HANDLE image_handle, struct efi_protocol *prot; struct efi_config_table *tab; EFI_STATUS efirc; + void *loaded_image; /* Store image handle and system table pointer for future use */ efi_image_handle = image_handle; @@ -80,8 +89,20 @@ EFI_STATUS efi_init ( EFI_HANDLE image_handle, } DBGC ( systab, "EFI handle %p systab %p\n", image_handle, systab ); - /* Look up used protocols */ bs = systab->BootServices; + efirc = bs->OpenProtocol ( image_handle, + &efi_loaded_image_protocol_guid, + &loaded_image, image_handle, NULL, + EFI_OPEN_PROTOCOL_GET_PROTOCOL ); + if ( efirc ) { + DBGC ( systab, "Could not get loaded image protocol" ); + return efirc; + } + + efi_loaded_image = loaded_image; + DBG ( "Image base address = %p\n", efi_loaded_image->ImageBase ); + + /* Look up used protocols */ for_each_table_entry ( prot, EFI_PROTOCOLS ) { if ( ( efirc = bs->LocateProtocol ( &prot->u.guid, NULL, prot->protocol ) ) == 0 ) { |