From: Haojian Zhuang <haojian.zhuang@linaro.org>
To: edk2-devel@lists.01.org
Subject: [PATCH v2 1/3] EmbeddedPkg: add NonDiscoverableDeviceDxe driver
Date: Wed, 15 Aug 2018 15:36:49 +0800 [thread overview]
Message-ID: <1534318611-11461-2-git-send-email-haojian.zhuang@linaro.org> (raw)
In-Reply-To: <1534318611-11461-1-git-send-email-haojian.zhuang@linaro.org>
It's used to create NonDiscoverableDevice in embedded platform. Since
there's no PCI bus.
Cc: Leif Lindholm <leif.lindholm@linaro.org>
Cc: Ard Biesheuvel <ard.biesheuvel@linaro.org>
Cc: Chris Co <Christopher.Co@microsoft.com>
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(+)
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;
+ }
+}
--
2.7.4
next prev parent reply other threads:[~2018-08-15 7:37 UTC|newest]
Thread overview: 6+ messages / expand[flat|nested] mbox.gz Atom feed top
2018-08-15 7:36 [PATCH v2 0/3] add DwMmcHcDxe driver Haojian Zhuang
2018-08-15 7:36 ` Haojian Zhuang [this message]
2018-08-15 7:36 ` [PATCH v2 2/3] EmbeddedPkg: add PlatformDwMmc protocol Haojian Zhuang
2018-08-15 7:36 ` [PATCH v2 3/3] EmbeddedPkg/Drivers: add DwMmcHcDxe driver Haojian Zhuang
2021-02-19 9:07 ` [edk2-devel] " Loh, Tien Hock
-- strict thread matches above, loose matches on Subject: below --
2019-07-24 9:26 [PATCH v2 0/3] " Loh, Tien Hock
2019-07-24 9:26 ` [PATCH v2 1/3] EmbeddedPkg: add NonDiscoverableDeviceDxe driver Loh, Tien Hock
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-list from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1534318611-11461-2-git-send-email-haojian.zhuang@linaro.org \
--to=devel@edk2.groups.io \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox