[efi] Provide component name protocol and device path protocol interfaces
[people/asdlkf/gpxe.git] / src / interface / efi / efi_snp.c
index f84bf10..9f92666 100644 (file)
@@ -29,6 +29,8 @@
 #include <gpxe/efi/Protocol/DriverBinding.h>
 #include <gpxe/efi/Protocol/PciIo.h>
 #include <gpxe/efi/Protocol/SimpleNetwork.h>
+#include <gpxe/efi/Protocol/ComponentName2.h>
+#include <config/general.h>
 
 /** @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;
@@ -174,7 +194,8 @@ efi_snp_initialize ( EFI_SIMPLE_NETWORK_PROTOCOL *snp,
        int rc;
 
        DBGC2 ( snpdev, "SNPDEV %p INITIALIZE (%ld extra RX, %ld extra TX)\n",
-               snpdev, extra_rx_bufsize, extra_tx_bufsize );
+               snpdev, ( ( unsigned long ) extra_rx_bufsize ),
+               ( ( unsigned long ) extra_tx_bufsize ) );
 
        if ( ( rc = netdev_open ( snpdev->netdev ) ) != 0 ) {
                DBGC ( snpdev, "SNPDEV %p could not open %s: %s\n",
@@ -252,9 +273,9 @@ efi_snp_receive_filters ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, UINT32 enable,
                container_of ( snp, struct efi_snp_device, snp );
        unsigned int i;
 
-       DBGC2 ( snpdev, "SNPDEV %p RECEIVE_FILTERS %08lx&~%08lx%s %ld mcast\n",
+       DBGC2 ( snpdev, "SNPDEV %p RECEIVE_FILTERS %08x&~%08x%s %ld mcast\n",
                snpdev, enable, disable, ( mcast_reset ? " reset" : "" ),
-               mcast_count );
+               ( ( unsigned long ) mcast_count ) );
        for ( i = 0 ; i < mcast_count ; i++ ) {
                DBGC2_HDA ( snpdev, i, &mcast[i],
                            snpdev->netdev->ll_protocol->ll_addr_len );
@@ -317,14 +338,14 @@ efi_snp_statistics ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, BOOLEAN reset,
 
        /* Gather statistics */
        memset ( &stats_buf, 0, sizeof ( stats_buf ) );
-       stats_buf.TxGoodFrames = snpdev->netdev->stats.tx_ok;
-       stats_buf.TxDroppedFrames = snpdev->netdev->stats.tx_err;
-       stats_buf.TxTotalFrames = ( snpdev->netdev->stats.tx_ok +
-                                   snpdev->netdev->stats.tx_err );
-       stats_buf.RxGoodFrames = snpdev->netdev->stats.rx_ok;
-       stats_buf.RxDroppedFrames = snpdev->netdev->stats.rx_err;
-       stats_buf.RxTotalFrames = ( snpdev->netdev->stats.rx_ok +
-                                   snpdev->netdev->stats.rx_err );
+       stats_buf.TxGoodFrames = snpdev->netdev->tx_stats.good;
+       stats_buf.TxDroppedFrames = snpdev->netdev->tx_stats.bad;
+       stats_buf.TxTotalFrames = ( snpdev->netdev->tx_stats.good +
+                                   snpdev->netdev->tx_stats.bad );
+       stats_buf.RxGoodFrames = snpdev->netdev->rx_stats.good;
+       stats_buf.RxDroppedFrames = snpdev->netdev->rx_stats.bad;
+       stats_buf.RxTotalFrames = ( snpdev->netdev->rx_stats.good +
+                                   snpdev->netdev->rx_stats.bad );
        if ( *stats_len > sizeof ( stats_buf ) )
                *stats_len = sizeof ( stats_buf );
        if ( stats )
@@ -332,8 +353,10 @@ efi_snp_statistics ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, BOOLEAN reset,
 
        /* Reset statistics if requested to do so */
        if ( reset ) {
-               memset ( &snpdev->netdev->stats, 0,
-                        sizeof ( snpdev->netdev->stats ) );
+               memset ( &snpdev->netdev->tx_stats, 0,
+                        sizeof ( snpdev->netdev->tx_stats ) );
+               memset ( &snpdev->netdev->rx_stats, 0,
+                        sizeof ( snpdev->netdev->rx_stats ) );
        }
 
        return 0;
@@ -389,7 +412,8 @@ efi_snp_nvdata ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, BOOLEAN read,
                container_of ( snp, struct efi_snp_device, snp );
 
        DBGC2 ( snpdev, "SNPDEV %p NVDATA %s %lx+%lx\n", snpdev,
-               ( read ? "read" : "write" ), offset, len );
+               ( read ? "read" : "write" ), ( ( unsigned long ) offset ),
+               ( ( unsigned long ) len ) );
        if ( ! read )
                DBGC2_HDA ( snpdev, offset, data, len );
 
@@ -433,7 +457,7 @@ efi_snp_get_status ( EFI_SIMPLE_NETWORK_PROTOCOL *snp,
                        *interrupts |= EFI_SIMPLE_NETWORK_RECEIVE_INTERRUPT;
                        snpdev->rx_count_interrupts--;
                }
