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