Blame SOURCES/0271-nx-add-memory-attribute-get-set-API.patch

1c6ba0
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
1c6ba0
From: Peter Jones <pjones@redhat.com>
1c6ba0
Date: Tue, 22 Mar 2022 10:56:21 -0400
1c6ba0
Subject: [PATCH] nx: add memory attribute get/set API
1c6ba0
1c6ba0
For NX, we need to set the page access permission attributes for write
1c6ba0
and execute permissions.
1c6ba0
1c6ba0
This patch adds two new primitives, grub_set_mem_attrs() and
1c6ba0
grub_clear_mem_attrs(), and associated constant definitions, to be used
1c6ba0
for that purpose.
1c6ba0
1c6ba0
For most platforms, it adds a dummy implementation that returns
1c6ba0
GRUB_ERR_NONE.  On EFI platforms, it adds a common helper function,
1c6ba0
grub_efi_status_to_err(), which translates EFI error codes to grub error
1c6ba0
codes, adds headers for the EFI Memory Attribute Protocol (still pending
1c6ba0
standardization), and an implementation of the grub nx primitives using
1c6ba0
it.
1c6ba0
1c6ba0
Signed-off-by: Peter Jones <pjones@redhat.com>
1c6ba0
[rharwood: add pjones's none/nyi fixup]
1c6ba0
Signed-off-by: Robbie Harwood <rharwood@redhat.com>
1c6ba0
(cherry picked from commit 35de78a8d32b9fad5291ec96fd3cbb9cf2f4a80b)
1c6ba0
---
1c6ba0
 grub-core/kern/efi/efi.c |  36 +++++++++++++
1c6ba0
 grub-core/kern/efi/mm.c  | 131 +++++++++++++++++++++++++++++++++++++++++++++++
1c6ba0
 include/grub/efi/api.h   |  25 +++++++++
1c6ba0
 include/grub/efi/efi.h   |   2 +
1c6ba0
 include/grub/mm.h        |  32 ++++++++++++
1c6ba0
 5 files changed, 226 insertions(+)
1c6ba0
1c6ba0
diff --git a/grub-core/kern/efi/efi.c b/grub-core/kern/efi/efi.c
1c6ba0
index 7fcca69c17..4ac2b2754e 100644
1c6ba0
--- a/grub-core/kern/efi/efi.c
1c6ba0
+++ b/grub-core/kern/efi/efi.c
1c6ba0
@@ -1096,3 +1096,39 @@ grub_efi_compare_device_paths (const grub_efi_device_path_t *dp1,
1c6ba0
 
1c6ba0
   return 0;
1c6ba0
 }
1c6ba0
+
1c6ba0
+grub_err_t
1c6ba0
+grub_efi_status_to_err (grub_efi_status_t status)
1c6ba0
+{
1c6ba0
+  grub_err_t err;
1c6ba0
+  switch (status)
1c6ba0
+    {
1c6ba0
+    case GRUB_EFI_SUCCESS:
1c6ba0
+      err = GRUB_ERR_NONE;
1c6ba0
+      break;
1c6ba0
+    case GRUB_EFI_INVALID_PARAMETER:
1c6ba0
+    default:
1c6ba0
+      err = GRUB_ERR_BAD_ARGUMENT;
1c6ba0
+      break;
1c6ba0
+    case GRUB_EFI_OUT_OF_RESOURCES:
1c6ba0
+      err = GRUB_ERR_OUT_OF_MEMORY;
1c6ba0
+      break;
1c6ba0
+    case GRUB_EFI_DEVICE_ERROR:
1c6ba0
+      err = GRUB_ERR_IO;
1c6ba0
+      break;
1c6ba0
+    case GRUB_EFI_WRITE_PROTECTED:
1c6ba0
+      err = GRUB_ERR_WRITE_ERROR;
1c6ba0
+      break;
1c6ba0
+    case GRUB_EFI_SECURITY_VIOLATION:
1c6ba0
+      err = GRUB_ERR_ACCESS_DENIED;
1c6ba0
+      break;
1c6ba0
+    case GRUB_EFI_NOT_FOUND:
1c6ba0
+      err = GRUB_ERR_FILE_NOT_FOUND;
1c6ba0
+      break;
1c6ba0
+    case GRUB_EFI_ABORTED:
1c6ba0
+      err = GRUB_ERR_WAIT;
1c6ba0
+      break;
1c6ba0
+    }
1c6ba0
+
1c6ba0
+  return err;
1c6ba0
+}
1c6ba0
diff --git a/grub-core/kern/efi/mm.c b/grub-core/kern/efi/mm.c
1c6ba0
index e84961d078..2c33758ed7 100644
1c6ba0
--- a/grub-core/kern/efi/mm.c
1c6ba0
+++ b/grub-core/kern/efi/mm.c
1c6ba0
@@ -738,3 +738,134 @@ grub_efi_get_ram_base(grub_addr_t *base_addr)
1c6ba0
   return GRUB_ERR_NONE;
1c6ba0
 }
