diff --git a/src/arch/x86/prefix/efidrvprefix.c b/src/arch/x86/prefix/efidrvprefix.c index 2215104b..a96c5c43 100644 --- a/src/arch/x86/prefix/efidrvprefix.c +++ b/src/arch/x86/prefix/efidrvprefix.c @@ -41,6 +41,7 @@ EFI_STATUS EFIAPI _efidrv_start ( EFI_HANDLE image_handle, initialise(); startup(); - /* Install SNP driver and return */ - return RC_TO_EFIRC ( efi_snp_install () ); + return 0; } + +REQUIRE_OBJECT ( efi_snp ); diff --git a/src/include/ipxe/efi/efi.h b/src/include/ipxe/efi/efi.h index 6dca1324..8a216b53 100644 --- a/src/include/ipxe/efi/efi.h +++ b/src/include/ipxe/efi/efi.h @@ -142,6 +142,5 @@ extern EFI_SYSTEM_TABLE *efi_systab; extern const char * efi_strerror ( EFI_STATUS efirc ); extern EFI_STATUS efi_init ( EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *systab ); -extern int efi_snp_install ( void ); #endif /* _IPXE_EFI_H */ diff --git a/src/include/ipxe/efi/efi_driver.h b/src/include/ipxe/efi/efi_driver.h new file mode 100644 index 00000000..06331647 --- /dev/null +++ b/src/include/ipxe/efi/efi_driver.h @@ -0,0 +1,49 @@ +#ifndef _IPXE_EFI_DRIVER_H +#define _IPXE_EFI_DRIVER_H + +/** @file + * + * EFI driver interface + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include +#include +#include +#include + +/** An EFI driver */ +struct efi_driver { + /** Name */ + const char *name; + /** EFI name */ + CHAR16 *wname; + /** EFI driver binding protocol */ + EFI_DRIVER_BINDING_PROTOCOL driver; + /** EFI component name protocol */ + EFI_COMPONENT_NAME2_PROTOCOL wtf; +}; + +/** 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, \ + } } + +extern EFI_DEVICE_PATH_PROTOCOL * +efi_devpath_end ( EFI_DEVICE_PATH_PROTOCOL *path ); + +extern EFI_STATUS efi_driver_install ( struct efi_driver *efidrv ); + +#endif /* _IPXE_EFI_DRIVER_H */ diff --git a/src/include/ipxe/efi/efi_pci.h b/src/include/ipxe/efi/efi_pci.h new file mode 100644 index 00000000..e226a569 --- /dev/null +++ b/src/include/ipxe/efi/efi_pci.h @@ -0,0 +1,40 @@ +#ifndef _IPXE_EFI_PCI_H +#define _IPXE_EFI_PCI_H + +/** @file + * + * EFI driver interface + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include +#include +#include + +struct efi_driver; +struct device; + +/** An EFI PCI device */ +struct efi_pci_device { + /** List of EFI PCI devices */ + struct list_head list; + /** iPXE PCI device */ + struct pci_device pci; + /** Underlying EFI device */ + EFI_HANDLE device; + /** PCI I/O protocol */ + EFI_PCI_IO_PROTOCOL *pci_io; + /** Device path */ + EFI_DEVICE_PATH_PROTOCOL *path; +}; + +extern struct efi_pci_device * efipci_create ( struct efi_driver *efidrv, + EFI_HANDLE device ); +extern EFI_STATUS efipci_enable ( struct efi_pci_device *efipci ); +extern struct efi_pci_device * efipci_find_efi ( EFI_HANDLE device ); +extern struct efi_pci_device * efipci_find ( struct device *dev ); +extern void efipci_destroy ( struct efi_driver *efidrv, + struct efi_pci_device *efipci ); + +#endif /* _IPXE_EFI_PCI_H */ diff --git a/src/interface/efi/efi_driver.c b/src/interface/efi/efi_driver.c new file mode 100644 index 00000000..17ec881d --- /dev/null +++ b/src/interface/efi/efi_driver.c @@ -0,0 +1,143 @@ +/* + * Copyright (C) 2011 Michael Brown . + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include +#include +#include +#include +#include +#include +#include + +/** @file + * + * EFI driver interface + * + */ + +/** EFI driver binding protocol GUID */ +static EFI_GUID efi_driver_binding_protocol_guid + = EFI_DRIVER_BINDING_PROTOCOL_GUID; + +/** EFI component name protocol GUID */ +static EFI_GUID efi_component_name2_protocol_guid + = EFI_COMPONENT_NAME2_PROTOCOL_GUID; + +/** + * Find end of device path + * + * @v path Path to device + * @ret path_end End of device path + */ +EFI_DEVICE_PATH_PROTOCOL * efi_devpath_end ( EFI_DEVICE_PATH_PROTOCOL *path ) { + + while ( path->Type != END_DEVICE_PATH_TYPE ) { + path = ( ( ( void * ) path ) + + /* There's this amazing new-fangled thing known as + * a UINT16, but who wants to use one of those? */ + ( ( path->Length[1] << 8 ) | path->Length[0] ) ); + } + + return path; +} + +/** + * Look up driver name + * + * @v wtf Component name protocol + * @v language Language to use + * @v driver_name Driver name to fill in + * @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 ); + + *driver_name = efidrv->wname; + return 0; +} + +/** + * Look up controller name + * + * @v wtf Component name protocol + * @v device Device + * @v child Child device, or NULL + * @v language Language to use + * @v driver_name Device name to fill in + * @ret efirc EFI status code + */ +static EFI_STATUS EFIAPI +efi_driver_get_controller_name ( EFI_COMPONENT_NAME2_PROTOCOL *wtf __unused, + EFI_HANDLE device __unused, + EFI_HANDLE child __unused, + CHAR8 *language __unused, + CHAR16 **controller_name __unused ) { + + /* Just let EFI use the default Device Path Name */ + return EFI_UNSUPPORTED; +} + +/** + * Install EFI driver + * + * @v efidrv EFI driver + * @ret efirc EFI status code + */ +EFI_STATUS 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; + char buf[ sizeof ( efidrv->wname ) / sizeof ( efidrv->wname[0] ) ]; + unsigned int i; + EFI_STATUS efirc; + + /* 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 */ + snprintf ( buf, sizeof ( buf ), PRODUCT_SHORT_NAME " - %s", + efidrv->name ); + for ( i = 0 ; i < sizeof ( buf ) ; i++ ) { + /* Damn Unicode names */ + efidrv->wname[i] = *( ( ( unsigned char * ) buf ) + i ); + } + + /* Install driver */ + if ( ( efirc = bs->InstallMultipleProtocolInterfaces ( + &driver->DriverBindingHandle, + &efi_driver_binding_protocol_guid, driver, + &efi_component_name2_protocol_guid, wtf, + NULL ) ) != 0 ) { + DBGC ( efidrv, "EFIDRV %s could not install protocol: %s\n", + efidrv->name, efi_strerror ( efirc ) ); + return efirc; + } + + DBGC ( efidrv, "EFIDRV %s installed\n", efidrv->name ); + return 0; +} diff --git a/src/interface/efi/efi_pci.c b/src/interface/efi/efi_pci.c index eb334b68..d866e30e 100644 --- a/src/interface/efi/efi_pci.c +++ b/src/interface/efi/efi_pci.c @@ -18,9 +18,14 @@ FILE_LICENCE ( GPL2_OR_LATER ); +#include #include #include +#include #include +#include +#include +#include #include /** @file @@ -29,6 +34,13 @@ FILE_LICENCE ( GPL2_OR_LATER ); * */ +/****************************************************************************** + * + * iPXE PCI API + * + ****************************************************************************** + */ + /** PCI root bridge I/O protocol */ static EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL *efipci; EFI_REQUIRE_PROTOCOL ( EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL, &efipci ); @@ -80,3 +92,369 @@ PROVIDE_PCIAPI_INLINE ( efi, pci_read_config_dword ); PROVIDE_PCIAPI_INLINE ( efi, pci_write_config_byte ); PROVIDE_PCIAPI_INLINE ( efi, pci_write_config_word ); PROVIDE_PCIAPI_INLINE ( efi, pci_write_config_dword ); + +/****************************************************************************** + * + * EFI PCI device instantiation + * + ****************************************************************************** + */ + +/** EFI PCI I/O protocol GUID */ +static EFI_GUID efi_pci_io_protocol_guid + = EFI_PCI_IO_PROTOCOL_GUID; + +/** EFI device path protocol GUID */ +static EFI_GUID efi_device_path_protocol_guid + = EFI_DEVICE_PATH_PROTOCOL_GUID; + +/** EFI PCI devices */ +static LIST_HEAD ( efi_pci_devices ); + +/** + * Create EFI PCI device + * + * @v efidrv EFI driver + * @v device EFI device + * @ret efipci EFI PCI device, or NULL + */ +struct efi_pci_device * efipci_create ( struct efi_driver *efidrv, + EFI_HANDLE device ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + struct efi_pci_device *efipci; + union { + EFI_PCI_IO_PROTOCOL *pci_io; + void *interface; + } pci_io; + union { + EFI_DEVICE_PATH_PROTOCOL *path; + void *interface; + } path; + UINTN pci_segment, pci_bus, pci_dev, pci_fn; + EFI_STATUS efirc; + int rc; + + /* Allocate PCI device */ + efipci = zalloc ( sizeof ( *efipci ) ); + if ( ! efipci ) + goto err_zalloc; + efipci->device = device; + + /* See if device is a PCI device */ + if ( ( efirc = bs->OpenProtocol ( device, + &efi_pci_io_protocol_guid, + &pci_io.interface, + efidrv->driver.DriverBindingHandle, + device, + EFI_OPEN_PROTOCOL_BY_DRIVER )) !=0 ){ + DBGCP ( efipci, "EFIPCI device %p is not a PCI device\n", + device ); + goto err_open_protocol; + } + efipci->pci_io = pci_io.pci_io; + + /* Get PCI bus:dev.fn address */ + if ( ( efirc = pci_io.pci_io->GetLocation ( pci_io.pci_io, + &pci_segment, + &pci_bus, &pci_dev, + &pci_fn ) ) != 0 ) { + DBGC ( efipci, "EFIPCI device %p could not get PCI " + "location: %s\n", device, efi_strerror ( efirc ) ); + 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 ) ); + + /* 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 ) ); + 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 ){ + DBGC ( efipci, "EFIPCI " PCI_FMT " has no device path\n", + PCI_ARGS ( &efipci->pci ) ); + goto err_no_device_path; + } + efipci->path = path.path; + + /* Add to list of PCI devices */ + list_add ( &efipci->list, &efi_pci_devices ); + + return efipci; + + bs->CloseProtocol ( device, &efi_device_path_protocol_guid, + efidrv->driver.DriverBindingHandle, device ); + err_no_device_path: + err_pci_read_config: + err_get_location: + bs->CloseProtocol ( device, &efi_pci_io_protocol_guid, + efidrv->driver.DriverBindingHandle, device ); + err_open_protocol: + free ( efipci ); + err_zalloc: + return NULL; +} + +/** + * Enable EFI PCI device + * + * @v efipci EFI PCI device + * @ret efirc EFI status code + */ +EFI_STATUS efipci_enable ( struct efi_pci_device *efipci ) { + EFI_PCI_IO_PROTOCOL *pci_io = efipci->pci_io; + EFI_STATUS efirc; + + /* Enable device */ + if ( ( efirc = pci_io->Attributes ( pci_io, + EfiPciIoAttributeOperationSet, + EFI_PCI_DEVICE_ENABLE, + NULL ) ) != 0 ) { + DBGC ( efipci, "EFIPCI " PCI_FMT " could not be enabled: %s\n", + PCI_ARGS ( &efipci->pci ), efi_strerror ( efirc ) ); + return efirc; + } + + return 0; +} + +/** + * Find EFI PCI device by EFI device + * + * @v device EFI device + * @ret efipci EFI PCI device, or NULL + */ +struct efi_pci_device * efipci_find_efi ( EFI_HANDLE device ) { + struct efi_pci_device *efipci; + + list_for_each_entry ( efipci, &efi_pci_devices, list ) { + if ( efipci->device == device ) + return efipci; + } + return NULL; +} + +/** + * Find EFI PCI device by iPXE device + * + * @v dev Device + * @ret efipci EFI PCI device, or NULL + */ +struct efi_pci_device * efipci_find ( struct device *dev ) { + struct efi_pci_device *efipci; + + list_for_each_entry ( efipci, &efi_pci_devices, list ) { + if ( &efipci->pci.dev == dev ) + return efipci; + } + return NULL; +} + +/** + * 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 ) { + 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 ); + bs->CloseProtocol ( efipci->device, &efi_pci_io_protocol_guid, + efidrv->driver.DriverBindingHandle, + efipci->device ); + free ( efipci ); +} + +/****************************************************************************** + * + * EFI PCI driver + * + ****************************************************************************** + */ + +/** + * 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 +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 ); + struct efi_pci_device *efipci; + EFI_STATUS efirc; + int rc; + + DBGCP ( efidrv, "EFIPCI DRIVER_SUPPORTED %p (%p)\n", device, child ); + + /* Create temporary corresponding PCI device, if any */ + efipci = efipci_create ( efidrv, device ); + if ( ! efipci ) { + /* Non-PCI devices are simply unsupported */ + efirc = EFI_UNSUPPORTED; + goto err_not_pci; + } + + /* 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 ) ); + efirc = EFI_UNSUPPORTED; + goto err_no_driver; + } + + DBGC ( efipci, "EFIPCI " PCI_FMT " is supported by driver \"%s\"\n", + PCI_ARGS ( &efipci->pci ), efipci->pci.id->name ); + + /* Destroy temporary PCI device */ + efipci_destroy ( efidrv, efipci ); + + return 0; + + err_no_driver: + efipci_destroy ( efidrv, efipci ); + err_not_pci: + return efirc; +} + +/** + * 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 +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 ); + struct efi_pci_device *efipci; + EFI_STATUS efirc; + int rc; + + DBGC ( efidrv, "EFIPCI DRIVER_START %p (%p)\n", device, child ); + + /* Create corresponding PCI device */ + efipci = efipci_create ( efidrv, device ); + if ( ! efipci ) { + efirc = EFI_OUT_OF_RESOURCES; + 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 ) ); + efirc = RC_TO_EFIRC ( rc ); + goto err_find_driver; + } + + /* Enable PCI device */ + if ( ( efirc = efipci_enable ( efipci ) ) != 0 ) + goto err_enable; + + /* 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 ), + efipci->pci.id->name, strerror ( rc ) ); + efirc = RC_TO_EFIRC ( rc ); + goto err_probe; + } + + return 0; + + pci_remove ( &efipci->pci ); + err_probe: + err_enable: + err_find_driver: + efipci_destroy ( efidrv, efipci ); + err_create: + return efirc; +} + +/** + * 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 ); + 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; + } + + /* Remove device */ + pci_remove ( &efipci->pci ); + + /* Delete EFI PCI device */ + efipci_destroy ( efidrv, efipci ); + + return 0; +} + +/** 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_init ( void ) { + struct efi_driver *efidrv = &efipci_driver; + EFI_STATUS efirc; + + /* Install driver */ + if ( ( efirc = efi_driver_install ( efidrv ) ) != 0 ) { + DBGC ( efidrv, "EFIPCI could not install driver: %s\n", + efi_strerror ( efirc ) ); + return; + } + + DBGC ( efidrv, "EFIPCI driver installed\n" ); +} + +/** EFI PCI startup function */ +struct startup_fn startup_pci __startup_fn ( STARTUP_NORMAL ) = { + .startup = efipci_driver_init, +}; diff --git a/src/interface/efi/efi_snp.c b/src/interface/efi/efi_snp.c index 6bfa1888..669d1896 100644 --- a/src/interface/efi/efi_snp.c +++ b/src/interface/efi/efi_snp.c @@ -28,12 +28,11 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include #include #include -#include -#include +#include +#include #include -#include #include -#include +#include /** @file * @@ -43,6 +42,8 @@ FILE_LICENCE ( GPL2_OR_LATER ); /** An SNP device */ struct efi_snp_device { + /** List of SNP devices */ + struct list_head list; /** The underlying iPXE network device */ struct net_device *netdev; /** EFI device handle */ @@ -81,14 +82,6 @@ struct efi_snp_device { static EFI_GUID efi_simple_network_protocol_guid = EFI_SIMPLE_NETWORK_PROTOCOL_GUID; -/** EFI driver binding protocol GUID */ -static EFI_GUID efi_driver_binding_protocol_guid - = EFI_DRIVER_BINDING_PROTOCOL_GUID; - -/** EFI component name protocol GUID */ -static EFI_GUID efi_component_name2_protocol_guid - = EFI_COMPONENT_NAME2_PROTOCOL_GUID; - /** EFI device path protocol GUID */ static EFI_GUID efi_device_path_protocol_guid = EFI_DEVICE_PATH_PROTOCOL_GUID; @@ -107,9 +100,8 @@ static EFI_GUID efi_nii31_protocol_guid = { { 0xBC, 0x81, 0x76, 0x7F, 0x1F, 0x97, 0x7A, 0x89 } }; -/** EFI PCI I/O protocol GUID */ -static EFI_GUID efi_pci_io_protocol_guid - = EFI_PCI_IO_PROTOCOL_GUID; +/** List of SNP devices */ +static LIST_HEAD ( efi_snp_devices ); /** * Set EFI SNP mode based on iPXE net device parameters @@ -750,188 +742,58 @@ static EFI_SIMPLE_NETWORK_PROTOCOL efi_snp_device_snp = { }; /** - * Locate net device corresponding to EFI device + * Locate SNP device corresponding to network device * - * @v driver EFI driver - * @v device EFI device - * @ret netdev Net device, or NULL if not found + * @v netdev Network device + * @ret snp SNP device, or NULL if not found */ -static struct net_device * -efi_snp_netdev ( EFI_DRIVER_BINDING_PROTOCOL *driver, EFI_HANDLE device ) { - EFI_BOOT_SERVICES *bs = efi_systab->BootServices; - union { - EFI_PCI_IO_PROTOCOL *pci; - void *interface; - } u; - UINTN pci_segment, pci_bus, pci_dev, pci_fn; - unsigned int pci_busdevfn; - struct net_device *netdev = NULL; - EFI_STATUS efirc; - - /* See if device is a PCI device */ - if ( ( efirc = bs->OpenProtocol ( device, - &efi_pci_io_protocol_guid, - &u.interface, - driver->DriverBindingHandle, - device, - EFI_OPEN_PROTOCOL_BY_DRIVER )) !=0 ){ - DBGCP ( driver, "SNPDRV %p device %p is not a PCI device\n", - driver, device ); - goto out_no_pci_io; - } - - /* Get PCI bus:dev.fn address */ - if ( ( efirc = u.pci->GetLocation ( u.pci, &pci_segment, &pci_bus, - &pci_dev, &pci_fn ) ) != 0 ) { - DBGC ( driver, "SNPDRV %p device %p could not get PCI " - "location: %s\n", - driver, device, efi_strerror ( efirc ) ); - goto out_no_pci_location; - } - DBGCP ( driver, "SNPDRV %p device %p is PCI %04lx:%02lx:%02lx.%lx\n", - driver, device, ( ( unsigned long ) pci_segment ), - ( ( unsigned long ) pci_bus ), ( ( unsigned long ) pci_dev ), - ( ( unsigned long ) pci_fn ) ); - - /* Look up corresponding network device */ - pci_busdevfn = PCI_BUSDEVFN ( pci_bus, pci_dev, pci_fn ); - if ( ( netdev = find_netdev_by_location ( BUS_TYPE_PCI, - pci_busdevfn ) ) == NULL ) { - DBGCP ( driver, "SNPDRV %p device %p is not a iPXE network " - "device\n", driver, device ); - goto out_no_netdev; - } - DBGC ( driver, "SNPDRV %p device %p is %s\n", - driver, device, netdev->name ); - - out_no_netdev: - out_no_pci_location: - bs->CloseProtocol ( device, &efi_pci_io_protocol_guid, - driver->DriverBindingHandle, device ); - out_no_pci_io: - return netdev; -} - -/** - * Locate SNP corresponding to EFI device - * - * @v driver EFI driver - * @v device EFI device - * @ret snp EFI SNP, or NULL if not found - */ -static struct efi_snp_device * -efi_snp_snpdev ( EFI_DRIVER_BINDING_PROTOCOL *driver, EFI_HANDLE device ) { - EFI_BOOT_SERVICES *bs = efi_systab->BootServices; - union { - EFI_SIMPLE_NETWORK_PROTOCOL *snp; - void *interface; - } u; - struct efi_snp_device *snpdev = NULL; - EFI_STATUS efirc; - - if ( ( efirc = bs->OpenProtocol ( device, - &efi_simple_network_protocol_guid, - &u.interface, - driver->DriverBindingHandle, - device, - EFI_OPEN_PROTOCOL_GET_PROTOCOL))!=0){ - DBGC ( driver, "SNPDRV %p device %p could not locate SNP: " - "%s\n", driver, device, efi_strerror ( efirc ) ); - goto err_no_snp; - } - - snpdev = container_of ( u.snp, struct efi_snp_device, snp ); - DBGCP ( driver, "SNPDRV %p device %p is SNPDEV %p\n", - driver, device, snpdev ); - - bs->CloseProtocol ( device, &efi_simple_network_protocol_guid, - driver->DriverBindingHandle, device ); - err_no_snp: - return snpdev; -} - -/** - * 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_snp_driver_supported ( EFI_DRIVER_BINDING_PROTOCOL *driver, - EFI_HANDLE device, - EFI_DEVICE_PATH_PROTOCOL *child ) { - struct net_device *netdev; - - DBGCP ( driver, "SNPDRV %p DRIVER_SUPPORTED %p (%p)\n", - driver, device, child ); - - netdev = efi_snp_netdev ( driver, device ); - return ( netdev ? 0 : 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_snp_driver_start ( EFI_DRIVER_BINDING_PROTOCOL *driver, - EFI_HANDLE device, - EFI_DEVICE_PATH_PROTOCOL *child ) { - EFI_BOOT_SERVICES *bs = efi_systab->BootServices; - EFI_DEVICE_PATH_PROTOCOL *path; - EFI_DEVICE_PATH_PROTOCOL *subpath; - MAC_ADDR_DEVICE_PATH *macpath; +static struct efi_snp_device * efi_snp_demux ( struct net_device *netdev ) { struct efi_snp_device *snpdev; - struct net_device *netdev; - size_t subpath_len; + + list_for_each_entry ( snpdev, &efi_snp_devices, list ) { + if ( snpdev->netdev == netdev ) + return snpdev; + } + return NULL; +} + +/** + * Create SNP device + * + * @v netdev Network device + * @ret rc Return status code + */ +static int efi_snp_probe ( struct net_device *netdev ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + struct efi_pci_device *efipci; + struct efi_snp_device *snpdev; + EFI_DEVICE_PATH_PROTOCOL *path_end; + MAC_ADDR_DEVICE_PATH *macpath; size_t path_prefix_len = 0; unsigned int i; EFI_STATUS efirc; + int rc; - DBGCP ( driver, "SNPDRV %p DRIVER_START %p (%p)\n", - driver, device, child ); + /* Find EFI PCI device */ + efipci = efipci_find ( netdev->dev ); + if ( ! efipci ) { + DBG ( "SNP skipping non-PCI device %s\n", netdev->name ); + rc = 0; + goto err_no_pci; + } - /* Determine device path prefix length */ - if ( ( efirc = bs->OpenProtocol ( device, - &efi_device_path_protocol_guid, - ( void * ) &path, - driver->DriverBindingHandle, - device, - EFI_OPEN_PROTOCOL_BY_DRIVER )) !=0 ){ - DBGCP ( driver, "SNPDRV %p device %p has no device path\n", - driver, device ); - goto err_no_device_path; - } - subpath = path; - while ( subpath->Type != END_DEVICE_PATH_TYPE ) { - subpath_len = ( ( subpath->Length[1] << 8 ) | - subpath->Length[0] ); - path_prefix_len += subpath_len; - subpath = ( ( ( void * ) subpath ) + subpath_len ); - } + /* Calculate device path prefix length */ + path_end = efi_devpath_end ( efipci->path ); + path_prefix_len = ( ( ( void * ) path_end ) - + ( ( void * ) efipci->path ) ); /* Allocate the SNP device */ snpdev = zalloc ( sizeof ( *snpdev ) + path_prefix_len + sizeof ( *macpath ) ); if ( ! snpdev ) { - efirc = EFI_OUT_OF_RESOURCES; + rc = -ENOMEM; goto err_alloc_snp; } - - /* Identify the net device */ - netdev = efi_snp_netdev ( driver, device ); - if ( ! netdev ) { - DBGC ( snpdev, "SNPDEV %p cannot find netdev for device %p\n", - snpdev, device ); - efirc = EFI_UNSUPPORTED; - goto err_no_netdev; - } snpdev->netdev = netdev_get ( netdev ); /* Sanity check */ @@ -939,7 +801,7 @@ efi_snp_driver_start ( EFI_DRIVER_BINDING_PROTOCOL *driver, DBGC ( snpdev, "SNPDEV %p cannot support link-layer address " "length %d for %s\n", snpdev, netdev->ll_protocol->ll_addr_len, netdev->name ); - efirc = EFI_INVALID_PARAMETER; + rc = -ENOTSUP; goto err_ll_addr_len; } @@ -951,6 +813,7 @@ efi_snp_driver_start ( EFI_DRIVER_BINDING_PROTOCOL *driver, &snpdev->snp.WaitForPacket ) ) != 0 ){ DBGC ( snpdev, "SNPDEV %p could not create event: %s\n", snpdev, efi_strerror ( efirc ) ); + rc = EFIRC_TO_RC ( efirc ); goto err_create_event; } @@ -973,9 +836,9 @@ efi_snp_driver_start ( EFI_DRIVER_BINDING_PROTOCOL *driver, } /* Populate the device path */ - memcpy ( &snpdev->path, path, path_prefix_len ); + memcpy ( &snpdev->path, efipci->path, path_prefix_len ); macpath = ( ( ( void * ) &snpdev->path ) + path_prefix_len ); - subpath = ( ( void * ) ( macpath + 1 ) ); + path_end = ( ( void * ) ( macpath + 1 ) ); memset ( macpath, 0, sizeof ( *macpath ) ); macpath->Header.Type = MESSAGING_DEVICE_PATH; macpath->Header.SubType = MSG_MAC_ADDR_DP; @@ -983,10 +846,10 @@ efi_snp_driver_start ( EFI_DRIVER_BINDING_PROTOCOL *driver, memcpy ( &macpath->MacAddress, netdev->ll_addr, sizeof ( macpath->MacAddress ) ); macpath->IfType = ntohs ( netdev->ll_protocol->ll_proto ); - memset ( subpath, 0, sizeof ( *subpath ) ); - subpath->Type = END_DEVICE_PATH_TYPE; - subpath->SubType = END_ENTIRE_DEVICE_PATH_SUBTYPE; - subpath->Length[0] = sizeof ( *subpath ); + memset ( path_end, 0, sizeof ( *path_end ) ); + path_end->Type = END_DEVICE_PATH_TYPE; + path_end->SubType = END_ENTIRE_DEVICE_PATH_SUBTYPE; + path_end->Length[0] = sizeof ( *path_end ); /* Install the SNP */ if ( ( efirc = bs->InstallMultipleProtocolInterfaces ( @@ -998,9 +861,13 @@ efi_snp_driver_start ( EFI_DRIVER_BINDING_PROTOCOL *driver, NULL ) ) != 0 ) { DBGC ( snpdev, "SNPDEV %p could not install protocols: " "%s\n", snpdev, efi_strerror ( efirc ) ); + rc = EFIRC_TO_RC ( efirc ); goto err_install_protocol_interface; } + /* Add to list of SNP devices */ + list_add ( &snpdev->list, &efi_snp_devices ); + DBGC ( snpdev, "SNPDEV %p installed for %s as device %p\n", snpdev, netdev->name, snpdev->handle ); return 0; @@ -1017,44 +884,39 @@ efi_snp_driver_start ( EFI_DRIVER_BINDING_PROTOCOL *driver, err_create_event: err_ll_addr_len: netdev_put ( netdev ); - err_no_netdev: free ( snpdev ); err_alloc_snp: - bs->CloseProtocol ( device, &efi_device_path_protocol_guid, - driver->DriverBindingHandle, device ); - err_no_device_path: - return efirc; + err_no_pci: + return rc; } /** - * Detach driver from device + * Handle SNP device or link state change * - * @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 + * @v netdev Network device */ -static EFI_STATUS EFIAPI -efi_snp_driver_stop ( EFI_DRIVER_BINDING_PROTOCOL *driver, - EFI_HANDLE device, - UINTN num_children, - EFI_HANDLE *children ) { +static void efi_snp_notify ( struct net_device *netdev __unused ) { + /* Nothing to do */ +} + +/** + * Destroy SNP device + * + * @v netdev Network device + */ +static void efi_snp_remove ( struct net_device *netdev ) { EFI_BOOT_SERVICES *bs = efi_systab->BootServices; struct efi_snp_device *snpdev; - DBGCP ( driver, "SNPDRV %p DRIVER_STOP %p (%ld %p)\n", - driver, device, ( ( unsigned long ) num_children ), children ); - /* Locate SNP device */ - snpdev = efi_snp_snpdev ( driver, device ); + snpdev = efi_snp_demux ( netdev ); if ( ! snpdev ) { - DBGC ( driver, "SNPDRV %p device %p could not find SNPDEV\n", - driver, device ); - return EFI_DEVICE_ERROR; + DBG ( "SNP skipping non-SNP device %s\n", netdev->name ); + return; } /* Uninstall the SNP */ + list_del ( &snpdev->list ); bs->UninstallMultipleProtocolInterfaces ( snpdev->handle, &efi_simple_network_protocol_guid, &snpdev->snp, @@ -1065,87 +927,12 @@ efi_snp_driver_stop ( EFI_DRIVER_BINDING_PROTOCOL *driver, bs->CloseEvent ( snpdev->snp.WaitForPacket ); netdev_put ( snpdev->netdev ); free ( snpdev ); - bs->CloseProtocol ( device, &efi_device_path_protocol_guid, - driver->DriverBindingHandle, device ); - return 0; } -/** EFI SNP driver binding */ -static EFI_DRIVER_BINDING_PROTOCOL efi_snp_binding = { - efi_snp_driver_supported, - efi_snp_driver_start, - efi_snp_driver_stop, - 0x10, - NULL, - NULL +/** SNP driver */ +struct net_driver efi_snp_driver __net_driver = { + .name = "SNP", + .probe = efi_snp_probe, + .notify = efi_snp_notify, + .remove = efi_snp_remove, }; - -/** - * Look up driver name - * - * @v wtf Component name protocol - * @v language Language to use - * @v driver_name Driver name to fill in - * @ret efirc EFI status code - */ -static EFI_STATUS EFIAPI -efi_snp_get_driver_name ( EFI_COMPONENT_NAME2_PROTOCOL *wtf __unused, - CHAR8 *language __unused, CHAR16 **driver_name ) { - - *driver_name = L"" PRODUCT_SHORT_NAME " Driver"; - return 0; -} - -/** - * Look up controller name - * - * @v wtf Component name protocol - * @v device Device - * @v child Child device, or NULL - * @v language Language to use - * @v driver_name Device name to fill in - * @ret efirc EFI status code - */ -static EFI_STATUS EFIAPI -efi_snp_get_controller_name ( EFI_COMPONENT_NAME2_PROTOCOL *wtf __unused, - EFI_HANDLE device __unused, - EFI_HANDLE child __unused, - CHAR8 *language __unused, - CHAR16 **controller_name __unused ) { - - /* Just let EFI use the default Device Path Name */ - return EFI_UNSUPPORTED; -} - -/** EFI SNP component name protocol */ -static EFI_COMPONENT_NAME2_PROTOCOL efi_snp_name = { - efi_snp_get_driver_name, - efi_snp_get_controller_name, - "en" -}; - -/** - * Install EFI SNP driver - * - * @ret rc Return status code - */ -int efi_snp_install ( void ) { - EFI_BOOT_SERVICES *bs = efi_systab->BootServices; - EFI_DRIVER_BINDING_PROTOCOL *driver = &efi_snp_binding; - EFI_STATUS efirc; - - driver->ImageHandle = efi_image_handle; - if ( ( efirc = bs->InstallMultipleProtocolInterfaces ( - &driver->DriverBindingHandle, - &efi_driver_binding_protocol_guid, driver, - &efi_component_name2_protocol_guid, &efi_snp_name, - NULL ) ) != 0 ) { - DBGC ( driver, "SNPDRV %p could not install protocols: " - "%s\n", driver, efi_strerror ( efirc ) ); - return EFIRC_TO_RC ( efirc ); - } - - DBGC ( driver, "SNPDRV %p driver binding installed as %p\n", - driver, driver->DriverBindingHandle ); - return 0; -}