-               DBGC2 ( snpdev, " INTS:%02lx", *interrupts );
+               DBGC2 ( snpdev, " INTS:%02x", *interrupts );
        }
 
        /* TX completions.  It would be possible to design a more
@@ -490,7 +514,8 @@ efi_snp_transmit ( EFI_SIMPLE_NETWORK_PROTOCOL *snp,
        int rc;
        EFI_STATUS efirc;
 
-       DBGC2 ( snpdev, "SNPDEV %p TRANSMIT %p+%lx", snpdev, data, len );
+       DBGC2 ( snpdev, "SNPDEV %p TRANSMIT %p+%lx", snpdev, data,
+               ( ( unsigned long ) len ) );
        if ( ll_header_len ) {
                if ( ll_src ) {
                        DBGC2 ( snpdev, " src %s",
@@ -510,13 +535,14 @@ efi_snp_transmit ( EFI_SIMPLE_NETWORK_PROTOCOL *snp,
        if ( ll_header_len ) {
                if ( ll_header_len != ll_protocol->ll_header_len ) {
                        DBGC ( snpdev, "SNPDEV %p TX invalid header length "
-                              "%ld\n", snpdev, ll_header_len );
+                              "%ld\n", snpdev,
+                              ( ( unsigned long ) ll_header_len ) );
                        efirc = EFI_INVALID_PARAMETER;
                        goto err_sanity;
                }
                if ( len < ll_header_len ) {
                        DBGC ( snpdev, "SNPDEV %p invalid packet length %ld\n",
-                              snpdev, len );
+                              snpdev, ( ( unsigned long ) len ) );
                        efirc = EFI_BUFFER_TOO_SMALL;
                        goto err_sanity;
                }
@@ -540,7 +566,7 @@ efi_snp_transmit ( EFI_SIMPLE_NETWORK_PROTOCOL *snp,
        iobuf = alloc_iob ( len );
        if ( ! iobuf ) {
                DBGC ( snpdev, "SNPDEV %p TX could not allocate %ld-byte "
-                      "buffer\n", snpdev, len );
+                      "buffer\n", snpdev, ( ( unsigned long ) len ) );
                efirc = EFI_DEVICE_ERROR;
                goto err_alloc_iob;
        }
@@ -608,7 +634,8 @@ efi_snp_receive ( EFI_SIMPLE_NETWORK_PROTOCOL *snp,
        int rc;
        EFI_STATUS efirc;
 
-       DBGC2 ( snpdev, "SNPDEV %p RECEIVE %p(+%lx)", snpdev, data, *len );
+       DBGC2 ( snpdev, "SNPDEV %p RECEIVE %p(+%lx)", snpdev, data,
+               ( ( unsigned long ) *len ) );
 
        /* Poll the network device */
        efi_snp_poll ( snpdev );
@@ -735,11 +762,14 @@ efi_snp_netdev ( EFI_DRIVER_BINDING_PROTOCOL *driver, EFI_HANDLE device ) {
        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: %lx\n", driver, device, efirc );
+                      "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, pci_segment, pci_bus, pci_dev, pci_fn );
+               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_DEVFN ( pci_dev, pci_fn ) );
@@ -774,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,
@@ -784,13 +814,17 @@ efi_snp_snpdev ( EFI_DRIVER_BINDING_PROTOCOL *driver, EFI_HANDLE device ) {
                                          device,
                                          EFI_OPEN_PROTOCOL_GET_PROTOCOL))!=0){
                DBGC ( driver, "SNPDRV %p device %p could not locate SNP: "
-                      "%lx\n", driver, device, efirc );
-               return NULL;
+                      "%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;
 }
 
@@ -828,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;
@@ -855,7 +915,7 @@ efi_snp_driver_start ( EFI_DRIVER_BINDING_PROTOCOL *driver,
        /* Sanity check */
        if ( netdev->ll_protocol->ll_addr_len > sizeof ( EFI_MAC_ADDRESS ) ) {
                DBGC ( snpdev, "SNPDEV %p cannot support link-layer address "
-                      "length %zd for %s\n", snpdev,
+                      "length %d for %s\n", snpdev,
                       netdev->ll_protocol->ll_addr_len, netdev->name );
                efirc = EFI_INVALID_PARAMETER;
                goto err_ll_addr_len;
@@ -867,8 +927,8 @@ efi_snp_driver_start ( EFI_DRIVER_BINDING_PROTOCOL *driver,
        if ( ( efirc = bs->CreateEvent ( EVT_NOTIFY_WAIT, TPL_NOTIFY,
                                         efi_snp_wait_for_packet, snpdev,
                                         &snpdev->snp.WaitForPacket ) ) != 0 ){
-               DBGC ( snpdev, "SNPDEV %p could not create event: %lx\n",
-                      snpdev, efirc );
+               DBGC ( snpdev, "SNPDEV %p could not create event: %s\n",
+                      snpdev, efi_strerror ( efirc ) );
                goto err_create_event;
        }
 
@@ -876,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: %lx\n",
-                      snpdev, 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:
@@ -900,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;
 }
 
@@ -921,7 +1012,7 @@ efi_snp_driver_stop ( EFI_DRIVER_BINDING_PROTOCOL *driver,
        struct efi_snp_device *snpdev;
 
        DBGCP ( driver, "SNPDRV %p DRIVER_STOP %p (%ld %p)\n",
-               driver, device, num_children, children );
+               driver, device, ( ( unsigned long ) num_children ), children );
 
        /* Locate SNP device */
        snpdev = efi_snp_snpdev ( driver, device );
@@ -932,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;
 }
 
@@ -951,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
  *
@@ -962,13 +1101,13 @@ 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: "
-                      "%lx\n", driver, efirc );
+       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 );
        }