]> git.proxmox.com Git - mirror_edk2.git/blobdiff - OptionRomPkg/UndiRuntimeDxe/Init.c
add UndiRuntimeDxe from MdeModulePkg to OptionRomPkg.
[mirror_edk2.git] / OptionRomPkg / UndiRuntimeDxe / Init.c
diff --git a/OptionRomPkg/UndiRuntimeDxe/Init.c b/OptionRomPkg/UndiRuntimeDxe/Init.c
new file mode 100644 (file)
index 0000000..42dd5fd
--- /dev/null
@@ -0,0 +1,1055 @@
+/** @file\r
+  Initialization functions for EFI UNDI32 driver.\r
+\r
+Copyright (c) 2006 - 2008, Intel Corporation\r
+All rights reserved. This program and the accompanying materials\r
+are licensed and made available under the terms and conditions of the BSD License\r
+which accompanies this distribution.  The full text of the license may be found at\r
+http://opensource.org/licenses/bsd-license.php\r
+\r
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include "Undi32.h"\r
+//\r
+// Global Variables\r
+//\r
+\r
+PXE_SW_UNDI             *pxe_31 = NULL;  // 3.1 entry\r
+UNDI32_DEV              *UNDI32DeviceList[MAX_NIC_INTERFACES];\r
+NII_TABLE               *UndiDataPointer = NULL;\r
+\r
+//\r
+// UNDI Class Driver Global Variables\r
+//\r
+EFI_DRIVER_BINDING_PROTOCOL  gUndiDriverBinding = {\r
+  UndiDriverSupported,\r
+  UndiDriverStart,\r
+  UndiDriverStop,\r
+  0xa,\r
+  NULL,\r
+  NULL\r
+};\r
+\r
+\r
+/**\r
+  When address mapping changes to virtual this should make the appropriate\r
+  address conversions.\r
+\r
+  (Standard Event handler)\r
+\r
+  @return None\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+UndiNotifyVirtual (\r
+  EFI_EVENT Event,\r
+  VOID      *Context\r
+  )\r
+{\r
+  UINT16  Index;\r
+  VOID    *Pxe31Pointer;\r
+\r
+  if (pxe_31 != NULL) {\r
+    Pxe31Pointer = (VOID *) pxe_31;\r
+\r
+    EfiConvertPointer (\r
+      EFI_OPTIONAL_PTR,\r
+      (VOID **) &Pxe31Pointer\r
+      );\r
+\r
+    //\r
+    // UNDI32DeviceList is an array of pointers\r
+    //\r
+    for (Index = 0; Index < pxe_31->IFcnt; Index++) {\r
+      UNDI32DeviceList[Index]->NIIProtocol_31.Id = (UINT64) (UINTN) Pxe31Pointer;\r
+      EfiConvertPointer (\r
+        EFI_OPTIONAL_PTR,\r
+        (VOID **) &(UNDI32DeviceList[Index])\r
+        );\r
+    }\r
+\r
+    EfiConvertPointer (\r
+      EFI_OPTIONAL_PTR,\r
+      (VOID **) &(pxe_31->EntryPoint)\r
+      );\r
+    pxe_31 = Pxe31Pointer;\r
+  }\r
+\r
+  for (Index = 0; Index <= PXE_OPCODE_LAST_VALID; Index++) {\r
+    EfiConvertPointer (\r
+      EFI_OPTIONAL_PTR,\r
+      (VOID **) &api_table[Index].api_ptr\r
+      );\r
+  }\r
+}\r
+\r
+\r
+/**\r
+  When EFI is shuting down the boot services, we need to install a\r
+  configuration table for UNDI to work at runtime!\r
+\r
+  (Standard Event handler)\r
+\r
+  @return None\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+UndiNotifyExitBs (\r
+  EFI_EVENT Event,\r
+  VOID      *Context\r
+  )\r
+{\r
+  InstallConfigTable ();\r
+}\r
+\r
+\r
+/**\r
+  Test to see if this driver supports ControllerHandle. Any ControllerHandle\r
+  than contains a  DevicePath, PciIo protocol, Class code of 2, Vendor ID of 0x8086,\r
+  and DeviceId of (D100_DEVICE_ID || D102_DEVICE_ID || ICH3_DEVICE_ID_1 ||\r
+  ICH3_DEVICE_ID_2 || ICH3_DEVICE_ID_3 || ICH3_DEVICE_ID_4 || ICH3_DEVICE_ID_5 ||\r
+  ICH3_DEVICE_ID_6 || ICH3_DEVICE_ID_7 || ICH3_DEVICE_ID_8) can be supported.\r
+\r
+  @param  This                 Protocol instance pointer.\r
+  @param  Controller           Handle of device to test.\r
+  @param  RemainingDevicePath  Not used.\r
+\r
+  @retval EFI_SUCCESS          This driver supports this device.\r
+  @retval other                This driver does not support this device.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+UndiDriverSupported (\r
+  IN EFI_DRIVER_BINDING_PROTOCOL    *This,\r
+  IN EFI_HANDLE                     Controller,\r
+  IN EFI_DEVICE_PATH_PROTOCOL       *RemainingDevicePath\r
+  )\r
+{\r
+  EFI_STATUS          Status;\r
+  EFI_PCI_IO_PROTOCOL *PciIo;\r
+  PCI_TYPE00          Pci;\r
+\r
+  Status = gBS->OpenProtocol (\r
+                  Controller,\r
+                  &gEfiDevicePathProtocolGuid,\r
+                  NULL,\r
+                  This->DriverBindingHandle,\r
+                  Controller,\r
+                  EFI_OPEN_PROTOCOL_TEST_PROTOCOL\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  Status = gBS->OpenProtocol (\r
+                  Controller,\r
+                  &gEfiPciIoProtocolGuid,\r
+                  (VOID **) &PciIo,\r
+                  This->DriverBindingHandle,\r
+                  Controller,\r
+                  EFI_OPEN_PROTOCOL_BY_DRIVER\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  Status = PciIo->Pci.Read (\r
+                        PciIo,\r
+                        EfiPciIoWidthUint8,\r
+                        0,\r
+                        sizeof (PCI_CONFIG_HEADER),\r
+                        &Pci\r
+                        );\r
+\r
+  if (!EFI_ERROR (Status)) {\r
+    Status = EFI_UNSUPPORTED;\r
+\r
+    if (Pci.Hdr.ClassCode[2] == 0x02 && Pci.Hdr.VendorId == PCI_VENDOR_ID_INTEL) {\r
+      switch (Pci.Hdr.DeviceId) {\r
+      case D100_DEVICE_ID:\r
+      case D102_DEVICE_ID:\r
+      case ICH3_DEVICE_ID_1:\r
+      case ICH3_DEVICE_ID_2:\r
+      case ICH3_DEVICE_ID_3:\r
+      case ICH3_DEVICE_ID_4:\r
+      case ICH3_DEVICE_ID_5:\r
+      case ICH3_DEVICE_ID_6:\r
+      case ICH3_DEVICE_ID_7:\r
+      case ICH3_DEVICE_ID_8:\r
+      case 0x1039:\r
+      case 0x103A:\r
+      case 0x103B:\r
+      case 0x103C:\r
+      case 0x103D:\r
+      case 0x103E:\r
+      case 0x1050:\r
+      case 0x1051:\r
+      case 0x1052:\r
+      case 0x1053:\r
+      case 0x1054:\r
+      case 0x1055:\r
+      case 0x1056:\r
+      case 0x1057:\r
+      case 0x1059:\r
+      case 0x1064:\r
+        Status = EFI_SUCCESS;\r
+      }\r
+    }\r
+  }\r
+\r
+  gBS->CloseProtocol (\r
+        Controller,\r
+        &gEfiPciIoProtocolGuid,\r
+        This->DriverBindingHandle,\r
+        Controller\r
+        );\r
+\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  Start this driver on Controller by opening PciIo and DevicePath protocol.\r
+  Initialize PXE structures, create a copy of the Controller Device Path with the\r
+  NIC's MAC address appended to it, install the NetworkInterfaceIdentifier protocol\r
+  on the newly created Device Path.\r
+\r
+  @param  This                 Protocol instance pointer.\r
+  @param  Controller           Handle of device to work with.\r
+  @param  RemainingDevicePath  Not used, always produce all possible children.\r
+\r
+  @retval EFI_SUCCESS          This driver is added to Controller.\r
+  @retval other                This driver does not support this device.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+UndiDriverStart (\r
+  IN EFI_DRIVER_BINDING_PROTOCOL    *This,\r
+  IN EFI_HANDLE                     Controller,\r
+  IN EFI_DEVICE_PATH_PROTOCOL       *RemainingDevicePath\r
+  )\r
+{\r
+  EFI_STATUS                Status;\r
+  EFI_DEVICE_PATH_PROTOCOL  *UndiDevicePath;\r
+  PCI_CONFIG_HEADER         *CfgHdr;\r
+  UNDI32_DEV                *UNDI32Device;\r
+  UINT16                    NewCommand;\r
+  UINT8                     *TmpPxePointer;\r
+  EFI_PCI_IO_PROTOCOL       *PciIoFncs;\r
+  UINTN                     Len;\r
+  UINT64                    Supports;\r
+  BOOLEAN                   PciAttributesSaved;\r
+\r
+  Status = gBS->OpenProtocol (\r
+                  Controller,\r
+                  &gEfiPciIoProtocolGuid,\r
+                  (VOID **) &PciIoFncs,\r
+                  This->DriverBindingHandle,\r
+                  Controller,\r
+                  EFI_OPEN_PROTOCOL_BY_DRIVER\r
+                  );\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  Status = gBS->OpenProtocol (\r
+                  Controller,\r
+                  &gEfiDevicePathProtocolGuid,\r
+                  (VOID **) &UndiDevicePath,\r
+                  This->DriverBindingHandle,\r
+                  Controller,\r
+                  EFI_OPEN_PROTOCOL_BY_DRIVER\r
+                  );\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    gBS->CloseProtocol (\r
+          Controller,\r
+          &gEfiPciIoProtocolGuid,\r
+          This->DriverBindingHandle,\r
+          Controller\r
+          );\r
+\r
+    return Status;\r
+  }\r
+\r
+  PciAttributesSaved = FALSE;\r
+\r
+  Status = gBS->AllocatePool (\r
+                  EfiRuntimeServicesData,\r
+                  sizeof (UNDI32_DEV),\r
+                  (VOID **) &UNDI32Device\r
+                  );\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    goto UndiError;\r
+  }\r
+\r
+  ZeroMem ((CHAR8 *) UNDI32Device, sizeof (UNDI32_DEV));\r
+\r
+  //\r
+  // Get original PCI attributes\r
+  //\r
+  Status = PciIoFncs->Attributes (\r
+                    PciIoFncs,\r
+                    EfiPciIoAttributeOperationGet,\r
+                    0,\r
+                    &UNDI32Device->NicInfo.OriginalPciAttributes\r
+                    );\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    goto UndiErrorDeleteDevice;\r
+  }\r
+  PciAttributesSaved = TRUE;\r
+\r
+  //\r
+  // allocate and initialize both (old and new) the !pxe structures here,\r
+  // there should only be one copy of each of these structure for any number\r
+  // of NICs this undi supports. Also, these structures need to be on a\r
+  // paragraph boundary as per the spec. so, while allocating space for these,\r
+  // make sure that there is space for 2 !pxe structures (old and new) and a\r
+  // 32 bytes padding for alignment adjustment (in case)\r
+  //\r
+  TmpPxePointer = NULL;\r
+  if (pxe_31 == NULL) {\r
+    Status = gBS->AllocatePool (\r
+                    EfiRuntimeServicesData,\r
+                    (sizeof (PXE_SW_UNDI) + sizeof (PXE_SW_UNDI) + 32),\r
+                    (VOID **) &TmpPxePointer\r
+                    );\r
+\r
+    if (EFI_ERROR (Status)) {\r
+      goto UndiErrorDeleteDevice;\r
+    }\r
+\r
+    ZeroMem (\r
+      TmpPxePointer,\r
+      sizeof (PXE_SW_UNDI) + sizeof (PXE_SW_UNDI) + 32\r
+      );\r
+    //\r
+    // check for paragraph alignment here, assuming that the pointer is\r
+    // already 8 byte aligned.\r
+    //\r
+    if (((UINTN) TmpPxePointer & 0x0F) != 0) {\r
+      pxe_31 = (PXE_SW_UNDI *) ((UINTN) (TmpPxePointer + 8));\r
+    } else {\r
+      pxe_31 = (PXE_SW_UNDI *) TmpPxePointer;\r
+    }\r
+\r
+    PxeStructInit (pxe_31);\r
+  }\r
+\r
+  UNDI32Device->NIIProtocol_31.Id = (UINT64) (UINTN) (pxe_31);\r
+\r
+  Status = PciIoFncs->Attributes (\r
+                        PciIoFncs,\r
+                        EfiPciIoAttributeOperationSupported,\r
+                        0,\r
+                        &Supports\r
+                        );\r
+  if (!EFI_ERROR (Status)) {\r
+    Supports &= EFI_PCI_DEVICE_ENABLE;\r
+    Status = PciIoFncs->Attributes (\r
+                          PciIoFncs,\r
+                          EfiPciIoAttributeOperationEnable,\r
+                          Supports,\r
+                          NULL\r
+                          );\r
+  }\r
+  //\r
+  // Read all the registers from device's PCI Configuration space\r
+  //\r
+  Status = PciIoFncs->Pci.Read (\r
+                            PciIoFncs,\r
+                            EfiPciIoWidthUint32,\r
+                            0,\r
+                            MAX_PCI_CONFIG_LEN,\r
+                            &UNDI32Device->NicInfo.Config\r
+                            );\r
+\r
+  CfgHdr = (PCI_CONFIG_HEADER *) &(UNDI32Device->NicInfo.Config[0]);\r
+\r
+  //\r
+  // make sure that this device is a PCI bus master\r
+  //\r
+\r
+  NewCommand = (UINT16) (CfgHdr->Command | PCI_COMMAND_MASTER | PCI_COMMAND_IO);\r
+  if (CfgHdr->Command != NewCommand) {\r
+    PciIoFncs->Pci.Write (\r
+                    PciIoFncs,\r
+                    EfiPciIoWidthUint16,\r
+                    PCI_COMMAND,\r
+                    1,\r
+                    &NewCommand\r
+                    );\r
+    CfgHdr->Command = NewCommand;\r
+  }\r
+\r
+  //\r
+  // make sure that the latency timer is at least 32\r
+  //\r
+  if (CfgHdr->LatencyTimer < 32) {\r
+    CfgHdr->LatencyTimer = 32;\r
+    PciIoFncs->Pci.Write (\r
+                    PciIoFncs,\r
+                    EfiPciIoWidthUint8,\r
+                    PCI_LATENCY_TIMER,\r
+                    1,\r
+                    &CfgHdr->LatencyTimer\r
+                    );\r
+  }\r
+  //\r
+  // the IfNum index for the current interface will be the total number\r
+  // of interfaces initialized so far\r
+  //\r
+  UNDI32Device->NIIProtocol_31.IfNum  = pxe_31->IFcnt;\r
+\r
+  PxeUpdate (&UNDI32Device->NicInfo, pxe_31);\r
+\r
+  UNDI32Device->NicInfo.Io_Function                    = PciIoFncs;\r
+  UNDI32DeviceList[UNDI32Device->NIIProtocol_31.IfNum] = UNDI32Device;\r
+  UNDI32Device->Undi32BaseDevPath                      = UndiDevicePath;\r
+\r
+  Status = AppendMac2DevPath (\r
+            &UNDI32Device->Undi32DevPath,\r
+            UNDI32Device->Undi32BaseDevPath,\r
+            &UNDI32Device->NicInfo\r
+            );\r
+\r
+  if (Status != 0) {\r
+    goto UndiErrorDeletePxe;\r
+  }\r
+\r
+  UNDI32Device->Signature                     = UNDI_DEV_SIGNATURE;\r
+\r
+  UNDI32Device->NIIProtocol_31.Revision       = EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL_REVISION_31;\r
+  UNDI32Device->NIIProtocol_31.Type           = EfiNetworkInterfaceUndi;\r
+  UNDI32Device->NIIProtocol_31.MajorVer       = PXE_ROMID_MAJORVER;\r
+  UNDI32Device->NIIProtocol_31.MinorVer       = PXE_ROMID_MINORVER_31;\r
+  UNDI32Device->NIIProtocol_31.ImageSize      = 0;\r
+  UNDI32Device->NIIProtocol_31.ImageAddr      = 0;\r
+  UNDI32Device->NIIProtocol_31.Ipv6Supported  = FALSE;\r
+\r
+  UNDI32Device->NIIProtocol_31.StringId[0]    = 'U';\r
+  UNDI32Device->NIIProtocol_31.StringId[1]    = 'N';\r
+  UNDI32Device->NIIProtocol_31.StringId[2]    = 'D';\r
+  UNDI32Device->NIIProtocol_31.StringId[3]    = 'I';\r
+\r
+  UNDI32Device->DeviceHandle                  = NULL;\r
+\r
+  //\r
+  // install both the 3.0 and 3.1 NII protocols.\r
+  //\r
+  Status = gBS->InstallMultipleProtocolInterfaces (\r
+                  &UNDI32Device->DeviceHandle,\r
+                  &gEfiNetworkInterfaceIdentifierProtocolGuid_31,\r
+                  &UNDI32Device->NIIProtocol_31,\r
+                  &gEfiDevicePathProtocolGuid,\r
+                  UNDI32Device->Undi32DevPath,\r
+                  NULL\r
+                  );\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    goto UndiErrorDeleteDevicePath;\r
+  }\r
+\r
+  //\r
+  // if the table exists, free it and alloc again, or alloc it directly\r
+  //\r
+  if (UndiDataPointer != NULL) {\r
+    Status = gBS->FreePool(UndiDataPointer);\r
+  }\r
+  if (EFI_ERROR (Status)) {\r
+    goto UndiErrorDeleteDevicePath;\r
+  }\r
+\r
+  Len = (pxe_31->IFcnt * sizeof (NII_ENTRY)) + sizeof (UndiDataPointer);\r
+  Status = gBS->AllocatePool (EfiRuntimeServicesData, Len, (VOID **) &UndiDataPointer);\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    goto UndiErrorAllocDataPointer;\r
+  }\r
+\r
+  //\r
+  // Open For Child Device\r
+  //\r
+  Status = gBS->OpenProtocol (\r
+                  Controller,\r
+                  &gEfiPciIoProtocolGuid,\r
+                  (VOID **) &PciIoFncs,\r
+                  This->DriverBindingHandle,\r
+                  UNDI32Device->DeviceHandle,\r
+                  EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER\r
+                  );\r
+\r
+  return EFI_SUCCESS;\r
+UndiErrorAllocDataPointer:\r
+  gBS->UninstallMultipleProtocolInterfaces (\r
+                  &UNDI32Device->DeviceHandle,\r
+                  &gEfiNetworkInterfaceIdentifierProtocolGuid_31,\r
+                  &UNDI32Device->NIIProtocol_31,\r
+                  &gEfiDevicePathProtocolGuid,\r
+                  UNDI32Device->Undi32DevPath,\r
+                  NULL\r
+                  );\r
+\r
+UndiErrorDeleteDevicePath:\r
+  UNDI32DeviceList[UNDI32Device->NIIProtocol_31.IfNum] = NULL;\r
+  gBS->FreePool (UNDI32Device->Undi32DevPath);\r
+\r
+UndiErrorDeletePxe:\r
+  PxeUpdate (NULL, pxe_31);\r
+  if (TmpPxePointer != NULL) {\r
+    gBS->FreePool (TmpPxePointer);\r
+\r
+  }\r
+\r
+UndiErrorDeleteDevice:\r
+  if (PciAttributesSaved) {\r
+    //\r
+    // Restore original PCI attributes\r
+    //\r
+    PciIoFncs->Attributes (\r
+                    PciIoFncs,\r
+                    EfiPciIoAttributeOperationSet,\r
+                    UNDI32Device->NicInfo.OriginalPciAttributes,\r
+                    NULL\r
+                    );\r
+  }\r
+\r
+  gBS->FreePool (UNDI32Device);\r
+\r
+UndiError:\r
+  gBS->CloseProtocol (\r
+        Controller,\r
+        &gEfiDevicePathProtocolGuid,\r
+        This->DriverBindingHandle,\r
+        Controller\r
+        );\r
+\r
+  gBS->CloseProtocol (\r
+        Controller,\r
+        &gEfiPciIoProtocolGuid,\r
+        This->DriverBindingHandle,\r
+        Controller\r
+        );\r
+\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  Stop this driver on Controller by removing NetworkInterfaceIdentifier protocol and\r
+  closing the DevicePath and PciIo protocols on Controller.\r
+\r
+  @param  This                 Protocol instance pointer.\r
+  @param  Controller           Handle of device to stop driver on.\r
+  @param  NumberOfChildren     How many children need to be stopped.\r
+  @param  ChildHandleBuffer    Not used.\r
+\r
+  @retval EFI_SUCCESS          This driver is removed Controller.\r
+  @retval other                This driver was not removed from this device.\r
+\r
+**/\r
+// TODO:    EFI_DEVICE_ERROR - add return value to function comment\r
+EFI_STATUS\r
+EFIAPI\r
+UndiDriverStop (\r
+  IN  EFI_DRIVER_BINDING_PROTOCOL    *This,\r
+  IN  EFI_HANDLE                     Controller,\r
+  IN  UINTN                          NumberOfChildren,\r
+  IN  EFI_HANDLE                     *ChildHandleBuffer\r
+  )\r
+{\r
+  EFI_STATUS                                Status;\r
+  BOOLEAN                                   AllChildrenStopped;\r
+  UINTN                                     Index;\r
+  UNDI32_DEV                                *UNDI32Device;\r
+  EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL *NIIProtocol;\r
+  EFI_PCI_IO_PROTOCOL                       *PciIo;\r
+\r
+  //\r
+  // Complete all outstanding transactions to Controller.\r
+  // Don't allow any new transaction to Controller to be started.\r
+  //\r
+  if (NumberOfChildren == 0) {\r
+\r
+    //\r
+    // Close the bus driver\r
+    //\r
+    Status = gBS->CloseProtocol (\r
+                    Controller,\r
+                    &gEfiDevicePathProtocolGuid,\r
+                    This->DriverBindingHandle,\r
+                    Controller\r
+                    );\r
+\r
+    Status = gBS->CloseProtocol (\r
+                    Controller,\r
+                    &gEfiPciIoProtocolGuid,\r
+                    This->DriverBindingHandle,\r
+                    Controller\r
+                    );\r
+\r
+    return Status;\r
+  }\r
+\r
+  AllChildrenStopped = TRUE;\r
+\r
+  for (Index = 0; Index < NumberOfChildren; Index++) {\r
+\r
+    Status = gBS->OpenProtocol (\r
+                    ChildHandleBuffer[Index],\r
+                    &gEfiNetworkInterfaceIdentifierProtocolGuid_31,\r
+                    (VOID **) &NIIProtocol,\r
+                    This->DriverBindingHandle,\r
+                    Controller,\r
+                    EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
+                    );\r
+    if (!EFI_ERROR (Status)) {\r
+\r
+      UNDI32Device = UNDI_DEV_FROM_THIS (NIIProtocol);\r
+\r
+      //\r
+      // Restore original PCI attributes\r
+      //\r
+      Status = UNDI32Device->NicInfo.Io_Function->Attributes (\r
+                            UNDI32Device->NicInfo.Io_Function,\r
+                            EfiPciIoAttributeOperationSet,\r
+                            UNDI32Device->NicInfo.OriginalPciAttributes,\r
+                            NULL\r
+                            );\r
+      ASSERT_EFI_ERROR (Status);\r
+\r
+      Status = gBS->CloseProtocol (\r
+                      Controller,\r
+                      &gEfiPciIoProtocolGuid,\r
+                      This->DriverBindingHandle,\r
+                      ChildHandleBuffer[Index]\r
+                      );\r
+\r
+      Status = gBS->UninstallMultipleProtocolInterfaces (\r
+                      ChildHandleBuffer[Index],\r
+                      &gEfiDevicePathProtocolGuid,\r
+                      UNDI32Device->Undi32DevPath,\r
+                      &gEfiNetworkInterfaceIdentifierProtocolGuid_31,\r
+                      &UNDI32Device->NIIProtocol_31,\r
+                      NULL\r
+                      );\r
+\r
+      if (EFI_ERROR (Status)) {\r
+        gBS->OpenProtocol (\r
+              Controller,\r
+              &gEfiPciIoProtocolGuid,\r
+              (VOID **) &PciIo,\r
+              This->DriverBindingHandle,\r
+              ChildHandleBuffer[Index],\r
+              EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER\r
+              );\r
+      } else {\r
+        gBS->FreePool (UNDI32Device->Undi32DevPath);\r
+        gBS->FreePool (UNDI32Device);\r
+      }\r
+    }\r
+\r
+    if (EFI_ERROR (Status)) {\r
+      AllChildrenStopped = FALSE;\r
+    }\r
+  }\r
+\r
+  if (!AllChildrenStopped) {\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+\r
+}\r
+\r
+\r
+/**\r
+  Use the EFI boot services to produce a pause. This is also the routine which\r
+  gets replaced during RunTime by the O/S in the NIC_DATA_INSTANCE so it can\r
+  do it's own pause.\r
+\r
+  @param  UnqId                Runtime O/S routine might use this, this temp\r
+                               routine does not use it\r
+  @param  MicroSeconds         Determines the length of pause.\r
+\r
+  @return none\r
+\r
+**/\r
+VOID\r
+TmpDelay (\r
+  IN UINT64 UnqId,\r
+  IN UINTN  MicroSeconds\r
+  )\r
+{\r
+  gBS->Stall ((UINT32) MicroSeconds);\r
+}\r
+\r
+\r
+/**\r
+  Use the PCI IO abstraction to issue memory or I/O reads and writes.  This is also the routine which\r
+  gets replaced during RunTime by the O/S in the NIC_DATA_INSTANCE so it can do it's own I/O abstractions.\r
+\r
+  @param  UnqId                Runtime O/S routine may use this field, this temp\r
+                               routine does not.\r
+  @param  ReadWrite            Determine if it is an I/O or Memory Read/Write\r
+                               Operation.\r
+  @param  Len                  Determines the width of the data operation.\r
+  @param  Port                 What port to Read/Write from.\r
+  @param  BuffAddr             Address to read to or write from.\r
+\r
+  @return none\r
+\r
+**/\r
+VOID\r
+TmpMemIo (\r
+  IN UINT64 UnqId,\r
+  IN UINT8  ReadWrite,\r
+  IN UINT8  Len,\r
+  IN UINT64 Port,\r
+  IN UINT64 BuffAddr\r
+  )\r
+{\r
+  EFI_PCI_IO_PROTOCOL_WIDTH Width;\r
+  NIC_DATA_INSTANCE         *AdapterInfo;\r
+\r
+  Width       = (EFI_PCI_IO_PROTOCOL_WIDTH) 0;\r
+  AdapterInfo = (NIC_DATA_INSTANCE *) (UINTN) UnqId;\r
+  switch (Len) {\r
+  case 2:\r
+    Width = (EFI_PCI_IO_PROTOCOL_WIDTH) 1;\r
+    break;\r
+\r
+  case 4:\r
+    Width = (EFI_PCI_IO_PROTOCOL_WIDTH) 2;\r
+    break;\r
+\r
+  case 8:\r
+    Width = (EFI_PCI_IO_PROTOCOL_WIDTH) 3;\r
+    break;\r
+  }\r
+\r
+  switch (ReadWrite) {\r
+  case PXE_IO_READ:\r
+    AdapterInfo->Io_Function->Io.Read (\r
+                                  AdapterInfo->Io_Function,\r
+                                  Width,\r
+                                  1,\r
+                                  Port,\r
+                                  1,\r
+                                  (VOID *) (UINTN) (BuffAddr)\r
+                                  );\r
+    break;\r
+\r
+  case PXE_IO_WRITE:\r
+    AdapterInfo->Io_Function->Io.Write (\r
+                                  AdapterInfo->Io_Function,\r
+                                  Width,\r
+                                  1,\r
+                                  Port,\r
+                                  1,\r
+                                  (VOID *) (UINTN) (BuffAddr)\r
+                                  );\r
+    break;\r
+\r
+  case PXE_MEM_READ:\r
+    AdapterInfo->Io_Function->Mem.Read (\r
+                                    AdapterInfo->Io_Function,\r
+                                    Width,\r
+                                    0,\r
+                                    Port,\r
+                                    1,\r
+                                    (VOID *) (UINTN) (BuffAddr)\r
+                                    );\r
+    break;\r
+\r
+  case PXE_MEM_WRITE:\r
+    AdapterInfo->Io_Function->Mem.Write (\r
+                                    AdapterInfo->Io_Function,\r
+                                    Width,\r
+                                    0,\r
+                                    Port,\r
+                                    1,\r
+                                    (VOID *) (UINTN) (BuffAddr)\r
+                                    );\r
+    break;\r
+  }\r
+\r
+  return ;\r
+}\r
+\r
+\r
+/**\r
+  Using the NIC data structure information, read the EEPROM to get the MAC address and then allocate space\r
+  for a new devicepath (**DevPtr) which will contain the original device path the NIC was found on (*BaseDevPtr)\r
+  and an added MAC node.\r
+\r
+  @param  DevPtr               Pointer which will point to the newly created device\r
+                               path with the MAC node attached.\r
+  @param  BaseDevPtr           Pointer to the device path which the UNDI device\r
+                               driver is latching on to.\r
+  @param  AdapterInfo          Pointer to the NIC data structure information which\r
+                               the UNDI driver is layering on..\r
+\r
+  @retval EFI_SUCCESS          A MAC address was successfully appended to the Base\r
+                               Device Path.\r
+  @retval other                Not enough resources available to create new Device\r
+                               Path node.\r
+\r
+**/\r
+EFI_STATUS\r
+AppendMac2DevPath (\r
+  IN OUT  EFI_DEVICE_PATH_PROTOCOL **DevPtr,\r
+  IN      EFI_DEVICE_PATH_PROTOCOL *BaseDevPtr,\r
+  IN      NIC_DATA_INSTANCE        *AdapterInfo\r
+  )\r
+{\r
+  EFI_MAC_ADDRESS           MACAddress;\r
+  PCI_CONFIG_HEADER         *CfgHdr;\r
+  INT32                     Val;\r
+  INT32                     Index;\r
+  INT32                     Index2;\r
+  UINT8                     AddrLen;\r
+  MAC_ADDR_DEVICE_PATH      MacAddrNode;\r
+  EFI_DEVICE_PATH_PROTOCOL  *EndNode;\r
+  UINT8                     *DevicePtr;\r
+  UINT16                    TotalPathLen;\r
+  UINT16                    BasePathLen;\r
+  EFI_STATUS                Status;\r
+\r
+  //\r
+  // set the environment ready (similar to UNDI_Start call) so that we can\r
+  // execute the other UNDI_ calls to get the mac address\r
+  // we are using undi 3.1 style\r
+  //\r
+  AdapterInfo->Delay      = TmpDelay;\r
+  AdapterInfo->Virt2Phys  = (VOID *) 0;\r
+  AdapterInfo->Block      = (VOID *) 0;\r
+  AdapterInfo->Map_Mem    = (VOID *) 0;\r
+  AdapterInfo->UnMap_Mem  = (VOID *) 0;\r
+  AdapterInfo->Sync_Mem   = (VOID *) 0;\r
+  AdapterInfo->Mem_Io     = TmpMemIo;\r
+  //\r
+  // these tmp call-backs follow 3.1 undi style\r
+  // i.e. they have the unique_id parameter.\r
+  //\r
+  AdapterInfo->VersionFlag  = 0x31;\r
+  AdapterInfo->Unique_ID    = (UINT64) (UINTN) AdapterInfo;\r
+\r
+  //\r
+  // undi init portion\r
+  //\r
+  CfgHdr              = (PCI_CONFIG_HEADER *) &(AdapterInfo->Config[0]);\r
+  AdapterInfo->ioaddr = 0;\r
+  AdapterInfo->RevID  = CfgHdr->RevID;\r
+\r
+  AddrLen             = E100bGetEepromAddrLen (AdapterInfo);\r
+\r
+  for (Index = 0, Index2 = 0; Index < 3; Index++) {\r
+    Val                       = E100bReadEeprom (AdapterInfo, Index, AddrLen);\r
+    MACAddress.Addr[Index2++] = (UINT8) Val;\r
+    MACAddress.Addr[Index2++] = (UINT8) (Val >> 8);\r
+  }\r
+\r
+  SetMem (MACAddress.Addr + Index2, sizeof (EFI_MAC_ADDRESS) - Index2, 0);\r
+  //for (; Index2 < sizeof (EFI_MAC_ADDRESS); Index2++) {\r
+  //  MACAddress.Addr[Index2] = 0;\r
+  //}\r
+  //\r
+  // stop undi\r
+  //\r
+  AdapterInfo->Delay  = (VOID *) 0;\r
+  AdapterInfo->Mem_Io = (VOID *) 0;\r
+\r
+  //\r
+  // fill the mac address node first\r
+  //\r
+  ZeroMem ((CHAR8 *) &MacAddrNode, sizeof MacAddrNode);\r
+  CopyMem (\r
+    (CHAR8 *) &MacAddrNode.MacAddress,\r
+    (CHAR8 *) &MACAddress,\r
+    sizeof (EFI_MAC_ADDRESS)\r
+    );\r
+\r
+  MacAddrNode.Header.Type       = MESSAGING_DEVICE_PATH;\r
+  MacAddrNode.Header.SubType    = MSG_MAC_ADDR_DP;\r
+  MacAddrNode.Header.Length[0]  = sizeof (MacAddrNode);\r
+  MacAddrNode.Header.Length[1]  = 0;\r
+\r
+  //\r
+  // find the size of the base dev path.\r
+  //\r
+  EndNode = BaseDevPtr;\r
+\r
+  while (!IsDevicePathEnd (EndNode)) {\r
+    EndNode = NextDevicePathNode (EndNode);\r
+  }\r
+\r
+  BasePathLen = (UINT16) ((UINTN) (EndNode) - (UINTN) (BaseDevPtr));\r
+\r
+  //\r
+  // create space for full dev path\r
+  //\r
+  TotalPathLen = (UINT16) (BasePathLen + sizeof (MacAddrNode) + sizeof (EFI_DEVICE_PATH_PROTOCOL));\r
+\r
+  Status = gBS->AllocatePool (\r
+                  EfiRuntimeServicesData,\r
+                  TotalPathLen,\r
+                  (VOID **) &DevicePtr\r
+                  );\r
+\r
+  if (Status != EFI_SUCCESS) {\r
+    return Status;\r
+  }\r
+  //\r
+  // copy the base path, mac addr and end_dev_path nodes\r
+  //\r
+  *DevPtr = (EFI_DEVICE_PATH_PROTOCOL *) DevicePtr;\r
+  CopyMem (DevicePtr, (CHAR8 *) BaseDevPtr, BasePathLen);\r
+  DevicePtr += BasePathLen;\r
+  CopyMem (DevicePtr, (CHAR8 *) &MacAddrNode, sizeof (MacAddrNode));\r
+  DevicePtr += sizeof (MacAddrNode);\r
+  CopyMem (DevicePtr, (CHAR8 *) EndNode, sizeof (EFI_DEVICE_PATH_PROTOCOL));\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+  Install a GUID/Pointer pair into the system's configuration table.\r
+\r
+  none\r
+\r
+  @retval EFI_SUCCESS          Install a GUID/Pointer pair into the system's\r
+                               configuration table.\r
+  @retval other                Did not successfully install the GUID/Pointer pair\r
+                               into the configuration table.\r
+\r
+**/\r
+// TODO:    VOID - add argument and description to function comment\r
+EFI_STATUS\r
+InstallConfigTable (\r
+  IN VOID\r
+  )\r
+{\r
+  EFI_STATUS              Status;\r
+  EFI_CONFIGURATION_TABLE *CfgPtr;\r
+  NII_TABLE               *TmpData;\r
+  UINT16                  Index;\r
+  NII_TABLE               *UndiData;\r
+\r
+  if (pxe_31 == NULL) {\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  if(UndiDataPointer == NULL) {\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  UndiData = (NII_TABLE *)UndiDataPointer;\r
+\r
+  UndiData->NumEntries  = pxe_31->IFcnt;\r
+  UndiData->NextLink    = NULL;\r
+\r
+  for (Index = 0; Index < pxe_31->IFcnt; Index++) {\r
+    UndiData->NiiEntry[Index].InterfacePointer  = &UNDI32DeviceList[Index]->NIIProtocol_31;\r
+    UndiData->NiiEntry[Index].DevicePathPointer = UNDI32DeviceList[Index]->Undi32DevPath;\r
+  }\r
+\r
+  //\r
+  // see if there is an entry in the config table already\r
+  //\r
+  CfgPtr = gST->ConfigurationTable;\r
+\r
+  for (Index = 0; Index < gST->NumberOfTableEntries; Index++) {\r
+    Status = CompareGuid (\r
+              &CfgPtr->VendorGuid,\r
+              &gEfiNetworkInterfaceIdentifierProtocolGuid_31\r
+              );\r
+    if (Status != EFI_SUCCESS) {\r
+      break;\r
+    }\r
+\r
+    CfgPtr++;\r
+  }\r
+\r
+  if (Index < gST->NumberOfTableEntries) {\r
+    TmpData = (NII_TABLE *) CfgPtr->VendorTable;\r
+\r
+    //\r
+    // go to the last link\r
+    //\r
+    while (TmpData->NextLink != NULL) {\r
+      TmpData = TmpData->NextLink;\r
+    }\r
+\r
+    TmpData->NextLink = UndiData;\r
+\r
+    //\r
+    // 1st one in chain\r
+    //\r
+    UndiData = (NII_TABLE *) CfgPtr->VendorTable;\r
+  }\r
+\r
+  //\r
+  // create an entry in the configuration table for our GUID\r
+  //\r
+  Status = gBS->InstallConfigurationTable (\r
+                  &gEfiNetworkInterfaceIdentifierProtocolGuid_31,\r
+                  UndiData\r
+                  );\r
+  return Status;\r
+}\r
+\r
+/**\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+InitializeUndi(\r
+  IN EFI_HANDLE           ImageHandle,\r
+  IN EFI_SYSTEM_TABLE     *SystemTable\r
+  )\r
+{\r
+  EFI_EVENT     Event;\r
+  EFI_STATUS    Status;\r
+\r
+  Status = EfiLibInstallDriverBinding (\r
+             ImageHandle,\r
+             SystemTable,\r
+             &gUndiDriverBinding,\r
+             ImageHandle\r
+             );\r
+  ASSERT_EFI_ERROR (Status);\r
+\r
+  Status = gBS->CreateEventEx (\r
+                  EVT_NOTIFY_SIGNAL,\r
+                  TPL_NOTIFY,\r
+                  UndiNotifyExitBs,\r
+                  NULL,\r
+                  &gEfiEventExitBootServicesGuid,\r
+                  &Event\r
+                  );\r
+  ASSERT_EFI_ERROR (Status);\r
+\r
+  Status = gBS->CreateEventEx (\r
+                  EVT_NOTIFY_SIGNAL,\r
+                  TPL_NOTIFY,\r
+                  UndiNotifyVirtual,\r
+                  NULL,\r
+                  &gEfiEventVirtualAddressChangeGuid,\r
+                  &Event\r
+                  );\r
+  ASSERT_EFI_ERROR (Status);\r
+\r
+  return Status;\r
+}\r