1c6ba0
 #endif
1c6ba0
+
1c6ba0
+static inline grub_uint64_t
1c6ba0
+grub_mem_attrs_to_uefi_mem_attrs (grub_uint64_t attrs)
1c6ba0
+{
1c6ba0
+  grub_uint64_t ret = GRUB_EFI_MEMORY_RP |
1c6ba0
+		      GRUB_EFI_MEMORY_RO |
1c6ba0
+		      GRUB_EFI_MEMORY_XP;
1c6ba0
+
1c6ba0
+  if (attrs & GRUB_MEM_ATTR_R)
1c6ba0
+    ret &= ~GRUB_EFI_MEMORY_RP;
1c6ba0
+
1c6ba0
+  if (attrs & GRUB_MEM_ATTR_W)
1c6ba0
+    ret &= ~GRUB_EFI_MEMORY_RO;
1c6ba0
+
1c6ba0
+  if (attrs & GRUB_MEM_ATTR_X)
1c6ba0
+    ret &= ~GRUB_EFI_MEMORY_XP;
1c6ba0
+
1c6ba0
+  return ret;
1c6ba0
+}
1c6ba0
+
1c6ba0
+static inline grub_uint64_t
1c6ba0
+uefi_mem_attrs_to_grub_mem_attrs (grub_uint64_t attrs)
1c6ba0
+{
1c6ba0
+  grub_uint64_t ret = GRUB_MEM_ATTR_R |
1c6ba0
+		      GRUB_MEM_ATTR_W |
1c6ba0
+		      GRUB_MEM_ATTR_X;
1c6ba0
+
1c6ba0
+  if (attrs & GRUB_EFI_MEMORY_RP)
1c6ba0
+    ret &= ~GRUB_MEM_ATTR_R;
1c6ba0
+
1c6ba0
+  if (attrs & GRUB_EFI_MEMORY_RO)
1c6ba0
+    ret &= ~GRUB_MEM_ATTR_W;
1c6ba0
+
1c6ba0
+  if (attrs & GRUB_EFI_MEMORY_XP)
1c6ba0
+    ret &= ~GRUB_MEM_ATTR_X;
1c6ba0
+
1c6ba0
+  return ret;
1c6ba0
+}
1c6ba0
+
1c6ba0
+grub_err_t
1c6ba0
+grub_get_mem_attrs (grub_addr_t addr, grub_size_t size, grub_uint64_t *attrs)
1c6ba0
+{
1c6ba0
+  grub_efi_memory_attribute_protocol_t *proto;
1c6ba0
+  grub_efi_physical_address_t physaddr = addr;
1c6ba0
+  grub_efi_guid_t protocol_guid = GRUB_EFI_MEMORY_ATTRIBUTE_PROTOCOL_GUID;
1c6ba0
+  grub_efi_status_t efi_status;
1c6ba0
+
1c6ba0
+  proto = grub_efi_locate_protocol (&protocol_guid, 0);
1c6ba0
+  if (!proto)
1c6ba0
+    return GRUB_ERR_NOT_IMPLEMENTED_YET;
1c6ba0
+
1c6ba0
+  if (physaddr & 0xfff || size & 0xfff || size == 0 || attrs == NULL)
1c6ba0
+    {
1c6ba0
+      grub_dprintf ("nx", "%s called on 0x%"PRIxGRUB_ADDR"-0x%"PRIxGRUB_ADDR" and attrs %p\n",
1c6ba0
+		    __func__, physaddr, physaddr+size-1, attrs);
1c6ba0
+      return 0;
1c6ba0
+    }
1c6ba0
+
1c6ba0
+  efi_status = efi_call_4(proto->get_memory_attributes,
1c6ba0
+			  proto, physaddr, size, attrs);
1c6ba0
+  *attrs = uefi_mem_attrs_to_grub_mem_attrs (*attrs);
1c6ba0
+
1c6ba0
+  return grub_efi_status_to_err (efi_status);
1c6ba0
+}
1c6ba0
+
1c6ba0
+grub_err_t
1c6ba0
+grub_update_mem_attrs (grub_addr_t addr, grub_size_t size,
1c6ba0
+		       grub_uint64_t set_attrs, grub_uint64_t clear_attrs)
1c6ba0
+{
1c6ba0
+  grub_efi_memory_attribute_protocol_t *proto;
1c6ba0
+  grub_efi_physical_address_t physaddr = addr;
1c6ba0
+  grub_efi_guid_t protocol_guid = GRUB_EFI_MEMORY_ATTRIBUTE_PROTOCOL_GUID;
1c6ba0
+  grub_efi_status_t efi_status = GRUB_EFI_SUCCESS;
1c6ba0
+  grub_uint64_t before = 0, after = 0, uefi_set_attrs, uefi_clear_attrs;
1c6ba0
+  grub_err_t err;
1c6ba0
+
1c6ba0
+  proto = grub_efi_locate_protocol (&protocol_guid, 0);
1c6ba0
+  if (!proto)
1c6ba0
+    return GRUB_ERR_NONE;
1c6ba0
+
1c6ba0
+  err = grub_get_mem_attrs (addr, size, &before);
1c6ba0
+  if (err)
1c6ba0
+    grub_dprintf ("nx", "grub_get_mem_attrs(0x%"PRIxGRUB_ADDR", %"PRIuGRUB_SIZE", %p) -> 0x%x\n",
1c6ba0
+		  addr, size, &before, err);
1c6ba0
+
1c6ba0
+  if (physaddr & 0xfff || size & 0xfff || size == 0)
1c6ba0
+    {
1c6ba0
+      grub_dprintf ("nx", "%s called on 0x%"PRIxGRUB_ADDR"-0x%"PRIxGRUB_ADDR" +%s%s%s -%s%s%s\n",
1c6ba0
+		    __func__, physaddr, physaddr + size - 1,
1c6ba0
+		    (set_attrs & GRUB_MEM_ATTR_R) ? "r" : "",
1c6ba0
+		    (set_attrs & GRUB_MEM_ATTR_W) ? "w" : "",
1c6ba0
+		    (set_attrs & GRUB_MEM_ATTR_X) ? "x" : "",
1c6ba0
+		    (clear_attrs & GRUB_MEM_ATTR_R) ? "r" : "",
1c6ba0
+		    (clear_attrs & GRUB_MEM_ATTR_W) ? "w" : "",
1c6ba0
+		    (clear_attrs & GRUB_MEM_ATTR_X) ? "x" : "");
1c6ba0
+      return 0;
1c6ba0
+    }
1c6ba0
+
1c6ba0
+  uefi_set_attrs = grub_mem_attrs_to_uefi_mem_attrs (set_attrs);
1c6ba0
+  grub_dprintf ("nx", "translating set_attrs from 0x%lx to 0x%lx\n", set_attrs, uefi_set_attrs);
1c6ba0
+  uefi_clear_attrs = grub_mem_attrs_to_uefi_mem_attrs (clear_attrs);
1c6ba0
+  grub_dprintf ("nx", "translating clear_attrs from 0x%lx to 0x%lx\n", clear_attrs, uefi_clear_attrs);
1c6ba0
+  if (uefi_set_attrs)
1c6ba0
+    efi_status = efi_call_4(proto->set_memory_attributes,
1c6ba0
+			    proto, physaddr, size, uefi_set_attrs);
1c6ba0
+  if (efi_status == GRUB_EFI_SUCCESS && uefi_clear_attrs)
1c6ba0
+    efi_status = efi_call_4(proto->clear_memory_attributes,
1c6ba0
+			    proto, physaddr, size, uefi_clear_attrs);
1c6ba0
+
1c6ba0
+  err = grub_get_mem_attrs (addr, size, &after);
1c6ba0
+  if (err)
1c6ba0
+    grub_dprintf ("nx", "grub_get_mem_attrs(0x%"PRIxGRUB_ADDR", %"PRIuGRUB_SIZE", %p) -> 0x%x\n",
1c6ba0
+		  addr, size, &after, err);
1c6ba0
+
1c6ba0
+  grub_dprintf ("nx", "set +%s%s%s -%s%s%s on 0x%"PRIxGRUB_ADDR"-0x%"PRIxGRUB_ADDR" before:%c%c%c after:%c%c%c\n",
1c6ba0
+		(set_attrs & GRUB_MEM_ATTR_R) ? "r" : "",
1c6ba0
+		(set_attrs & GRUB_MEM_ATTR_W) ? "w" : "",
1c6ba0
+		(set_attrs & GRUB_MEM_ATTR_X) ? "x" : "",
1c6ba0
+		(clear_attrs & GRUB_MEM_ATTR_R) ? "r" : "",
1c6ba0
+		(clear_attrs & GRUB_MEM_ATTR_W) ? "w" : "",
1c6ba0
+		(clear_attrs & GRUB_MEM_ATTR_X) ? "x" : "",
1c6ba0
+		addr, addr + size - 1,
1c6ba0
+		(before & GRUB_MEM_ATTR_R) ? 'r' : '-',
1c6ba0
+		(before & GRUB_MEM_ATTR_W) ? 'w' : '-',
1c6ba0
+		(before & GRUB_MEM_ATTR_X) ? 'x' : '-',
1c6ba0
+		(after & GRUB_MEM_ATTR_R) ? 'r' : '-',
1c6ba0
+		(after & GRUB_MEM_ATTR_W) ? 'w' : '-',
1c6ba0
+		(after & GRUB_MEM_ATTR_X) ? 'x' : '-');
1c6ba0
+
1c6ba0
+  return grub_efi_status_to_err (efi_status);
1c6ba0
+}
1c6ba0
diff --git a/include/grub/efi/api.h b/include/grub/efi/api.h
1c6ba0
index f431f49973..464842ba37 100644
1c6ba0
--- a/include/grub/efi/api.h
1c6ba0
+++ b/include/grub/efi/api.h
1c6ba0
@@ -363,6 +363,11 @@
1c6ba0
       { 0x89, 0x29, 0x48, 0xbc, 0xd9, 0x0a, 0xd3, 0x1a } \
