aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/include/ipxe/efi/efi_pci.h6
-rw-r--r--src/interface/efi/efi_pci.c49
-rw-r--r--src/interface/efi/efi_snp.c15
3 files changed, 70 insertions, 0 deletions
diff --git a/src/include/ipxe/efi/efi_pci.h b/src/include/ipxe/efi/efi_pci.h
index e226a56..24890eb 100644
--- a/src/include/ipxe/efi/efi_pci.h
+++ b/src/include/ipxe/efi/efi_pci.h
@@ -27,6 +27,8 @@ struct efi_pci_device {
EFI_PCI_IO_PROTOCOL *pci_io;
/** Device path */
EFI_DEVICE_PATH_PROTOCOL *path;
+ /** EFI driver */
+ struct efi_driver *efidrv;
};
extern struct efi_pci_device * efipci_create ( struct efi_driver *efidrv,
@@ -34,6 +36,10 @@ extern struct efi_pci_device * efipci_create ( struct efi_driver *efidrv,
extern EFI_STATUS efipci_enable ( struct efi_pci_device *efipci );
extern struct efi_pci_device * efipci_find_efi ( EFI_HANDLE device );
extern struct efi_pci_device * efipci_find ( struct device *dev );
+extern EFI_STATUS efipci_child_add ( struct efi_pci_device *efipci,
+ EFI_HANDLE device );
+extern void efipci_child_del ( struct efi_pci_device *efipci,
+ EFI_HANDLE device );
extern void efipci_destroy ( struct efi_driver *efidrv,
struct efi_pci_device *efipci );
diff --git a/src/interface/efi/efi_pci.c b/src/interface/efi/efi_pci.c
index d866e30..3b393fc 100644
--- a/src/interface/efi/efi_pci.c
+++ b/src/interface/efi/efi_pci.c
@@ -139,6 +139,7 @@ struct efi_pci_device * efipci_create ( struct efi_driver *efidrv,
if ( ! efipci )
goto err_zalloc;
efipci->device = device;
+ efipci->efidrv = efidrv;
/* See if device is a PCI device */
if ( ( efirc = bs->OpenProtocol ( device,
@@ -263,6 +264,54 @@ struct efi_pci_device * efipci_find ( struct device *dev ) {
}
/**
+ * Add EFI device as child of EFI PCI device
+ *
+ * @v efipci EFI PCI device
+ * @v device EFI child device
+ * @ret efirc EFI status code
+ */
+EFI_STATUS efipci_child_add ( struct efi_pci_device *efipci,
+ EFI_HANDLE device ) {
+ EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
+ struct efi_driver *efidrv = efipci->efidrv;
+ union {
+ EFI_PCI_IO_PROTOCOL *pci_io;
+ void *interface;
+ } pci_io;
+ EFI_STATUS efirc;
+
+ /* Re-open the PCI_IO_PROTOCOL */
+ if ( ( efirc = bs->OpenProtocol ( efipci->device,
+ &efi_pci_io_protocol_guid,
+ &pci_io.interface,
+ efidrv->driver.DriverBindingHandle,
+ device,
+ EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
+ ) ) != 0 ) {
+ DBGC ( efipci, "EFIPCI " PCI_FMT " could not add child: %s\n",
+ PCI_ARGS ( &efipci->pci ), efi_strerror ( efirc ) );
+ return efirc;
+ }
+
+ return 0;
+}
+
+/**
+ * Remove EFI device as child of PCI device
+ *
+ * @v efipci EFI PCI device
+ * @v device EFI child device
+ * @ret efirc EFI status code
+ */
+void efipci_child_del ( struct efi_pci_device *efipci, EFI_HANDLE device ) {
+ EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
+ struct efi_driver *efidrv = efipci->efidrv;
+
+ bs->CloseProtocol ( efipci->device, &efi_pci_io_protocol_guid,
+ efidrv->driver.DriverBindingHandle, device );
+}
+
+/**
* Destroy EFI PCI device
*
* @v efidrv EFI driver
diff --git a/src/interface/efi/efi_snp.c b/src/interface/efi/efi_snp.c
index 669d189..cc09d7d 100644
--- a/src/interface/efi/efi_snp.c
+++ b/src/interface/efi/efi_snp.c
@@ -46,6 +46,8 @@ struct efi_snp_device {
struct list_head list;
/** The underlying iPXE network device */
struct net_device *netdev;
+ /** The underlying EFI PCI device */
+ struct efi_pci_device *efipci;
/** EFI device handle */
EFI_HANDLE handle;
/** The SNP structure itself */
@@ -795,6 +797,7 @@ static int efi_snp_probe ( struct net_device *netdev ) {
goto err_alloc_snp;
}
snpdev->netdev = netdev_get ( netdev );
+ snpdev->efipci = efipci;
/* Sanity check */
if ( netdev->ll_protocol->ll_addr_len > sizeof ( EFI_MAC_ADDRESS ) ) {
@@ -865,6 +868,15 @@ static int efi_snp_probe ( struct net_device *netdev ) {
goto err_install_protocol_interface;
}
+ /* Add as child of PCI device */
+ if ( ( efirc = efipci_child_add ( efipci, snpdev->handle ) ) != 0 ) {
+ DBGC ( snpdev, "SNPDEV %p could not become child of " PCI_FMT
+ ": %s\n", snpdev, PCI_ARGS ( &efipci->pci ),
+ efi_strerror ( efirc ) );
+ rc = EFIRC_TO_RC ( efirc );
+ goto err_efipci_child_add;
+ }
+
/* Add to list of SNP devices */
list_add ( &snpdev->list, &efi_snp_devices );
@@ -872,6 +884,8 @@ static int efi_snp_probe ( struct net_device *netdev ) {
snpdev, netdev->name, snpdev->handle );
return 0;
+ efipci_child_del ( efipci, snpdev->handle );
+ err_efipci_child_add:
bs->UninstallMultipleProtocolInterfaces (
snpdev->handle,
&efi_simple_network_protocol_guid, &snpdev->snp,
@@ -916,6 +930,7 @@ static void efi_snp_remove ( struct net_device *netdev ) {
}
/* Uninstall the SNP */
+ efipci_child_del ( snpdev->efipci, snpdev->handle );
list_del ( &snpdev->list );
bs->UninstallMultipleProtocolInterfaces (
snpdev->handle,