summaryrefslogtreecommitdiffstats
path: root/src/core
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/core
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/core')
-rw-r--r--src/core/acpi.c85
-rw-r--r--src/core/dummy_sanboot.c25
-rw-r--r--src/core/null_sanboot.c5
-rw-r--r--src/core/sanboot.c101
4 files changed, 173 insertions, 43 deletions
diff --git a/src/core/acpi.c b/src/core/acpi.c
index 955637e0..8ebe4b19 100644
--- a/src/core/acpi.c
+++ b/src/core/acpi.c
@@ -43,27 +43,11 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
*/
/**
- * Transcribe ACPI table signature (for debugging)
- *
- * @v signature ACPI table signature
- * @ret name ACPI table signature name
- */
-static const char * acpi_name ( uint32_t signature ) {
- static union {
- uint32_t signature;
- char name[5];
- } u;
-
- u.signature = cpu_to_le32 ( signature );
- return u.name;
-}
-
-/**
* Fix up ACPI table checksum
*
* @v acpi ACPI table header
*/
-void acpi_fix_checksum ( struct acpi_description_header *acpi ) {
+void acpi_fix_checksum ( struct acpi_header *acpi ) {
unsigned int i = 0;
uint8_t sum = 0;
@@ -147,7 +131,7 @@ userptr_t acpi_find_rsdt ( userptr_t ebda ) {
* @ret table Table, or UNULL if not found
*/
userptr_t acpi_find ( userptr_t rsdt, uint32_t signature, unsigned int index ) {
- struct acpi_description_header acpi;
+ struct acpi_header acpi;
struct acpi_rsdt *rsdtab;
typeof ( rsdtab->entry[0] ) entry;
userptr_t table;
@@ -227,7 +211,7 @@ userptr_t acpi_find ( userptr_t rsdt, uint32_t signature, unsigned int index ) {
* the ACPI specification itself.
*/
static int acpi_sx_zsdt ( userptr_t zsdt, uint32_t signature ) {
- struct acpi_description_header acpi;
+ struct acpi_header acpi;
union {
uint32_t dword;
uint8_t byte[4];
@@ -331,34 +315,73 @@ int acpi_sx ( userptr_t rsdt, uint32_t signature ) {
/******************************************************************************
*
- * Interface methods
+ * Descriptors
*
******************************************************************************
*/
/**
- * Describe object in an ACPI table
+ * Add ACPI descriptor
+ *
+ * @v desc ACPI descriptor
+ */
+void acpi_add ( struct acpi_descriptor *desc ) {
+
+ /* Add to list of descriptors */
+ ref_get ( desc->refcnt );
+ list_add_tail ( &desc->list, &desc->model->descs );
+}
+
+/**
+ * Remove ACPI descriptor
+ *
+ * @v desc ACPI descriptor
+ */
+void acpi_del ( struct acpi_descriptor *desc ) {
+
+ /* Remove from list of descriptors */
+ list_check_contains_entry ( desc, &desc->model->descs, list );
+ list_del ( &desc->list );
+ ref_put ( desc->refcnt );
+}
+
+/**
+ * Get object's ACPI descriptor
*
* @v intf Interface
- * @v acpi ACPI table
- * @v len Length of ACPI table
- * @ret rc Return status code
+ * @ret desc ACPI descriptor, or NULL
*/
-int acpi_describe ( struct interface *intf,
- struct acpi_description_header *acpi, size_t len ) {
+struct acpi_descriptor * acpi_describe ( struct interface *intf ) {
struct interface *dest;
acpi_describe_TYPE ( void * ) *op =
intf_get_dest_op ( intf, acpi_describe, &dest );
void *object = intf_object ( dest );
- int rc;
+ struct acpi_descriptor *desc;
if ( op ) {
- rc = op ( object, acpi, len );
+ desc = op ( object );
} else {
- /* Default is to fail to describe */
- rc = -EOPNOTSUPP;
+ desc = NULL;
}
intf_put ( dest );
- return rc;
+ return desc;
+}
+
+/**
+ * Install ACPI tables
+ *
+ * @v install Table installation method
+ * @ret rc Return status code
+ */
+int acpi_install ( int ( * install ) ( struct acpi_header *acpi ) ){
+ struct acpi_model *model;
+ int rc;
+
+ for_each_table_entry ( model, ACPI_MODELS ) {
+ if ( ( rc = model->install ( install ) ) != 0 )
+ return rc;
+ }
+
+ return 0;
}
diff --git a/src/core/dummy_sanboot.c b/src/core/dummy_sanboot.c
index 64d5206f..08180852 100644
--- a/src/core/dummy_sanboot.c
+++ b/src/core/dummy_sanboot.c
@@ -38,10 +38,11 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
* @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 dummy_san_hook ( unsigned int drive, struct uri **uris,
- unsigned int count ) {
+ unsigned int count, unsigned int flags ) {
struct san_device *sandev;
int rc;
@@ -51,10 +52,9 @@ static int dummy_san_hook ( unsigned int drive, struct uri **uris,
rc = -ENOMEM;
goto err_alloc;
}
- sandev->drive = drive;
/* Register SAN device */
- if ( ( rc = register_sandev ( sandev ) ) != 0 ) {
+ if ( ( rc = register_sandev ( sandev, drive, flags ) ) != 0 ) {
DBGC ( sandev, "SAN %#02x could not register: %s\n",
sandev->drive, strerror ( rc ) );
goto err_register;
@@ -102,15 +102,28 @@ static int dummy_san_boot ( unsigned int drive __unused ) {
}
/**
- * Describe dummy SAN device
+ * Install ACPI table
*
- * @v drive Drive number
+ * @v acpi ACPI description header
+ * @ret rc Return status code
*/
-static int dummy_san_describe ( unsigned int drive __unused ) {
+static int dummy_install ( struct acpi_header *acpi ) {
+ DBGC ( acpi, "ACPI table %s:\n", acpi_name ( acpi->signature ) );
+ DBGC_HDA ( acpi, 0, acpi, le32_to_cpu ( acpi->length ) );
return 0;
}
+/**
+ * Describe dummy SAN device
+ *
+ * @ret rc Return status code
+ */
+static int dummy_san_describe ( void ) {
+
+ return acpi_install ( dummy_install );
+}
+
PROVIDE_SANBOOT ( dummy, san_hook, dummy_san_hook );
PROVIDE_SANBOOT ( dummy, san_unhook, dummy_san_unhook );
PROVIDE_SANBOOT ( dummy, san_boot, dummy_san_boot );
diff --git a/src/core/null_sanboot.c b/src/core/null_sanboot.c
index 42fb0682..b09562e2 100644
--- a/src/core/null_sanboot.c
+++ b/src/core/null_sanboot.c
@@ -28,7 +28,8 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
static int null_san_hook ( unsigned int drive __unused,
struct uri **uris __unused,
- unsigned int count __unused ) {
+ unsigned int count __unused,
+ unsigned int flags __unused ) {
return -EOPNOTSUPP;
}
@@ -40,7 +41,7 @@ static int null_san_boot ( unsigned int drive __unused ) {
return -EOPNOTSUPP;
}
-static int null_san_describe ( unsigned int drive __unused ) {
+static int null_san_describe ( void ) {
return -EOPNOTSUPP;
}
diff --git a/src/core/sanboot.c b/src/core/sanboot.c
index 03beae79..f134f76a 100644
--- a/src/core/sanboot.c
+++ b/src/core/sanboot.c
@@ -119,8 +119,10 @@ static void sandev_free ( struct refcnt *refcnt ) {
assert ( ! timer_running ( &sandev->timer ) );
assert ( ! sandev->active );
assert ( list_empty ( &sandev->opened ) );
- for ( i = 0 ; i < sandev->paths ; i++ )
+ for ( i = 0 ; i < sandev->paths ; i++ ) {
uri_put ( sandev->path[i].uri );
+ assert ( sandev->path[i].desc == NULL );
+ }
free ( sandev );
}
@@ -199,6 +201,15 @@ static int sanpath_open ( struct san_path *sanpath ) {
return rc;
}
+ /* Update ACPI descriptor, if applicable */
+ if ( ! ( sandev->flags & SAN_NO_DESCRIBE ) ) {
+ if ( sanpath->desc )
+ acpi_del ( sanpath->desc );
+ sanpath->desc = acpi_describe ( &sanpath->block );
+ if ( sanpath->desc )
+ acpi_add ( sanpath->desc );
+ }
+
/* Start process */
process_add ( &sanpath->process );
@@ -607,6 +618,72 @@ int sandev_rw ( struct san_device *sandev, uint64_t lba,
}
/**
+ * Describe SAN device
+ *
+ * @v sandev SAN device
+ * @ret rc Return status code
+ *
+ * Allow connections to progress until all existent path descriptors
+ * are complete.
+ */
+static int sandev_describe ( struct san_device *sandev ) {
+ struct san_path *sanpath;
+ struct acpi_descriptor *desc;
+ int rc;
+
+ /* Wait for all paths to be either described or closed */
+ while ( 1 ) {
+
+ /* Allow connections to progress */
+ step();
+
+ /* Fail if any closed path has an incomplete descriptor */
+ list_for_each_entry ( sanpath, &sandev->closed, list ) {
+ desc = sanpath->desc;
+ if ( ! desc )
+ continue;
+ if ( ( rc = desc->model->complete ( desc ) ) != 0 ) {
+ DBGC ( sandev, "SAN %#02x.%d could not be "
+ "described: %s\n", sandev->drive,
+ sanpath->index, strerror ( rc ) );
+ return rc;
+ }
+ }
+
+ /* Succeed if no paths have an incomplete descriptor */
+ rc = 0;
+ list_for_each_entry ( sanpath, &sandev->opened, list ) {
+ desc = sanpath->desc;
+ if ( ! desc )
+ continue;
+ if ( ( rc = desc->model->complete ( desc ) ) != 0 )
+ break;
+ }
+ if ( rc == 0 )
+ return 0;
+ }
+}
+
+/**
+ * Remove SAN device descriptors
+ *
+ * @v sandev SAN device
+ */
+static void sandev_undescribe ( struct san_device *sandev ) {
+ struct san_path *sanpath;
+ unsigned int i;
+
+ /* Remove all ACPI descriptors */
+ for ( i = 0 ; i < sandev->paths ; i++ ) {
+ sanpath = &sandev->path[i];
+ if ( sanpath->desc ) {
+ acpi_del ( sanpath->desc );
+ sanpath->desc = NULL;
+ }
+ }
+}
+
+/**
* Configure SAN device as a CD-ROM, if applicable
*
* @v sandev SAN device
@@ -729,18 +806,25 @@ struct san_device * alloc_sandev ( struct uri **uris, unsigned int count,
* Register SAN device
*
* @v sandev SAN device
+ * @v drive Drive number
+ * @v flags Flags
* @ret rc Return status code
*/
-int register_sandev ( struct san_device *sandev ) {
+int register_sandev ( struct san_device *sandev, unsigned int drive,
+ unsigned int flags ) {
int rc;
/* Check that drive number is not in use */
- if ( sandev_find ( sandev->drive ) != NULL ) {
- DBGC ( sandev, "SAN %#02x is already in use\n", sandev->drive );
+ if ( sandev_find ( drive ) != NULL ) {
+ DBGC ( sandev, "SAN %#02x is already in use\n", drive );
rc = -EADDRINUSE;
goto err_in_use;
}
+ /* Record drive number and flags */
+ sandev->drive = drive;
+ sandev->flags = flags;
+
/* Check that device is capable of being opened (i.e. that all
* URIs are well-formed and that at least one path is
* working).
@@ -748,6 +832,10 @@ int register_sandev ( struct san_device *sandev ) {
if ( ( rc = sandev_reopen ( sandev ) ) != 0 )
goto err_reopen;
+ /* Describe device */
+ if ( ( rc = sandev_describe ( sandev ) ) != 0 )
+ goto err_describe;
+
/* Read device capacity */
if ( ( rc = sandev_command ( sandev, sandev_command_read_capacity,
NULL ) ) != 0 )
@@ -766,8 +854,10 @@ int register_sandev ( struct san_device *sandev ) {
list_del ( &sandev->list );
err_iso9660:
err_capacity:
+ err_describe:
err_reopen:
sandev_restart ( sandev, rc );
+ sandev_undescribe ( sandev );
err_in_use:
return rc;
}
@@ -788,6 +878,9 @@ void unregister_sandev ( struct san_device *sandev ) {
/* Shut down interfaces */
sandev_restart ( sandev, 0 );
+ /* Remove ACPI descriptors */
+ sandev_undescribe ( sandev );
+
DBGC ( sandev, "SAN %#02x unregistered\n", sandev->drive );
}