diff options
author | Michael Brown | 2014-07-01 18:58:09 +0200 |
---|---|---|
committer | Michael Brown | 2014-07-03 16:28:17 +0200 |
commit | c7051d826b43954b1e191667a75b21b44ec02c35 (patch) | |
tree | c29a4e22878c42f4c31900e5582a055287bbe939 /src/interface/efi/efi_pci.c | |
parent | [build] Add yet another potential location for isolinux.bin (diff) | |
download | ipxe-c7051d826b43954b1e191667a75b21b44ec02c35.tar.gz ipxe-c7051d826b43954b1e191667a75b21b44ec02c35.tar.xz ipxe-c7051d826b43954b1e191667a75b21b44ec02c35.zip |
[efi] Allow network devices to be created on top of arbitrary SNP devices
Signed-off-by: Michael Brown <mcb30@ipxe.org>
Diffstat (limited to 'src/interface/efi/efi_pci.c')
-rw-r--r-- | src/interface/efi/efi_pci.c | 341 |
1 files changed, 99 insertions, 242 deletions
diff --git a/src/interface/efi/efi_pci.c b/src/interface/efi/efi_pci.c index dd65b20f..7132263b 100644 --- a/src/interface/efi/efi_pci.c +++ b/src/interface/efi/efi_pci.c @@ -37,7 +37,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); /* Disambiguate the various error causes */ #define EINFO_EEFI_PCI \ __einfo_uniqify ( EINFO_EPLATFORM, 0x01, \ - "Could not open PCI I/O protocols" ) + "Could not open PCI I/O protocol" ) #define EINFO_EEFI_PCI_NOT_PCI \ __einfo_platformify ( EINFO_EEFI_PCI, EFI_UNSUPPORTED, \ "Not a PCI device" ) @@ -124,49 +124,28 @@ PROVIDE_PCIAPI_INLINE ( efi, pci_write_config_dword ); static EFI_GUID efi_pci_io_protocol_guid = EFI_PCI_IO_PROTOCOL_GUID; -/** EFI device path protocol GUID */ -static EFI_GUID efi_device_path_protocol_guid - = EFI_DEVICE_PATH_PROTOCOL_GUID; - -/** EFI PCI devices */ -static LIST_HEAD ( efi_pci_devices ); - /** - * Create EFI PCI device + * Open EFI PCI device * - * @v device EFI device + * @v device EFI device handle * @v attributes Protocol opening attributes - * @v efipci EFI PCI device to fill in + * @v pci PCI device to fill in * @ret rc Return status code */ -int efipci_create ( EFI_HANDLE device, UINT32 attributes, - struct efi_pci_device **efipci ) { +int efipci_open ( EFI_HANDLE device, UINT32 attributes, + struct pci_device *pci ) { EFI_BOOT_SERVICES *bs = efi_systab->BootServices; union { EFI_PCI_IO_PROTOCOL *pci_io; void *interface; } pci_io; - union { - EFI_DEVICE_PATH_PROTOCOL *path; - void *interface; - } path; UINTN pci_segment, pci_bus, pci_dev, pci_fn; EFI_STATUS efirc; int rc; - /* Allocate PCI device */ - *efipci = zalloc ( sizeof ( **efipci ) ); - if ( ! *efipci ) { - rc = -ENOMEM; - goto err_zalloc; - } - (*efipci)->device = device; - /* See if device is a PCI device */ - if ( ( efirc = bs->OpenProtocol ( device, - &efi_pci_io_protocol_guid, - &pci_io.interface, - efi_image_handle, + if ( ( efirc = bs->OpenProtocol ( device, &efi_pci_io_protocol_guid, + &pci_io.interface, efi_image_handle, device, attributes ) ) != 0 ) { rc = -EEFI_PCI ( efirc ); DBGCP ( device, "EFIPCI %p %s cannot open PCI protocols: %s\n", @@ -174,16 +153,14 @@ int efipci_create ( EFI_HANDLE device, UINT32 attributes, strerror ( rc ) ); goto err_open_protocol; } - (*efipci)->pci_io = pci_io.pci_io; /* Get PCI bus:dev.fn address */ - if ( ( efirc = pci_io.pci_io->GetLocation ( pci_io.pci_io, - &pci_segment, + if ( ( efirc = pci_io.pci_io->GetLocation ( pci_io.pci_io, &pci_segment, &pci_bus, &pci_dev, &pci_fn ) ) != 0 ) { rc = -EEFI ( efirc ); - DBGC ( device, "EFIPCI %p %s could not get PCI location: " - "%s\n", device, efi_handle_devpath_text ( device ), + DBGC ( device, "EFIPCI %p %s could not get PCI location: %s\n", + device, efi_handle_devpath_text ( device ), strerror ( rc ) ); goto err_get_location; } @@ -192,172 +169,72 @@ int efipci_create ( EFI_HANDLE device, UINT32 attributes, ( ( unsigned long ) pci_segment ), ( ( unsigned long ) pci_bus), ( ( unsigned long ) pci_dev ), ( ( unsigned long ) pci_fn ) ); + /* Try to enable I/O cycles, memory cycles, and bus mastering. + * Some platforms will 'helpfully' report errors if these bits + * can't be enabled (for example, if the card doesn't actually + * support I/O cycles). Work around any such platforms by + * enabling bits individually and simply ignoring any errors. + */ + pci_io.pci_io->Attributes ( pci_io.pci_io, + EfiPciIoAttributeOperationEnable, + EFI_PCI_IO_ATTRIBUTE_IO, NULL ); + pci_io.pci_io->Attributes ( pci_io.pci_io, + EfiPciIoAttributeOperationEnable, + EFI_PCI_IO_ATTRIBUTE_MEMORY, NULL ); + pci_io.pci_io->Attributes ( pci_io.pci_io, + EfiPciIoAttributeOperationEnable, + EFI_PCI_IO_ATTRIBUTE_BUS_MASTER, NULL ); + /* Populate PCI device */ - pci_init ( &(*efipci)->pci, PCI_BUSDEVFN ( pci_bus, pci_dev, pci_fn ) ); - if ( ( rc = pci_read_config ( &(*efipci)->pci ) ) != 0 ) { + pci_init ( pci, PCI_BUSDEVFN ( pci_bus, pci_dev, pci_fn ) ); + if ( ( rc = pci_read_config ( pci ) ) != 0 ) { DBGC ( device, "EFIPCI %p %s cannot read PCI configuration: " "%s\n", device, efi_handle_devpath_text ( device ), strerror ( rc ) ); goto err_pci_read_config; } - /* Retrieve device path */ - if ( ( efirc = bs->OpenProtocol ( device, - &efi_device_path_protocol_guid, - &path.interface, efi_image_handle, - device, attributes ) ) != 0 ) { - rc = -EEFI ( efirc ); - DBGC ( device, "EFIPCI %p %s has no device path\n", - device, efi_handle_devpath_text ( device ) ); - goto err_no_device_path; - } - (*efipci)->path = path.path; - - /* Add to list of PCI devices */ - list_add ( &(*efipci)->list, &efi_pci_devices ); - return 0; - bs->CloseProtocol ( device, &efi_device_path_protocol_guid, - efi_image_handle, device ); - err_no_device_path: err_pci_read_config: err_get_location: bs->CloseProtocol ( device, &efi_pci_io_protocol_guid, efi_image_handle, device ); err_open_protocol: - free ( *efipci ); - err_zalloc: return rc; } /** - * Enable EFI PCI device - * - * @v efipci EFI PCI device - * @ret rc Return status code - */ -int efipci_enable ( struct efi_pci_device *efipci ) { - EFI_PCI_IO_PROTOCOL *pci_io = efipci->pci_io; - - /* Try to enable I/O cycles, memory cycles, and bus mastering. - * Some platforms will 'helpfully' report errors if these bits - * can't be enabled (for example, if the card doesn't actually - * support I/O cycles). Work around any such platforms by - * enabling bits individually and simply ignoring any errors. - */ - pci_io->Attributes ( pci_io, EfiPciIoAttributeOperationEnable, - EFI_PCI_IO_ATTRIBUTE_IO, NULL ); - pci_io->Attributes ( pci_io, EfiPciIoAttributeOperationEnable, - EFI_PCI_IO_ATTRIBUTE_MEMORY, NULL ); - pci_io->Attributes ( pci_io, EfiPciIoAttributeOperationEnable, - EFI_PCI_IO_ATTRIBUTE_BUS_MASTER, NULL ); - - return 0; -} - -/** - * Find EFI PCI device by EFI device - * - * @v device EFI device - * @ret efipci EFI PCI device, or NULL - */ -struct efi_pci_device * efipci_find_efi ( EFI_HANDLE device ) { - struct efi_pci_device *efipci; - - list_for_each_entry ( efipci, &efi_pci_devices, list ) { - if ( efipci->device == device ) - return efipci; - } - return NULL; -} - -/** - * Find EFI PCI device by iPXE device + * Close EFI PCI device * - * @v dev Device - * @ret efipci EFI PCI device, or NULL + * @v device EFI device handle */ -struct efi_pci_device * efipci_find ( struct device *dev ) { - struct efi_pci_device *efipci; +void efipci_close ( EFI_HANDLE device ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; - list_for_each_entry ( efipci, &efi_pci_devices, list ) { - if ( &efipci->pci.dev == dev ) - return efipci; - } - return NULL; + bs->CloseProtocol ( device, &efi_pci_io_protocol_guid, + efi_image_handle, device ); } /** - * Add EFI device as child of EFI PCI device + * Get EFI PCI device information * - * @v efipci EFI PCI device - * @v device EFI child device - * @ret efirc EFI status code + * @v device EFI device handle + * @v pci PCI device to fill in + * @ret rc Return status code */ -int efipci_child_add ( struct efi_pci_device *efipci, EFI_HANDLE device ) { - EFI_BOOT_SERVICES *bs = efi_systab->BootServices; - union { - EFI_PCI_IO_PROTOCOL *pci_io; - void *interface; - } pci_io; - EFI_STATUS efirc; +int efipci_info ( EFI_HANDLE device, struct pci_device *pci ) { int rc; - /* Re-open the PCI_IO_PROTOCOL */ - if ( ( efirc = bs->OpenProtocol ( efipci->device, - &efi_pci_io_protocol_guid, - &pci_io.interface, - efi_image_handle, device, - EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER - ) ) != 0 ) { - rc = -EEFI ( efirc ); - DBGC ( efipci->device, "EFIPCI %p %s could not add child", - efipci->device, efi_devpath_text ( efipci->path ) ); - DBGC ( efipci->device, " %p %s: %s\n", device, - efi_handle_devpath_text ( device ), strerror ( rc ) ); + /* Open PCI device, if possible */ + if ( ( rc = efipci_open ( device, EFI_OPEN_PROTOCOL_GET_PROTOCOL, + pci ) ) != 0 ) return rc; - } - - DBGC2 ( efipci->device, "EFIPCI %p %s added child", - efipci->device, efi_devpath_text ( efipci->path ) ); - DBGC2 ( efipci->device, " %p %s\n", - device, efi_handle_devpath_text ( device ) ); - 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; - - bs->CloseProtocol ( efipci->device, &efi_pci_io_protocol_guid, - efi_image_handle, device ); - DBGC2 ( efipci->device, "EFIPCI %p %s removed child", - efipci->device, efi_devpath_text ( efipci->path ) ); - DBGC2 ( efipci->device, " %p %s\n", - device, efi_handle_devpath_text ( device ) ); -} -/** - * Destroy EFI PCI device - * - * @v efipci EFI PCI device - */ -void efipci_destroy ( struct efi_pci_device *efipci ) { - EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + /* Close PCI device */ + efipci_close ( device ); - list_del ( &efipci->list ); - bs->CloseProtocol ( efipci->device, &efi_device_path_protocol_guid, - efi_image_handle, efipci->device ); - bs->CloseProtocol ( efipci->device, &efi_pci_io_protocol_guid, - efi_image_handle, efipci->device ); - free ( efipci ); + return 0; } /****************************************************************************** @@ -370,125 +247,105 @@ void efipci_destroy ( struct efi_pci_device *efipci ) { /** * Check to see if driver supports a device * - * @v device EFI device + * @v device EFI device handle * @ret rc Return status code */ static int efipci_supported ( EFI_HANDLE device ) { - struct efi_pci_device *efipci; + struct pci_device pci; int rc; - /* Do nothing if we are already driving this device */ - efipci = efipci_find_efi ( device ); - if ( efipci ) { - DBGCP ( device, "EFIPCI %p %s already started\n", - device, efi_devpath_text ( efipci->path ) ); - rc = -EALREADY; - goto err_already_started; - } - - /* Create temporary corresponding PCI device, if any */ - if ( ( rc = efipci_create ( device, EFI_OPEN_PROTOCOL_GET_PROTOCOL, - &efipci ) ) != 0 ) - goto err_create; + /* Get PCI device information */ + if ( ( rc = efipci_info ( device, &pci ) ) != 0 ) + return rc; /* Look for a driver */ - if ( ( rc = pci_find_driver ( &efipci->pci ) ) != 0 ) { + if ( ( rc = pci_find_driver ( &pci ) ) != 0 ) { DBGCP ( device, "EFIPCI %p %s has no driver\n", - device, efi_devpath_text ( efipci->path ) ); - goto err_no_driver; + device, efi_handle_devpath_text ( device ) ); + return rc; } - - DBGC ( device, "EFIPCI %p %s has driver \"%s\"\n", device, - efi_devpath_text ( efipci->path ), efipci->pci.id->name ); - - /* Destroy temporary PCI device */ - efipci_destroy ( efipci ); + DBGC ( device, "EFIPCI %p %s has driver \"%s\"\n", + device, efi_handle_devpath_text ( device ), pci.id->name ); return 0; - - err_no_driver: - efipci_destroy ( efipci ); - err_create: - err_already_started: - return rc; } /** * Attach driver to device * - * @v device EFI device + * @v efidev EFI device * @ret rc Return status code */ -static int efipci_start ( EFI_HANDLE device ) { - struct efi_pci_device *efipci; +static int efipci_start ( struct efi_device *efidev ) { + EFI_HANDLE device = efidev->device; + struct pci_device *pci; int rc; - /* Do nothing if we are already driving this device */ - efipci = efipci_find_efi ( device ); - if ( efipci ) { - DBGCP ( device, "EFIPCI %p %s already started\n", - device, efi_devpath_text ( efipci->path ) ); - rc = -EALREADY; - goto err_already_started; + /* Allocate PCI device */ + pci = zalloc ( sizeof ( *pci ) ); + if ( ! pci ) { + rc = -ENOMEM; + goto err_alloc; } - /* Create corresponding PCI device */ - if ( ( rc = efipci_create ( device, ( EFI_OPEN_PROTOCOL_BY_DRIVER | - EFI_OPEN_PROTOCOL_EXCLUSIVE ), - &efipci ) ) != 0 ) - goto err_create; + /* Open PCI device */ + if ( ( rc = efipci_open ( device, ( EFI_OPEN_PROTOCOL_BY_DRIVER | + EFI_OPEN_PROTOCOL_EXCLUSIVE ), + pci ) ) != 0 ) { + DBGC ( device, "EFIPCI %p %s could not open PCI device: %s\n", + device, efi_devpath_text ( efidev->path ), + strerror ( rc ) ); + goto err_open; + } /* Find driver */ - if ( ( rc = pci_find_driver ( &efipci->pci ) ) != 0 ) { + if ( ( rc = pci_find_driver ( pci ) ) != 0 ) { DBGC ( device, "EFIPCI %p %s has no driver\n", - device, efi_devpath_text ( efipci->path ) ); + device, efi_devpath_text ( efidev->path ) ); goto err_find_driver; } - /* Enable PCI device */ - if ( ( rc = efipci_enable ( efipci ) ) != 0 ) - goto err_enable; + /* Mark PCI device as a child of the EFI device */ + pci->dev.parent = &efidev->dev; + list_add ( &pci->dev.siblings, &efidev->dev.children ); /* Probe driver */ - if ( ( rc = pci_probe ( &efipci->pci ) ) != 0 ) { + if ( ( rc = pci_probe ( pci ) ) != 0 ) { DBGC ( device, "EFIPCI %p %s could not probe driver \"%s\": " - "%s\n", device, efi_devpath_text ( efipci->path ), - efipci->pci.id->name, strerror ( rc ) ); + "%s\n", device, efi_devpath_text ( efidev->path ), + pci->id->name, strerror ( rc ) ); goto err_probe; } DBGC ( device, "EFIPCI %p %s using driver \"%s\"\n", device, - efi_devpath_text ( efipci->path ), efipci->pci.id->name ); + efi_devpath_text ( efidev->path ), pci->id->name ); + efidev_set_drvdata ( efidev, pci ); return 0; - pci_remove ( &efipci->pci ); + pci_remove ( pci ); err_probe: - err_enable: + list_del ( &pci->dev.siblings ); err_find_driver: - efipci_destroy ( efipci ); - err_create: - err_already_started: + efipci_close ( device ); + err_open: + free ( pci ); + err_alloc: return rc; } /** * Detach driver from device * - * @v device EFI device + * @v efidev EFI device */ -static void efipci_stop ( EFI_HANDLE device ) { - struct efi_pci_device *efipci; - - /* Find PCI device */ - efipci = efipci_find_efi ( device ); - if ( ! efipci ) - return; - - /* Remove device */ - pci_remove ( &efipci->pci ); - - /* Delete EFI PCI device */ - efipci_destroy ( efipci ); +static void efipci_stop ( struct efi_device *efidev ) { + struct pci_device *pci = efidev_get_drvdata ( efidev ); + EFI_HANDLE device = efidev->device; + + pci_remove ( pci ); + list_del ( &pci->dev.siblings ); + efipci_close ( device ); + free ( pci ); } /** EFI PCI driver */ |