From patchwork Wed Aug 15 07:36:49 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Haojian Zhuang X-Patchwork-Id: 144273 Delivered-To: patch@linaro.org Received: by 2002:a2e:9754:0:0:0:0:0 with SMTP id f20-v6csp512377ljj; Wed, 15 Aug 2018 00:37:04 -0700 (PDT) X-Google-Smtp-Source: AA+uWPypvH6nVcIhPua5Wb09jAFyq3MxWUVFYYDBb3aqYxmwyGu9eZTo9k/2fRIf48eZgR5B8FsH X-Received: by 2002:a17:902:6ac7:: with SMTP id i7-v6mr23894532plt.288.1534318624291; Wed, 15 Aug 2018 00:37:04 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1534318624; cv=none; d=google.com; s=arc-20160816; b=om2COi4hZN2o6TsRmqEKgSqXmXMfKNftvlzj/nEY0Loc8tlOs0UITdTwlHscFzr8uo pOGgJvB2ynzaEu0lZTGPiikWz+OS5pblr9HLPuPLDy7OPkiu/dCw6ED+M5TtNNialWNw jMBReAqESB2urXN/qjJX9nTyAaPTxt2W1+u1S1DewAFW1iOC3tZWx8mvIbmVM6hCPbTf l/C1oZeU5yoM2Hqt4TbqaqS6yRnWytzvHERkTzAJR8pfSylI0BmOMF0OD5yu2WI25FcP 2ZUzHWqvueeEwKYiStXCqNVTg8mpDwXHE/OgBjPcYhXDXrpfNQ2Yb+KEnBWc5UBdJVfO f6iQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:content-transfer-encoding:mime-version :list-subscribe:list-help:list-post:list-archive:list-unsubscribe :list-id:precedence:subject:references:in-reply-to:message-id:date :to:from:dkim-signature:delivered-to:arc-authentication-results; bh=lH5ttZ99MS4yYXiPF6iLvm05fs6UhdPq6hEe5MlJmvA=; b=LXLJmvQijz4XnWaUTyF54P3XwDOsvNwGLrxwdUTqlFUF1tNvgzHbPUdPASEv1Jw0Gt wJTlJT5RbVf/avcy5KQ6SfmTRQRiAefaQY+4oboUEoXoQgxVKEWQFb0WGNhJVgukik80 BXfW0XjEyCjy6F9yC4QL/xP8HjtYDKIbew08L5HnzKJrs3qVse9/0uONnnrjAiMfxuNQ z5Qk8bHIDsbSnlNTkiAkJ+qEtymIaJc/DBDl+An3EnOpx5F2twmy3m/e1jbPBxl4IPF0 VTkppC7L2KunKuH/FHFupf7bbw2U5OckJTIv+rhh0igBJPLRqxwDBG8Ba/tWuN1NmURr aNPg== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@linaro.org header.s=google header.b=b0gJohmD; spf=pass (google.com: best guess record for domain of edk2-devel-bounces@lists.01.org designates 2001:19d0:306:5::1 as permitted sender) smtp.mailfrom=edk2-devel-bounces@lists.01.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=linaro.org Return-Path: Received: from ml01.01.org (ml01.01.org. [2001:19d0:306:5::1]) by mx.google.com with ESMTPS id 3-v6si13325616plx.173.2018.08.15.00.37.03 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Wed, 15 Aug 2018 00:37:04 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of edk2-devel-bounces@lists.01.org designates 2001:19d0:306:5::1 as permitted sender) client-ip=2001:19d0:306:5::1; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@linaro.org header.s=google header.b=b0gJohmD; spf=pass (google.com: best guess record for domain of edk2-devel-bounces@lists.01.org designates 2001:19d0:306:5::1 as permitted sender) smtp.mailfrom=edk2-devel-bounces@lists.01.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=linaro.org Received: from [127.0.0.1] (localhost [IPv6:::1]) by ml01.01.org (Postfix) with ESMTP id D6520210F16DD; Wed, 15 Aug 2018 00:37:03 -0700 (PDT) X-Original-To: edk2-devel@lists.01.org Delivered-To: edk2-devel@lists.01.org Received-SPF: Pass (sender SPF authorized) identity=mailfrom; client-ip=2607:f8b0:4864:20::52c; helo=mail-pg1-x52c.google.com; envelope-from=haojian.zhuang@linaro.org; receiver=edk2-devel@lists.01.org Received: from mail-pg1-x52c.google.com (mail-pg1-x52c.google.com [IPv6:2607:f8b0:4864:20::52c]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by ml01.01.org (Postfix) with ESMTPS id BA37621962301 for ; Wed, 15 Aug 2018 00:37:02 -0700 (PDT) Received: by mail-pg1-x52c.google.com with SMTP id r5-v6so211315pgv.0 for ; Wed, 15 Aug 2018 00:37:02 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=Yy4s5/NwkhzR3Urh6DgSDe8qh9kRGaWzL/3oG2dkrH4=; b=b0gJohmDH3MJVuTaGx/fV9pnkAuPzxivNpKHBeqTaTTW1nMU7rsrA/gOxW2xbEHY9J EEuX7deLRSTaTfILJ4rSanHd63AGHAW3/P/Y8IK8DZJiVuiubnkKyKokssOqLpRA06Aa 3COPNHeqlsWTD9mD3kp0vhIFwI9Mi55oGXvBo= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=Yy4s5/NwkhzR3Urh6DgSDe8qh9kRGaWzL/3oG2dkrH4=; b=BEWfXd8yebNqZpdCra9pNHqC2bP502qDjncdLUHe0tsTzqAgrYFUe3xhU2UzDIho0D okFw3YmxWe52j2tw6zZGUnDt95HLzXNG3k/lLWDD/h0Dex7X/QnTQHSFV5gPEFY7qQ6h xxrzBO+uwC04prX/1DJ6a6bFlrTTV0ZNuccc7SQ36l4tLqYeCmuR+DfWJCT2QesFeypO 9FkysYEz+7m3VTRx8K1BZf9IddB+KQGHgnoaXZZEaA6hktX/5+OY28/9vcOPf6SwXlXA REVGisCrT80lR0Sr3pVbpHLvg4Q48hg/lO/byEk7keutY6aFmrOQ2TihvKwLs8Yq4KHb dpBA== X-Gm-Message-State: AOUpUlG+qqcpOTrI9tfxmVwXnHfB/jVq0LamkOqgkwwBaAd3hJ4U+Akg f9JHK9MG8Vj84i+8sxqJfETvWB3Xf2U2Mg== X-Received: by 2002:a62:4796:: with SMTP id p22-v6mr21804179pfi.170.1534318621769; Wed, 15 Aug 2018 00:37:01 -0700 (PDT) Received: from localhost.localdomain ([64.64.108.254]) by smtp.gmail.com with ESMTPSA id w2-v6sm22786177pge.44.2018.08.15.00.36.58 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Wed, 15 Aug 2018 00:37:00 -0700 (PDT) From: Haojian Zhuang To: edk2-devel@lists.01.org Date: Wed, 15 Aug 2018 15:36:49 +0800 Message-Id: <1534318611-11461-2-git-send-email-haojian.zhuang@linaro.org> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1534318611-11461-1-git-send-email-haojian.zhuang@linaro.org> References: <1534318611-11461-1-git-send-email-haojian.zhuang@linaro.org> Subject: [edk2] [PATCH v2 1/3] EmbeddedPkg: add NonDiscoverableDeviceDxe driver X-BeenThere: edk2-devel@lists.01.org X-Mailman-Version: 2.1.27 Precedence: list List-Id: EDK II Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: edk2-devel-bounces@lists.01.org Sender: "edk2-devel" It's used to create NonDiscoverableDevice in embedded platform. Since there's no PCI bus. Cc: Leif Lindholm Cc: Ard Biesheuvel Cc: Chris Co Contributed-under: TianoCore Contribution Agreement 1.1 Signed-off-by: Haojian Zhuang --- 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 --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.
+ + 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 + +#include +#include +#include +#include +#include + +#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.
+ + 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 + +#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.
+ + 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 +#include +#include +#include +#include + +#include + +#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.
+ Copyright (c) 2016-2018, Linaro, Ltd. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include + +#include +#include +#include +#include +#include + +#include +#include + +#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; + } +}