From cced04ef3bba35dedb4e4c1da97643bc88260fc2 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sat, 10 Jan 2009 00:21:38 +0000 Subject: [PATCH] [efi] Provide component name protocol and device path protocol interfaces Include a minimal component name protocol so that the driver name shows up as something other than "" in the driver list, and a device path protocol so that the network interface shows up as a separate device in the device list, rather than being attached directly to the PCI device. Incidentally, the EFI component name protocol reaches new depths for signal-to-noise ratio in program code. A typical instance within the EFI development kit will use an additional 300 lines of code to provide slightly less functionality than GNU gettext achieves with three additional characters. --- .../gpxe/efi/Protocol/ComponentName2.h | 174 ++++++++++++++++++ src/interface/efi/efi_snp.c | 173 ++++++++++++++--- 2 files changed, 325 insertions(+), 22 deletions(-) create mode 100644 src/include/gpxe/efi/Protocol/ComponentName2.h diff --git a/src/include/gpxe/efi/Protocol/ComponentName2.h b/src/include/gpxe/efi/Protocol/ComponentName2.h new file mode 100644 index 00000000..0f010142 --- /dev/null +++ b/src/include/gpxe/efi/Protocol/ComponentName2.h @@ -0,0 +1,174 @@ +/** @file + UEFI Component Name 2 Protocol as defined in the UEFI 2.1 specification. + This protocol is used to retrieve user readable names of drivers + and controllers managed by UEFI Drivers. + + Copyright (c) 2006 - 2008, Intel Corporation + All rights reserved. This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef __EFI_COMPONENT_NAME2_H__ +#define __EFI_COMPONENT_NAME2_H__ + +/// +/// Global ID for the Component Name Protocol +/// +#define EFI_COMPONENT_NAME2_PROTOCOL_GUID \ + {0x6a7a5cff, 0xe8d9, 0x4f70, { 0xba, 0xda, 0x75, 0xab, 0x30, 0x25, 0xce, 0x14 } } + +typedef struct _EFI_COMPONENT_NAME2_PROTOCOL EFI_COMPONENT_NAME2_PROTOCOL; + + +/** + Retrieves a Unicode string that is the user readable name of + the EFI Driver. + + @param This A pointer to the + EFI_COMPONENT_NAME2_PROTOCOL instance. + + @param Language A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller + is requesting, and it must match one of the + languages specified in SupportedLanguages. + The number of languages supported by a + driver is up to the driver writer. Language + is specified in RFC 3066 language code + format. + + @param DriverName A pointer to the Unicode string to return. + This Unicode string is the name of the + driver specified by This in the language + specified by Language. + + @retval EFI_SUCCESS The Unicode string for the + Driver specified by This and the + language specified by Language + was returned in DriverName. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER DriverName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This + does not support the language + specified by Language. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_COMPONENT_NAME2_GET_DRIVER_NAME)( + IN EFI_COMPONENT_NAME2_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ); + + +/** + Retrieves a Unicode string that is the user readable name of + the controller that is being managed by an EFI Driver. + + @param This A pointer to the + EFI_COMPONENT_NAME2_PROTOCOL instance. + + @param ControllerHandle The handle of a controller that the + driver specified by This is managing. + This handle specifies the controller + whose name is to be returned. + + @param ChildHandle The handle of the child controller to + retrieve the name of. This is an + optional parameter that may be NULL. + It will be NULL for device drivers. + It will also be NULL for a bus + drivers that wish to retrieve the + name of the bus controller. It will + not be NULL for a bus driver that + wishes to retrieve the name of a + child controller. + + @param Language A pointer to a Null-terminated ASCII + string array indicating the language. + This is the language of the driver + name that the caller is requesting, + and it must match one of the + languages specified in + SupportedLanguages. The number of + languages supported by a driver is up + to the driver writer. Language is + specified in RFC 3066 language code + format. + + @param ControllerName A pointer to the Unicode string to + return. This Unicode string is the + name of the controller specified by + ControllerHandle and ChildHandle in + the language specified by Language + from the point of view of the driver + specified by This. + + @retval EFI_SUCCESS The Unicode string for the user + readable name in the language + specified by Language for the + driver specified by This was + returned in DriverName. + + @retval EFI_INVALID_PARAMETER ControllerHandle is not a valid + EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it + is not a valid EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER ControllerName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This is + not currently managing the + controller specified by + ControllerHandle and + ChildHandle. + + @retval EFI_UNSUPPORTED The driver specified by This + does not support the language + specified by Language. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME)( + IN EFI_COMPONENT_NAME2_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ); + +/// +/// This protocol is used to retrieve user readable names of drivers +/// and controllers managed by UEFI Drivers. +/// +struct _EFI_COMPONENT_NAME2_PROTOCOL { + EFI_COMPONENT_NAME2_GET_DRIVER_NAME GetDriverName; + EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME GetControllerName; + + /// + /// A Null-terminated ASCII string array that contains one or more + /// supported language codes. This is the list of language codes that + /// this protocol supports. The number of languages supported by a + /// driver is up to the driver writer. SupportedLanguages is + /// specified in RFC 3066 format. + /// + CHAR8 *SupportedLanguages; +}; + +extern EFI_GUID gEfiComponentName2ProtocolGuid; + +#endif diff --git a/src/interface/efi/efi_snp.c b/src/interface/efi/efi_snp.c index 102130c8..9f92666b 100644 --- a/src/interface/efi/efi_snp.c +++ b/src/interface/efi/efi_snp.c @@ -29,6 +29,8 @@ #include #include #include +#include +#include /** @file * @@ -40,6 +42,8 @@ struct efi_snp_device { /** The underlying gPXE network device */ struct net_device *netdev; + /** EFI device handle */ + EFI_HANDLE handle; /** The SNP structure itself */ EFI_SIMPLE_NETWORK_PROTOCOL snp; /** The SNP "mode" (parameters) */ @@ -58,6 +62,14 @@ struct efi_snp_device { unsigned int rx_count_interrupts; /** Outstanding RX packet count (via WaitForPacket event) */ unsigned int rx_count_events; + /** Device name */ + wchar_t name[ sizeof ( ( ( struct net_device * ) NULL )->name ) ]; + /** The device path + * + * This field is variable in size and must appear at the end + * of the structure. + */ + EFI_DEVICE_PATH_PROTOCOL path; }; /** EFI simple network protocol GUID */ @@ -68,6 +80,14 @@ static EFI_GUID efi_simple_network_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; + /** EFI PCI I/O protocol GUID */ static EFI_GUID efi_pci_io_protocol_guid = EFI_PCI_IO_PROTOCOL_GUID; @@ -784,7 +804,7 @@ efi_snp_snpdev ( EFI_DRIVER_BINDING_PROTOCOL *driver, EFI_HANDLE device ) { EFI_SIMPLE_NETWORK_PROTOCOL *snp; void *interface; } u; - struct efi_snp_device *snpdev; + struct efi_snp_device *snpdev = NULL; EFI_STATUS efirc; if ( ( efirc = bs->OpenProtocol ( device, @@ -795,12 +815,16 @@ efi_snp_snpdev ( EFI_DRIVER_BINDING_PROTOCOL *driver, EFI_HANDLE device ) { EFI_OPEN_PROTOCOL_GET_PROTOCOL))!=0){ DBGC ( driver, "SNPDRV %p device %p could not locate SNP: " "%s\n", driver, device, efi_strerror ( efirc ) ); - return NULL; + 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; } @@ -838,15 +862,41 @@ 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; struct efi_snp_device *snpdev; struct net_device *netdev; + size_t subpath_len; + size_t path_prefix_len = 0; + unsigned int i; EFI_STATUS efirc; DBGCP ( driver, "SNPDRV %p DRIVER_START %p (%p)\n", driver, device, child ); + /* 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 ); + } + /* Allocate the SNP device */ - snpdev = zalloc ( sizeof ( *snpdev ) ); + snpdev = zalloc ( sizeof ( *snpdev ) + path_prefix_len + + sizeof ( *macpath ) ); if ( ! snpdev ) { efirc = EFI_OUT_OF_RESOURCES; goto err_alloc_snp; @@ -886,22 +936,50 @@ efi_snp_driver_start ( EFI_DRIVER_BINDING_PROTOCOL *driver, snpdev->mode.State = EfiSimpleNetworkStopped; efi_snp_set_mode ( snpdev ); + /* Populate the device name */ + for ( i = 0 ; i < sizeof ( netdev->name ) ; i++ ) { + /* Damn Unicode names */ + assert ( i < ( sizeof ( snpdev->name ) / + sizeof ( snpdev->name[0] ) ) ); + snpdev->name[i] = netdev->name[i]; + } + + /* Populate the device path */ + memcpy ( &snpdev->path, path, path_prefix_len ); + macpath = ( ( ( void * ) &snpdev->path ) + path_prefix_len ); + subpath = ( ( void * ) ( macpath + 1 ) ); + memset ( macpath, 0, sizeof ( *macpath ) ); + macpath->Header.Type = MESSAGING_DEVICE_PATH; + macpath->Header.SubType = MSG_MAC_ADDR_DP; + macpath->Header.Length[0] = sizeof ( *macpath ); + 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 ); + /* Install the SNP */ - if ( ( efirc = bs->InstallProtocolInterface ( &device, - &efi_simple_network_protocol_guid, - EFI_NATIVE_INTERFACE, &snpdev->snp ) ) != 0 ) { - DBGC ( snpdev, "SNPDEV %p could not install protocol: %s\n", - snpdev, efi_strerror ( efirc ) ); + if ( ( efirc = bs->InstallMultipleProtocolInterfaces ( + &snpdev->handle, + &efi_simple_network_protocol_guid, &snpdev->snp, + &efi_device_path_protocol_guid, &snpdev->path, + NULL ) ) != 0 ) { + DBGC ( snpdev, "SNPDEV %p could not install protocols: " + "%s\n", snpdev, efi_strerror ( efirc ) ); goto err_install_protocol_interface; } - DBGC ( snpdev, "SNPDEV %p installed for %s on device %p\n", - snpdev, netdev->name, device ); + DBGC ( snpdev, "SNPDEV %p installed for %s as device %p\n", + snpdev, netdev->name, snpdev->handle ); return 0; - bs->UninstallProtocolInterface ( device, - &efi_simple_network_protocol_guid, - &snpdev->snp ); + bs->UninstallMultipleProtocolInterfaces ( + snpdev->handle, + &efi_simple_network_protocol_guid, &snpdev->snp, + &efi_device_path_protocol_guid, &snpdev->path, + NULL ); err_install_protocol_interface: bs->CloseEvent ( snpdev->snp.WaitForPacket ); err_create_event: @@ -910,6 +988,9 @@ efi_snp_driver_start ( EFI_DRIVER_BINDING_PROTOCOL *driver, 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; } @@ -942,12 +1023,16 @@ efi_snp_driver_stop ( EFI_DRIVER_BINDING_PROTOCOL *driver, } /* Uninstall the SNP */ - bs->UninstallProtocolInterface ( device, - &efi_simple_network_protocol_guid, - &snpdev->snp ); + bs->UninstallMultipleProtocolInterfaces ( + snpdev->handle, + &efi_simple_network_protocol_guid, &snpdev->snp, + &efi_device_path_protocol_guid, &snpdev->path, + NULL ); bs->CloseEvent ( snpdev->snp.WaitForPacket ); netdev_put ( snpdev->netdev ); free ( snpdev ); + bs->CloseProtocol ( device, &efi_device_path_protocol_guid, + driver->DriverBindingHandle, device ); return 0; } @@ -961,6 +1046,50 @@ static EFI_DRIVER_BINDING_PROTOCOL efi_snp_binding = { NULL }; +/** + * 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 * @@ -972,12 +1101,12 @@ int efi_snp_install ( void ) { EFI_STATUS efirc; driver->ImageHandle = efi_image_handle; - if ( ( efirc = bs->InstallProtocolInterface ( - &driver->DriverBindingHandle, - &efi_driver_binding_protocol_guid, - EFI_NATIVE_INTERFACE, - driver ) ) != 0 ) { - DBGC ( driver, "SNPDRV %p could not install driver binding: " + 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 ); }