1c6ba0
   }
1c6ba0
 
1c6ba0
+#define GRUB_EFI_MEMORY_ATTRIBUTE_PROTOCOL_GUID \
1c6ba0
+  { 0xf4560cf6, 0x40ec, 0x4b4a, \
1c6ba0
+    { 0xa1, 0x92, 0xbf, 0x1d, 0x57, 0xd0, 0xb1, 0x89 } \
1c6ba0
+  }
1c6ba0
+
1c6ba0
 struct grub_efi_sal_system_table
1c6ba0
 {
1c6ba0
   grub_uint32_t signature;
1c6ba0
@@ -2102,6 +2107,26 @@ struct grub_efi_ip6_config_manual_address {
1c6ba0
 };
1c6ba0
 typedef struct grub_efi_ip6_config_manual_address grub_efi_ip6_config_manual_address_t;
1c6ba0
 
1c6ba0
+struct grub_efi_memory_attribute_protocol
1c6ba0
+{
1c6ba0
+  grub_efi_status_t (*get_memory_attributes) (
1c6ba0
+			    struct grub_efi_memory_attribute_protocol *this,
1c6ba0
+			    grub_efi_physical_address_t base_address,
1c6ba0
+			    grub_efi_uint64_t length,
1c6ba0
+			    grub_efi_uint64_t *attributes);
1c6ba0
+  grub_efi_status_t (*set_memory_attributes) (
1c6ba0
+			    struct grub_efi_memory_attribute_protocol *this,
1c6ba0
+			    grub_efi_physical_address_t base_address,
1c6ba0
+			    grub_efi_uint64_t length,
1c6ba0
+			    grub_efi_uint64_t attributes);
1c6ba0
+  grub_efi_status_t (*clear_memory_attributes) (
1c6ba0
+			    struct grub_efi_memory_attribute_protocol *this,
1c6ba0
+			    grub_efi_physical_address_t base_address,
1c6ba0
+			    grub_efi_uint64_t length,
1c6ba0
+			    grub_efi_uint64_t attributes);
1c6ba0
+};
1c6ba0
+typedef struct grub_efi_memory_attribute_protocol grub_efi_memory_attribute_protocol_t;
1c6ba0
+
1c6ba0
 #if (GRUB_TARGET_SIZEOF_VOID_P == 4) || defined (__ia64__) \
1c6ba0
   || defined (__aarch64__) || defined (__MINGW64__) || defined (__CYGWIN__) \
1c6ba0
   || defined(__riscv)
1c6ba0
diff --git a/include/grub/efi/efi.h b/include/grub/efi/efi.h
1c6ba0
index ec52083c49..34825c4adc 100644
1c6ba0
--- a/include/grub/efi/efi.h
1c6ba0
+++ b/include/grub/efi/efi.h
1c6ba0
@@ -164,4 +164,6 @@ struct grub_net_card;
1c6ba0
 grub_efi_handle_t
1c6ba0
 grub_efinet_get_device_handle (struct grub_net_card *card);
1c6ba0
 
1c6ba0
+grub_err_t EXPORT_FUNC(grub_efi_status_to_err) (grub_efi_status_t status);
1c6ba0
+
1c6ba0
 #endif /* ! GRUB_EFI_EFI_HEADER */
1c6ba0
diff --git a/include/grub/mm.h b/include/grub/mm.h
1c6ba0
index 9c38dd3ca5..d81623d226 100644
1c6ba0
--- a/include/grub/mm.h
1c6ba0
+++ b/include/grub/mm.h
1c6ba0
@@ -22,6 +22,7 @@
1c6ba0
 
1c6ba0
 #include <grub/types.h>
1c6ba0
 #include <grub/symbol.h>
1c6ba0
+#include <grub/err.h>
1c6ba0
 #include <config.h>
1c6ba0
 
1c6ba0
 #ifndef NULL
1c6ba0
@@ -38,6 +39,37 @@ void *EXPORT_FUNC(grub_realloc) (void *ptr, grub_size_t size);
1c6ba0
 void *EXPORT_FUNC(grub_memalign) (grub_size_t align, grub_size_t size);
1c6ba0
 #endif
1c6ba0
 
1c6ba0
+#define GRUB_MEM_ATTR_R	0x0000000000000004LLU
1c6ba0
+#define GRUB_MEM_ATTR_W	0x0000000000000002LLU
1c6ba0
+#define GRUB_MEM_ATTR_X	0x0000000000000001LLU
1c6ba0
+
1c6ba0
+#ifdef GRUB_MACHINE_EFI
1c6ba0
+grub_err_t EXPORT_FUNC(grub_get_mem_attrs) (grub_addr_t addr,
1c6ba0
+					    grub_size_t size,
1c6ba0
+					    grub_uint64_t *attrs);
1c6ba0
+grub_err_t EXPORT_FUNC(grub_update_mem_attrs) (grub_addr_t addr,
1c6ba0
+					       grub_size_t size,
1c6ba0
+					       grub_uint64_t set_attrs,
1c6ba0
+					       grub_uint64_t clear_attrs);
1c6ba0
+#else /* !GRUB_MACHINE_EFI */
1c6ba0
+static inline grub_err_t
1c6ba0
+grub_get_mem_attrs (grub_addr_t addr __attribute__((__unused__)),
1c6ba0
+		    grub_size_t size __attribute__((__unused__)),
1c6ba0
+		    grub_uint64_t *attrs __attribute__((__unused__)))
1c6ba0
+{
1c6ba0
+  return GRUB_ERR_NONE;
1c6ba0
+}
1c6ba0
+
1c6ba0
+static inline grub_err_t
1c6ba0
+grub_update_mem_attrs (grub_addr_t addr __attribute__((__unused__)),
1c6ba0
+		       grub_size_t size __attribute__((__unused__)),
1c6ba0
+		       grub_uint64_t set_attrs __attribute__((__unused__)),
1c6ba0
+		       grub_uint64_t clear_attrs __attribute__((__unused__)))
1c6ba0
+{
1c6ba0
+  return GRUB_ERR_NONE;
1c6ba0
+}
1c6ba0
+#endif /* GRUB_MACHINE_EFI */
1c6ba0
+
1c6ba0
 void grub_mm_check_real (const char *file, int line);
1c6ba0
 #define grub_mm_check() grub_mm_check_real (GRUB_FILE, __LINE__);
1c6ba0