From b9326c365520806978b6ab9b8532cbc983f36da1 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 15 Mar 2011 16:55:04 +0000 Subject: [PATCH] [efi] Mark SNP devices as children of EFI PCI device Re-open the EFI_PCI_IO_PROTOCOL specifying an Attributes value of EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER. This causes the SNP devices to be marked as children of the EFI PCI device (as shown in the "devtree" command). On at least one IBM blade system, this is required in order to have the relevant drivers automatically attach to the SNP controller at device creation time. Signed-off-by: Michael Brown --- src/include/ipxe/efi/efi_pci.h | 6 +++++ src/interface/efi/efi_pci.c | 49 ++++++++++++++++++++++++++++++++++ src/interface/efi/efi_snp.c | 15 +++++++++++ 3 files changed, 70 insertions(+) diff --git a/src/include/ipxe/efi/efi_pci.h b/src/include/ipxe/efi/efi_pci.h index e226a5693..24890eb40 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 d866e30e7..3b393fcbe 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, @@ -262,6 +263,54 @@ struct efi_pci_device * efipci_find ( struct device *dev ) { return NULL; } +/** + * 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 * diff --git a/src/interface/efi/efi_snp.c b/src/interface/efi/efi_snp.c index 669d18962..cc09d7dbc 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,