summaryrefslogtreecommitdiffstats
path: root/src/interface/efi
diff options
context:
space:
mode:
authorMichael Brown2017-03-27 17:20:34 +0200
committerMichael Brown2017-03-28 18:12:48 +0200
commit7cfdd769aac76d605aa31146c69ba518b194bea7 (patch)
tree7c09e144792833f81297e6dacf0823733a2a399a /src/interface/efi
parent[block] Ignore redundant xfer_window_changed() messages (diff)
downloadipxe-7cfdd769aac76d605aa31146c69ba518b194bea7.tar.gz
ipxe-7cfdd769aac76d605aa31146c69ba518b194bea7.tar.xz
ipxe-7cfdd769aac76d605aa31146c69ba518b194bea7.zip
[block] Describe all SAN devices via ACPI tables
Describe all SAN devices via ACPI tables such as the iBFT. For tables that can describe only a single device (i.e. the aBFT and sBFT), one table is installed per device. For multi-device tables (i.e. the iBFT), all devices are described in a single table. An underlying SAN device connection may be closed at the time that we need to construct an ACPI table. We therefore introduce the concept of an "ACPI descriptor" which enables the SAN boot code to maintain an opaque pointer to the underlying object, and an "ACPI model" which can build tables from a list of such descriptors. This separates the lifecycles of ACPI descriptions from the lifecycles of the block device interfaces, and allows for construction of the ACPI tables even if the block device interface has been closed. For a multipath SAN device, iPXE will wait until sufficient information is available to describe all devices but will not wait for all paths to connect successfully. For example: with a multipath iSCSI boot iPXE will wait until at least one path has become available and name resolution has completed on all other paths. We do this since the iBFT has to include IP addresses rather than DNS names. We will commence booting without waiting for the inactive paths to either become available or close; this avoids unnecessary boot delays. Note that the Linux kernel will refuse to accept an iBFT with more than two NIC or target structures. We therefore describe only the NICs that are actually required in order to reach the described targets. Any iBFT with at most two targets is therefore guaranteed to describe at most two NICs. Signed-off-by: Michael Brown <mcb30@ipxe.org>
Diffstat (limited to 'src/interface/efi')
-rw-r--r--src/interface/efi/efi_block.c148
1 files changed, 84 insertions, 64 deletions
diff --git a/src/interface/efi/efi_block.c b/src/interface/efi/efi_block.c
index 9679fc0d..fa2271b3 100644
--- a/src/interface/efi/efi_block.c
+++ b/src/interface/efi/efi_block.c
@@ -254,10 +254,11 @@ static void efi_block_connect ( struct san_device *sandev ) {
* @v drive Drive number
* @v uris List of URIs
* @v count Number of URIs
+ * @v flags Flags
* @ret drive Drive number, or negative error
*/
static int efi_block_hook ( unsigned int drive, struct uri **uris,
- unsigned int count ) {
+ unsigned int count, unsigned int flags ) {
EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
EFI_DEVICE_PATH_PROTOCOL *end;
struct efi_block_vendor_path *vendor;
@@ -301,7 +302,6 @@ static int efi_block_hook ( unsigned int drive, struct uri **uris,
rc = -ENOMEM;
goto err_alloc;
}
- sandev->drive = drive;
block = sandev->priv;
block->sandev = sandev;
block->media.MediaPresent = 1;
@@ -331,12 +331,12 @@ static int efi_block_hook ( unsigned int drive, struct uri **uris,
end->SubType = END_ENTIRE_DEVICE_PATH_SUBTYPE;
end->Length[0] = sizeof ( *end );
DBGC ( sandev, "EFIBLK %#02x has device path %s\n",
- sandev->drive, efi_devpath_text ( block->path ) );
+ drive, efi_devpath_text ( block->path ) );
/* Register SAN device */
- if ( ( rc = register_sandev ( sandev ) ) != 0 ) {
+ if ( ( rc = register_sandev ( sandev, drive, flags ) ) != 0 ) {
DBGC ( sandev, "EFIBLK %#02x could not register: %s\n",
- sandev->drive, strerror ( rc ) );
+ drive, strerror ( rc ) );
goto err_register;
}
@@ -408,85 +408,105 @@ static void efi_block_unhook ( unsigned int drive ) {
sandev_put ( sandev );
}
+/** An installed ACPI table */
+struct efi_acpi_table {
+ /** List of installed tables */
+ struct list_head list;
+ /** Table key */
+ UINTN key;
+};
+
+/** List of installed ACPI tables */
+static LIST_HEAD ( efi_acpi_tables );
+
/**
- * Describe EFI block device
+ * Install ACPI table
*
- * @v drive Drive number
+ * @v hdr ACPI description header
* @ret rc Return status code
*/
-static int efi_block_describe ( unsigned int drive ) {
- static union {
- /** ACPI header */
- struct acpi_description_header acpi;
- /** Padding */
- char pad[768];
- } xbftab;
- static UINTN key;
- struct san_device *sandev;
- struct san_path *sanpath;
- size_t len;
+static int efi_block_install ( struct acpi_header *hdr ) {
+ size_t len = le32_to_cpu ( hdr->length );
+ struct efi_acpi_table *installed;
EFI_STATUS efirc;
int rc;
- /* Find SAN device */
- sandev = sandev_find ( drive );
- if ( ! sandev ) {
- DBG ( "EFIBLK cannot find drive %#02x\n", drive );
- return -ENODEV;
+ /* Allocate installed table record */
+ installed = zalloc ( sizeof ( *installed ) );
+ if ( ! installed ) {
+ rc = -ENOMEM;
+ goto err_alloc;
}
+ /* Fill in common parameters */
+ strncpy ( hdr->oem_id, "FENSYS", sizeof ( hdr->oem_id ) );
+ strncpy ( hdr->oem_table_id, "iPXE", sizeof ( hdr->oem_table_id ) );
+
+ /* Fix up ACPI checksum */
+ acpi_fix_checksum ( hdr );
+
+ /* Install table */
+ if ( ( efirc = acpi->InstallAcpiTable ( acpi, hdr, len,
+ &installed->key ) ) != 0 ){
+ rc = -EEFI ( efirc );
+ DBGC ( acpi, "EFIBLK could not install %s: %s\n",
+ acpi_name ( hdr->signature ), strerror ( rc ) );
+ DBGC_HDA ( acpi, 0, hdr, len );
+ goto err_install;
+ }
+
+ /* Add to list of installed tables */
+ list_add_tail ( &installed->list, &efi_acpi_tables );
+
+ DBGC ( acpi, "EFIBLK installed %s as ACPI table %#lx:\n",
+ acpi_name ( hdr->signature ),
+ ( ( unsigned long ) installed->key ) );
+ DBGC_HDA ( acpi, 0, hdr, len );
+ return 0;
+
+ list_del ( &installed->list );
+ err_install:
+ free ( installed );
+ err_alloc:
+ return rc;
+}
+
+/**
+ * Describe EFI block devices
+ *
+ * @ret rc Return status code
+ */
+static int efi_block_describe ( void ) {
+ struct efi_acpi_table *installed;
+ struct efi_acpi_table *tmp;
+ UINTN key;
+ EFI_STATUS efirc;
+ int rc;
+
/* Sanity check */
if ( ! acpi ) {
- DBGC ( sandev, "EFIBLK %#02x has no ACPI table protocol\n",
- sandev->drive );
+ DBG ( "EFIBLK has no ACPI table protocol\n" );
return -ENOTSUP;
}
- /* Remove existing table, if any */
- if ( key ) {
+ /* Uninstall any existing ACPI tables */
+ list_for_each_entry_safe ( installed, tmp, &efi_acpi_tables, list ) {
+ key = installed->key;
if ( ( efirc = acpi->UninstallAcpiTable ( acpi, key ) ) != 0 ) {
rc = -EEFI ( efirc );
- DBGC ( sandev, "EFIBLK %#02x could not uninstall ACPI "
- "table: %s\n", sandev->drive, strerror ( rc ) );
+ DBGC ( acpi, "EFIBLK could not uninstall ACPI table "
+ "%#lx: %s\n", ( ( unsigned long ) key ),
+ strerror ( rc ) );
/* Continue anyway */
}
- key = 0;
- }
-
- /* Reopen block device if necessary */
- if ( sandev_needs_reopen ( sandev ) &&
- ( ( rc = sandev_reopen ( sandev ) ) != 0 ) )
- return rc;
- sanpath = sandev->active;
- assert ( sanpath != NULL );
-
- /* Clear table */
- memset ( &xbftab, 0, sizeof ( xbftab ) );
-
- /* Fill in common parameters */
- strncpy ( xbftab.acpi.oem_id, "FENSYS",
- sizeof ( xbftab.acpi.oem_id ) );
- strncpy ( xbftab.acpi.oem_table_id, "iPXE",
- sizeof ( xbftab.acpi.oem_table_id ) );
-
- /* Fill in remaining parameters */
- if ( ( rc = acpi_describe ( &sanpath->block, &xbftab.acpi,
- sizeof ( xbftab ) ) ) != 0 ) {
- DBGC ( sandev, "EFIBLK %#02x could not create ACPI "
- "description: %s\n", sandev->drive, strerror ( rc ) );
- return rc;
+ list_del ( &installed->list );
+ free ( installed );
}
- len = le32_to_cpu ( xbftab.acpi.length );
-
- /* Fix up ACPI checksum */
- acpi_fix_checksum ( &xbftab.acpi );
- /* Install table */
- if ( ( efirc = acpi->InstallAcpiTable ( acpi, &xbftab, len,
- &key ) ) != 0 ) {
- rc = -EEFI ( efirc );
- DBGC ( sandev, "EFIBLK %#02x could not install ACPI table: "
- "%s\n", sandev->drive, strerror ( rc ) );
+ /* Install ACPI tables */
+ if ( ( rc = acpi_install ( efi_block_install ) ) != 0 ) {
+ DBGC ( acpi, "EFIBLK could not install ACPI tables: %s\n",
+ strerror ( rc ) );
return rc;
}