[Linaro-uefi,v2,1/2] Drivers/Net: add MAC override driver for Realtek 8169

Message ID 20170628191349.5233-2-ard.biesheuvel@linaro.org
State New
Headers show
Series
  • Platforms/AMD/Cello: implement MAC override
Related show

Commit Message

Ard Biesheuvel June 28, 2017, 7:13 p.m.
Sadly, the Cellos have been delivered with a Realtek NIC that lacks
a MAC address in its OTP. This can be worked around from the OS, but
this confuses some installers, and it is much better to deal with this
in the firmware.

Since network boot using the full Realtek 8169 driver is impossible
anyway in this case, for the same reason, let's provide a driver that
programs a MAC into the volatile NIC registers in a way that allows
the OS to run unmodified.

Contributed-under: TianoCore Contribution Agreement 1.0
Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
Reviewed-by: Leif Lindholm <leif.lindholm@linaro.org>
---
 Drivers/Net/Realtek8169MacOverride/Realtek8169MacOverride.c   | 263 ++++++++++++++++++++
 Drivers/Net/Realtek8169MacOverride/Realtek8169MacOverride.inf |  44 ++++
 OpenPlatformPkg.dec                                           |   3 +
 3 files changed, 310 insertions(+)

Patch hide | download patch | download mbox

