diff --git a/src/arch/x86/prefix/efiprefix.c b/src/arch/x86/prefix/efiprefix.c index eb8aa738a..216b99884 100644 --- a/src/arch/x86/prefix/efiprefix.c +++ b/src/arch/x86/prefix/efiprefix.c @@ -21,7 +21,9 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include #include +#include #include +#include /** * EFI entry point @@ -47,6 +49,39 @@ EFI_STATUS EFIAPI _efi_start ( EFI_HANDLE image_handle, err_main: efi_loaded_image->Unload ( image_handle ); + efi_driver_reconnect_all(); err_init: return efirc; } + +/** + * Probe EFI root bus + * + * @v rootdev EFI root device + */ +static int efi_probe ( struct root_device *rootdev __unused ) { + + return efi_driver_connect_all(); +} + +/** + * Remove EFI root bus + * + * @v rootdev EFI root device + */ +static void efi_remove ( struct root_device *rootdev __unused ) { + + efi_driver_disconnect_all(); +} + +/** EFI root device driver */ +static struct root_driver efi_root_driver = { + .probe = efi_probe, + .remove = efi_remove, +}; + +/** EFI root device */ +struct root_device efi_root_device __root_device = { + .dev = { .name = "EFI" }, + .driver = &efi_root_driver, +}; diff --git a/src/include/ipxe/efi/efi_driver.h b/src/include/ipxe/efi/efi_driver.h index afa574d08..600d304f0 100644 --- a/src/include/ipxe/efi/efi_driver.h +++ b/src/include/ipxe/efi/efi_driver.h @@ -8,43 +8,52 @@ FILE_LICENCE ( GPL2_OR_LATER ); +#include #include -#include -#include #include /** An EFI driver */ struct efi_driver { /** Name */ const char *name; - /** EFI name */ - CHAR16 wname[32]; - /** EFI driver binding protocol */ - EFI_DRIVER_BINDING_PROTOCOL driver; - /** EFI component name protocol */ - EFI_COMPONENT_NAME2_PROTOCOL wtf; + /** + * Check if driver supports device + * + * @v device Device + * @ret rc Return status code + */ + int ( * supported ) ( EFI_HANDLE device ); + /** + * Attach driver to device + * + * @v device Device + * @ret rc Return status code + */ + int ( * start ) ( EFI_HANDLE device ); + /** + * Detach driver from device + * + * @v device Device + */ + void ( * stop ) ( EFI_HANDLE device ); }; -/** Initialise an EFI driver - * - * @v name Driver name - * @v supported Device supported method - * @v start Device start method - * @v stop Device stop method - */ -#define EFI_DRIVER_INIT( _name, _supported, _start, _stop ) { \ - .name = _name, \ - .driver = { \ - .Supported = _supported, \ - .Start = _start, \ - .Stop = _stop, \ - .Version = 0x10, \ - } } +/** EFI driver table */ +#define EFI_DRIVERS __table ( struct efi_driver, "efi_drivers" ) + +/** Declare an EFI driver */ +#define __efi_driver( order ) __table_entry ( EFI_DRIVERS, order ) + +#define EFI_DRIVER_EARLY 01 /**< Early drivers */ +#define EFI_DRIVER_NORMAL 02 /**< Normal drivers */ +#define EFI_DRIVER_LATE 03 /**< Late drivers */ extern EFI_DEVICE_PATH_PROTOCOL * efi_devpath_end ( EFI_DEVICE_PATH_PROTOCOL *path ); - -extern int efi_driver_install ( struct efi_driver *efidrv ); -extern void efi_driver_uninstall ( struct efi_driver *efidrv ); +extern int efi_driver_install ( void ); +extern void efi_driver_uninstall ( void ); +extern int efi_driver_connect_all ( void ); +extern void efi_driver_disconnect_all ( void ); +extern void efi_driver_reconnect_all ( void ); #endif /* _IPXE_EFI_DRIVER_H */ diff --git a/src/include/ipxe/efi/efi_pci.h b/src/include/ipxe/efi/efi_pci.h index e6b319709..c5ff366ca 100644 --- a/src/include/ipxe/efi/efi_pci.h +++ b/src/include/ipxe/efi/efi_pci.h @@ -17,7 +17,6 @@ static inline EFIAPI uint64_t LShiftU64 ( UINT64 value, UINTN shift ) { return ( value << shift ); } -struct efi_driver; struct device; /** An EFI PCI device */ @@ -32,12 +31,10 @@ 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, - EFI_HANDLE device ); +extern int efipci_create ( EFI_HANDLE device, UINT32 attributes, + struct efi_pci_device **efipci ); extern int 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 ); @@ -45,7 +42,6 @@ extern int 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 ); +extern void efipci_destroy ( struct efi_pci_device *efipci ); #endif /* _IPXE_EFI_PCI_H */ diff --git a/src/interface/efi/efi_bofm.c b/src/interface/efi/efi_bofm.c index 4982b22cc..2a258a9b5 100644 --- a/src/interface/efi/efi_bofm.c +++ b/src/interface/efi/efi_bofm.c @@ -21,7 +21,6 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include #include -#include #include #include #include @@ -156,17 +155,10 @@ static EFI_GUID bofm2_protocol_guid = /** * Check if device is supported * - * @v driver EFI driver * @v device EFI device - * @v child Path to child device, if any - * @ret efirc EFI status code + * @ret rc Return status code */ -static EFI_STATUS EFIAPI -efi_bofm_supported ( EFI_DRIVER_BINDING_PROTOCOL *driver, - EFI_HANDLE device, - EFI_DEVICE_PATH_PROTOCOL *child ) { - struct efi_driver *efidrv = - container_of ( driver, struct efi_driver, driver ); +static int efi_bofm_supported ( EFI_HANDLE device ) { EFI_BOOT_SERVICES *bs = efi_systab->BootServices; union { IBM_BOFM_DRIVER_CONFIGURATION_PROTOCOL *bofm1; @@ -176,19 +168,24 @@ efi_bofm_supported ( EFI_DRIVER_BINDING_PROTOCOL *driver, EFI_STATUS efirc; int rc; - DBGCP ( efidrv, "EFIBOFM DRIVER_SUPPORTED %p (%p)\n", device, child ); + /* Do nothing if we are already driving this device */ + efipci = efipci_find_efi ( device ); + if ( efipci ) { + DBGCP ( device, "EFIBOFM %p %s already started\n", + device, efi_devpath_text ( efipci->path ) ); + rc = -EALREADY; + goto err_already_started; + } /* Create corresponding PCI device, if any */ - efipci = efipci_create ( efidrv, device ); - if ( ! efipci ) { - rc = -ENOTSUP; - goto err_not_pci; - } + if ( ( rc = efipci_create ( device, EFI_OPEN_PROTOCOL_GET_PROTOCOL, + &efipci ) ) != 0 ) + goto err_create; /* Look for a BOFM driver */ if ( ( rc = bofm_find_driver ( &efipci->pci ) ) != 0 ) { - DBGCP ( efidrv, "EFIBOFM " PCI_FMT " has no driver\n", - PCI_ARGS ( &efipci->pci ) ); + DBGCP ( device, "EFIBOFM %p %s has no driver\n", + device, efi_devpath_text ( efipci->path ) ); goto err_no_driver; } @@ -196,8 +193,8 @@ efi_bofm_supported ( EFI_DRIVER_BINDING_PROTOCOL *driver, if ( ( efirc = bs->LocateProtocol ( &bofm1_protocol_guid, NULL, &bofm1.interface ) ) != 0 ) { rc = -EEFI ( efirc ); - DBGC ( efidrv, "EFIBOFM " PCI_FMT " cannot find BOFM " - "protocol\n", PCI_ARGS ( &efipci->pci ) ); + DBGC ( device, "EFIBOFM %p %s cannot find BOFM protocol\n", + device, efi_devpath_text ( efipci->path ) ); goto err_not_bofm; } @@ -207,41 +204,36 @@ efi_bofm_supported ( EFI_DRIVER_BINDING_PROTOCOL *driver, 0x00 /* No iSCSI */, 0x02 /* Version */ ))!=0){ rc = -EEFI ( efirc ); - DBGC ( efidrv, "EFIBOFM " PCI_FMT " could not register " - "support: %s\n", PCI_ARGS ( &efipci->pci ), + DBGC ( device, "EFIBOFM %p %s could not register support: %s\n", + device, efi_devpath_text ( efipci->path ), strerror ( rc ) ); goto err_cannot_register; } - DBGC ( efidrv, "EFIBOFM " PCI_FMT " is supported by driver \"%s\"\n", - PCI_ARGS ( &efipci->pci ), efipci->pci.id->name ); + DBGC ( device, "EFIBOFM %p %s has driver \"%s\"\n", device, + efi_devpath_text ( efipci->path ), efipci->pci.id->name ); /* Destroy temporary PCI device */ - efipci_destroy ( efidrv, efipci ); + efipci_destroy ( efipci ); return 0; err_cannot_register: err_not_bofm: err_no_driver: - efipci_destroy ( efidrv, efipci ); - err_not_pci: - return EFIRC ( rc ); + efipci_destroy ( efipci ); + err_create: + err_already_started: + return rc; } /** * Attach driver to device * - * @v driver EFI driver * @v device EFI device - * @v child Path to child device, if any - * @ret efirc EFI status code + * @ret rc Return status code */ -static EFI_STATUS EFIAPI efi_bofm_start ( EFI_DRIVER_BINDING_PROTOCOL *driver, - EFI_HANDLE device, - EFI_DEVICE_PATH_PROTOCOL *child ) { - struct efi_driver *efidrv = - container_of ( driver, struct efi_driver, driver ); +static int efi_bofm_start ( EFI_HANDLE device ) { EFI_BOOT_SERVICES *bs = efi_systab->BootServices; union { IBM_BOFM_DRIVER_CONFIGURATION_PROTOCOL *bofm1; @@ -258,14 +250,19 @@ static EFI_STATUS EFIAPI efi_bofm_start ( EFI_DRIVER_BINDING_PROTOCOL *driver, EFI_STATUS efirc; int rc; - DBGCP ( efidrv, "EFIBOFM DRIVER_START %p (%p)\n", device, child ); + /* 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 corresponding PCI device */ - efipci = efipci_create ( efidrv, device ); - if ( ! efipci ) { - rc = -ENOMEM; + if ( ( rc = efipci_create ( device, EFI_OPEN_PROTOCOL_GET_PROTOCOL, + &efipci ) ) != 0 ) goto err_create; - } /* Enable PCI device */ if ( ( rc = efipci_enable ( efipci ) ) != 0 ) @@ -275,51 +272,51 @@ static EFI_STATUS EFIAPI efi_bofm_start ( EFI_DRIVER_BINDING_PROTOCOL *driver, if ( ( efirc = bs->LocateProtocol ( &bofm1_protocol_guid, NULL, &bofm1.interface ) ) != 0 ) { rc = -EEFI ( efirc ); - DBGC ( efidrv, "EFIBOFM " PCI_FMT " cannot find BOFM " - "protocol\n", PCI_ARGS ( &efipci->pci ) ); + DBGC ( device, "EFIBOFM %p %s cannot find BOFM protocol\n", + device, efi_devpath_text ( efipci->path ) ); goto err_locate_bofm; } bofmtab = &bofm1.bofm1->BofmTable; - DBGC ( efidrv, "EFIBOFM " PCI_FMT " found version 1 BOFM table at " - "%p+%04x\n", PCI_ARGS ( &efipci->pci ), bofmtab, + DBGC ( device, "EFIBOFM %p %s found version 1 BOFM table at %p+%04x\n", + device, efi_devpath_text ( efipci->path ), bofmtab, bofmtab->Parameters.Length ); /* Locate BOFM2 protocol, if available */ if ( ( efirc = bs->LocateProtocol ( &bofm2_protocol_guid, NULL, &bofm2.interface ) ) == 0 ) { bofmtab2 = &bofm2.bofm2->BofmTable; - DBGC ( efidrv, "EFIBOFM " PCI_FMT " found version 2 BOFM table " - "at %p+%04x\n", PCI_ARGS ( &efipci->pci ), bofmtab2, - bofmtab2->Parameters.Length ); + DBGC ( device, "EFIBOFM %p %s found version 2 BOFM table at " + "%p+%04x\n", device, efi_devpath_text ( efipci->path ), + bofmtab2, bofmtab2->Parameters.Length ); assert ( bofm2.bofm2->RegisterSupport == bofm1.bofm1->RegisterSupport ); } else { - DBGC ( efidrv, "EFIBOFM " PCI_FMT " cannot find BOFM2 " - "protocol\n", PCI_ARGS ( &efipci->pci ) ); + DBGC ( device, "EFIBOFM %p %s cannot find BOFM2 protocol\n", + device, efi_devpath_text ( efipci->path ) ); /* Not a fatal error; may be a BOFM1-only system */ bofmtab2 = NULL; } /* Process BOFM table */ - DBGC2 ( efidrv, "EFIBOFM " PCI_FMT " version 1 before processing:\n", - PCI_ARGS ( &efipci->pci ) ); - DBGC2_HD ( efidrv, bofmtab, bofmtab->Parameters.Length ); + DBGC2 ( device, "EFIBOFM %p %s version 1 before processing:\n", + device, efi_devpath_text ( efipci->path ) ); + DBGC2_HD ( device, bofmtab, bofmtab->Parameters.Length ); if ( bofmtab2 ) { - DBGC2 ( efidrv, "EFIBOFM " PCI_FMT " version 2 before " - "processing:\n", PCI_ARGS ( &efipci->pci ) ); - DBGC2_HD ( efidrv, bofmtab2, bofmtab2->Parameters.Length ); + DBGC2 ( device, "EFIBOFM %p %s version 2 before processing:\n", + device, efi_devpath_text ( efipci->path ) ); + DBGC2_HD ( device, bofmtab2, bofmtab2->Parameters.Length ); } bofmrc = bofm ( virt_to_user ( bofmtab2 ? bofmtab2 : bofmtab ), &efipci->pci ); - DBGC ( efidrv, "EFIBOFM " PCI_FMT " status %08x\n", - PCI_ARGS ( &efipci->pci ), bofmrc ); - DBGC2 ( efidrv, "EFIBOFM " PCI_FMT " version 1 after processing:\n", - PCI_ARGS ( &efipci->pci ) ); - DBGC2_HD ( efidrv, bofmtab, bofmtab->Parameters.Length ); + DBGC ( device, "EFIBOFM %p %s status %08x\n", + device, efi_devpath_text ( efipci->path ), bofmrc ); + DBGC2 ( device, "EFIBOFM %p %s version 1 after processing:\n", + device, efi_devpath_text ( efipci->path ) ); + DBGC2_HD ( device, bofmtab, bofmtab->Parameters.Length ); if ( bofmtab2 ) { - DBGC2 ( efidrv, "EFIBOFM " PCI_FMT " version 2 after " - "processing:\n", PCI_ARGS ( &efipci->pci ) ); - DBGC2_HD ( efidrv, bofmtab2, bofmtab2->Parameters.Length ); + DBGC2 ( device, "EFIBOFM %p %s version 2 after processing:\n", + device, efi_devpath_text ( efipci->path ) ); + DBGC2_HD ( device, bofmtab2, bofmtab2->Parameters.Length ); } /* Return BOFM status */ @@ -327,8 +324,9 @@ static EFI_STATUS EFIAPI efi_bofm_start ( EFI_DRIVER_BINDING_PROTOCOL *driver, if ( ( efirc = bofm2.bofm2->SetStatus ( bofm2.bofm2, device, FALSE, bofmrc ) ) != 0){ rc = -EEFI ( efirc ); - DBGC ( efidrv, "EFIBOFM " PCI_FMT " could not set " - "BOFM2 status: %s\n", PCI_ARGS ( &efipci->pci ), + DBGC ( device, "EFIBOFM %p %s could not set BOFM2 " + "status: %s\n", + device, efi_devpath_text ( efipci->path ), strerror ( rc ) ); goto err_set_status; } @@ -336,84 +334,42 @@ static EFI_STATUS EFIAPI efi_bofm_start ( EFI_DRIVER_BINDING_PROTOCOL *driver, if ( ( efirc = bofm1.bofm1->SetStatus ( bofm1.bofm1, device, FALSE, bofmrc ) ) != 0){ rc = -EEFI ( efirc ); - DBGC ( efidrv, "EFIBOFM " PCI_FMT " could not set " - "BOFM status: %s\n", PCI_ARGS ( &efipci->pci ), + DBGC ( device, "EFIBOFM %p %s could not set BOFM " + "status: %s\n", + device, efi_devpath_text ( efipci->path ), strerror ( rc ) ); goto err_set_status; } } /* Destroy the PCI device anyway; we have no further use for it */ - efipci_destroy ( efidrv, efipci ); + efipci_destroy ( efipci ); /* BOFM (ab)uses the "start" method to mean "process and exit" */ - return EFI_NOT_READY; + return -EAGAIN; err_set_status: err_locate_bofm: err_enable: - efipci_destroy ( efidrv, efipci ); + efipci_destroy ( efipci ); err_create: - return EFIRC ( rc ); + err_already_started: + return rc; } /** * Detach driver from device * - * @v driver EFI driver * @v device EFI device - * @v num_children Number of child devices - * @v children List of child devices - * @ret efirc EFI status code */ -static EFI_STATUS EFIAPI efi_bofm_stop ( EFI_DRIVER_BINDING_PROTOCOL *driver, - EFI_HANDLE device, UINTN num_children, - EFI_HANDLE *children ) { - struct efi_driver *efidrv = - container_of ( driver, struct efi_driver, driver ); - - DBGCP ( efidrv, "EFIBOFM DRIVER_STOP %p (%ld %p)\n", - device, ( ( unsigned long ) num_children ), children ); - - return 0; +static void efi_bofm_stop ( EFI_HANDLE device __unused ) { + /* Nothing to do */ } /** EFI BOFM driver */ -static struct efi_driver efi_bofm_driver = - EFI_DRIVER_INIT ( "BOFM", - efi_bofm_supported, efi_bofm_start, efi_bofm_stop ); - -/** - * Install EFI BOFM driver - * - */ -static void efi_bofm_driver_startup ( void ) { - struct efi_driver *efidrv = &efi_bofm_driver; - int rc; - - /* Install driver */ - if ( ( rc = efi_driver_install ( efidrv ) ) != 0 ) { - DBGC ( efidrv, "EFIBOFM could not install driver: %s\n", - strerror ( rc ) ); - return; - } - - DBGC ( efidrv, "EFIBOFM driver installed\n" ); -} - -/** - * Shut down EFI BOFM driver - * - * @v booting System is shutting down for OS boot - */ -static void efi_bofm_driver_shutdown ( int booting __unused ) { - struct efi_driver *efidrv = &efi_bofm_driver; - - efi_driver_uninstall ( efidrv ); -} - -/** EFI BOFM startup function */ -struct startup_fn startup_bofm __startup_fn ( STARTUP_EARLY ) = { - .startup = efi_bofm_driver_startup, - .shutdown = efi_bofm_driver_shutdown, +struct efi_driver efi_bofm_driver __efi_driver ( EFI_DRIVER_EARLY ) = { + .name = "BOFM", + .supported = efi_bofm_supported, + .start = efi_bofm_start, + .stop = efi_bofm_stop, }; diff --git a/src/interface/efi/efi_driver.c b/src/interface/efi/efi_driver.c index 6d49eca52..8ef471337 100644 --- a/src/interface/efi/efi_driver.c +++ b/src/interface/efi/efi_driver.c @@ -36,6 +36,8 @@ FILE_LICENCE ( GPL2_OR_LATER ); * */ +static EFI_DRIVER_BINDING_PROTOCOL efi_driver_binding; + /** EFI driver binding protocol GUID */ static EFI_GUID efi_driver_binding_protocol_guid = EFI_DRIVER_BINDING_PROTOCOL_GUID; @@ -62,6 +64,116 @@ EFI_DEVICE_PATH_PROTOCOL * efi_devpath_end ( EFI_DEVICE_PATH_PROTOCOL *path ) { return path; } +/** + * Check to see if driver supports a device + * + * @v driver EFI driver + * @v device EFI device + * @v child Path to child device, if any + * @ret efirc EFI status code + */ +static EFI_STATUS EFIAPI +efi_driver_supported ( EFI_DRIVER_BINDING_PROTOCOL *driver __unused, + EFI_HANDLE device, EFI_DEVICE_PATH_PROTOCOL *child ) { + struct efi_driver *efidrv; + int rc; + + DBGCP ( device, "EFIDRV %p %s DRIVER_SUPPORTED", + device, efi_handle_devpath_text ( device ) ); + if ( child ) + DBGCP ( device, " (child %s)", efi_devpath_text ( child ) ); + DBGCP ( device, "\n" ); + + /* Look for a driver claiming to support this device */ + for_each_table_entry ( efidrv, EFI_DRIVERS ) { + if ( ( rc = efidrv->supported ( device ) ) == 0 ) { + DBGC ( device, "EFIDRV %p %s has driver \"%s\"\n", + device, efi_handle_devpath_text ( device ), + efidrv->name ); + return 0; + } + } + DBGCP ( device, "EFIDRV %p %s has no driver\n", + device, efi_handle_devpath_text ( device ) ); + + return EFI_UNSUPPORTED; +} + +/** + * Attach driver to device + * + * @v driver EFI driver + * @v device EFI device + * @v child Path to child device, if any + * @ret efirc EFI status code + */ +static EFI_STATUS EFIAPI +efi_driver_start ( EFI_DRIVER_BINDING_PROTOCOL *driver __unused, + EFI_HANDLE device, EFI_DEVICE_PATH_PROTOCOL *child ) { + struct efi_driver *efidrv; + int rc; + + DBGC ( device, "EFIDRV %p %s DRIVER_START", + device, efi_handle_devpath_text ( device ) ); + if ( child ) + DBGC ( device, " (child %s)", efi_devpath_text ( child ) ); + DBGC ( device, "\n" ); + + /* Try to start this device */ + for_each_table_entry ( efidrv, EFI_DRIVERS ) { + if ( ( rc = efidrv->start ( device ) ) == 0 ) { + DBGC ( device, "EFIDRV %p %s using driver \"%s\"\n", + device, efi_handle_devpath_text ( device ), + efidrv->name ); + return 0; + } + DBGC ( device, "EFIDRV %p %s could not start driver \"%s\": " + "%s\n", device, efi_handle_devpath_text ( device ), + efidrv->name, strerror ( rc ) ); + } + + return EFI_UNSUPPORTED; +} + +/** + * Detach driver from device + * + * @v driver EFI driver + * @v device EFI device + * @v pci PCI device + * @v num_children Number of child devices + * @v children List of child devices + * @ret efirc EFI status code + */ +static EFI_STATUS EFIAPI +efi_driver_stop ( EFI_DRIVER_BINDING_PROTOCOL *driver __unused, + EFI_HANDLE device, UINTN num_children, + EFI_HANDLE *children ) { + struct efi_driver *efidrv; + UINTN i; + + DBGC ( device, "EFIDRV %p %s DRIVER_STOP", + device, efi_handle_devpath_text ( device ) ); + for ( i = 0 ; i < num_children ; i++ ) { + DBGC ( device, "%s%p %s", ( i ? ", " : " child " ), + children[i], efi_handle_devpath_text ( children[i] ) ); + } + DBGC ( device, "\n" ); + + /* Try to stop this device */ + for_each_table_entry ( efidrv, EFI_DRIVERS ) + efidrv->stop ( device ); + + return 0; +} + +/** EFI driver binding protocol */ +static EFI_DRIVER_BINDING_PROTOCOL efi_driver_binding = { + .Supported = efi_driver_supported, + .Start = efi_driver_start, + .Stop = efi_driver_stop, +}; + /** * Look up driver name * @@ -71,12 +183,12 @@ EFI_DEVICE_PATH_PROTOCOL * efi_devpath_end ( EFI_DEVICE_PATH_PROTOCOL *path ) { * @ret efirc EFI status code */ static EFI_STATUS EFIAPI -efi_driver_get_driver_name ( EFI_COMPONENT_NAME2_PROTOCOL *wtf, - CHAR8 *language __unused, CHAR16 **driver_name ) { - struct efi_driver *efidrv = - container_of ( wtf, struct efi_driver, wtf ); +efi_driver_name ( EFI_COMPONENT_NAME2_PROTOCOL *wtf __unused, + CHAR8 *language __unused, CHAR16 **driver_name ) { + const wchar_t *name; - *driver_name = efidrv->wname; + name = ( product_wname[0] ? product_wname : build_wname ); + *driver_name = ( ( wchar_t * ) name ); return 0; } @@ -91,9 +203,9 @@ efi_driver_get_driver_name ( EFI_COMPONENT_NAME2_PROTOCOL *wtf, * @ret efirc EFI status code */ static EFI_STATUS EFIAPI -efi_driver_get_controller_name ( EFI_COMPONENT_NAME2_PROTOCOL *wtf __unused, - EFI_HANDLE device, EFI_HANDLE child, - CHAR8 *language, CHAR16 **controller_name ) { +efi_driver_controller_name ( EFI_COMPONENT_NAME2_PROTOCOL *wtf __unused, + EFI_HANDLE device, EFI_HANDLE child, + CHAR8 *language, CHAR16 **controller_name ) { EFI_BOOT_SERVICES *bs = efi_systab->BootServices; union { EFI_COMPONENT_NAME2_PROTOCOL *name2; @@ -118,43 +230,139 @@ efi_driver_get_controller_name ( EFI_COMPONENT_NAME2_PROTOCOL *wtf __unused, return EFI_UNSUPPORTED; } +/** EFI component name protocol */ +static EFI_COMPONENT_NAME2_PROTOCOL efi_wtf = { + .GetDriverName = efi_driver_name, + .GetControllerName = efi_driver_controller_name, + .SupportedLanguages = "en", +}; + +/** + * Install EFI driver + * + * @ret rc Return status code + */ +int efi_driver_install ( void ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + EFI_STATUS efirc; + int rc; + + /* Calculate driver version number. We use the build + * timestamp (in seconds since the Epoch) shifted right by six + * bits: this gives us an approximately one-minute resolution + * and a scheme which will last until the year 10680. + */ + efi_driver_binding.Version = ( build_timestamp >> 6 ); + + /* Install protocols on image handle */ + efi_driver_binding.ImageHandle = efi_image_handle; + efi_driver_binding.DriverBindingHandle = efi_image_handle; + if ( ( efirc = bs->InstallMultipleProtocolInterfaces ( + &efi_image_handle, + &efi_driver_binding_protocol_guid, &efi_driver_binding, + &efi_component_name2_protocol_guid, &efi_wtf, + NULL ) ) != 0 ) { + rc = -EEFI ( efirc ); + DBGC ( &efi_driver_binding, "EFIDRV could not install " + "protocols: %s\n", strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** + * Uninstall EFI driver + * + */ +void efi_driver_uninstall ( void ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + + /* Uninstall protocols */ + bs->UninstallMultipleProtocolInterfaces ( + efi_image_handle, + &efi_driver_binding_protocol_guid, &efi_driver_binding, + &efi_component_name2_protocol_guid, &efi_wtf, NULL ); +} + /** * Try to connect EFI driver * - * @v efidrv EFI driver - * @v handle Controller handle + * @v device EFI device + * @ret rc Return status code */ -static void efi_driver_connect ( struct efi_driver *efidrv, EFI_HANDLE handle ){ +static int efi_driver_connect ( EFI_HANDLE device ) { EFI_BOOT_SERVICES *bs = efi_systab->BootServices; - EFI_HANDLE drivers[2] = { efidrv->driver.DriverBindingHandle, NULL }; + EFI_HANDLE drivers[2] = + { efi_driver_binding.DriverBindingHandle, NULL }; + EFI_STATUS efirc; + int rc; - bs->ConnectController ( handle, drivers, NULL, FALSE ); + /* Check if we want to drive this device */ + if ( ( efirc = efi_driver_supported ( &efi_driver_binding, device, + NULL ) ) != 0 ) { + /* Not supported; not an error */ + return 0; + } + + /* Disconnect any existing drivers */ + DBGC ( device, "EFIDRV %p %s disconnecting existing drivers\n", + device, efi_handle_devpath_text ( device ) ); + bs->DisconnectController ( device, NULL, NULL ); + + /* Connect our driver */ + DBGC ( device, "EFIDRV %p %s connecting new drivers\n", + device, efi_handle_devpath_text ( device ) ); + if ( ( efirc = bs->ConnectController ( device, drivers, NULL, + FALSE ) ) != 0 ) { + rc = -EEFI ( efirc ); + DBGC ( device, "EFIDRV %p %s could not connect new drivers: " + "%s\n", device, efi_handle_devpath_text ( device ), + strerror ( rc ) ); + return rc; + } + + return 0; } /** * Try to disconnect EFI driver * - * @v efidrv EFI driver - * @v handle Controller handle + * @v device EFI device + * @ret rc Return status code */ -static void efi_driver_disconnect ( struct efi_driver *efidrv, - EFI_HANDLE handle ) { +static int efi_driver_disconnect ( EFI_HANDLE device ) { EFI_BOOT_SERVICES *bs = efi_systab->BootServices; - bs->DisconnectController ( handle, efidrv->driver.DriverBindingHandle, + /* Disconnect our driver */ + bs->DisconnectController ( device, + efi_driver_binding.DriverBindingHandle, NULL ); + return 0; +} + +/** + * Reconnect original EFI driver + * + * @v device EFI device + * @ret rc Return status code + */ +static int efi_driver_reconnect ( EFI_HANDLE device ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + + /* Reconnect any available driver */ + bs->ConnectController ( device, NULL, NULL, FALSE ); + + return 0; } /** * Connect/disconnect EFI driver from all handles * - * @v efidrv EFI driver * @v method Connect/disconnect method * @ret rc Return status code */ -static int efi_driver_handles ( struct efi_driver *efidrv, - void ( * method ) ( struct efi_driver *efidrv, - EFI_HANDLE handle ) ) { +static int efi_driver_handles ( int ( * method ) ( EFI_HANDLE handle ) ) { EFI_BOOT_SERVICES *bs = efi_systab->BootServices; EFI_HANDLE *handles; UINTN num_handles; @@ -167,87 +375,55 @@ static int efi_driver_handles ( struct efi_driver *efidrv, &num_handles, &handles ) ) != 0 ) { rc = -EEFI ( efirc ); - DBGC ( efidrv, "EFIDRV %s could not list handles: %s\n", - efidrv->name, strerror ( rc ) ); - return rc; + DBGC ( &efi_driver_binding, "EFIDRV could not list handles: " + "%s\n", strerror ( rc ) ); + goto err_locate; } /* Connect/disconnect driver from all handles */ - for ( i = 0 ; i < num_handles ; i++ ) - method ( efidrv, handles[i] ); - - /* Free list of handles */ - bs->FreePool ( handles ); - - return 0; -} - -/** - * Install EFI driver - * - * @v efidrv EFI driver - * @ret rc Return status code - */ -int efi_driver_install ( struct efi_driver *efidrv ) { - EFI_BOOT_SERVICES *bs = efi_systab->BootServices; - EFI_DRIVER_BINDING_PROTOCOL *driver = &efidrv->driver; - EFI_COMPONENT_NAME2_PROTOCOL *wtf = &efidrv->wtf; - EFI_STATUS efirc; - int rc; - - /* Configure driver binding protocol */ - driver->ImageHandle = efi_image_handle; - - /* Configure component name protocol */ - wtf->GetDriverName = efi_driver_get_driver_name; - wtf->GetControllerName = efi_driver_get_controller_name; - wtf->SupportedLanguages = "en"; - - /* Fill in driver name */ - efi_snprintf ( efidrv->wname, - ( sizeof ( efidrv->wname ) / - sizeof ( efidrv->wname[0] ) ), - "%s%s%s", product_short_name, - ( efidrv->name ? " - " : "" ), - ( efidrv->name ? efidrv->name : "" ) ); - - /* Install driver */ - if ( ( efirc = bs->InstallMultipleProtocolInterfaces ( - &driver->DriverBindingHandle, - &efi_driver_binding_protocol_guid, driver, - &efi_component_name2_protocol_guid, wtf, - NULL ) ) != 0 ) { - rc = -EEFI ( efirc ); - DBGC ( efidrv, "EFIDRV %s could not install protocol: %s\n", - efidrv->name, strerror ( rc ) ); - return rc; + for ( i = 0 ; i < num_handles ; i++ ) { + if ( ( rc = method ( handles[i] ) ) != 0 ) + goto err_method; } - /* Connect devices */ - DBGC ( efidrv, "EFIDRV %s connecting devices\n", efidrv->name ); - efi_driver_handles ( efidrv, efi_driver_connect ); + /* Success */ + rc = 0; - DBGC ( efidrv, "EFIDRV %s installed\n", efidrv->name ); - return 0; + err_method: + bs->FreePool ( handles ); + err_locate: + return rc; } /** - * Uninstall EFI driver + * Connect EFI driver to all possible devices * - * @v efidrv EFI driver + * @ret rc Return status code */ -void efi_driver_uninstall ( struct efi_driver *efidrv ) { - EFI_BOOT_SERVICES *bs = efi_systab->BootServices; +int efi_driver_connect_all ( void ) { - /* Disconnect the driver from its devices */ - DBGC ( efidrv, "EFIDRV %s disconnecting devices\n", efidrv->name ); - efi_driver_handles ( efidrv, efi_driver_disconnect ); - - /* Uninstall the driver */ - bs->UninstallMultipleProtocolInterfaces ( - efidrv->driver.DriverBindingHandle, - &efi_driver_binding_protocol_guid, &efidrv->driver, - &efi_component_name2_protocol_guid, &efidrv->wtf, - NULL ); - DBGC ( efidrv, "EFIDRV %s uninstalled\n", efidrv->name ); + DBGC ( &efi_driver_binding, "EFIDRV connecting our drivers\n" ); + return efi_driver_handles ( efi_driver_connect ); +} + +/** + * Disconnect EFI driver from all possible devices + * + * @ret rc Return status code + */ +void efi_driver_disconnect_all ( void ) { + + DBGC ( &efi_driver_binding, "EFIDRV disconnecting our drivers\n" ); + efi_driver_handles ( efi_driver_disconnect ); +} + +/** + * Reconnect original EFI drivers to all possible devices + * + * @ret rc Return status code + */ +void efi_driver_reconnect_all ( void ) { + + DBGC ( &efi_driver_binding, "EFIDRV reconnecting old drivers\n" ); + efi_driver_handles ( efi_driver_reconnect ); } diff --git a/src/interface/efi/efi_init.c b/src/interface/efi/efi_init.c index 5eb101350..4204b79b3 100644 --- a/src/interface/efi/efi_init.c +++ b/src/interface/efi/efi_init.c @@ -21,12 +21,10 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include #include +#include #include #include #include -#include -#include -#include /** Image handle passed to entry point */ EFI_HANDLE efi_image_handle; @@ -47,61 +45,6 @@ static EFI_EVENT efi_shutdown_event; /* Forward declarations */ static EFI_STATUS EFIAPI efi_unload ( EFI_HANDLE image_handle ); -/** - * Check to see if driver supports a device - * - * @v driver EFI driver - * @v device EFI device - * @v child Path to child device, if any - * @ret efirc EFI status code - */ -static EFI_STATUS EFIAPI -efi_image_supported ( EFI_DRIVER_BINDING_PROTOCOL *driver __unused, - EFI_HANDLE device __unused, - EFI_DEVICE_PATH_PROTOCOL *child __unused ) { - - return EFI_UNSUPPORTED; -} - -/** - * Attach driver to device - * - * @v driver EFI driver - * @v device EFI device - * @v child Path to child device, if any - * @ret efirc EFI status code - */ -static EFI_STATUS EFIAPI -efi_image_start ( EFI_DRIVER_BINDING_PROTOCOL *driver __unused, - EFI_HANDLE device __unused, - EFI_DEVICE_PATH_PROTOCOL *child __unused ) { - - return EFI_UNSUPPORTED; -} - -/** - * Detach driver from device - * - * @v driver EFI driver - * @v device EFI device - * @v pci PCI device - * @v num_children Number of child devices - * @v children List of child devices - * @ret efirc EFI status code - */ -static EFI_STATUS EFIAPI -efi_image_stop ( EFI_DRIVER_BINDING_PROTOCOL *driver __unused, - EFI_HANDLE device __unused, UINTN num_children __unused, - EFI_HANDLE *children __unused ) { - - return EFI_UNSUPPORTED; -} - -/** EFI loaded image driver */ -static struct efi_driver efi_image_driver = - EFI_DRIVER_INIT ( NULL, efi_image_supported, efi_image_start, - efi_image_stop ); - /** * Shut down in preparation for booting an OS. * @@ -153,18 +96,24 @@ EFI_STATUS efi_init ( EFI_HANDLE image_handle, efi_systab = systab; /* Sanity checks */ - if ( ! systab ) - return EFI_NOT_AVAILABLE_YET; - if ( ! systab->ConOut ) - return EFI_NOT_AVAILABLE_YET; + if ( ! systab ) { + efirc = EFI_NOT_AVAILABLE_YET; + goto err_sanity; + } + if ( ! systab->ConOut ) { + efirc = EFI_NOT_AVAILABLE_YET; + goto err_sanity; + } if ( ! systab->BootServices ) { DBGC ( systab, "EFI provided no BootServices entry point\n" ); - return EFI_NOT_AVAILABLE_YET; + efirc = EFI_NOT_AVAILABLE_YET; + goto err_sanity; } if ( ! systab->RuntimeServices ) { DBGC ( systab, "EFI provided no RuntimeServices entry " "point\n" ); - return EFI_NOT_AVAILABLE_YET; + efirc = EFI_NOT_AVAILABLE_YET; + goto err_sanity; } DBGC ( systab, "EFI handle %p systab %p\n", image_handle, systab ); bs = systab->BootServices; @@ -181,7 +130,7 @@ EFI_STATUS efi_init ( EFI_HANDLE image_handle, efi_guid_ntoa ( &prot->guid ) ); /* Fail if protocol is required */ if ( prot->required ) - return efirc; + goto err_missing_protocol; } } @@ -193,8 +142,10 @@ EFI_STATUS efi_init ( EFI_HANDLE image_handle, } else { DBGC ( systab, "EFI does not provide configuration " "table %s\n", efi_guid_ntoa ( &tab->guid ) ); - if ( tab->required ) - return EFI_NOT_AVAILABLE_YET; + if ( tab->required ) { + efirc = EFI_NOT_AVAILABLE_YET; + goto err_missing_table; + } } } @@ -206,7 +157,7 @@ EFI_STATUS efi_init ( EFI_HANDLE image_handle, rc = -EEFI ( efirc ); DBGC ( systab, "EFI could not get loaded image protocol: %s", strerror ( rc ) ); - return efirc; + goto err_no_loaded_image; } efi_loaded_image = loaded_image; DBGC ( systab, "EFI image base address %p\n", @@ -223,23 +174,31 @@ EFI_STATUS efi_init ( EFI_HANDLE image_handle, rc = -EEFI ( efirc ); DBGC ( systab, "EFI could not create ExitBootServices event: " "%s\n", strerror ( rc ) ); - return efirc; + goto err_create_event; } - /* Install an EFI driver on the image handle, to allow the - * driver to be subsequently unloaded. - */ - efi_image_driver.driver.DriverBindingHandle = image_handle; - if ( ( rc = efi_driver_install ( &efi_image_driver ) ) != 0 ) { - DBGC ( systab, "EFI could not install loaded image driver: " - "%s\n", strerror ( rc ) ); - return EFIRC ( rc ); + /* Install driver binding protocol */ + if ( ( rc = efi_driver_install() ) != 0 ) { + DBGC ( systab, "EFI could not install driver: %s\n", + strerror ( rc ) ); + efirc = EFIRC ( rc ); + goto err_driver_install; } /* Install image unload method */ efi_loaded_image->Unload = efi_unload; return 0; + + efi_driver_uninstall(); + err_driver_install: + bs->CloseEvent ( efi_shutdown_event ); + err_create_event: + err_no_loaded_image: + err_missing_table: + err_missing_protocol: + err_sanity: + return efirc; } /** @@ -256,12 +215,15 @@ static EFI_STATUS EFIAPI efi_unload ( EFI_HANDLE image_handle __unused ) { /* Shut down */ shutdown_exit(); + /* Disconnect any remaining devices */ + efi_driver_disconnect_all(); + + /* Uninstall driver binding protocol */ + efi_driver_uninstall(); + /* Uninstall exit boot services event */ bs->CloseEvent ( efi_shutdown_event ); - /* Uninstall loaded image driver */ - efi_driver_uninstall ( &efi_image_driver ); - DBGC ( systab, "EFI image unloaded\n" ); return 0; diff --git a/src/interface/efi/efi_pci.c b/src/interface/efi/efi_pci.c index dc7304a35..dd65b20f1 100644 --- a/src/interface/efi/efi_pci.c +++ b/src/interface/efi/efi_pci.c @@ -22,7 +22,6 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include #include #include -#include #include #include #include @@ -35,6 +34,22 @@ 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" ) +#define EINFO_EEFI_PCI_NOT_PCI \ + __einfo_platformify ( EINFO_EEFI_PCI, EFI_UNSUPPORTED, \ + "Not a PCI device" ) +#define EEFI_PCI_NOT_PCI __einfo_error ( EINFO_EEFI_PCI_NOT_PCI ) +#define EINFO_EEFI_PCI_IN_USE \ + __einfo_platformify ( EINFO_EEFI_PCI, EFI_ACCESS_DENIED, \ + "PCI device already has a driver" ) +#define EEFI_PCI_IN_USE __einfo_error ( EINFO_EEFI_PCI_IN_USE ) +#define EEFI_PCI( efirc ) \ + EPLATFORM ( EINFO_EEFI_PCI, efirc, \ + EEFI_PCI_NOT_PCI, EEFI_PCI_IN_USE ) + /****************************************************************************** * * iPXE PCI API @@ -119,14 +134,14 @@ static LIST_HEAD ( efi_pci_devices ); /** * Create EFI PCI device * - * @v efidrv EFI driver * @v device EFI device - * @ret efipci EFI PCI device, or NULL + * @v attributes Protocol opening attributes + * @v efipci EFI PCI device to fill in + * @ret rc Return status code */ -struct efi_pci_device * efipci_create ( struct efi_driver *efidrv, - EFI_HANDLE device ) { +int efipci_create ( EFI_HANDLE device, UINT32 attributes, + struct efi_pci_device **efipci ) { EFI_BOOT_SERVICES *bs = efi_systab->BootServices; - struct efi_pci_device *efipci; union { EFI_PCI_IO_PROTOCOL *pci_io; void *interface; @@ -140,25 +155,26 @@ struct efi_pci_device * efipci_create ( struct efi_driver *efidrv, int rc; /* Allocate PCI device */ - efipci = zalloc ( sizeof ( *efipci ) ); - if ( ! efipci ) + *efipci = zalloc ( sizeof ( **efipci ) ); + if ( ! *efipci ) { + rc = -ENOMEM; goto err_zalloc; - efipci->device = device; - efipci->efidrv = efidrv; + } + (*efipci)->device = device; /* See if device is a PCI device */ if ( ( efirc = bs->OpenProtocol ( device, &efi_pci_io_protocol_guid, &pci_io.interface, - efidrv->driver.DriverBindingHandle, - device, - EFI_OPEN_PROTOCOL_BY_DRIVER )) !=0 ){ - rc = -EEFI ( efirc ); - DBGCP ( efipci, "EFIPCI device %p is not a PCI device\n", - device ); + efi_image_handle, + device, attributes ) ) != 0 ) { + rc = -EEFI_PCI ( efirc ); + DBGCP ( device, "EFIPCI %p %s cannot open PCI protocols: %s\n", + device, efi_handle_devpath_text ( device ), + strerror ( rc ) ); goto err_open_protocol; } - efipci->pci_io = pci_io.pci_io; + (*efipci)->pci_io = pci_io.pci_io; /* Get PCI bus:dev.fn address */ if ( ( efirc = pci_io.pci_io->GetLocation ( pci_io.pci_io, @@ -166,54 +182,53 @@ struct efi_pci_device * efipci_create ( struct efi_driver *efidrv, &pci_bus, &pci_dev, &pci_fn ) ) != 0 ) { rc = -EEFI ( efirc ); - DBGC ( efipci, "EFIPCI device %p could not get PCI " - "location: %s\n", device, strerror ( rc ) ); + DBGC ( device, "EFIPCI %p %s could not get PCI location: " + "%s\n", device, efi_handle_devpath_text ( device ), + strerror ( rc ) ); goto err_get_location; } - DBGC2 ( efipci, "EFIPCI device %p is PCI %04lx:%02lx:%02lx.%lx\n", - device, ( ( unsigned long ) pci_segment ), - ( ( unsigned long ) pci_bus ), ( ( unsigned long ) pci_dev ), - ( ( unsigned long ) pci_fn ) ); + DBGC2 ( device, "EFIPCI %p %s is PCI %04lx:%02lx:%02lx.%lx\n", + device, efi_handle_devpath_text ( device ), + ( ( unsigned long ) pci_segment ), ( ( unsigned long ) pci_bus), + ( ( unsigned long ) pci_dev ), ( ( unsigned long ) pci_fn ) ); /* Populate PCI device */ - pci_init ( &efipci->pci, PCI_BUSDEVFN ( pci_bus, pci_dev, pci_fn ) ); - if ( ( rc = pci_read_config ( &efipci->pci ) ) != 0 ) { - DBGC ( efipci, "EFIPCI " PCI_FMT " cannot read PCI " - "configuration: %s\n", - PCI_ARGS ( &efipci->pci ), strerror ( rc ) ); + pci_init ( &(*efipci)->pci, PCI_BUSDEVFN ( pci_bus, pci_dev, pci_fn ) ); + if ( ( rc = pci_read_config ( &(*efipci)->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, - efidrv->driver.DriverBindingHandle, - device, - EFI_OPEN_PROTOCOL_BY_DRIVER )) !=0 ){ + &path.interface, efi_image_handle, + device, attributes ) ) != 0 ) { rc = -EEFI ( efirc ); - DBGC ( efipci, "EFIPCI " PCI_FMT " has no device path\n", - PCI_ARGS ( &efipci->pci ) ); + 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; + (*efipci)->path = path.path; /* Add to list of PCI devices */ - list_add ( &efipci->list, &efi_pci_devices ); + list_add ( &(*efipci)->list, &efi_pci_devices ); - return efipci; + return 0; bs->CloseProtocol ( device, &efi_device_path_protocol_guid, - efidrv->driver.DriverBindingHandle, device ); + efi_image_handle, device ); err_no_device_path: err_pci_read_config: err_get_location: bs->CloseProtocol ( device, &efi_pci_io_protocol_guid, - efidrv->driver.DriverBindingHandle, device ); + efi_image_handle, device ); err_open_protocol: - free ( efipci ); + free ( *efipci ); err_zalloc: - return NULL; + return rc; } /** @@ -282,7 +297,6 @@ struct efi_pci_device * efipci_find ( struct device *dev ) { */ int 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; @@ -294,16 +308,21 @@ int efipci_child_add ( struct efi_pci_device *efipci, EFI_HANDLE device ) { if ( ( efirc = bs->OpenProtocol ( efipci->device, &efi_pci_io_protocol_guid, &pci_io.interface, - efidrv->driver.DriverBindingHandle, - device, + efi_image_handle, device, EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER ) ) != 0 ) { rc = -EEFI ( efirc ); - DBGC ( efipci, "EFIPCI " PCI_FMT " could not add child: %s\n", - PCI_ARGS ( &efipci->pci ), strerror ( rc ) ); + 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 ) ); 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; } @@ -316,29 +335,28 @@ int efipci_child_add ( struct efi_pci_device *efipci, EFI_HANDLE device ) { */ 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 ); + 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 efidrv EFI driver * @v efipci EFI PCI device */ -void efipci_destroy ( struct efi_driver *efidrv, - struct efi_pci_device *efipci ) { +void efipci_destroy ( struct efi_pci_device *efipci ) { EFI_BOOT_SERVICES *bs = efi_systab->BootServices; list_del ( &efipci->list ); bs->CloseProtocol ( efipci->device, &efi_device_path_protocol_guid, - efidrv->driver.DriverBindingHandle, - efipci->device ); + efi_image_handle, efipci->device ); bs->CloseProtocol ( efipci->device, &efi_pci_io_protocol_guid, - efidrv->driver.DriverBindingHandle, - efipci->device ); + efi_image_handle, efipci->device ); free ( efipci ); } @@ -352,79 +370,78 @@ void efipci_destroy ( struct efi_driver *efidrv, /** * Check to see if driver supports a device * - * @v driver EFI driver * @v device EFI device - * @v child Path to child device, if any - * @ret efirc EFI status code + * @ret rc Return status code */ -static EFI_STATUS EFIAPI -efipci_supported ( EFI_DRIVER_BINDING_PROTOCOL *driver, EFI_HANDLE device, - EFI_DEVICE_PATH_PROTOCOL *child ) { - struct efi_driver *efidrv = - container_of ( driver, struct efi_driver, driver ); +static int efipci_supported ( EFI_HANDLE device ) { struct efi_pci_device *efipci; int rc; - DBGCP ( efidrv, "EFIPCI DRIVER_SUPPORTED %p (%p)\n", device, child ); + /* 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 */ - efipci = efipci_create ( efidrv, device ); - if ( ! efipci ) { - /* Non-PCI devices are simply unsupported */ - rc = -ENOTSUP; - goto err_not_pci; - } + if ( ( rc = efipci_create ( device, EFI_OPEN_PROTOCOL_GET_PROTOCOL, + &efipci ) ) != 0 ) + goto err_create; /* Look for a driver */ if ( ( rc = pci_find_driver ( &efipci->pci ) ) != 0 ) { - DBGCP ( efipci, "EFIPCI " PCI_FMT " has no driver\n", - PCI_ARGS ( &efipci->pci ) ); + DBGCP ( device, "EFIPCI %p %s has no driver\n", + device, efi_devpath_text ( efipci->path ) ); goto err_no_driver; } - DBGC ( efipci, "EFIPCI " PCI_FMT " is supported by driver \"%s\"\n", - PCI_ARGS ( &efipci->pci ), efipci->pci.id->name ); + 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 ( efidrv, efipci ); + efipci_destroy ( efipci ); return 0; err_no_driver: - efipci_destroy ( efidrv, efipci ); - err_not_pci: - return EFIRC ( rc ); + efipci_destroy ( efipci ); + err_create: + err_already_started: + return rc; } /** * Attach driver to device * - * @v driver EFI driver * @v device EFI device - * @v child Path to child device, if any - * @ret efirc EFI status code + * @ret rc Return status code */ -static EFI_STATUS EFIAPI -efipci_start ( EFI_DRIVER_BINDING_PROTOCOL *driver, EFI_HANDLE device, - EFI_DEVICE_PATH_PROTOCOL *child ) { - struct efi_driver *efidrv = - container_of ( driver, struct efi_driver, driver ); +static int efipci_start ( EFI_HANDLE device ) { struct efi_pci_device *efipci; int rc; - DBGC ( efidrv, "EFIPCI DRIVER_START %p (%p)\n", device, child ); + /* 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 corresponding PCI device */ - efipci = efipci_create ( efidrv, device ); - if ( ! efipci ) { - rc = -ENOMEM; + if ( ( rc = efipci_create ( device, ( EFI_OPEN_PROTOCOL_BY_DRIVER | + EFI_OPEN_PROTOCOL_EXCLUSIVE ), + &efipci ) ) != 0 ) goto err_create; - } /* Find driver */ if ( ( rc = pci_find_driver ( &efipci->pci ) ) != 0 ) { - DBGC ( efipci, "EFIPCI " PCI_FMT " has no driver\n", - PCI_ARGS ( &efipci->pci ) ); + DBGC ( device, "EFIPCI %p %s has no driver\n", + device, efi_devpath_text ( efipci->path ) ); goto err_find_driver; } @@ -434,11 +451,13 @@ efipci_start ( EFI_DRIVER_BINDING_PROTOCOL *driver, EFI_HANDLE device, /* Probe driver */ if ( ( rc = pci_probe ( &efipci->pci ) ) != 0 ) { - DBGC ( efipci, "EFIPCI " PCI_FMT " could not probe driver " - "\"%s\": %s\n", PCI_ARGS ( &efipci->pci ), + DBGC ( device, "EFIPCI %p %s could not probe driver \"%s\": " + "%s\n", device, efi_devpath_text ( efipci->path ), efipci->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 ); return 0; @@ -446,93 +465,36 @@ efipci_start ( EFI_DRIVER_BINDING_PROTOCOL *driver, EFI_HANDLE device, err_probe: err_enable: err_find_driver: - efipci_destroy ( efidrv, efipci ); + efipci_destroy ( efipci ); err_create: - return EFIRC ( rc ); + err_already_started: + return rc; } /** * Detach driver from device * - * @v driver EFI driver * @v device EFI device - * @v pci PCI device - * @v num_children Number of child devices - * @v children List of child devices - * @ret efirc EFI status code - */ -static EFI_STATUS EFIAPI -efipci_stop ( EFI_DRIVER_BINDING_PROTOCOL *driver, EFI_HANDLE device, - UINTN num_children, EFI_HANDLE *children ) { - struct efi_driver *efidrv = - container_of ( driver, struct efi_driver, driver ); + */ +static void efipci_stop ( EFI_HANDLE device ) { struct efi_pci_device *efipci; - DBGC ( efidrv, "EFIPCI DRIVER_STOP %p (%ld %p)\n", - device, ( ( unsigned long ) num_children ), children ); - /* Find PCI device */ efipci = efipci_find_efi ( device ); - if ( ! efipci ) { - DBGC ( efidrv, "EFIPCI device %p not started!\n", device ); - return EFI_INVALID_PARAMETER; - } + if ( ! efipci ) + return; /* Remove device */ pci_remove ( &efipci->pci ); /* Delete EFI PCI device */ - efipci_destroy ( efidrv, efipci ); - - return 0; + efipci_destroy ( efipci ); } /** EFI PCI driver */ -static struct efi_driver efipci_driver = - EFI_DRIVER_INIT ( "PCI", efipci_supported, efipci_start, efipci_stop ); - -/** - * Install EFI PCI driver - * - */ -static void efipci_driver_startup ( void ) { - struct efi_driver *efidrv = &efipci_driver; - int rc; - - /* Install driver */ - if ( ( rc = efi_driver_install ( efidrv ) ) != 0 ) { - DBGC ( efidrv, "EFIPCI could not install driver: %s\n", - strerror ( rc ) ); - return; - } - - DBGC ( efidrv, "EFIPCI driver installed\n" ); -} - -/** - * Shut down EFI PCI driver - * - * @v booting System is shutting down for OS boot - */ -static void efipci_driver_shutdown ( int booting __unused ) { - struct efi_driver *efidrv = &efipci_driver; - struct efi_pci_device *efipci; - struct efi_pci_device *tmp; - - /* Uninstall driver */ - efi_driver_uninstall ( efidrv ); - - /* Shut down any remaining devices */ - list_for_each_entry_safe ( efipci, tmp, &efi_pci_devices, list ) { - DBGC ( efipci, "EFIPCI " PCI_FMT " still active at shutdown; " - "forcing close\n", PCI_ARGS ( &efipci->pci ) ); - pci_remove ( &efipci->pci ); - efipci_destroy ( efidrv, efipci ); - } -} - -/** EFI PCI startup function */ -struct startup_fn startup_pci __startup_fn ( STARTUP_NORMAL ) = { - .startup = efipci_driver_startup, - .shutdown = efipci_driver_shutdown, +struct efi_driver efipci_driver __efi_driver ( EFI_DRIVER_NORMAL ) = { + .name = "PCI", + .supported = efipci_supported, + .start = efipci_start, + .stop = efipci_stop, };