2
0
mirror of https://github.com/xcat2/xNBA.git synced 2024-11-22 01:21:45 +00:00

[efi] Restructure EFI driver model

Provide a single instance of EFI_DRIVER_BINDING_PROTOCOL (attached to
our image handle); this matches the expectations scattered throughout
the EFI specification.

Open the underlying hardware device using EFI_OPEN_PROTOCOL_BY_DRIVER
and EFI_OPEN_PROTOCOL_EXCLUSIVE, to prevent other drivers from
attaching to the same device.

Do not automatically connect to devices when being loaded as a driver;
leave this task to the platform firmware (or to the user, if loading
directly from the EFI shell).

When running as an application, forcibly disconnect any existing
drivers from devices that we want to control, and reconnect them on
exit.

Provide a meaningful driver version number (based on the build
timestamp), to allow platform firmware to automatically load newer
versions of iPXE drivers if multiple drivers are present.

Include device paths within debug messages where possible, to aid in
debugging.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
This commit is contained in:
Michael Brown 2014-06-25 14:47:35 +01:00
parent f2c116ff7d
commit 0e3ab6064e
7 changed files with 590 additions and 494 deletions

View File

@ -21,7 +21,9 @@ FILE_LICENCE ( GPL2_OR_LATER );
#include <stdlib.h>
#include <errno.h>
#include <ipxe/device.h>
#include <ipxe/efi/efi.h>
#include <ipxe/efi/efi_driver.h>
/**
* 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,
};

View File

@ -8,43 +8,52 @@
FILE_LICENCE ( GPL2_OR_LATER );
#include <ipxe/tables.h>
#include <ipxe/efi/efi.h>
#include <ipxe/efi/Protocol/DriverBinding.h>
#include <ipxe/efi/Protocol/ComponentName2.h>
#include <ipxe/efi/Protocol/DevicePath.h>
/** 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 */

View File

@ -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 */

View File

@ -21,7 +21,6 @@ FILE_LICENCE ( GPL2_OR_LATER );
#include <errno.h>
#include <ipxe/bofm.h>
#include <ipxe/init.h>
#include <ipxe/efi/efi.h>
#include <ipxe/efi/efi_pci.h>
#include <ipxe/efi/efi_driver.h>
@ -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,
};

View File

@ -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 );
}

View File

@ -21,12 +21,10 @@ FILE_LICENCE ( GPL2_OR_LATER );
#include <string.h>
#include <errno.h>
#include <ipxe/init.h>
#include <ipxe/efi/efi.h>
#include <ipxe/efi/efi_driver.h>
#include <ipxe/efi/Protocol/LoadedImage.h>
#include <ipxe/efi/Protocol/DevicePath.h>
#include <ipxe/uuid.h>
#include <ipxe/init.h>
/** 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;

View File

@ -22,7 +22,6 @@ FILE_LICENCE ( GPL2_OR_LATER );
#include <stdlib.h>
#include <errno.h>
#include <ipxe/pci.h>
#include <ipxe/init.h>
#include <ipxe/efi/efi.h>
#include <ipxe/efi/efi_pci.h>
#include <ipxe/efi/efi_driver.h>
@ -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,
};