mirror of
https://github.com/ipxe/ipxe
synced 2025-12-07 18:00:28 +03:00
[efi] Allow for creating devices with no EFI parent device
On some systems (observed on an AWS m8g.medium instance in eu-west-2), the UEFI firmware fails to enumerate some of the underlying hardware devices. On these systems, we cannot comply with the UEFI device model by adding our SNP device as a child of the hardware device and appending to the parent hardware device path, since no parent hardware device has been created. Work around these systems by allowing for the creation of SNP devices with no parent device. Signed-off-by: Michael Brown <mcb30@ipxe.org>
This commit is contained in:
@@ -30,8 +30,8 @@ struct efi_snp_device {
|
||||
struct list_head list;
|
||||
/** The underlying iPXE network device */
|
||||
struct net_device *netdev;
|
||||
/** The underlying EFI device */
|
||||
struct efi_device *efidev;
|
||||
/** EFI parent device handle (if any) */
|
||||
EFI_HANDLE parent;
|
||||
/** EFI device handle */
|
||||
EFI_HANDLE handle;
|
||||
/** The SNP structure itself */
|
||||
|
||||
@@ -47,6 +47,34 @@ FILE_LICENCE ( GPL2_OR_LATER );
|
||||
*
|
||||
*/
|
||||
|
||||
/** Dummy parent device path
|
||||
*
|
||||
* This is used as the parent device path when we need to construct a
|
||||
* path for a device that has no EFI parent device.
|
||||
*/
|
||||
static struct {
|
||||
BBS_BBS_DEVICE_PATH bbs;
|
||||
CHAR8 tring[4];
|
||||
EFI_DEVICE_PATH_PROTOCOL end;
|
||||
} __attribute__ (( packed )) efi_dummy_parent_path = {
|
||||
.bbs = {
|
||||
.Header = {
|
||||
.Type = BBS_DEVICE_PATH,
|
||||
.SubType = BBS_BBS_DP,
|
||||
.Length[0] = ( sizeof ( efi_dummy_parent_path.bbs ) +
|
||||
sizeof ( efi_dummy_parent_path.tring )),
|
||||
},
|
||||
.DeviceType = BBS_TYPE_UNKNOWN,
|
||||
.String[0] = 'i',
|
||||
},
|
||||
.tring = "PXE",
|
||||
.end = {
|
||||
.Type = END_DEVICE_PATH_TYPE,
|
||||
.SubType = END_ENTIRE_DEVICE_PATH_SUBTYPE,
|
||||
.Length[0] = sizeof ( efi_dummy_parent_path.end ),
|
||||
},
|
||||
};
|
||||
|
||||
/** An EFI device path settings block */
|
||||
struct efi_path_settings {
|
||||
/** Settings interface */
|
||||
@@ -352,6 +380,24 @@ EFI_DEVICE_PATH_PROTOCOL * efi_paths ( EFI_DEVICE_PATH_PROTOCOL *first, ... ) {
|
||||
return path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct EFI parent device path
|
||||
*
|
||||
* @v dev Generic device
|
||||
* @ret path Parent (or dummy) device path
|
||||
*/
|
||||
static EFI_DEVICE_PATH_PROTOCOL * efi_parent_path ( struct device *dev ) {
|
||||
struct efi_device *efidev;
|
||||
|
||||
/* Use EFI parent device's path, if possible */
|
||||
efidev = efidev_parent ( dev );
|
||||
if ( efidev )
|
||||
return efidev->path;
|
||||
|
||||
/* Otherwise, use a dummy parent device path */
|
||||
return &efi_dummy_parent_path.bbs.Header;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct EFI device path for network device
|
||||
*
|
||||
@@ -362,7 +408,7 @@ EFI_DEVICE_PATH_PROTOCOL * efi_paths ( EFI_DEVICE_PATH_PROTOCOL *first, ... ) {
|
||||
* allocated device path.
|
||||
*/
|
||||
EFI_DEVICE_PATH_PROTOCOL * efi_netdev_path ( struct net_device *netdev ) {
|
||||
struct efi_device *efidev;
|
||||
EFI_DEVICE_PATH_PROTOCOL *parent;
|
||||
EFI_DEVICE_PATH_PROTOCOL *path;
|
||||
MAC_ADDR_DEVICE_PATH *macpath;
|
||||
VLAN_DEVICE_PATH *vlanpath;
|
||||
@@ -371,13 +417,11 @@ EFI_DEVICE_PATH_PROTOCOL * efi_netdev_path ( struct net_device *netdev ) {
|
||||
size_t prefix_len;
|
||||
size_t len;
|
||||
|
||||
/* Find parent EFI device */
|
||||
efidev = efidev_parent ( netdev->dev );
|
||||
if ( ! efidev )
|
||||
return NULL;
|
||||
/* Get parent EFI device path */
|
||||
parent = efi_parent_path ( netdev->dev );
|
||||
|
||||
/* Calculate device path length */
|
||||
prefix_len = efi_path_len ( efidev->path );
|
||||
prefix_len = efi_path_len ( parent );
|
||||
len = ( prefix_len + sizeof ( *macpath ) + sizeof ( *vlanpath ) +
|
||||
sizeof ( *end ) );
|
||||
|
||||
@@ -387,7 +431,7 @@ EFI_DEVICE_PATH_PROTOCOL * efi_netdev_path ( struct net_device *netdev ) {
|
||||
return NULL;
|
||||
|
||||
/* Construct device path */
|
||||
memcpy ( path, efidev->path, prefix_len );
|
||||
memcpy ( path, parent, prefix_len );
|
||||
macpath = ( ( ( void * ) path ) + prefix_len );
|
||||
macpath->Header.Type = MESSAGING_DEVICE_PATH;
|
||||
macpath->Header.SubType = MSG_MAC_ADDR_DP;
|
||||
@@ -601,20 +645,18 @@ EFI_DEVICE_PATH_PROTOCOL * efi_ib_srp_path ( struct ib_srp_device *ib_srp ) {
|
||||
union ib_srp_target_port_id *id =
|
||||
container_of ( &sbft->srp.target, union ib_srp_target_port_id,
|
||||
srp );
|
||||
struct efi_device *efidev;
|
||||
EFI_DEVICE_PATH_PROTOCOL *parent;
|
||||
EFI_DEVICE_PATH_PROTOCOL *path;
|
||||
INFINIBAND_DEVICE_PATH *ibpath;
|
||||
EFI_DEVICE_PATH_PROTOCOL *end;
|
||||
size_t prefix_len;
|
||||
size_t len;
|
||||
|
||||
/* Find parent EFI device */
|
||||
efidev = efidev_parent ( ib_srp->ibdev->dev );
|
||||
if ( ! efidev )
|
||||
return NULL;
|
||||
/* Get parent EFI device path */
|
||||
parent = efi_parent_path ( ib_srp->ibdev->dev );
|
||||
|
||||
/* Calculate device path length */
|
||||
prefix_len = efi_path_len ( efidev->path );
|
||||
prefix_len = efi_path_len ( parent );
|
||||
len = ( prefix_len + sizeof ( *ibpath ) + sizeof ( *end ) );
|
||||
|
||||
/* Allocate device path */
|
||||
@@ -623,7 +665,7 @@ EFI_DEVICE_PATH_PROTOCOL * efi_ib_srp_path ( struct ib_srp_device *ib_srp ) {
|
||||
return NULL;
|
||||
|
||||
/* Construct device path */
|
||||
memcpy ( path, efidev->path, prefix_len );
|
||||
memcpy ( path, parent, prefix_len );
|
||||
ibpath = ( ( ( void * ) path ) + prefix_len );
|
||||
ibpath->Header.Type = MESSAGING_DEVICE_PATH;
|
||||
ibpath->Header.SubType = MSG_INFINIBAND_DP;
|
||||
@@ -653,7 +695,7 @@ EFI_DEVICE_PATH_PROTOCOL * efi_ib_srp_path ( struct ib_srp_device *ib_srp ) {
|
||||
*/
|
||||
EFI_DEVICE_PATH_PROTOCOL * efi_usb_path ( struct usb_function *func ) {
|
||||
struct usb_device *usb = func->usb;
|
||||
struct efi_device *efidev;
|
||||
EFI_DEVICE_PATH_PROTOCOL *parent;
|
||||
EFI_DEVICE_PATH_PROTOCOL *path;
|
||||
EFI_DEVICE_PATH_PROTOCOL *end;
|
||||
USB_DEVICE_PATH *usbpath;
|
||||
@@ -664,14 +706,12 @@ EFI_DEVICE_PATH_PROTOCOL * efi_usb_path ( struct usb_function *func ) {
|
||||
/* Sanity check */
|
||||
assert ( func->desc.count >= 1 );
|
||||
|
||||
/* Find parent EFI device */
|
||||
efidev = efidev_parent ( &func->dev );
|
||||
if ( ! efidev )
|
||||
return NULL;
|
||||
/* Get parent EFI device path */
|
||||
parent = efi_parent_path ( &func->dev );
|
||||
|
||||
/* Calculate device path length */
|
||||
count = ( usb_depth ( usb ) + 1 );
|
||||
prefix_len = efi_path_len ( efidev->path );
|
||||
prefix_len = efi_path_len ( parent );
|
||||
len = ( prefix_len + ( count * sizeof ( *usbpath ) ) +
|
||||
sizeof ( *end ) );
|
||||
|
||||
@@ -681,7 +721,7 @@ EFI_DEVICE_PATH_PROTOCOL * efi_usb_path ( struct usb_function *func ) {
|
||||
return NULL;
|
||||
|
||||
/* Construct device path */
|
||||
memcpy ( path, efidev->path, prefix_len );
|
||||
memcpy ( path, parent, prefix_len );
|
||||
end = ( ( ( void * ) path ) + len - sizeof ( *end ) );
|
||||
efi_path_terminate ( end );
|
||||
usbpath = ( ( ( void * ) end ) - sizeof ( *usbpath ) );
|
||||
|
||||
@@ -1792,14 +1792,6 @@ static int efi_snp_probe ( struct net_device *netdev, void *priv __unused ) {
|
||||
EFI_STATUS efirc;
|
||||
int rc;
|
||||
|
||||
/* Find parent EFI device */
|
||||
efidev = efidev_parent ( netdev->dev );
|
||||
if ( ! efidev ) {
|
||||
DBG ( "SNP skipping non-EFI device %s\n", netdev->name );
|
||||
rc = 0;
|
||||
goto err_no_efidev;
|
||||
}
|
||||
|
||||
/* Allocate the SNP device */
|
||||
snpdev = zalloc ( sizeof ( *snpdev ) );
|
||||
if ( ! snpdev ) {
|
||||
@@ -1807,9 +1799,13 @@ static int efi_snp_probe ( struct net_device *netdev, void *priv __unused ) {
|
||||
goto err_alloc_snp;
|
||||
}
|
||||
snpdev->netdev = netdev_get ( netdev );
|
||||
snpdev->efidev = efidev;
|
||||
INIT_LIST_HEAD ( &snpdev->rx );
|
||||
|
||||
/* Find parent EFI device, if any */
|
||||
efidev = efidev_parent ( netdev->dev );
|
||||
if ( efidev )
|
||||
snpdev->parent = efidev->device;
|
||||
|
||||
/* Sanity check */
|
||||
if ( netdev->ll_protocol->ll_addr_len > sizeof ( EFI_MAC_ADDRESS ) ) {
|
||||
DBGC ( snpdev, "SNPDEV %p cannot support link-layer address "
|
||||
@@ -1931,8 +1927,10 @@ static int efi_snp_probe ( struct net_device *netdev, void *priv __unused ) {
|
||||
goto err_open_nii31;
|
||||
}
|
||||
|
||||
/* Add as child of EFI parent device */
|
||||
if ( ( rc = efi_child_add ( efidev->device, snpdev->handle ) ) != 0 ) {
|
||||
/* Add as child of EFI parent device, if applicable */
|
||||
if ( snpdev->parent &&
|
||||
( ( rc = efi_child_add ( snpdev->parent,
|
||||
snpdev->handle ) ) != 0 ) ) {
|
||||
DBGC ( snpdev, "SNPDEV %p could not become child of %s: %s\n",
|
||||
snpdev, efi_handle_name ( efidev->device ),
|
||||
strerror ( rc ) );
|
||||
@@ -1959,7 +1957,8 @@ static int efi_snp_probe ( struct net_device *netdev, void *priv __unused ) {
|
||||
list_del ( &snpdev->list );
|
||||
if ( snpdev->package_list )
|
||||
leak |= efi_snp_hii_uninstall ( snpdev );
|
||||
efi_child_del ( efidev->device, snpdev->handle );
|
||||
if ( snpdev->parent )
|
||||
efi_child_del ( snpdev->parent, snpdev->handle );
|
||||
err_efi_child_add:
|
||||
efi_close_by_driver ( snpdev->handle, &efi_nii31_protocol_guid );
|
||||
err_open_nii31:
|
||||
@@ -1996,7 +1995,6 @@ static int efi_snp_probe ( struct net_device *netdev, void *priv __unused ) {
|
||||
free ( snpdev );
|
||||
}
|
||||
err_alloc_snp:
|
||||
err_no_efidev:
|
||||
if ( leak )
|
||||
DBGC ( snpdev, "SNPDEV %p nullified and leaked\n", snpdev );
|
||||
return rc;
|
||||
@@ -2051,7 +2049,8 @@ static void efi_snp_remove ( struct net_device *netdev, void *priv __unused ) {
|
||||
list_del ( &snpdev->list );
|
||||
if ( snpdev->package_list )
|
||||
leak |= efi_snp_hii_uninstall ( snpdev );
|
||||
efi_child_del ( snpdev->efidev->device, snpdev->handle );
|
||||
if ( snpdev->parent )
|
||||
efi_child_del ( snpdev->parent, snpdev->handle );
|
||||
efi_close_by_driver ( snpdev->handle, &efi_nii_protocol_guid );
|
||||
efi_close_by_driver ( snpdev->handle, &efi_nii31_protocol_guid );
|
||||
if ( ( ! efi_shutdown_in_progress ) &&
|
||||
|
||||
Reference in New Issue
Block a user