diff --git a/Drivers/Net/Realtek8169MacOverride/Realtek8169MacOverride.c b/Drivers/Net/Realtek8169MacOverride/Realtek8169MacOverride.c
new file mode 100644
index 000000000000..12029269e490
--- /dev/null
+++ b/Drivers/Net/Realtek8169MacOverride/Realtek8169MacOverride.c
@@ -0,0 +1,263 @@ 
+/**  @file
+  Realtek 8169 MAC override driver
+
+Copyright (c) 2017, Linaro Limited. 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.
+
+**/
+
+#include <PiDxe.h>
+
+#include <Library/BaseMemoryLib.h>
+#include <Library/DebugLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiLib.h>
+
+#include <IndustryStandard/Pci22.h>
+
+#include <Protocol/PciIo.h>
+
+#define PCI_VENDOR_ID_REALTEK               0x10EC
+#define PCI_DEVICE_ID_REALTEK_8169          0x8168
+
+STATIC CONST UINT8 *mMacOverride;
+
+#define MAC                                 0x0
+#define CFG9346                             0x50
+
+STATIC CONST UINT8 Cfg9346_Unlock = 0xc0;
+STATIC CONST UINT8 Cfg9346_Lock = 0x0;
+
+STATIC BOOLEAN Done;
+
+/**
+  Test to see if this driver supports ControllerHandle. This service
+  is called by the EFI boot service ConnectController(). In
+  order to make drivers as small as possible, there are a few calling
+  restrictions for this service. ConnectController() must
+  follow these calling restrictions. If any other agent wishes to call
+  Supported() it must also follow these calling restrictions.
+
+   @param  This                   Protocol instance pointer.
+   @param  ControllerHandle       Handle of device to test.
+   @param  RemainingDevicePath    Optional parameter use to pick a specific child
+                                                         device to start.
+
+   @retval EFI_SUCCESS            This driver supports this device.
+   @retval EFI_ALREADY_STARTED    This driver is already running on this device.
+   @retval other                  This driver does not support this device.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+Realtek8169MacOverrideDriverSupported (
+    IN EFI_DRIVER_BINDING_PROTOCOL    *This,
+    IN EFI_HANDLE                     Controller,
+    IN EFI_DEVICE_PATH_PROTOCOL       *RemainingDevicePath
+    )
+{
+  EFI_STATUS              Status;
+  EFI_PCI_IO_PROTOCOL     *PciIo;
+  UINT32                  PciID;
+  UINT64                  Supports;
+  UINT32                  MacWord;
+
+  //
+  // Execute only once
+  //
+  if (Done) {
+    return EFI_UNSUPPORTED;
+  }
+
+  //
+  // Check for the PCI IO Protocol
+  //
+  Status = gBS->OpenProtocol (Controller, &gEfiPciIoProtocolGuid,
+                  (VOID **)&PciIo, This->DriverBindingHandle, Controller,
+                  EFI_OPEN_PROTOCOL_BY_DRIVER);
+
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  Status = PciIo->Pci.Read (PciIo, EfiPciIoWidthUint32, PCI_VENDOR_ID_OFFSET,
+                        1, &PciID);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR,
+      "%a: Pci->Pci.Read() of vendor/device id failed (Status == %r)\n",
+      __FUNCTION__, Status));
+    goto CloseProtocol;
+  }
+
+  if ((PciID & 0xffff) != PCI_VENDOR_ID_REALTEK ||
+      (PciID >> 16) != PCI_DEVICE_ID_REALTEK_8169) {
+    DEBUG ((DEBUG_INFO, "%a: ignoring unsupported PCI device 0x%04x:0x%04x\n",
+      __FUNCTION__, PciID & 0xffff, PciID >> 16));
+    goto CloseProtocol;
+  }
+
+  //
+  // Enable the device so we can poke at its MMIO registers
+  //
+  Status = PciIo->Attributes (PciIo, EfiPciIoAttributeOperationSupported,
+                    0, &Supports);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_WARN,
+      "%a: failed to get PCI attributes, aborting (Status == %r)...\n",
+      __FUNCTION__, Status));
+    goto CloseProtocol;
+  }
+
+  Supports &= EFI_PCI_DEVICE_ENABLE;
+  Status = PciIo->Attributes (PciIo, EfiPciIoAttributeOperationEnable,
+                    Supports, NULL);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_WARN,
+      "%a: failed to set PCI attributes, aborting (Status == %r)...\n",
+      __FUNCTION__, Status));
+    goto CloseProtocol;
+  }
+
+  //
+  // Program the MAC address
+  //
+  Status = PciIo->Mem.Write (PciIo, EfiPciIoWidthUint8, 2, CFG9346, 0x1,
+                        (VOID *)&Cfg9346_Unlock);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_WARN, "%a: unlock failed, aborting (Status == %r)...\n", __FUNCTION__, Status));
+    goto CloseProtocol;
+  }
+
+  CopyMem (&MacWord, (UINT8 *)mMacOverride + 4, sizeof MacWord);
+  Status = PciIo->Mem.Write (PciIo, EfiPciIoWidthUint32, 2, MAC + 4, 0x1, &MacWord);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_WARN, "%a: failed to set MAC address (#1) ...\n", __FUNCTION__));
+  }
+
+  CopyMem (&MacWord, (UINT8 *)mMacOverride, sizeof MacWord);
+  Status = PciIo->Mem.Write (PciIo, EfiPciIoWidthUint32, 2, MAC, 0x1, &MacWord);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_WARN, "%a: failed to set MAC address (#2) ...\n", __FUNCTION__));
+  }
+
+  Status = PciIo->Mem.Write (PciIo, EfiPciIoWidthUint8, 2, CFG9346, 0x1,
+                        (VOID *)&Cfg9346_Lock);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_WARN, "%a: relock failed ...\n", __FUNCTION__));
+    goto CloseProtocol;
+  }
+
+  Done = TRUE;
+
+  PciIo->Attributes (PciIo, EfiPciIoAttributeOperationDisable, Supports, NULL);
+
+CloseProtocol:
+  gBS->CloseProtocol (Controller, &gEfiPciIoProtocolGuid,
+         This->DriverBindingHandle, Controller);
+
+  //
+  // Always return unsupported: we are not interested in driving the device,
+  // only in having the opportunity to updating the MAC address before the real
+  // driver attaches to it.
+  //
+  return EFI_UNSUPPORTED;
+}
+
+/**
+  Start this driver on Controller. Not used.
+
+  @param [in] This                    Protocol instance pointer.
+  @param [in] Controller              Handle of device to work with.
+  @param [in] RemainingDevicePath     Not used, always produce all possible children.
+
+  @retval EFI_SUCCESS                 This driver is added to Controller.
+  @retval other                       This driver does not support this device.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+Realtek8169MacOverrideDriverStart (
+    IN EFI_DRIVER_BINDING_PROTOCOL  *This,
+    IN EFI_HANDLE                   Controller,
+    IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath
+    )
+{
+  //
+  // We are not interested in driving the device, we only poke the firmware
+  // in the .Supported() callback.
+  //
+  ASSERT (FALSE);
+  return EFI_INVALID_PARAMETER;
+}
+
+/**
+  Stop this driver on Controller. Not used.
+
+  @param [in] This                    Protocol instance pointer.
+  @param [in] Controller              Handle of device to stop driver on.
+  @param [in] NumberOfChildren        How many children need to be stopped.
+  @param [in] ChildHandleBuffer       Not used.
+
+  @retval EFI_SUCCESS                 This driver is removed Controller.
+  @retval EFI_DEVICE_ERROR            The device could not be stopped due to a device error.
+  @retval other                       This driver was not removed from this device.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+Realtek8169MacOverrideDriverStop (
+    IN  EFI_DRIVER_BINDING_PROTOCOL *This,
+    IN  EFI_HANDLE                  Controller,
+    IN  UINTN                       NumberOfChildren,
+    IN  EFI_HANDLE                  *ChildHandleBuffer
+    )
+{
+  ASSERT (FALSE);
+  return EFI_SUCCESS;
+}
+
+//
+// UEFI Driver Model entry point
+//
+STATIC EFI_DRIVER_BINDING_PROTOCOL Realtek8169MacOverrideDriverBinding = {
+  Realtek8169MacOverrideDriverSupported,
+  Realtek8169MacOverrideDriverStart,
+  Realtek8169MacOverrideDriverStop,
+
+  // Version values of 0xfffffff0-0xffffffff are reserved for platform/OEM
+  // specific drivers. Protocol instances with higher 'Version' properties
+  // will be used before lower 'Version' ones. XhciDxe uses version 0x30,
+  // so this driver will be called in preference, and XhciDxe will be invoked
+  // after Realtek8169MacOverrideDriverSupported returns EFI_UNSUPPORTED.
+  0xfffffff0,
+  NULL,
+  NULL
+};
+
+EFI_STATUS
+EFIAPI
+InitializeRealtek8169MacOverride (
+    IN EFI_HANDLE       ImageHandle,
+    IN EFI_SYSTEM_TABLE *SystemTable
+    )
+{
+  mMacOverride = PcdGetPtr (PcdMacOverride);
+
+  DEBUG ((DEBUG_WARN, "%a: using MAC override value %X:%X:%X:%X:%X:%X\n",
+    __FUNCTION__, mMacOverride[0], mMacOverride[1], mMacOverride[2],
+    mMacOverride[3], mMacOverride[4], mMacOverride[5]
+    ));
+
+  return EfiLibInstallDriverBinding (ImageHandle, SystemTable,
+        &Realtek8169MacOverrideDriverBinding, NULL);
+}
diff --git a/Drivers/Net/Realtek8169MacOverride/Realtek8169MacOverride.inf b/Drivers/Net/Realtek8169MacOverride/Realtek8169MacOverride.inf
new file mode 100644
index 000000000000..4ff1047281d3
--- /dev/null
+++ b/Drivers/Net/Realtek8169MacOverride/Realtek8169MacOverride.inf
@@ -0,0 +1,44 @@ 
+##  <at> file
+# Component description file for Realtek 8169 MAC override driver
+#
+# Copyright (c) 2017, Linaro Ltd. All rights reserved.<BR>
+#
+# 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.
+#
+##
+
+[Defines]
+  INF_VERSION                    = 0x00010005
+  BASE_NAME                      = Realtek8169MacOverride
+  FILE_GUID                      = 8d97e056-777c-4850-ab61-8166b1777f2d
+  MODULE_TYPE                    = UEFI_DRIVER
+  VERSION_STRING                 = 1.0
+  ENTRY_POINT                    = InitializeRealtek8169MacOverride
+
+[Sources]
+  Realtek8169MacOverride.c
+
+[Packages]
+  MdeModulePkg/MdeModulePkg.dec
+  MdePkg/MdePkg.dec
+  OpenPlatformPkg/OpenPlatformPkg.dec
+
+[LibraryClasses]
+  BaseMemoryLib
+  DebugLib
+  PcdLib
+  UefiBootServicesTableLib
+  UefiLib
+  UefiDriverEntryPoint
+
+[Protocols]
+  gEfiPciIoProtocolGuid
+
+[Pcd]
+  gOpenPlatformTokenSpaceGuid.PcdMacOverride
diff --git a/OpenPlatformPkg.dec b/OpenPlatformPkg.dec
index 2db143d637b3..34323bc90b9a 100644
--- a/OpenPlatformPkg.dec
+++ b/OpenPlatformPkg.dec
@@ -41,3 +41,6 @@ 
   gOpenPlatformTokenSpaceGuid.PcdRamDiskMaxSize|0|UINT32|0x00000001
 
 [PcdsFeatureFlag]
+
+[PcdsFixedAtBuild,PcdsDynamic]
+  gOpenPlatformTokenSpaceGuid.PcdMacOverride|{0x0,0x0,0x0,0x0,0x0,0x0}|VOID*|0x00000002