diff mbox series

[edk2,v1,1/2] EmbeddedPkg: add NonDiscoverableDeviceDxe driver

Message ID 1533890955-13005-2-git-send-email-haojian.zhuang@linaro.org
State Superseded
Headers show
Series add DwMmcHcDxe driver | expand

Commit Message

Haojian Zhuang Aug. 10, 2018, 8:49 a.m. UTC
It's used to create NonDiscoverableDevice in embedded platform. Since
there's no PCI bus.

Contributed-under: TianoCore Contribution Agreement 1.1
Signed-off-by: Haojian Zhuang <haojian.zhuang@linaro.org>

---
 EmbeddedPkg/EmbeddedPkg.dec                                                 |   1 +
 EmbeddedPkg/Universal/NonDiscoverableDeviceDxe/NonDiscoverableDeviceDxe.inf |  52 ++
 EmbeddedPkg/Universal/NonDiscoverableDeviceDxe/NonDiscoverableDeviceIo.h    |  92 ++
 EmbeddedPkg/Universal/NonDiscoverableDeviceDxe/ComponentName.c              | 124 +++
 EmbeddedPkg/Universal/NonDiscoverableDeviceDxe/NonDiscoverableDeviceDxe.c   | 243 +++++
 EmbeddedPkg/Universal/NonDiscoverableDeviceDxe/NonDiscoverableDeviceIo.c    | 972 ++++++++++++++++++++
 6 files changed, 1484 insertions(+)

-- 
2.7.4

_______________________________________________
edk2-devel mailing list
edk2-devel@lists.01.org
https://lists.01.org/mailman/listinfo/edk2-devel
diff mbox series

Patch

diff --git a/EmbeddedPkg/EmbeddedPkg.dec b/EmbeddedPkg/EmbeddedPkg.dec
index 28a143865d0e..6a80f31e95c0 100644
--- a/EmbeddedPkg/EmbeddedPkg.dec
+++ b/EmbeddedPkg/EmbeddedPkg.dec
@@ -85,6 +85,7 @@  [Protocols.common]
   gPlatformGpioProtocolGuid = { 0x52ce9845, 0x5af4, 0x43e2, {0xba, 0xfd, 0x23, 0x08, 0x12, 0x54, 0x7a, 0xc2 }}
   gPlatformVirtualKeyboardProtocolGuid = { 0x0e3606d2, 0x1dc3, 0x4e6f, { 0xbe, 0x65, 0x39, 0x49, 0x82, 0xa2, 0x65, 0x47 }}
   gAndroidBootImgProtocolGuid = { 0x9859bb19, 0x407c, 0x4f8b, {0xbc, 0xe1, 0xf8, 0xda, 0x65, 0x65, 0xf4, 0xa5 }}
+  gEmbeddedNonDiscoverableIoProtocolGuid = { 0x6937742f, 0xf611, 0x4a40, { 0xb1, 0xc6, 0xe7, 0xb4, 0x6e, 0x3c, 0x6e, 0x32 }}
 
 [Ppis]
   gEdkiiEmbeddedGpioPpiGuid = { 0x21c3b115, 0x4e0b, 0x470c, { 0x85, 0xc7, 0xe1, 0x05, 0xa5, 0x75, 0xc9, 0x7b }}
