Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
[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>
  • Loading branch information
mcb30 committed Mar 28, 2017
1 parent 414b4fc commit 7cfdd76
Show file tree
Hide file tree
Showing 21 changed files with 926 additions and 487 deletions.
117 changes: 65 additions & 52 deletions src/arch/x86/interface/pcbios/int13.c
Expand Up @@ -1239,13 +1239,14 @@ static void int13_unhook_vector ( void ) {
* @v drive Drive number
* @v uris List of URIs
* @v count Number of URIs
* @v flags Flags
* @ret drive Drive number, or negative error
*
* Registers the drive with the INT 13 emulation subsystem, and hooks
* the INT 13 interrupt vector (if not already hooked).
*/
static int int13_hook ( unsigned int drive, struct uri **uris,
unsigned int count ) {
unsigned int count, unsigned int flags ) {
struct san_device *sandev;
struct int13_data *int13;
unsigned int natural_drive;
Expand All @@ -1267,14 +1268,13 @@ static int int13_hook ( unsigned int drive, struct uri **uris,
rc = -ENOMEM;
goto err_alloc;
}
sandev->drive = drive;
int13 = sandev->priv;
int13->natural_drive = natural_drive;

/* Register SAN device */
if ( ( rc = register_sandev ( sandev ) ) != 0 ) {
if ( ( rc = register_sandev ( sandev, drive, flags ) ) != 0 ) {
DBGC ( sandev, "INT13 drive %02x could not register: %s\n",
sandev->drive, strerror ( rc ) );
drive, strerror ( rc ) );
goto err_register;
}

Expand Down Expand Up @@ -1544,70 +1544,83 @@ static int int13_boot ( unsigned int drive ) {
return -ECANCELED; /* -EIMPOSSIBLE */
}

/** A boot firmware table generated by iPXE */
union xbft_table {
/** ACPI header */
struct acpi_description_header acpi;
/** Padding */
char pad[768];
};
/** Maximum size of boot firmware table(s) */
#define XBFTAB_SIZE 768

/** Alignment of boot firmware table entries */
#define XBFTAB_ALIGN 16

/** The boot firmware table generated by iPXE */
static union xbft_table __bss16 ( xbftab ) __attribute__ (( aligned ( 16 ) ));
/** The boot firmware table(s) generated by iPXE */
static uint8_t __bss16_array ( xbftab, [XBFTAB_SIZE] )
__attribute__ (( aligned ( XBFTAB_ALIGN ) ));
#define xbftab __use_data16 ( xbftab )

/** Total used length of boot firmware tables */
static size_t xbftab_used;

/**
* Describe SAN device for SAN-booted operating system
* Install ACPI table
*
* @v drive Drive number
* @v acpi ACPI description header
* @ret rc Return status code
*/
static int int13_describe ( unsigned int drive ) {
struct san_device *sandev;
struct san_path *sanpath;
static int int13_install ( struct acpi_header *acpi ) {
struct segoff xbft_address;
int rc;

/* Find drive */
sandev = sandev_find ( drive );
if ( ! sandev ) {
DBG ( "INT13 cannot find drive %02x\n", drive );
return -ENODEV;
struct acpi_header *installed;
size_t len;

/* Check length */
len = acpi->length;
if ( len > ( sizeof ( xbftab ) - xbftab_used ) ) {
DBGC ( acpi, "INT13 out of space for %s table\n",
acpi_name ( acpi->signature ) );
return -ENOSPC;
}

/* Reopen block device if necessary */
if ( sandev_needs_reopen ( sandev ) &&
( ( rc = sandev_reopen ( sandev ) ) != 0 ) )
return rc;
sanpath = sandev->active;
assert ( sanpath != NULL );
/* Install table */
installed = ( ( ( void * ) xbftab ) + xbftab_used );
memcpy ( installed, acpi, len );
xbft_address.segment = rm_ds;
xbft_address.offset = __from_data16 ( installed );

/* Fill in common parameters */
strncpy ( installed->oem_id, "FENSYS",
sizeof ( installed->oem_id ) );
strncpy ( installed->oem_table_id, "iPXE",
sizeof ( installed->oem_table_id ) );

/* Fix checksum */
acpi_fix_checksum ( installed );

/* Update used length */
xbftab_used = ( ( xbftab_used + len + XBFTAB_ALIGN - 1 ) &
~( XBFTAB_ALIGN - 1 ) );

DBGC ( acpi, "INT13 installed %s:\n",
acpi_name ( installed->signature ) );
DBGC_HDA ( acpi, xbft_address, installed, len );
return 0;
}

/* Clear table */
/**
* Describe SAN devices for SAN-booted operating system
*
* @ret rc Return status code
*/
static int int13_describe ( void ) {
int rc;

/* Clear tables */
memset ( &xbftab, 0, sizeof ( xbftab ) );
xbftab_used = 0;

/* 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, "INT13 drive %02x could not create ACPI "
"description: %s\n", sandev->drive, strerror ( rc ) );
/* Install ACPI tables */
if ( ( rc = acpi_install ( int13_install ) ) != 0 ) {
DBG ( "INT13 could not install ACPI tables: %s\n",
strerror ( rc ) );
return rc;
}

/* Fix up ACPI checksum */
acpi_fix_checksum ( &xbftab.acpi );
xbft_address.segment = rm_ds;
xbft_address.offset = __from_data16 ( &xbftab );
DBGC ( sandev, "INT13 drive %02x described using boot firmware "
"table:\n", sandev->drive );
DBGC_HDA ( sandev, xbft_address, &xbftab,
le32_to_cpu ( xbftab.acpi.length ) );

return 0;
}

Expand Down
85 changes: 54 additions & 31 deletions src/core/acpi.c
Expand Up @@ -42,28 +42,12 @@ 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;

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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];
Expand Down Expand Up @@ -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;
}
25 changes: 19 additions & 6 deletions src/core/dummy_sanboot.c
Expand Up @@ -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;

Expand All @@ -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;
Expand Down Expand Up @@ -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 );
Expand Down
5 changes: 3 additions & 2 deletions src/core/null_sanboot.c
Expand Up @@ -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;
}

Expand All @@ -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;
}

Expand Down

0 comments on commit 7cfdd76

Please sign in to comment.