summaryrefslogtreecommitdiffstats
path: root/ArmPkg/Drivers
diff options
context:
space:
mode:
authorArd Biesheuvel <ardb@kernel.org>2023-01-31 23:26:25 +0100
committermergify[bot] <37929162+mergify[bot]@users.noreply.github.com>2023-03-16 21:14:49 +0000
commit1c4dfadb4611ef511816dfdfbdb37d7d100b5a4b (patch)
treea98df9c9fa4fbc5f31a1524f3c759fcabaac6eba /ArmPkg/Drivers
parentb977956a6cebebd18d2140036770e201842e5483 (diff)
downloadedk2-1c4dfadb4611ef511816dfdfbdb37d7d100b5a4b.tar.gz
edk2-1c4dfadb4611ef511816dfdfbdb37d7d100b5a4b.tar.bz2
edk2-1c4dfadb4611ef511816dfdfbdb37d7d100b5a4b.zip
ArmPkg/CpuDxe: Implement EFI memory attributes protocol
Expose the protocol introduced in v2.10 that permits the caller to manage mapping permissions in the page tables. Signed-off-by: Ard Biesheuvel <ardb@kernel.org> Reviewed-by: Leif Lindholm <quic_llindhol@quicinc.com>
Diffstat (limited to 'ArmPkg/Drivers')
-rw-r--r--ArmPkg/Drivers/CpuDxe/CpuDxe.c2
-rw-r--r--ArmPkg/Drivers/CpuDxe/CpuDxe.h3
-rw-r--r--ArmPkg/Drivers/CpuDxe/CpuDxe.inf2
-rw-r--r--ArmPkg/Drivers/CpuDxe/MemoryAttribute.c319
4 files changed, 326 insertions, 0 deletions
diff --git a/ArmPkg/Drivers/CpuDxe/CpuDxe.c b/ArmPkg/Drivers/CpuDxe/CpuDxe.c
index e6742f0a25..d04958e79e 100644
--- a/ArmPkg/Drivers/CpuDxe/CpuDxe.c
+++ b/ArmPkg/Drivers/CpuDxe/CpuDxe.c
@@ -244,6 +244,8 @@ CpuDxeInitialize (
&mCpuHandle,
&gEfiCpuArchProtocolGuid,
&mCpu,
+ &gEfiMemoryAttributeProtocolGuid,
+ &mMemoryAttribute,
NULL
);
diff --git a/ArmPkg/Drivers/CpuDxe/CpuDxe.h b/ArmPkg/Drivers/CpuDxe/CpuDxe.h
index 39e1bbed44..c6613b939a 100644
--- a/ArmPkg/Drivers/CpuDxe/CpuDxe.h
+++ b/ArmPkg/Drivers/CpuDxe/CpuDxe.h
@@ -30,9 +30,12 @@
#include <Protocol/Cpu.h>
#include <Protocol/DebugSupport.h>
#include <Protocol/LoadedImage.h>
+#include <Protocol/MemoryAttribute.h>
extern BOOLEAN mIsFlushingGCD;
+extern EFI_MEMORY_ATTRIBUTE_PROTOCOL mMemoryAttribute;
+
/**
This function registers and enables the handler specified by InterruptHandler for a processor
interrupt or exception type specified by InterruptType. If InterruptHandler is NULL, then the
diff --git a/ArmPkg/Drivers/CpuDxe/CpuDxe.inf b/ArmPkg/Drivers/CpuDxe/CpuDxe.inf
index 10792b393f..e732e21cb9 100644
--- a/ArmPkg/Drivers/CpuDxe/CpuDxe.inf
+++ b/ArmPkg/Drivers/CpuDxe/CpuDxe.inf
@@ -23,6 +23,7 @@
CpuDxe.h
CpuMmuCommon.c
Exception.c
+ MemoryAttribute.c
[Sources.ARM]
Arm/Mmu.c
@@ -53,6 +54,7 @@
[Protocols]
gEfiCpuArchProtocolGuid
+ gEfiMemoryAttributeProtocolGuid
[Guids]
gEfiDebugImageInfoTableGuid
diff --git a/ArmPkg/Drivers/CpuDxe/MemoryAttribute.c b/ArmPkg/Drivers/CpuDxe/MemoryAttribute.c
new file mode 100644
index 0000000000..cbc8261a68
--- /dev/null
+++ b/ArmPkg/Drivers/CpuDxe/MemoryAttribute.c
@@ -0,0 +1,319 @@
+/** @file
+
+ Copyright (c) 2023, Google LLC. All rights reserved.
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "CpuDxe.h"
+
+/**
+ Check whether the provided memory range is covered by a single entry of type
+ EfiGcdSystemMemory in the GCD memory map.
+
+ @param BaseAddress The physical address that is the start address of
+ a memory region.
+ @param Length The size in bytes of the memory region.
+
+ @return Whether the region is system memory or not.
+**/
+STATIC
+BOOLEAN
+RegionIsSystemMemory (
+ IN EFI_PHYSICAL_ADDRESS BaseAddress,
+ IN UINT64 Length
+ )
+{
+ EFI_GCD_MEMORY_SPACE_DESCRIPTOR GcdDescriptor;
+ EFI_PHYSICAL_ADDRESS GcdEndAddress;
+ EFI_STATUS Status;
+
+ Status = gDS->GetMemorySpaceDescriptor (BaseAddress, &GcdDescriptor);
+ if (EFI_ERROR (Status) ||
+ (GcdDescriptor.GcdMemoryType != EfiGcdMemoryTypeSystemMemory))
+ {
+ return FALSE;
+ }
+
+ GcdEndAddress = GcdDescriptor.BaseAddress + GcdDescriptor.Length;
+
+ //
+ // Return TRUE if the GCD descriptor covers the range entirely
+ //
+ return GcdEndAddress >= (BaseAddress + Length);
+}
+
+/**
+ This function retrieves the attributes of the memory region specified by
+ BaseAddress and Length. If different attributes are obtained from different
+ parts of the memory region, EFI_NO_MAPPING will be returned.
+
+ @param This The EFI_MEMORY_ATTRIBUTE_PROTOCOL instance.
+ @param BaseAddress The physical address that is the start address of
+ a memory region.
+ @param Length The size in bytes of the memory region.
+ @param Attributes Pointer to attributes returned.
+
+ @retval EFI_SUCCESS The attributes got for the memory region.
+ @retval EFI_INVALID_PARAMETER Length is zero.
+ Attributes is NULL.
+ @retval EFI_NO_MAPPING Attributes are not consistent cross the memory
+ region.
+ @retval EFI_UNSUPPORTED The processor does not support one or more
+ bytes of the memory resource range specified
+ by BaseAddress and Length.
+
+**/
+STATIC
+EFI_STATUS
+GetMemoryAttributes (
+ IN EFI_MEMORY_ATTRIBUTE_PROTOCOL *This,
+ IN EFI_PHYSICAL_ADDRESS BaseAddress,
+ IN UINT64 Length,
+ OUT UINT64 *Attributes
+ )
+{
+ UINTN RegionAddress;
+ UINTN RegionLength;
+ UINTN RegionAttributes;
+ UINTN Union;
+ UINTN Intersection;
+ EFI_STATUS Status;
+
+ if ((Length == 0) || (Attributes == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (!RegionIsSystemMemory (BaseAddress, Length)) {
+ return EFI_UNSUPPORTED;
+ }
+
+ DEBUG ((
+ DEBUG_VERBOSE,
+ "%a: BaseAddress == 0x%lx, Length == 0x%lx\n",
+ __FUNCTION__,
+ BaseAddress,
+ Length
+ ));
+
+ Union = 0;
+ Intersection = MAX_UINTN;
+
+ for (RegionAddress = (UINTN)BaseAddress;
+ RegionAddress < (UINTN)(BaseAddress + Length);
+ RegionAddress += RegionLength)
+ {
+ Status = GetMemoryRegion (
+ &RegionAddress,
+ &RegionLength,
+ &RegionAttributes
+ );
+
+ DEBUG ((
+ DEBUG_VERBOSE,
+ "%a: RegionAddress == 0x%lx, RegionLength == 0x%lx, RegionAttributes == 0x%lx\n",
+ __FUNCTION__,
+ (UINT64)RegionAddress,
+ (UINT64)RegionLength,
+ (UINT64)RegionAttributes
+ ));
+
+ if (EFI_ERROR (Status)) {
+ return EFI_NO_MAPPING;
+ }
+
+ Union |= RegionAttributes;
+ Intersection &= RegionAttributes;
+ }
+
+ DEBUG ((
+ DEBUG_VERBOSE,
+ "%a: Union == %lx, Intersection == %lx\n",
+ __FUNCTION__,
+ (UINT64)Union,
+ (UINT64)Intersection
+ ));
+
+ if (Union != Intersection) {
+ return EFI_NO_MAPPING;
+ }
+
+ *Attributes = RegionAttributeToGcdAttribute (Union);
+ *Attributes &= EFI_MEMORY_RP | EFI_MEMORY_RO | EFI_MEMORY_XP;
+ return EFI_SUCCESS;
+}
+
+/**
+ This function set given attributes of the memory region specified by
+ BaseAddress and Length.
+
+ The valid Attributes is EFI_MEMORY_RP, EFI_MEMORY_XP, and EFI_MEMORY_RO.
+
+ @param This The EFI_MEMORY_ATTRIBUTE_PROTOCOL instance.
+ @param BaseAddress The physical address that is the start address of
+ a memory region.
+ @param Length The size in bytes of the memory region.
+ @param Attributes The bit mask of attributes to set for the memory
+ region.
+
+ @retval EFI_SUCCESS The attributes were set for the memory region.
+ @retval EFI_INVALID_PARAMETER Length is zero.
+ Attributes specified an illegal combination of
+ attributes that cannot be set together.
+ @retval EFI_UNSUPPORTED The processor does not support one or more
+ bytes of the memory resource range specified
+ by BaseAddress and Length.
+ The bit mask of attributes is not supported for
+ the memory resource range specified by
+ BaseAddress and Length.
+ @retval EFI_OUT_OF_RESOURCES Requested attributes cannot be applied due to
+ lack of system resources.
+ @retval EFI_ACCESS_DENIED Attributes for the requested memory region are
+ controlled by system firmware and cannot be
+ updated via the protocol.
+
+**/
+STATIC
+EFI_STATUS
+SetMemoryAttributes (
+ IN EFI_MEMORY_ATTRIBUTE_PROTOCOL *This,
+ IN EFI_PHYSICAL_ADDRESS BaseAddress,
+ IN UINT64 Length,
+ IN UINT64 Attributes
+ )
+{
+ EFI_STATUS Status;
+
+ DEBUG ((
+ DEBUG_INFO,
+ "%a: BaseAddress == 0x%lx, Length == 0x%lx, Attributes == 0x%lx\n",
+ __FUNCTION__,
+ (UINTN)BaseAddress,
+ (UINTN)Length,
+ (UINTN)Attributes
+ ));
+
+ if ((Length == 0) ||
+ ((Attributes & ~(EFI_MEMORY_RO | EFI_MEMORY_RP | EFI_MEMORY_XP)) != 0))
+ {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (!RegionIsSystemMemory (BaseAddress, Length)) {
+ return EFI_UNSUPPORTED;
+ }
+
+ if ((Attributes & EFI_MEMORY_RP) != 0) {
+ Status = ArmSetMemoryRegionNoAccess (BaseAddress, Length);
+ if (EFI_ERROR (Status)) {
+ return EFI_UNSUPPORTED;
+ }
+ }
+
+ if ((Attributes & EFI_MEMORY_RO) != 0) {
+ Status = ArmSetMemoryRegionReadOnly (BaseAddress, Length);
+ if (EFI_ERROR (Status)) {
+ return EFI_UNSUPPORTED;
+ }
+ }
+
+ if ((Attributes & EFI_MEMORY_XP) != 0) {
+ Status = ArmSetMemoryRegionNoExec (BaseAddress, Length);
+ if (EFI_ERROR (Status)) {
+ return EFI_UNSUPPORTED;
+ }
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ This function clears given attributes of the memory region specified by
+ BaseAddress and Length.
+
+ The valid Attributes is EFI_MEMORY_RP, EFI_MEMORY_XP, and EFI_MEMORY_RO.
+
+ @param This The EFI_MEMORY_ATTRIBUTE_PROTOCOL instance.
+ @param BaseAddress The physical address that is the start address of
+ a memory region.
+ @param Length The size in bytes of the memory region.
+ @param Attributes The bit mask of attributes to clear for the memory
+ region.
+
+ @retval EFI_SUCCESS The attributes were cleared for the memory region.
+ @retval EFI_INVALID_PARAMETER Length is zero.
+ Attributes specified an illegal combination of
+ attributes that cannot be cleared together.
+ @retval EFI_UNSUPPORTED The processor does not support one or more
+ bytes of the memory resource range specified
+ by BaseAddress and Length.
+ The bit mask of attributes is not supported for
+ the memory resource range specified by
+ BaseAddress and Length.
+ @retval EFI_OUT_OF_RESOURCES Requested attributes cannot be applied due to
+ lack of system resources.
+ @retval EFI_ACCESS_DENIED Attributes for the requested memory region are
+ controlled by system firmware and cannot be
+ updated via the protocol.
+
+**/
+STATIC
+EFI_STATUS
+ClearMemoryAttributes (
+ IN EFI_MEMORY_ATTRIBUTE_PROTOCOL *This,
+ IN EFI_PHYSICAL_ADDRESS BaseAddress,
+ IN UINT64 Length,
+ IN UINT64 Attributes
+ )
+{
+ EFI_STATUS Status;
+
+ DEBUG ((
+ DEBUG_INFO,
+ "%a: BaseAddress == 0x%lx, Length == 0x%lx, Attributes == 0x%lx\n",
+ __FUNCTION__,
+ (UINTN)BaseAddress,
+ (UINTN)Length,
+ (UINTN)Attributes
+ ));
+
+ if ((Length == 0) ||
+ ((Attributes & ~(EFI_MEMORY_RO | EFI_MEMORY_RP | EFI_MEMORY_XP)) != 0))
+ {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (!RegionIsSystemMemory (BaseAddress, Length)) {
+ return EFI_UNSUPPORTED;
+ }
+
+ if ((Attributes & EFI_MEMORY_RP) != 0) {
+ Status = ArmClearMemoryRegionNoAccess (BaseAddress, Length);
+ if (EFI_ERROR (Status)) {
+ return EFI_UNSUPPORTED;
+ }
+ }
+
+ if ((Attributes & EFI_MEMORY_RO) != 0) {
+ Status = ArmClearMemoryRegionReadOnly (BaseAddress, Length);
+ if (EFI_ERROR (Status)) {
+ return EFI_UNSUPPORTED;
+ }
+ }
+
+ if ((Attributes & EFI_MEMORY_XP) != 0) {
+ Status = ArmClearMemoryRegionNoExec (BaseAddress, Length);
+ if (EFI_ERROR (Status)) {
+ return EFI_UNSUPPORTED;
+ }
+ }
+
+ return EFI_SUCCESS;
+}
+
+EFI_MEMORY_ATTRIBUTE_PROTOCOL mMemoryAttribute = {
+ GetMemoryAttributes,
+ SetMemoryAttributes,
+ ClearMemoryAttributes
+};