diff --git a/EmbeddedPkg/Universal/NonDiscoverableDeviceDxe/NonDiscoverableDeviceDxe.inf b/EmbeddedPkg/Universal/NonDiscoverableDeviceDxe/NonDiscoverableDeviceDxe.inf
new file mode 100644
index 000000000000..b3f7c8bc2976
--- /dev/null
+++ b/EmbeddedPkg/Universal/NonDiscoverableDeviceDxe/NonDiscoverableDeviceDxe.inf
@@ -0,0 +1,52 @@ 
+## @file
+# I/O driver for non-discoverable devices.
+#
+# Copyright (C) 2016-2018, Linaro Ltd.
+#
+# 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                    = 0x0001001a
+  BASE_NAME                      = NonDiscoverableDeviceDxe
+  FILE_GUID                      = 66c8ca38-4c1e-4730-8c77-6c248ad89abd
+  MODULE_TYPE                    = UEFI_DRIVER
+  VERSION_STRING                 = 1.0
+  ENTRY_POINT                    = NonDiscoverableDeviceDxeEntryPoint
+
+[Sources]
+  ComponentName.c
+  NonDiscoverableDeviceDxe.c
+  NonDiscoverableDeviceIo.c
+  NonDiscoverableDeviceIo.h
+
+[Packages]
+  EmbeddedPkg/EmbeddedPkg.dec
+  MdePkg/MdePkg.dec
+  MdeModulePkg/MdeModulePkg.dec
+
+[LibraryClasses]
+  BaseMemoryLib
+  DebugLib
+  DxeServicesTableLib
+  MemoryAllocationLib
+  UefiBootServicesTableLib
+  UefiDriverEntryPoint
+  UefiLib
+
+[Protocols]
+  gEdkiiNonDiscoverableDeviceProtocolGuid       ## TO_START
+  gEfiCpuArchProtocolGuid                       ## CONSUMES
+  gEmbeddedNonDiscoverableIoProtocolGuid
+
+[Guids]
+  gEdkiiNonDiscoverableNvmeDeviceGuid       ## CONSUMES ## GUID
+  gEdkiiNonDiscoverableSdhciDeviceGuid      ## CONSUMES ## GUID
+  gEdkiiNonDiscoverableUfsDeviceGuid        ## CONSUMES ## GUID
diff --git a/EmbeddedPkg/Universal/NonDiscoverableDeviceDxe/NonDiscoverableDeviceIo.h b/EmbeddedPkg/Universal/NonDiscoverableDeviceDxe/NonDiscoverableDeviceIo.h
new file mode 100644
index 000000000000..faa0bfcc17d4
--- /dev/null
+++ b/EmbeddedPkg/Universal/NonDiscoverableDeviceDxe/NonDiscoverableDeviceIo.h
@@ -0,0 +1,92 @@ 
+/** @file
+
+  Copyright (C) 2016-2018, 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.
+
+**/
+
+#ifndef __NON_DISCOVERABLE_DEVICE_IO_H__
+#define __NON_DISCOVERABLE_DEVICE_IO_H__
+
+#include <Library/DebugLib.h>
+
+#include <Protocol/ComponentName.h>
+#include <Protocol/ComponentName2.h>
+#include <Protocol/Cpu.h>
+#include <Protocol/DeviceIo.h>
+#include <Protocol/NonDiscoverableDevice.h>
+
+#define NON_DISCOVERABLE_IO_DEVICE_SIG SIGNATURE_32 ('N', 'D', 'I', 'D')
+
+#define NON_DISCOVERABLE_IO_DEVICE_FROM_IO(IoPointer)      \
+        CR (IoPointer, NON_DISCOVERABLE_IO_DEVICE, Io,     \
+            NON_DISCOVERABLE_IO_DEVICE_SIG)
+
+extern EFI_CPU_ARCH_PROTOCOL      *mCpu;
+
+typedef struct {
+  //
+  // The linked-list next pointer
+  //
+  LIST_ENTRY          List;
+  //
+  // The address of the uncached allocation
+  //
+  VOID                *HostAddress;
+  //
+  // The number of pages in the allocation
+  //
+  UINTN               NumPages;
+  //
+  // The attributes of the allocation
+  //
+  UINT64              Attributes;
+} NON_DISCOVERABLE_DEVICE_UNCACHED_ALLOCATION;
+
+typedef struct {
+  UINT32                    Signature;
+  //
+  // The bound non-discoverable device protocol instance
+  //
+  NON_DISCOVERABLE_DEVICE   *Device;
+  //
+  // The exposed I/O protocol instance.
+  //
+  EFI_DEVICE_IO_PROTOCOL    Io;
+  //
+  // The I/O attributes for this device
+  //
+  UINT64                    Attributes;
+  //
+  // Whether this device has been enabled
+  //
+  BOOLEAN                   Enabled;
+  //
+  // Linked list to keep track of uncached allocations performed
+  // on behalf of this device
+  //
+  LIST_ENTRY                UncachedAllocationList;
+} NON_DISCOVERABLE_IO_DEVICE;
+
+/**
+  Initialize Io Protocol.
+
+  @param  Device      Point to NON_DISCOVERABLE_IO_DEVICE instance.
+
+**/
+VOID
+InitializeIoProtocol (
+  NON_DISCOVERABLE_IO_DEVICE     *Device
+  );
+
+extern EFI_COMPONENT_NAME_PROTOCOL gComponentName;
+extern EFI_COMPONENT_NAME2_PROTOCOL gComponentName2;
+
+#endif /* __NON_DISCOVERABLE_DEVICE_IO_H__ */
diff --git a/EmbeddedPkg/Universal/NonDiscoverableDeviceDxe/ComponentName.c b/EmbeddedPkg/Universal/NonDiscoverableDeviceDxe/ComponentName.c
new file mode 100644
index 000000000000..613938697ee4
--- /dev/null
+++ b/EmbeddedPkg/Universal/NonDiscoverableDeviceDxe/ComponentName.c
@@ -0,0 +1,124 @@ 
+/** @file
+
+  Copyright (C) 2016-2018, 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.
+
+**/
+
+#include <Library/UefiLib.h>
+
+#include "NonDiscoverableDeviceIo.h"
+
+//
+// The purpose of the following scaffolding (EFI_COMPONENT_NAME_PROTOCOL and
+// EFI_COMPONENT_NAME2_PROTOCOL implementation) is to format the driver's name
+// in English, for display on standard console devices. This is recommended for
+// UEFI drivers that follow the UEFI Driver Model. Refer to the Driver Writer's
+// Guide for UEFI 2.3.1 v1.01, 11 UEFI Driver and Controller Names.
+//
+
+STATIC
+EFI_UNICODE_STRING_TABLE mDriverNameTable[] = {
+  { "eng;en", L"I/O protocol emulation driver for non-discoverable devices" },
+  { NULL,     NULL                   }
+};
+
+EFI_COMPONENT_NAME_PROTOCOL gComponentName;
+
+/**
+  Retrieves a Unicode string that is the user readable name of the UEFI Driver.
+
+  @param This           A pointer to the EFI_COMPONENT_NAME_PROTOCOL instance.
+  @param Language       A pointer to a three character ISO 639-2 language identifier.
+                        This is the language of the driver name that 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.
+  @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.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+NonDiscoverableGetDriverName (
+  IN  EFI_COMPONENT_NAME_PROTOCOL *This,
+  IN  CHAR8                       *Language,
+  OUT CHAR16                      **DriverName
+  )
+{
+  return LookupUnicodeString2 (
+           Language,
+           This->SupportedLanguages,
+           mDriverNameTable,
+           DriverName,
+           (BOOLEAN)(This == &gComponentName) // Iso639Language
+           );
+}
+
+/**
+  Retrieves a Unicode string that is the user readable name of the controller
+  that is being managed by an UEFI Driver.
+
+  @param This                   A pointer to the EFI_COMPONENT_NAME_PROTOCOL instance.
+  @param DeviceHandle           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 three character ISO 639-2 language
+                                identifier.  This is the language of the controller name
+                                that 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.
+  @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.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+NonDiscoverableGetDeviceName (
+  IN  EFI_COMPONENT_NAME_PROTOCOL *This,
+  IN  EFI_HANDLE                  DeviceHandle,
+  IN  EFI_HANDLE                  ChildHandle,
+  IN  CHAR8                       *Language,
+  OUT CHAR16                      **ControllerName
+  )
+{
+  return EFI_UNSUPPORTED;
+}
+
+EFI_COMPONENT_NAME_PROTOCOL gComponentName = {
+  &NonDiscoverableGetDriverName,
+  &NonDiscoverableGetDeviceName,
+  "eng" // SupportedLanguages, ISO 639-2 language codes
+};
+
+EFI_COMPONENT_NAME2_PROTOCOL gComponentName2 = {
+  (EFI_COMPONENT_NAME2_GET_DRIVER_NAME)     &NonDiscoverableGetDriverName,
+  (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) &NonDiscoverableGetDeviceName,
+  "en" // SupportedLanguages, RFC 4646 language codes
+};
diff --git a/EmbeddedPkg/Universal/NonDiscoverableDeviceDxe/NonDiscoverableDeviceDxe.c b/EmbeddedPkg/Universal/NonDiscoverableDeviceDxe/NonDiscoverableDeviceDxe.c
new file mode 100644
index 000000000000..654b33002346
--- /dev/null
+++ b/EmbeddedPkg/Universal/NonDiscoverableDeviceDxe/NonDiscoverableDeviceDxe.c
@@ -0,0 +1,243 @@ 
+/** @file
+
+  Copyright (C) 2016-2018, 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.
+
+**/
+
+#include <Library/BaseMemoryLib.h>
+#include <Library/DebugLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiLib.h>
+
+#include <Protocol/DriverBinding.h>
+
+#include "NonDiscoverableDeviceIo.h"
+
+
+EFI_CPU_ARCH_PROTOCOL      *mCpu;
+
+//
+// We only support the following device types
+//
+STATIC
+CONST EFI_GUID * CONST
+SupportedNonDiscoverableDevices[] = {
+  &gEdkiiNonDiscoverableSdhciDeviceGuid,
+  &gEdkiiNonDiscoverableUfsDeviceGuid,
+};
+
+//
+// Probe, start and stop functions of this driver, called by the DXE core for
+// specific devices.
+//
+// The following specifications document these interfaces:
+// - Driver Writer's Guide for UEFI 2.3.1 v1.01, 9 Driver Binding Protocol
+// - UEFI Spec 2.3.1 + Errata C, 10.1 EFI Driver Binding Protocol
+//
+// The implementation follows:
+// - Driver Writer's Guide for UEFI 2.3.1 v1.01
+//   - 5.1.3.4 OpenProtocol() and CloseProtocol()
+// - UEFI Spec 2.3.1 + Errata C
+//   -  6.3 Protocol Handler Services
+//
+
+/**
+  Supported function of Driver Binding protocol for this driver.
+  Test to see if this driver supports ControllerHandle.
+
+  @param This                   Protocol instance pointer.
+  @param DeviceHandle           Handle of device to test.
+  @param RemainingDevicePath    A pointer to the device path.
+                                it should be ignored by device driver.
+
+  @retval EFI_SUCCESS           This driver supports this device.
+  @retval other                 This driver does not support this device.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+NonDiscoverableIoDeviceSupported (
+  IN EFI_DRIVER_BINDING_PROTOCOL *This,
+  IN EFI_HANDLE                  DeviceHandle,
+  IN EFI_DEVICE_PATH_PROTOCOL    *RemainingDevicePath
+  )
+{
+  NON_DISCOVERABLE_DEVICE             *Device;
+  EFI_STATUS                          Status;
+  INTN                                Idx;
+
+  Status = gBS->OpenProtocol (DeviceHandle,
+                  &gEdkiiNonDiscoverableDeviceProtocolGuid, (VOID **)&Device,
+                  This->DriverBindingHandle, DeviceHandle,
+                  EFI_OPEN_PROTOCOL_BY_DRIVER);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  Status = EFI_UNSUPPORTED;
+  for (Idx = 0; Idx < ARRAY_SIZE (SupportedNonDiscoverableDevices); Idx++) {
+    if (CompareGuid (Device->Type, SupportedNonDiscoverableDevices [Idx])) {
+      Status = EFI_SUCCESS;
+      break;
+    }
+  }
+
+  if (EFI_ERROR (Status)) {
+    goto CloseProtocol;
+  }
+
+CloseProtocol:
+  gBS->CloseProtocol (DeviceHandle, &gEdkiiNonDiscoverableDeviceProtocolGuid,
+         This->DriverBindingHandle, DeviceHandle);
+
+  return Status;
+}
+
+/**
+  This routine is called right after the .Supported() called and
+  Start this driver on ControllerHandle.
+
+  @param This                   Protocol instance pointer.
+  @param DeviceHandle           Handle of device to bind driver to.
+  @param RemainingDevicePath    A pointer to the device path.
+                                it should be ignored by device driver.
+
+  @retval EFI_SUCCESS           This driver is added to this device.
+  @retval other                 Some error occurs when binding this driver to this device.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+NonDiscoverableIoDeviceStart (
+  IN EFI_DRIVER_BINDING_PROTOCOL *This,
+  IN EFI_HANDLE                  DeviceHandle,
+  IN EFI_DEVICE_PATH_PROTOCOL    *RemainingDevicePath
+  )
+{
+  EFI_STATUS                     Status;
+  NON_DISCOVERABLE_IO_DEVICE     *Dev;
+
+  Dev = AllocateZeroPool (sizeof *Dev);
+  if (Dev == NULL) {
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  Status = gBS->OpenProtocol (DeviceHandle,
+                  &gEdkiiNonDiscoverableDeviceProtocolGuid,
+                  (VOID **)&Dev->Device, This->DriverBindingHandle,
+                  DeviceHandle, EFI_OPEN_PROTOCOL_BY_DRIVER);
+  if (EFI_ERROR (Status)) {
+    goto FreeDev;
+  }
+
+  Dev->Signature = NON_DISCOVERABLE_IO_DEVICE_SIG;
+
+  InitializeIoProtocol (Dev);
+
+  Status = gBS->InstallProtocolInterface (
+                  &DeviceHandle,
+                  &gEmbeddedNonDiscoverableIoProtocolGuid,
+                  EFI_NATIVE_INTERFACE,
+                  &Dev->Io
+                  );
+  if (EFI_ERROR (Status)) {
+    goto CloseProtocol;
+  }
+  return EFI_SUCCESS;
+
+CloseProtocol:
+  gBS->CloseProtocol (DeviceHandle, &gEdkiiNonDiscoverableDeviceProtocolGuid,
+         This->DriverBindingHandle, DeviceHandle);
+FreeDev:
+  FreePool (Dev);
+
+  return Status;
+}
+
+/**
+  Stop this driver on ControllerHandle.
+
+  @param This               Protocol instance pointer.
+  @param DeviceHandle       Handle of device to stop driver on.
+  @param NumberOfChildren   Not used.
+  @param ChildHandleBuffer  Not used.
+
+  @retval EFI_SUCCESS   This driver is removed from this device.
+  @retval other         Some error occurs when removing this driver from this
+                        device.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+NonDiscoverableIoDeviceStop (
+  IN EFI_DRIVER_BINDING_PROTOCOL *This,
+  IN EFI_HANDLE                  DeviceHandle,
+  IN UINTN                       NumberOfChildren,
+  IN EFI_HANDLE                  *ChildHandleBuffer
+  )
+{
+
+  gBS->CloseProtocol (DeviceHandle, &gEdkiiNonDiscoverableDeviceProtocolGuid,
+         This->DriverBindingHandle, DeviceHandle);
+
+  return EFI_SUCCESS;
+}
+
+
+//
+// The static object that groups the Supported() (ie. probe), Start() and
+// Stop() functions of the driver together. Refer to UEFI Spec 2.3.1 + Errata
+// C, 10.1 EFI Driver Binding Protocol.
+//
+STATIC EFI_DRIVER_BINDING_PROTOCOL gDriverBinding = {
+  &NonDiscoverableIoDeviceSupported,
+  &NonDiscoverableIoDeviceStart,
+  &NonDiscoverableIoDeviceStop,
+  0x10, // Version, must be in [0x10 .. 0xFFFFFFEF] for IHV-developed drivers
+  NULL,
+  NULL
+};
+
+/**
+  Entry point of this driver.
+
+  @param  ImageHandle     Image handle this driver.
+  @param  SystemTable     Pointer to the System Table.
+
+  @retval EFI_SUCCESS     The entry point is executed successfully.
+  @retval other           Some error occurred when executing this entry point.
+
+**/
+EFI_STATUS
+EFIAPI
+NonDiscoverableDeviceDxeEntryPoint (
+  IN EFI_HANDLE       ImageHandle,
+  IN EFI_SYSTEM_TABLE *SystemTable
+  )
+{
+  EFI_STATUS      Status;
+
+  Status = gBS->LocateProtocol (&gEfiCpuArchProtocolGuid, NULL, (VOID **)&mCpu);
+  ASSERT_EFI_ERROR(Status);
+
+  return EfiLibInstallDriverBindingComponentName2 (
+           ImageHandle,
+           SystemTable,
+           &gDriverBinding,
+           ImageHandle,
+           &gComponentName,
+           &gComponentName2
+           );
+}
diff --git a/EmbeddedPkg/Universal/NonDiscoverableDeviceDxe/NonDiscoverableDeviceIo.c b/EmbeddedPkg/Universal/NonDiscoverableDeviceDxe/NonDiscoverableDeviceIo.c
new file mode 100644
index 000000000000..178f10b216b9
--- /dev/null
+++ b/EmbeddedPkg/Universal/NonDiscoverableDeviceDxe/NonDiscoverableDeviceIo.c
@@ -0,0 +1,972 @@ 
+/** @file
+
+  Copyright (c) 2008 - 2009, Apple Inc. All rights reserved.<BR>
+  Copyright (c) 2016-2018, 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.
+
+**/
+
+#include <PiDxe.h>
+
+#include <Library/BaseMemoryLib.h>
+#include <Library/DxeServicesTableLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiLib.h>
+
+#include <Protocol/IoMmu.h>
+#include <Protocol/NonDiscoverableDevice.h>
+
+#include "NonDiscoverableDeviceIo.h"
+
+typedef struct {
+  EFI_PHYSICAL_ADDRESS            AllocAddress;
+  VOID                            *HostAddress;
+  EFI_IO_OPERATION_TYPE           Operation;
+  UINTN                           NumberOfBytes;
+} NON_DISCOVERABLE_IO_DEVICE_MAP_INFO;
+
+/**
+  Enable a driver to access controller registers in the memory or I/O space.
+
+  @param  Width         Signifies the width of the memory or I/O operations.
+  @param  Count         The number of memory or I/O operations to perform.
+  @param  DstStride     The stride of the destination buffer.
+  @param  Dst           For read operations, the destination buffer to store
+                        the results. For write operations, the destination
+                        buffer to write data to.
+  @param  SrcStride     The stride of the source buffer.
+  @param  Src           For read operations, the source buffer to read data
+                        from. For write operations, the source buffer to write
+                        data from.
+
+  @retval EFI_SUCCESS            The data was read from or written to the
+                                 controller.
+  @retval EFI_INVALID_PARAMETER  One or more parameters are invalid.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+IoMemRW (
+  IN     EFI_IO_WIDTH                  Width,
+  IN     UINTN                         Count,
+  IN     UINTN                         DstStride,
+  IN     VOID                          *Dst,
+  IN     UINTN                         SrcStride,
+     OUT CONST VOID                    *Src
+  )
+{
+  volatile UINT8             *Dst8;
+  volatile UINT16            *Dst16;
+  volatile UINT32            *Dst32;
+  volatile CONST UINT8       *Src8;
+  volatile CONST UINT16      *Src16;
+  volatile CONST UINT32      *Src32;
+
+  //
+  // Loop for each iteration and move the data
+  //
+  switch (Width & 0x3) {
+  case IO_UINT8:
+    Dst8 = (UINT8 *)Dst;
+    Src8 = (UINT8 *)Src;
+    for (;Count > 0; Count--, Dst8 += DstStride, Src8 += SrcStride) {
+      *Dst8 = *Src8;
+    }
+    break;
+  case IO_UINT16:
+    Dst16 = (UINT16 *)Dst;
+    Src16 = (UINT16 *)Src;
+    for (;Count > 0; Count--, Dst16 += DstStride, Src16 += SrcStride) {
+      *Dst16 = *Src16;
+    }
+    break;
+  case IO_UINT32:
+    Dst32 = (UINT32 *)Dst;
+    Src32 = (UINT32 *)Src;
+    for (;Count > 0; Count--, Dst32 += DstStride, Src32 += SrcStride) {
+      *Dst32 = *Src32;
+    }
+    break;
+  default:
+    return EFI_INVALID_PARAMETER;
+  }
+
+  return EFI_SUCCESS;
+}
+
+/**
+  Enable a driver to access controller registers in the memory or I/O space.
+
+  @param  This                  A pointer to the EFI_DEVICE_IO_PROTOCOL
+                                instance.
+  @param  Width                 Signifies the width of the memory or I/O
+                                operations.
+  @param  Offset                The offset to start the memory or I/O operation.
+  @param  Count                 The number of memory or I/O operations to
+                                perform.
+  @param  Buffer                For read operations, the destination buffer to
+                                store the results. For write operations, the
+                                source buffer to write data from.
+
+  @retval EFI_SUCCESS           The data was read from or written to the
+                                controller.
+  @retval EFI_UNSUPPORTED       The address range specified by Offset, Width,
+                                and Count is not valid.
+  @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+IoMemRead (
+  IN     EFI_DEVICE_IO_PROTOCOL        *This,
+  IN     EFI_IO_WIDTH                  Width,
+  IN     UINT64                        Offset,
+  IN     UINTN                         Count,
+  IN OUT VOID                          *Buffer
+  )
+{
+  NON_DISCOVERABLE_IO_DEVICE           *Dev;
+  UINTN                                AlignMask;
+  UINT64                               Address;
+  EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR    *Desc;
+
+  if (Buffer == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  Dev = NON_DISCOVERABLE_IO_DEVICE_FROM_IO(This);
+
+  Desc = Dev->Device->Resources;
+  Address = Desc->AddrRangeMin + Offset;
+
+  if (Address + (Count << (Width & 0x3)) > Desc->AddrRangeMax) {
+    return EFI_UNSUPPORTED;
+  }
+
+  AlignMask = (1 << (Width & 0x03)) - 1;
+  if ((UINTN)Address & AlignMask) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  switch (Width) {
+  case IO_UINT8:
+  case IO_UINT16:
+  case IO_UINT32:
+  case IO_UINT64:
+    return IoMemRW (Width, Count, 1, Buffer, 1, (VOID *)Address);
+  default:
+    break;
+  }
+  return EFI_INVALID_PARAMETER;
+}
+
+/**
+  Enable a driver to access controller registers in the memory or I/O space.
+
+  @param  This                  A pointer to the EFI_DEVICE_IO_PROTOCOL
+                                instance.
+  @param  Width                 Signifies the width of the memory or I/O
+                                operations.
+  @param  Offset                The offset to start the memory or I/O operation.
+  @param  Count                 The number of memory or I/O operations to
+                                perform.
+  @param  Buffer                For read operations, the destination buffer to
+                                store the results. For write operations, the
+                                source buffer to write data from.
+
+  @retval EFI_SUCCESS           The data was read from or written to the
+                                controller.
+  @retval EFI_UNSUPPORTED       The address range specified by Offset, Width,
+                                and Count is not valid.
+  @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+IoMemWrite (
+  IN     EFI_DEVICE_IO_PROTOCOL        *This,
+  IN     EFI_IO_WIDTH                  Width,
+  IN     UINT64                        Offset,
+  IN     UINTN                         Count,
+  IN OUT VOID                          *Buffer
+  )
+{
+  NON_DISCOVERABLE_IO_DEVICE           *Dev;
+  UINTN                                AlignMask;
+  UINT64                               Address;
+  EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR    *Desc;
+
+  if (Buffer == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  Dev = NON_DISCOVERABLE_IO_DEVICE_FROM_IO(This);
+
+  Desc = Dev->Device->Resources;
+
+  Address = Desc->AddrRangeMin + Offset;
+  if (Address + (Count << (Width & 0x3)) > Desc->AddrRangeMax) {
+    return EFI_UNSUPPORTED;
+  }
+
+  AlignMask = (1 << (Width & 0x03)) - 1;
+  if ((UINTN)Address & AlignMask) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  switch (Width) {
+  case IO_UINT8:
+  case IO_UINT16:
+  case IO_UINT32:
+  case IO_UINT64:
+    return IoMemRW (Width, Count, 1, (VOID *)Address, 1, Buffer);
+  default:
+    break;
+  }
+  return EFI_INVALID_PARAMETER;
+}
+
+/**
+  Enable a driver to access controller registers in the memory or I/O space.
+
+  @param  This                  A pointer to the EFI_DEVICE_IO_PROTOCOL
+                                instance.
+  @param  Width                 Signifies the width of the memory or I/O
+                                operations.
+  @param  Offset                The offset to start the memory or I/O operation.
+  @param  Count                 The number of memory or I/O operations to
+                                perform.
+  @param  Buffer                For read operations, the destination buffer
+                                to store the results. For write operations,
+                                the source buffer to write data from.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+IoIoRead (
+  IN     EFI_DEVICE_IO_PROTOCOL       *This,
+  IN     EFI_IO_WIDTH                 Width,
+  IN     UINT64                       Offset,
+  IN     UINTN                        Count,
+  IN OUT VOID                         *Buffer
+  )
+{
+  ASSERT (FALSE);
+  return EFI_UNSUPPORTED;
+}
+
+/**
+  Enable a driver to access controller registers in the memory or I/O space.
+
+  @param  This                  A pointer to the EFI_DEVICE_IO_PROTOCOL
+                                instance.
+  @param  Width                 Signifies the width of the memory or I/O
+                                operations.
+  @param  Offset                The offset to start the memory or I/O operation.
+  @param  Count                 The number of memory or I/O operations to
+                                perform.
+  @param  Buffer                For read operations, the destination buffer to
+                                store the results. For write operations, the
+                                source buffer to write data from.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+IoIoWrite (
+  IN     EFI_DEVICE_IO_PROTOCOL       *This,
+  IN     EFI_IO_WIDTH                 Width,
+  IN     UINT64                       Address,
+  IN     UINTN                        Count,
+  IN OUT VOID                         *Buffer
+  )
+{
+  ASSERT (FALSE);
+  return EFI_UNSUPPORTED;
+}
+
+/**
+  Provides the controller-specific addresses needed to access system memory.
+
+  @param  This                  A pointer to the EFI_DEVICE_IO_PROTOCOL
+                                instance.
+  @param  Operation             Indicates if the bus master is going to read
+                                or write to system memory.
+  @param  HostAddress           The system memory address to map to the
+                                controller.
+  @param  NumberOfBytes         On input the number of bytes to map. On output
+                                the number of bytes that were mapped.
+  @param  DeviceAddress         The resulting map address for the bus master
+                                controller to use to access the hosts
+                                HostAddress.
+  @param  Mapping               A resulting value to pass to Unmap().
+
+  @retval EFI_SUCCESS           The range was mapped for the returned
+                                NumberOfBytes.
+  @retval EFI_UNSUPPORTED       The HostAddress cannot be mapped as a common
+                                buffer.
+  @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
+  @retval EFI_OUT_OF_RESOURCES  The request could not be completed due to a
+                                lack of resources.
+  @retval EFI_DEVICE_ERROR      The system hardware could not map the requested
+                                address.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+CoherentIoMap (
+  IN     EFI_DEVICE_IO_PROTOCOL        *This,
+  IN     EFI_IO_OPERATION_TYPE         Operation,
+  IN     EFI_PHYSICAL_ADDRESS          *HostAddress,
+  IN OUT UINTN                         *NumberOfBytes,
+     OUT EFI_PHYSICAL_ADDRESS          *DeviceAddress,
+     OUT VOID                          **Mapping
+  )
+{
+  EFI_STATUS                           Status;
+  NON_DISCOVERABLE_IO_DEVICE           *Dev;
+  NON_DISCOVERABLE_IO_DEVICE_MAP_INFO  *MapInfo;
+
+  //
+  // If HostAddress exceeds 4 GB, and this device does not support 64-bit DMA
+  // addressing, we need to allocate a bounce buffer and copy over the data.
+  //
+  Dev = NON_DISCOVERABLE_IO_DEVICE_FROM_IO(This);
+  if ((EFI_PHYSICAL_ADDRESS)(UINTN)HostAddress + *NumberOfBytes > SIZE_4GB) {
+    //
+    // Bounce buffering is not possible for consistent mappings
+    //
+    if (Operation == EfiBusMasterCommonBuffer) {
+      return EFI_UNSUPPORTED;
+    }
+
+    MapInfo = AllocatePool (sizeof *MapInfo);
+    if (MapInfo == NULL) {
+      return EFI_OUT_OF_RESOURCES;
+    }
+
+    MapInfo->AllocAddress = MAX_UINT32;
+    MapInfo->HostAddress = HostAddress;
+    MapInfo->Operation = Operation;
+    MapInfo->NumberOfBytes = *NumberOfBytes;
+
+    Status = gBS->AllocatePages (
+                    AllocateMaxAddress,
+                    EfiBootServicesData,
+                    EFI_SIZE_TO_PAGES (MapInfo->NumberOfBytes),
+                    &MapInfo->AllocAddress
+                    );
+    if (EFI_ERROR (Status)) {
+      //
+      // If we fail here, it is likely because the system has no memory below
+      // 4 GB to begin with. There is not much we can do about that other than
+      // fail the map request.
+      //
+      FreePool (MapInfo);
+      return EFI_DEVICE_ERROR;
+    }
+    if (Operation == EfiBusMasterRead) {
+      gBS->CopyMem (
+             (VOID *)(UINTN)MapInfo->AllocAddress,
+             HostAddress,
+             *NumberOfBytes
+             );
+    }
+    *DeviceAddress = MapInfo->AllocAddress;
+    *Mapping = MapInfo;
+  } else {
+    *DeviceAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)HostAddress;
+    *Mapping = NULL;
+  }
+  return EFI_SUCCESS;
+}
+
+/**
+  Completes the Map() operation and releases any corresponding resources.
+
+  @param  This                  A pointer to the EFI_DEVICE_IO_PROTOCOL
+                                instance.
+  @param  Mapping               The mapping value returned from Map().
+
+  @retval EFI_SUCCESS           The range was unmapped.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+CoherentIoUnmap (
+  IN EFI_DEVICE_IO_PROTOCOL            *This,
+  IN VOID                              *Mapping
+  )
+{
+  NON_DISCOVERABLE_IO_DEVICE_MAP_INFO  *MapInfo;
+
+  MapInfo = Mapping;
+  if (MapInfo != NULL) {
+    if (MapInfo->Operation == EfiBusMasterWrite) {
+      gBS->CopyMem (
+             MapInfo->HostAddress,
+             (VOID *)(UINTN)MapInfo->AllocAddress,
+             MapInfo->NumberOfBytes
+             );
+    }
+    gBS->FreePages (
+           MapInfo->AllocAddress,
+           EFI_SIZE_TO_PAGES (MapInfo->NumberOfBytes)
+           );
+    FreePool (MapInfo);
+  }
+  return EFI_SUCCESS;
+}
+
+/**
+  Allocates pages.
+
+  @param  This                  A pointer to the EFI_PCI_IO_PROTOCOL instance.
+  @param  Type                  This parameter is not used and must be ignored.
+  @param  MemoryType            The type of memory to allocate,
+                                EfiBootServicesData or EfiRuntimeServicesData.
+  @param  Pages                 The number of pages to allocate.
+  @param  HostAddress           A pointer to store the base system memory
+                                address of the allocated range.
+  @param  Attributes            The requested bit mask of attributes for the
+                                allocated range.
+
+  @retval EFI_SUCCESS           The requested memory pages were allocated.
+  @retval EFI_UNSUPPORTED       Attributes is unsupported. The only legal
+                                attribute bits are MEMORY_WRITE_COMBINE and
+                                MEMORY_CACHED.
+  @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
+  @retval EFI_OUT_OF_RESOURCES  The memory pages could not be allocated.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+CoherentIoAllocateBuffer (
+  IN     EFI_DEVICE_IO_PROTOCOL        *This,
+  IN     EFI_ALLOCATE_TYPE             Type,
+  IN     EFI_MEMORY_TYPE               MemoryType,
+  IN     UINTN                         Pages,
+  IN OUT EFI_PHYSICAL_ADDRESS          *HostAddress
+  )
+{
+  EFI_STATUS                           Status;
+  NON_DISCOVERABLE_IO_DEVICE           *Dev;
+  EFI_ALLOCATE_TYPE                    AllocType;
+  EFI_PHYSICAL_ADDRESS                 AllocAddress;
+
+  //
+  // Allocate below 4 GB if the dual address cycle attribute has not
+  // been set. If the system has no memory available below 4 GB, there
+  // is little we can do except propagate the error.
+  //
+  Dev = NON_DISCOVERABLE_IO_DEVICE_FROM_IO(This);
+  if ((Dev->Attributes & EDKII_IOMMU_ATTRIBUTE_DUAL_ADDRESS_CYCLE) == 0) {
+    AllocAddress = MAX_UINT32;
+    AllocType = AllocateMaxAddress;
+  } else {
+    AllocType = AllocateAnyPages;
+  }
+
+  Status = gBS->AllocatePages (AllocType, MemoryType, Pages, &AllocAddress);
+  if (!EFI_ERROR (Status)) {
+    *HostAddress = AllocAddress;
+  }
+  return Status;
+}
+
+/**
+  Frees memory that was allocated in function CoherentIoAllocateBuffer ().
+
+  @param  This                  A pointer to the EFI_DEVICE_IO_PROTOCOL
+                                instance.
+  @param  Pages                 The number of pages to free.
+  @param  HostAddress           The base system memory address of the
+                                allocated range.
+
+  @retval EFI_SUCCESS           The requested memory pages were freed.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+CoherentIoFreeBuffer (
+  IN EFI_DEVICE_IO_PROTOCOL            *This,
+  IN UINTN                             Pages,
+  IN EFI_PHYSICAL_ADDRESS              HostAddress
+  )
+{
+  FreePages ((VOID *)HostAddress, Pages);
+  return EFI_SUCCESS;
+}
+
+/**
+  Frees memory that was allocated in function NonCoherentIoAllocateBuffer ().
+
+  @param  This                  A pointer to the EFI_DEVICE_IO_PROTOCOL
+                                instance.
+  @param  Pages                 The number of pages to free.
+  @param  HostAddress           The base system memory address of the allocated
+                                range.
+
+  @retval EFI_SUCCESS           The requested memory pages were freed.
+  @retval others                The operation contain some errors.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+NonCoherentIoFreeBuffer (
+  IN EFI_DEVICE_IO_PROTOCOL            *This,
+  IN UINTN                             Pages,
+  IN EFI_PHYSICAL_ADDRESS              HostAddress
+  )
+{
+  NON_DISCOVERABLE_IO_DEVICE                    *Dev;
+  LIST_ENTRY                                    *Entry;
+  EFI_STATUS                                    Status;
+  NON_DISCOVERABLE_DEVICE_UNCACHED_ALLOCATION   *Alloc;
+  BOOLEAN                                       Found;
+
+  Dev = NON_DISCOVERABLE_IO_DEVICE_FROM_IO(This);
+
+  Found = FALSE;
+  Alloc = NULL;
+
+  //
+  // Find the uncached allocation list entry associated
+  // with this allocation
+  //
+  for (Entry = Dev->UncachedAllocationList.ForwardLink;
+       Entry != &Dev->UncachedAllocationList;
+       Entry = Entry->ForwardLink) {
+
+    Alloc = BASE_CR (Entry, NON_DISCOVERABLE_DEVICE_UNCACHED_ALLOCATION, List);
+    if (Alloc->HostAddress == (VOID *)HostAddress && Alloc->NumPages == Pages) {
+      //
+      // We are freeing the exact allocation we were given
+      // before by AllocateBuffer()
+      //
+      Found = TRUE;
+      break;
+    }
+  }
+
+  if (!Found) {
+    ASSERT_EFI_ERROR (EFI_NOT_FOUND);
+    return EFI_NOT_FOUND;
+  }
+
+  RemoveEntryList (&Alloc->List);
+
+  Status = gDS->SetMemorySpaceAttributes (
+                  (EFI_PHYSICAL_ADDRESS)(UINTN)HostAddress,
+                  EFI_PAGES_TO_SIZE (Pages),
+                  Alloc->Attributes);
+  if (EFI_ERROR (Status)) {
+    goto FreeAlloc;
+  }
+
+  //
+  // If we fail to restore the original attributes, it is better to leak the
+  // memory than to return it to the heap
+  //
+  FreePages ((VOID *)HostAddress, Pages);
+
+FreeAlloc:
+  FreePool (Alloc);
+  return Status;
+}
+
+/**
+  Allocates pages.
+
+  @param  This                  A pointer to the EFI_DEVICE_IO_PROTOCOL
+                                instance.
+  @param  Type                  This parameter is not used and must be ignored.
+  @param  MemoryType            The type of memory to allocate,
+                                EfiBootServicesData or EfiRuntimeServicesData.
+  @param  Pages                 The number of pages to allocate.
+  @param  HostAddress           A pointer to store the base system memory
+                                address of the allocated range.
+  @param  Attributes            The requested bit mask of attributes for the
+                                allocated range.
+
+  @retval EFI_SUCCESS           The requested memory pages were allocated.
+  @retval EFI_UNSUPPORTED       Attributes is unsupported. The only legal
+                                attribute bits are MEMORY_WRITE_COMBINE and
+                                MEMORY_CACHED.
+  @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
+  @retval EFI_OUT_OF_RESOURCES  The memory pages could not be allocated.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+NonCoherentIoAllocateBuffer (
+  IN     EFI_DEVICE_IO_PROTOCOL        *This,
+  IN     EFI_ALLOCATE_TYPE             Type,
+  IN     EFI_MEMORY_TYPE               MemoryType,
+  IN     UINTN                         Pages,
+  IN OUT EFI_PHYSICAL_ADDRESS          *HostAddress
+  )
+{
+  NON_DISCOVERABLE_IO_DEVICE                  *Dev;
+  EFI_GCD_MEMORY_SPACE_DESCRIPTOR             GcdDescriptor;
+  EFI_STATUS                                  Status;
+  UINT64                                      MemType;
+  NON_DISCOVERABLE_DEVICE_UNCACHED_ALLOCATION *Alloc;
+  EFI_PHYSICAL_ADDRESS                        AllocAddress;
+
+  Dev = NON_DISCOVERABLE_IO_DEVICE_FROM_IO(This);
+
+  Status = CoherentIoAllocateBuffer (
+             This,
+             Type,
+             MemoryType,
+             Pages,
+             &AllocAddress
+             );
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  Status = gDS->GetMemorySpaceDescriptor (
+                  (EFI_PHYSICAL_ADDRESS)(UINTN)AllocAddress,
+                  &GcdDescriptor
+                  );
+  if (EFI_ERROR (Status)) {
+    goto FreeBuffer;
+  }
+
+  if ((GcdDescriptor.Capabilities & (EFI_MEMORY_WC | EFI_MEMORY_UC)) == 0) {
+    Status = EFI_UNSUPPORTED;
+    goto FreeBuffer;
+  }
+
+  //
+  // Set the preferred memory attributes
+  //
+  if ((GcdDescriptor.Capabilities & EFI_MEMORY_UC) == 0) {
+    //
+    // Use write combining if it was requested, or if it is the only
+    // type supported by the region.
+    //
+    MemType = EFI_MEMORY_WC;
+  } else {
+    MemType = EFI_MEMORY_UC;
+  }
+
+  Alloc = AllocatePool (sizeof *Alloc);
+  if (Alloc == NULL) {
+    goto FreeBuffer;
+  }
+
+  Alloc->HostAddress = (VOID *)AllocAddress;
+  Alloc->NumPages = Pages;
+  Alloc->Attributes = GcdDescriptor.Attributes;
+
+  //
+  // Record this allocation in the linked list, so we
+  // can restore the memory space attributes later
+  //
+  InsertHeadList (&Dev->UncachedAllocationList, &Alloc->List);
+
+  Status = gDS->SetMemorySpaceAttributes (
+                  (EFI_PHYSICAL_ADDRESS)(UINTN)AllocAddress,
+                  EFI_PAGES_TO_SIZE (Pages),
+                  MemType
+                  );
+  if (EFI_ERROR (Status)) {
+    goto RemoveList;
+  }
+
+  Status = mCpu->FlushDataCache (
+                   mCpu,
+                   (EFI_PHYSICAL_ADDRESS)(UINTN)AllocAddress,
+                   EFI_PAGES_TO_SIZE (Pages),
+                   EfiCpuFlushTypeInvalidate);
+  if (EFI_ERROR (Status)) {
+    goto RemoveList;
+  }
+
+  *HostAddress = AllocAddress;
+
+  return EFI_SUCCESS;
+
+RemoveList:
+  RemoveEntryList (&Alloc->List);
+  FreePool (Alloc);
+
+FreeBuffer:
+  CoherentIoFreeBuffer (This, Pages, AllocAddress);
+  return Status;
+}
+
+/**
+  Provides the controller-specific addresses needed to access system memory.
+
+  @param  This                  A pointer to the EFI_DEVICE_IO_PROTOCOL
+                                instance.
+  @param  Operation             Indicates if the bus master is going to read or
+                                write to system memory.
+  @param  HostAddress           The system memory address to map to the
+                                controller.
+  @param  NumberOfBytes         On input the number of bytes to map. On output
+                                the number of bytes that were mapped.
+  @param  DeviceAddress         The resulting map address for the bus master
+                                controller to use to access the hosts
+                                HostAddress.
+  @param  Mapping               A resulting value to pass to Unmap().
+
+  @retval EFI_SUCCESS           The range was mapped for the returned
+                                NumberOfBytes.
+  @retval EFI_UNSUPPORTED       The HostAddress cannot be mapped as a common
+                                buffer.
+  @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
+  @retval EFI_OUT_OF_RESOURCES  The request could not be completed due to a
+                                lack of resources.
+  @retval EFI_DEVICE_ERROR      The system hardware could not map the requested
+                                address.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+NonCoherentIoMap (
+  IN     EFI_DEVICE_IO_PROTOCOL        *This,
+  IN     EFI_IO_OPERATION_TYPE         Operation,
+  IN     EFI_PHYSICAL_ADDRESS          *HostAddress,
+  IN OUT UINTN                         *NumberOfBytes,
+     OUT EFI_PHYSICAL_ADDRESS          *DeviceAddress,
+     OUT VOID                          **Mapping
+  )
+{
+  NON_DISCOVERABLE_IO_DEVICE           *Dev;
+  EFI_STATUS                           Status;
+  NON_DISCOVERABLE_IO_DEVICE_MAP_INFO  *MapInfo;
+  UINTN                                AlignMask;
+  EFI_PHYSICAL_ADDRESS                 AllocAddress;
+  EFI_GCD_MEMORY_SPACE_DESCRIPTOR      GcdDescriptor;
+  BOOLEAN                              Bounce;
+
+  MapInfo = AllocatePool (sizeof *MapInfo);
+  if (MapInfo == NULL) {
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  MapInfo->HostAddress = HostAddress;
+  MapInfo->Operation = Operation;
+  MapInfo->NumberOfBytes = *NumberOfBytes;
+
+  Dev = NON_DISCOVERABLE_IO_DEVICE_FROM_IO(This);
+
+  //
+  // If this device does not support 64-bit DMA addressing, we need to allocate
+  // a bounce buffer and copy over the data in case HostAddress >= 4 GB.
+  //
+  Bounce = ((Dev->Attributes & EDKII_IOMMU_ATTRIBUTE_DUAL_ADDRESS_CYCLE) == 0 &&
+            (EFI_PHYSICAL_ADDRESS)(UINTN)HostAddress + *NumberOfBytes > SIZE_4GB);
+
+  if (!Bounce) {
+    switch (Operation) {
+    case EfiBusMasterRead:
+    case EfiBusMasterWrite:
+      //
+      // For streaming DMA, it is sufficient if the buffer is aligned to
+      // the CPUs DMA buffer alignment.
+      //
+      AlignMask = mCpu->DmaBufferAlignment - 1;
+      if ((((UINTN) HostAddress | *NumberOfBytes) & AlignMask) == 0) {
+        break;
+      }
+      // fall through
+
+    case EfiBusMasterCommonBuffer:
+      //
+      // Check whether the host address refers to an uncached mapping.
+      //
+      Status = gDS->GetMemorySpaceDescriptor (
+                      (EFI_PHYSICAL_ADDRESS)(UINTN)HostAddress,
+                      &GcdDescriptor
+                      );
+      if (EFI_ERROR (Status) ||
+          (GcdDescriptor.Attributes & (EFI_MEMORY_WB|EFI_MEMORY_WT)) != 0) {
+        Bounce = TRUE;
+      }
+      break;
+
+    default:
+      ASSERT (FALSE);
+    }
+  }
+
+  if (Bounce) {
+    if (Operation == EfiBusMasterCommonBuffer) {
+      Status = EFI_DEVICE_ERROR;
+      goto FreeMapInfo;
+    }
+
+    Status = NonCoherentIoAllocateBuffer (
+               This,
+               AllocateAnyPages,
+               EfiBootServicesData,
+               EFI_SIZE_TO_PAGES (MapInfo->NumberOfBytes),
+               &AllocAddress);
+    if (EFI_ERROR (Status)) {
+      goto FreeMapInfo;
+    }
+    MapInfo->AllocAddress = AllocAddress;
+    if (Operation == EfiBusMasterRead) {
+      gBS->CopyMem ((VOID *)AllocAddress, HostAddress, *NumberOfBytes);
+    }
+    *DeviceAddress = MapInfo->AllocAddress;
+  } else {
+    MapInfo->AllocAddress = 0;
+    *DeviceAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)HostAddress;
+
+    //
+    // We are not using a bounce buffer: the mapping is sufficiently
+    // aligned to allow us to simply flush the caches. Note that cleaning
+    // the caches is necessary for both data directions:
+    // - for bus master read, we want the latest data to be present
+    //   in main memory
+    // - for bus master write, we don't want any stale dirty cachelines that
+    //   may be written back unexpectedly, and clobber the data written to
+    //   main memory by the device.
+    //
+    mCpu->FlushDataCache (
+            mCpu,
+            (EFI_PHYSICAL_ADDRESS)(UINTN)HostAddress,
+            *NumberOfBytes,
+            EfiCpuFlushTypeWriteBack
+            );
+  }
+
+  *Mapping = MapInfo;
+  return EFI_SUCCESS;
+
+FreeMapInfo:
+  FreePool (MapInfo);
+
+  return Status;
+}
+
+/**
+  Completes the Map() operation and releases any corresponding resources.
+
+  @param  This                  A pointer to the EFI_DEVICE_IO_PROTOCOL
+                                instance.
+  @param  Mapping               The mapping value returned from Map().
+
+  @retval EFI_SUCCESS           The range was unmapped.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+NonCoherentIoUnmap (
+  IN  EFI_DEVICE_IO_PROTOCOL           *This,
+  IN  VOID                             *Mapping
+  )
+{
+  NON_DISCOVERABLE_IO_DEVICE_MAP_INFO  *MapInfo;
+
+  if (Mapping == NULL) {
+    return EFI_DEVICE_ERROR;
+  }
+
+  MapInfo = Mapping;
+  if (MapInfo->AllocAddress != 0) {
+    //
+    // We are using a bounce buffer: copy back the data if necessary,
+    // and free the buffer.
+    //
+    if (MapInfo->Operation == EfiBusMasterWrite) {
+      gBS->CopyMem (
+             MapInfo->HostAddress,
+             (VOID *)(UINTN)MapInfo->AllocAddress,
+             MapInfo->NumberOfBytes
+             );
+    }
+    NonCoherentIoFreeBuffer (
+      This,
+      EFI_SIZE_TO_PAGES (MapInfo->NumberOfBytes),
+      MapInfo->AllocAddress
+      );
+  } else {
+    //
+    // We are *not* using a bounce buffer: if this is a bus master write,
+    // we have to invalidate the caches so the CPU will see the uncached
+    // data written by the device.
+    //
+    if (MapInfo->Operation == EfiBusMasterWrite) {
+      mCpu->FlushDataCache (
+              mCpu,
+              (EFI_PHYSICAL_ADDRESS)(UINTN)MapInfo->HostAddress,
+              MapInfo->NumberOfBytes,
+              EfiCpuFlushTypeInvalidate
+              );
+    }
+  }
+  FreePool (MapInfo);
+  return EFI_SUCCESS;
+}
+
+STATIC CONST EFI_DEVICE_IO_PROTOCOL IoTemplate =
+{
+  { IoMemRead, IoMemWrite },
+  { IoIoRead,  IoIoWrite },
+  { 0, 0 },
+  CoherentIoMap,
+  0,
+  CoherentIoUnmap,
+  CoherentIoAllocateBuffer,
+  0,
+  CoherentIoFreeBuffer,
+};
+
+/**
+  Initialize DevIo Protocol.
+
+  @param  Dev      Point to NON_DISCOVERABLE_IO_DEVICE instance.
+
+**/
+VOID
+InitializeIoProtocol (
+  NON_DISCOVERABLE_IO_DEVICE           *Dev
+  )
+{
+  InitializeListHead (&Dev->UncachedAllocationList);
+
+  //
+  // Copy protocol structure
+  //
+  CopyMem(&Dev->Io, &IoTemplate, sizeof (IoTemplate));
+
+  if (Dev->Device->DmaType == NonDiscoverableDeviceDmaTypeNonCoherent) {
+    Dev->Io.AllocateBuffer  = NonCoherentIoAllocateBuffer;
+    Dev->Io.FreeBuffer      = NonCoherentIoFreeBuffer;
+    Dev->Io.Map             = NonCoherentIoMap;
+    Dev->Io.Unmap           = NonCoherentIoUnmap;
+  } else {
+    Dev->Io.AllocateBuffer  = CoherentIoAllocateBuffer;
+    Dev->Io.FreeBuffer      = CoherentIoFreeBuffer;
+    Dev->Io.Map             = CoherentIoMap;
+    Dev->Io.Unmap           = CoherentIoUnmap;
+  }
+}