b9d01e
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
b9d01e
From: Peter Jones <pjones@redhat.com>
b9d01e
Date: Tue, 22 Mar 2022 10:57:07 -0400
b9d01e
Subject: [PATCH] nx: set attrs in our kernel loaders
b9d01e
b9d01e
For NX, our kernel loaders need to set write and execute page
b9d01e
permissions on allocated pages and the stack.
b9d01e
b9d01e
This patch adds those calls.
b9d01e
b9d01e
Signed-off-by: Peter Jones <pjones@redhat.com>
b9d01e
[rharwood: fix aarch64 callsites]
b9d01e
(cherry-picked from commit a9f79a997f01a83b36cdfa89ef2e72ac2a17c06c)
b9d01e
[rharwood: double verification]
b9d01e
(cherry picked from commit daba852bd3e4d7b7784b19cf7acf107dc3c0dce4)
b9d01e
[rharwood: stack_attrs initialization, no risc-v, arm renames, arm age]
b9d01e
Signed-off-by: Robbie Harwood <rharwood@redhat.com>
b9d01e
---
b9d01e
 grub-core/kern/efi/mm.c            |  78 ++++++++++++++++++
b9d01e
 grub-core/loader/arm64/linux.c     |  16 +++-
b9d01e
 grub-core/loader/arm64/xen_boot.c  |   4 +-
b9d01e
 grub-core/loader/efi/chainloader.c |  11 +++
b9d01e
 grub-core/loader/efi/linux.c       | 162 ++++++++++++++++++++++++++++++++++++-
b9d01e
 grub-core/loader/i386/efi/linux.c  |  26 +++++-
b9d01e
 grub-core/loader/i386/linux.c      |   5 ++
b9d01e
 include/grub/efi/efi.h             |   6 +-
b9d01e
 include/grub/efi/linux.h           |  17 +++-
b9d01e
 include/grub/efi/pe32.h            |   2 +
b9d01e
 10 files changed, 312 insertions(+), 15 deletions(-)
b9d01e
b9d01e
diff --git a/grub-core/kern/efi/mm.c b/grub-core/kern/efi/mm.c
b9d01e
index 2cf4a4883a..8a896144df 100644
b9d01e
--- a/grub-core/kern/efi/mm.c
b9d01e
+++ b/grub-core/kern/efi/mm.c
b9d01e
@@ -602,6 +602,82 @@ print_memory_map (grub_efi_memory_descriptor_t *memory_map,
b9d01e
 }
b9d01e
 #endif
b9d01e
 
b9d01e
+grub_addr_t grub_stack_addr = (grub_addr_t)-1ll;
b9d01e
+grub_size_t grub_stack_size = 0;
b9d01e
+
b9d01e
+static void
b9d01e
+grub_nx_init (void)
b9d01e
+{
b9d01e
+  grub_uint64_t attrs, stack_attrs;
b9d01e
+  grub_err_t err;
b9d01e
+  grub_addr_t stack_current, stack_end;
b9d01e
+  const grub_uint64_t page_size = 4096;
b9d01e
+  const grub_uint64_t page_mask = ~(page_size - 1);
b9d01e
+
b9d01e
+  /*
b9d01e
+   * These are to confirm that the flags are working as expected when
b9d01e
+   * debugging.
b9d01e
+   */
b9d01e
+  attrs = 0;
b9d01e
+  stack_current = (grub_addr_t)grub_nx_init & page_mask;
b9d01e
+  err = grub_get_mem_attrs (stack_current, page_size, &attrs);
b9d01e
+  if (err)
b9d01e
+    {
b9d01e
+      grub_dprintf ("nx",
b9d01e
+		    "grub_get_mem_attrs(0x%"PRIxGRUB_UINT64_T", ...) -> 0x%x\n",
b9d01e
+		    stack_current, err);
b9d01e
+      grub_error_pop ();
b9d01e
+    }
b9d01e
+  else
b9d01e
+    grub_dprintf ("nx", "page attrs for grub_nx_init (%p) are %c%c%c\n",
b9d01e
+		  grub_dl_load_core,
b9d01e
+		  (attrs & GRUB_MEM_ATTR_R) ? 'r' : '-',
b9d01e
+		  (attrs & GRUB_MEM_ATTR_R) ? 'w' : '-',
b9d01e
+		  (attrs & GRUB_MEM_ATTR_R) ? 'x' : '-');
b9d01e
+
b9d01e
+  stack_current = (grub_addr_t)&stack_current & page_mask;
b9d01e
+  err = grub_get_mem_attrs (stack_current, page_size, &stack_attrs);
b9d01e
+  if (err)
b9d01e
+    {
b9d01e
+      grub_dprintf ("nx",
b9d01e
+		    "grub_get_mem_attrs(0x%"PRIxGRUB_UINT64_T", ...) -> 0x%x\n",
b9d01e
+		    stack_current, err);
b9d01e
+      grub_error_pop ();
b9d01e
+    }
b9d01e
+  else
b9d01e
+    {
b9d01e
+      attrs = stack_attrs;
b9d01e
+      grub_dprintf ("nx", "page attrs for stack (%p) are %c%c%c\n",
b9d01e
+                    &attrs,
b9d01e
+                    (attrs & GRUB_MEM_ATTR_R) ? 'r' : '-',
b9d01e
+                    (attrs & GRUB_MEM_ATTR_R) ? 'w' : '-',
b9d01e
+                    (attrs & GRUB_MEM_ATTR_R) ? 'x' : '-');
b9d01e
+    }
b9d01e
+
b9d01e
+  for (stack_end = stack_current + page_size ;
b9d01e
+       !(attrs & GRUB_MEM_ATTR_R);
b9d01e
+       stack_end += page_size)
b9d01e
+    {
b9d01e
+      err = grub_get_mem_attrs (stack_current, page_size, &attrs);
b9d01e
+      if (err)
b9d01e
+	{
b9d01e
+	  grub_dprintf ("nx",
b9d01e
+			"grub_get_mem_attrs(0x%"PRIxGRUB_UINT64_T", ...) -> 0x%x\n",
b9d01e
+			stack_current, err);
b9d01e
+	  grub_error_pop ();
b9d01e
+	  break;
b9d01e
+	}
b9d01e
+    }
b9d01e
+  if (stack_end > stack_current)
b9d01e
+    {
b9d01e
+      grub_stack_addr = stack_current;
b9d01e
+      grub_stack_size = stack_end - stack_current;
b9d01e
+      grub_dprintf ("nx",
b9d01e
+		    "detected stack from 0x%"PRIxGRUB_ADDR" to 0x%"PRIxGRUB_ADDR"\n",
b9d01e
+		    grub_stack_addr, grub_stack_addr + grub_stack_size - 1);
b9d01e
+    }
b9d01e
+}
b9d01e
+
b9d01e
 void
b9d01e
 grub_efi_mm_init (void)
b9d01e
 {
b9d01e
@@ -615,6 +691,8 @@ grub_efi_mm_init (void)
b9d01e
   grub_efi_uint64_t required_pages;
b9d01e
   int mm_status;
b9d01e
 
b9d01e
+  grub_nx_init ();
b9d01e
+
b9d01e
   /* Prepare a memory region to store two memory maps.  */
b9d01e
   memory_map = grub_efi_allocate_any_pages (2 * BYTES_TO_PAGES (MEMORY_MAP_SIZE));
b9d01e
   if (! memory_map)
b9d01e
diff --git a/grub-core/loader/arm64/linux.c b/grub-core/loader/arm64/linux.c
b9d01e
index 24ab0f0074..37f5d0c7eb 100644
b9d01e
--- a/grub-core/loader/arm64/linux.c
b9d01e
+++ b/grub-core/loader/arm64/linux.c
b9d01e
@@ -191,7 +191,8 @@ free_params (void)
b9d01e
 }
b9d01e
 
b9d01e
 grub_err_t
b9d01e
-grub_armxx_efi_linux_boot_image (grub_addr_t addr, char *args)
b9d01e
+grub_armxx_efi_linux_boot_image (grub_addr_t addr, grub_size_t size, char *args,
b9d01e
+				int nx_supported)
b9d01e
 {
b9d01e
   grub_err_t retval;
b9d01e
 
b9d01e
@@ -201,7 +202,8 @@ grub_armxx_efi_linux_boot_image (grub_addr_t addr, char *args)
b9d01e
 
b9d01e
   grub_dprintf ("linux", "linux command line: '%s'\n", args);
b9d01e
 
b9d01e
-  retval = grub_efi_linux_boot ((char *)addr, handover_offset, (void *)addr);
b9d01e
+  retval = grub_efi_linux_boot (addr, size, handover_offset,
b9d01e
+				(void *)addr, nx_supported);
b9d01e
 
b9d01e
   /* Never reached... */
b9d01e
   free_params();
b9d01e
@@ -211,7 +213,10 @@ grub_armxx_efi_linux_boot_image (grub_addr_t addr, char *args)
b9d01e
 static grub_err_t
b9d01e
 grub_linux_boot (void)
b9d01e
 {
b9d01e
-  return grub_armxx_efi_linux_boot_image((grub_addr_t)kernel_addr, linux_args);
b9d01e
+  return grub_armxx_efi_linux_boot_image((grub_addr_t)kernel_addr,
b9d01e
+					(grub_size_t)kernel_size,
b9d01e
+					linux_args,
b9d01e
+					0);
b9d01e
 }
b9d01e
 
b9d01e
 static grub_err_t
b9d01e
@@ -340,6 +345,7 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
b9d01e
   struct grub_armxx_linux_pe_header *pe;
b9d01e
   int rc;
b9d01e
   grub_err_t err;
b9d01e
+  int nx_supported = 1;
b9d01e
 
b9d01e
   grub_dl_ref (my_mod);
b9d01e
 
b9d01e
@@ -395,6 +401,10 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
b9d01e
 	}
b9d01e
     }
b9d01e
 
b9d01e
+  err = grub_efi_check_nx_image_support((grub_addr_t) kernel_addr, kernel_size, &nx_supported);
b9d01e
+  if (err != GRUB_ERR_NONE)
b9d01e
+    goto fail;
b9d01e
+
b9d01e
   pe = (void *)((unsigned long)kernel_addr + lh.hdr_offset);
b9d01e
   handover_offset = pe->opt.entry_addr;
b9d01e
 
b9d01e
diff --git a/grub-core/loader/arm64/xen_boot.c b/grub-core/loader/arm64/xen_boot.c
b9d01e
index 1a337866f0..1fd1bbb4bd 100644
b9d01e
--- a/grub-core/loader/arm64/xen_boot.c
b9d01e
+++ b/grub-core/loader/arm64/xen_boot.c
b9d01e
@@ -266,7 +266,9 @@ xen_boot (void)
b9d01e
     return err;
b9d01e
 
b9d01e
   return grub_armxx_efi_linux_boot_image (xen_hypervisor->start,
b9d01e
-					  xen_hypervisor->cmdline);
b9d01e
+                                         xen_hypervisor->size,
b9d01e
+                                         xen_hypervisor->cmdline,
b9d01e
+                                         0);
b9d01e
 }
b9d01e
 
b9d01e
 static void
b9d01e
diff --git a/grub-core/loader/efi/chainloader.c b/grub-core/loader/efi/chainloader.c
b9d01e
index 8e658f713e..b72e6bd5e3 100644
b9d01e
--- a/grub-core/loader/efi/chainloader.c
b9d01e
+++ b/grub-core/loader/efi/chainloader.c
b9d01e
@@ -1055,6 +1055,17 @@ grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)),
b9d01e
       goto fail;
b9d01e
     }
b9d01e
 
b9d01e
+  /*
b9d01e
+   * The OS kernel is going to set its own permissions when it takes over
b9d01e
+   * paging a few million instructions from now, and load_image() will set up
b9d01e
+   * anything that's needed based on the section headers, so there's no point
b9d01e
+   * in doing anything but clearing the protection bits here.
b9d01e
+   */
b9d01e
+  grub_dprintf("nx", "setting attributes for %p (%lu bytes) to %llx\n",
b9d01e
+	       (void *)(grub_addr_t)address, fsize, 0llu);
b9d01e
+  grub_update_mem_attrs (address, fsize,
b9d01e
+			 GRUB_MEM_ATTR_R|GRUB_MEM_ATTR_W|GRUB_MEM_ATTR_X, 0);
b9d01e
+
b9d01e
 #if defined (__i386__) || defined (__x86_64__)
b9d01e
   if (fsize >= (grub_ssize_t) sizeof (struct grub_macho_fat_header))
b9d01e
     {
b9d01e
diff --git a/grub-core/loader/efi/linux.c b/grub-core/loader/efi/linux.c
b9d01e
index 927d89a90d..421502bd25 100644
b9d01e
--- a/grub-core/loader/efi/linux.c
b9d01e
+++ b/grub-core/loader/efi/linux.c
b9d01e
@@ -66,16 +66,125 @@ grub_linuxefi_secure_validate (void *data, grub_uint32_t size)
b9d01e
 
b9d01e
 #pragma GCC diagnostic push
b9d01e
 #pragma GCC diagnostic ignored "-Wcast-align"
b9d01e
+#pragma GCC diagnostic ignored "-Wint-to-pointer-cast"
b9d01e
+
b9d01e
+grub_err_t
b9d01e
+grub_efi_check_nx_image_support (grub_addr_t kernel_addr,
b9d01e
+				 grub_size_t kernel_size,
b9d01e
+				 int *nx_supported)
b9d01e
+{
b9d01e
+  struct grub_dos_header *doshdr;
b9d01e
+  grub_size_t sz = sizeof (*doshdr);
b9d01e
+
b9d01e
+  struct grub_pe32_header_32 *pe32;
b9d01e
+  struct grub_pe32_header_64 *pe64;
b9d01e
+
b9d01e
+  int image_is_compatible = 0;
b9d01e
+  int is_64_bit;
b9d01e
+
b9d01e
+  if (kernel_size < sz)
b9d01e
+    return grub_error (GRUB_ERR_BAD_OS, N_("kernel is too small"));
b9d01e
+
b9d01e
+  doshdr = (void *)kernel_addr;
b9d01e
+
b9d01e
+  if ((doshdr->magic & 0xffff) != GRUB_DOS_MAGIC)
b9d01e
+    return grub_error (GRUB_ERR_BAD_OS, N_("kernel DOS magic is invalid"));
b9d01e
+
b9d01e
+  sz = doshdr->lfanew + sizeof (*pe32);
b9d01e
+  if (kernel_size < sz)
b9d01e
+    return grub_error (GRUB_ERR_BAD_OS, N_("kernel is too small"));
b9d01e
+
b9d01e
+  pe32 = (struct grub_pe32_header_32 *)(kernel_addr + doshdr->lfanew);
b9d01e
+  pe64 = (struct grub_pe32_header_64 *)pe32;
b9d01e
+
b9d01e
+  if (grub_memcmp (pe32->signature, GRUB_PE32_SIGNATURE,
b9d01e
+		   GRUB_PE32_SIGNATURE_SIZE) != 0)
b9d01e
+    return grub_error (GRUB_ERR_BAD_OS, N_("kernel PE magic is invalid"));
b9d01e
+
b9d01e
+  switch (pe32->coff_header.machine)
b9d01e
+    {
b9d01e
+    case GRUB_PE32_MACHINE_ARMTHUMB_MIXED:
b9d01e
+    case GRUB_PE32_MACHINE_I386:
b9d01e
+      is_64_bit = 0;
b9d01e
+      break;
b9d01e
+    case GRUB_PE32_MACHINE_ARM64:
b9d01e
+    case GRUB_PE32_MACHINE_IA64:
b9d01e
+    case GRUB_PE32_MACHINE_X86_64:
b9d01e
+      is_64_bit = 1;
b9d01e
+      break;
b9d01e
+    default:
b9d01e
+      return grub_error (GRUB_ERR_BAD_OS, N_("PE machine type 0x%04hx unknown"),
b9d01e
+			 pe32->coff_header.machine);
b9d01e
+    }
b9d01e
+
b9d01e
+  if (is_64_bit)
b9d01e
+    {
b9d01e
+      sz = doshdr->lfanew + sizeof (*pe64);
b9d01e
+      if (kernel_size < sz)
b9d01e
+	return grub_error (GRUB_ERR_BAD_OS, N_("kernel is too small"));
b9d01e
+
b9d01e
+      if (pe64->optional_header.dll_characteristics & GRUB_PE32_NX_COMPAT)
b9d01e
+	image_is_compatible = 1;
b9d01e
+    }
b9d01e
+  else
b9d01e
+    {
b9d01e
+      if (pe32->optional_header.dll_characteristics & GRUB_PE32_NX_COMPAT)
b9d01e
+	image_is_compatible = 1;
b9d01e
+    }
b9d01e
+
b9d01e
+  *nx_supported = image_is_compatible;
b9d01e
+  return GRUB_ERR_NONE;
b9d01e
+}
b9d01e
+
b9d01e
+grub_err_t
b9d01e
+grub_efi_check_nx_required (int *nx_required)
b9d01e
+{
b9d01e
+  grub_efi_status_t status;
b9d01e
+  grub_efi_guid_t guid = GRUB_EFI_SHIM_LOCK_GUID;
b9d01e
+  grub_size_t mok_policy_sz = 0;
b9d01e
+  char *mok_policy = NULL;
b9d01e
+  grub_uint32_t mok_policy_attrs = 0;
b9d01e
+
b9d01e
+  status = grub_efi_get_variable_with_attributes ("MokPolicy", &guid,
b9d01e
+						  &mok_policy_sz,
b9d01e
+						  (void **)&mok_policy,
b9d01e
+						  &mok_policy_attrs);
b9d01e
+  if (status == GRUB_EFI_NOT_FOUND ||
b9d01e
+      mok_policy_sz == 0 ||
b9d01e
+      mok_policy == NULL)
b9d01e
+    {
b9d01e
+      *nx_required = 0;
b9d01e
+      return GRUB_ERR_NONE;
b9d01e
+    }
b9d01e
+
b9d01e
+  *nx_required = 0;
b9d01e
+  if (mok_policy_sz < 1 ||
b9d01e
+      mok_policy_attrs != (GRUB_EFI_VARIABLE_BOOTSERVICE_ACCESS |
b9d01e
+			   GRUB_EFI_VARIABLE_RUNTIME_ACCESS) ||
b9d01e
+      (mok_policy[mok_policy_sz-1] & GRUB_MOK_POLICY_NX_REQUIRED))
b9d01e
+    *nx_required = 1;
b9d01e
+
b9d01e
+  return GRUB_ERR_NONE;
b9d01e
+}
b9d01e
 
b9d01e
 typedef void (*handover_func) (void *, grub_efi_system_table_t *, void *);
b9d01e
 
b9d01e
 grub_err_t
b9d01e
-grub_efi_linux_boot (void *kernel_addr, grub_off_t handover_offset,
b9d01e
-		     void *kernel_params)
b9d01e
+grub_efi_linux_boot (grub_addr_t kernel_addr, grub_size_t kernel_size,
b9d01e
+		     grub_off_t handover_offset, void *kernel_params,
b9d01e
+		     int nx_supported)
b9d01e
 {
b9d01e
   grub_efi_loaded_image_t *loaded_image = NULL;
b9d01e
   handover_func hf;
b9d01e
   int offset = 0;
b9d01e
+  grub_uint64_t stack_set_attrs = GRUB_MEM_ATTR_R |
b9d01e
+				  GRUB_MEM_ATTR_W |
b9d01e
+				  GRUB_MEM_ATTR_X;
b9d01e
+  grub_uint64_t stack_clear_attrs = 0;
b9d01e
+  grub_uint64_t kernel_set_attrs = stack_set_attrs;
b9d01e
+  grub_uint64_t kernel_clear_attrs = stack_clear_attrs;
b9d01e
+  grub_uint64_t attrs;
b9d01e
+  int nx_required = 0;
b9d01e
 
b9d01e
 #ifdef __x86_64__
b9d01e
   offset = 512;
b9d01e
@@ -88,12 +197,57 @@ grub_efi_linux_boot (void *kernel_addr, grub_off_t handover_offset,
b9d01e
    */
b9d01e
   loaded_image = grub_efi_get_loaded_image (grub_efi_image_handle);
b9d01e
   if (loaded_image)
b9d01e
-    loaded_image->image_base = kernel_addr;
b9d01e
+    loaded_image->image_base = (void *)kernel_addr;
b9d01e
   else
b9d01e
     grub_dprintf ("linux", "Loaded Image base address could not be set\n");
b9d01e
 
b9d01e
   grub_dprintf ("linux", "kernel_addr: %p handover_offset: %p params: %p\n",
b9d01e
-		kernel_addr, (void *)(grub_efi_uintn_t)handover_offset, kernel_params);
b9d01e
+		(void *)kernel_addr, (void *)handover_offset, kernel_params);
b9d01e
+
b9d01e
+
b9d01e
+  if (nx_required && !nx_supported)
b9d01e
+    return grub_error (GRUB_ERR_BAD_OS, N_("kernel does not support NX loading required by policy"));
b9d01e
+
b9d01e
+  if (nx_supported)
b9d01e
+    {
b9d01e
+      kernel_set_attrs &= ~GRUB_MEM_ATTR_W;
b9d01e
+      kernel_clear_attrs |= GRUB_MEM_ATTR_W;
b9d01e
+      stack_set_attrs &= ~GRUB_MEM_ATTR_X;
b9d01e
+      stack_clear_attrs |= GRUB_MEM_ATTR_X;
b9d01e
+    }
b9d01e
+
b9d01e
+  grub_dprintf ("nx", "Setting attributes for 0x%"PRIxGRUB_ADDR"-0x%"PRIxGRUB_ADDR" to r%cx\n",
b9d01e
+		    kernel_addr, kernel_addr + kernel_size - 1,
b9d01e
+		    (kernel_set_attrs & GRUB_MEM_ATTR_W) ? 'w' : '-');
b9d01e
+  grub_update_mem_attrs (kernel_addr, kernel_size,
b9d01e
+			 kernel_set_attrs, kernel_clear_attrs);
b9d01e
+
b9d01e
+  grub_get_mem_attrs (kernel_addr, 4096, &attrs);
b9d01e
+  grub_dprintf ("nx", "permissions for 0x%"PRIxGRUB_ADDR" are %s%s%s\n",
b9d01e
+		(grub_addr_t)kernel_addr,
b9d01e
+		(attrs & GRUB_MEM_ATTR_R) ? "r" : "-",
b9d01e
+		(attrs & GRUB_MEM_ATTR_W) ? "w" : "-",
b9d01e
+		(attrs & GRUB_MEM_ATTR_X) ? "x" : "-");
b9d01e
+  if (grub_stack_addr != (grub_addr_t)-1ll)
b9d01e
+    {
b9d01e
+      grub_dprintf ("nx", "Setting attributes for stack at 0x%"PRIxGRUB_ADDR"-0x%"PRIxGRUB_ADDR" to rw%c\n",
b9d01e
+		    grub_stack_addr, grub_stack_addr + grub_stack_size - 1,
b9d01e
+		    (stack_set_attrs & GRUB_MEM_ATTR_X) ? 'x' : '-');
b9d01e
+      grub_update_mem_attrs (grub_stack_addr, grub_stack_size,
b9d01e
+			     stack_set_attrs, stack_clear_attrs);
b9d01e
+
b9d01e
+      grub_get_mem_attrs (grub_stack_addr, 4096, &attrs);
b9d01e
+      grub_dprintf ("nx", "permissions for 0x%"PRIxGRUB_ADDR" are %s%s%s\n",
b9d01e
+		    grub_stack_addr,
b9d01e
+		    (attrs & GRUB_MEM_ATTR_R) ? "r" : "-",
b9d01e
+		    (attrs & GRUB_MEM_ATTR_W) ? "w" : "-",
b9d01e
+		    (attrs & GRUB_MEM_ATTR_X) ? "x" : "-");
b9d01e
+    }
b9d01e
+
b9d01e
+#if defined(__i386__) || defined(__x86_64__)
b9d01e
+  asm volatile ("cli");
b9d01e
+#endif
b9d01e
+
b9d01e
   hf = (handover_func)((char *)kernel_addr + handover_offset + offset);
b9d01e
   grub_dprintf ("linux", "handover_func() = %p\n", hf);
b9d01e
   hf (grub_efi_image_handle, grub_efi_system_table, kernel_params);
b9d01e
diff --git a/grub-core/loader/i386/efi/linux.c b/grub-core/loader/i386/efi/linux.c
b9d01e
index 3d4069e4c6..d80d6ec312 100644
b9d01e
--- a/grub-core/loader/i386/efi/linux.c
b9d01e
+++ b/grub-core/loader/i386/efi/linux.c
b9d01e
@@ -44,7 +44,7 @@ struct grub_linuxefi_context {
b9d01e
   grub_uint32_t handover_offset;
b9d01e
   struct linux_kernel_params *params;
b9d01e
   char *cmdline;
b9d01e
-
b9d01e
+  int nx_supported;
b9d01e
   void *initrd_mem;
b9d01e
 };
b9d01e
 
b9d01e
@@ -110,13 +110,19 @@ kernel_alloc(grub_efi_uintn_t size,
b9d01e
       pages = BYTES_TO_PAGES(size);
b9d01e
       grub_dprintf ("linux", "Trying to allocate %lu pages from %p\n",
b9d01e
 		    pages, (void *)max);
b9d01e
+      size = pages * GRUB_EFI_PAGE_SIZE;
b9d01e
 
b9d01e
       prev_max = max;
b9d01e
       addr = grub_efi_allocate_pages_real (max, pages,
b9d01e
 					   max_addresses[i].alloc_type,
b9d01e
 					   memtype);
b9d01e
       if (addr)
b9d01e
-	grub_dprintf ("linux", "Allocated at %p\n", addr);
b9d01e
+	{
b9d01e
+	  grub_dprintf ("linux", "Allocated at %p\n", addr);
b9d01e
+	  grub_update_mem_attrs ((grub_addr_t)addr, size,
b9d01e
+				 GRUB_MEM_ATTR_R|GRUB_MEM_ATTR_W,
b9d01e
+				 GRUB_MEM_ATTR_X);
b9d01e
+	}
b9d01e
     }
b9d01e
 
b9d01e
   while (grub_error_pop ())
b9d01e
@@ -137,9 +143,11 @@ grub_linuxefi_boot (void *data)
b9d01e
 
b9d01e
   asm volatile ("cli");
b9d01e
 
b9d01e
-  return grub_efi_linux_boot ((char *)context->kernel_mem,
b9d01e
+  return grub_efi_linux_boot ((grub_addr_t)context->kernel_mem,
b9d01e
+			      context->kernel_size,
b9d01e
 			      context->handover_offset,
b9d01e
-			      context->params);
b9d01e
+			      context->params,
b9d01e
+			      context->nx_supported);
b9d01e
 }
b9d01e
 
b9d01e
 static grub_err_t
b9d01e
@@ -308,7 +316,9 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
b9d01e
   grub_uint32_t handover_offset;
b9d01e
   struct linux_kernel_params *params = 0;
b9d01e
   char *cmdline = 0;
b9d01e
+  int nx_supported = 1;
b9d01e
   struct grub_linuxefi_context *context = 0;
b9d01e
+  grub_err_t err;
b9d01e
 
b9d01e
   grub_dl_ref (my_mod);
b9d01e
 
b9d01e
@@ -352,6 +362,13 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
b9d01e
 	}
b9d01e
     }
b9d01e
 
b9d01e
+  err = grub_efi_check_nx_image_support ((grub_addr_t)kernel, filelen,
b9d01e
+					 &nx_supported);
b9d01e
+  if (err != GRUB_ERR_NONE)
b9d01e
+    return err;
b9d01e
+  grub_dprintf ("linux", "nx is%s supported by this kernel\n",
b9d01e
+		nx_supported ? "" : " not");
b9d01e
+
b9d01e
   lh = (struct linux_i386_kernel_header *)kernel;
b9d01e
   grub_dprintf ("linux", "original lh is at %p\n", kernel);
b9d01e
 
b9d01e
@@ -515,6 +532,7 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
b9d01e
   context->handover_offset = handover_offset;
b9d01e
   context->params = params;
b9d01e
   context->cmdline = cmdline;
b9d01e
+  context->nx_supported = nx_supported;
b9d01e
 
b9d01e
   grub_loader_set_ex (grub_linuxefi_boot, grub_linuxefi_unload, context, 0);
b9d01e
 
b9d01e
diff --git a/grub-core/loader/i386/linux.c b/grub-core/loader/i386/linux.c
b9d01e
index ef8fcb9e1b..c160ddb0ea 100644
b9d01e
--- a/grub-core/loader/i386/linux.c
b9d01e
+++ b/grub-core/loader/i386/linux.c
b9d01e
@@ -831,6 +831,11 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
b9d01e
   grub_memset (&linux_params, 0, sizeof (linux_params));
b9d01e
   grub_memcpy (&linux_params.setup_sects, &lh.setup_sects, sizeof (lh) - 0x1F1);
b9d01e
 
b9d01e
+  grub_dprintf("efi", "setting attributes for %p (%zu bytes) to +rw-x\n",
b9d01e
+	       &linux_params, sizeof (lh) + len);
b9d01e
+  grub_update_mem_attrs ((grub_addr_t)&linux_params, sizeof (lh) + len,
b9d01e
+			 GRUB_MEM_ATTR_R|GRUB_MEM_ATTR_W, GRUB_MEM_ATTR_X);
b9d01e
+
b9d01e
   linux_params.code32_start = prot_mode_target + lh.code32_start - GRUB_LINUX_BZIMAGE_ADDR;
b9d01e
   linux_params.kernel_alignment = (1 << align);
b9d01e
   linux_params.ps_mouse = linux_params.padding10 =  0;
b9d01e
diff --git a/include/grub/efi/efi.h b/include/grub/efi/efi.h
b9d01e
index a635bcb0a9..8ca8c38f9a 100644
b9d01e
--- a/include/grub/efi/efi.h
b9d01e
+++ b/include/grub/efi/efi.h
b9d01e
@@ -135,12 +135,16 @@ extern void (*EXPORT_VAR(grub_efi_net_config)) (grub_efi_handle_t hnd,
b9d01e
 						char **device,
b9d01e
 						char **path);
b9d01e
 
b9d01e
+extern grub_addr_t EXPORT_VAR(grub_stack_addr);
b9d01e
+extern grub_size_t EXPORT_VAR(grub_stack_size);
b9d01e
+
b9d01e
 #if defined(__arm__) || defined(__aarch64__)
b9d01e
 void *EXPORT_FUNC(grub_efi_get_firmware_fdt)(void);
b9d01e
 grub_err_t EXPORT_FUNC(grub_efi_get_ram_base)(grub_addr_t *);
b9d01e
 #include <grub/cpu/linux.h>
b9d01e
 grub_err_t grub_armxx_efi_linux_check_image(struct linux_armxx_kernel_header *lh);
b9d01e
-grub_err_t grub_armxx_efi_linux_boot_image(grub_addr_t addr, char *args);
b9d01e
+grub_err_t grub_armxx_efi_linux_boot_image(grub_addr_t addr, grub_size_t size,
b9d01e
+					  char *args, int nx_enabled);
b9d01e
 #endif
b9d01e
 
b9d01e
 grub_addr_t grub_efi_section_addr (const char *section);
b9d01e
diff --git a/include/grub/efi/linux.h b/include/grub/efi/linux.h
b9d01e
index 0033d9305a..8130b19590 100644
b9d01e
--- a/include/grub/efi/linux.h
b9d01e
+++ b/include/grub/efi/linux.h
b9d01e
@@ -22,10 +22,23 @@
b9d01e
 #include <grub/err.h>
b9d01e
 #include <grub/symbol.h>
b9d01e
 
b9d01e
+#define GRUB_MOK_POLICY_NX_REQUIRED   0x1
b9d01e
+
b9d01e
 int
b9d01e
 EXPORT_FUNC(grub_linuxefi_secure_validate) (void *data, grub_uint32_t size);
b9d01e
+
b9d01e
 grub_err_t
b9d01e
-EXPORT_FUNC(grub_efi_linux_boot) (void *kernel_address, grub_off_t offset,
b9d01e
-				  void *kernel_param);
b9d01e
+EXPORT_FUNC(grub_efi_linux_boot) (grub_addr_t kernel_address,
b9d01e
+				  grub_size_t kernel_size,
b9d01e
+				  grub_off_t handover_offset,
b9d01e
+				  void *kernel_param, int nx_enabled);
b9d01e
+
b9d01e
+grub_err_t
b9d01e
+EXPORT_FUNC(grub_efi_check_nx_image_support) (grub_addr_t kernel_addr,
b9d01e
+					      grub_size_t kernel_size,
b9d01e
+					      int *nx_supported);
b9d01e
+
b9d01e
+grub_err_t
b9d01e
+EXPORT_FUNC(grub_efi_check_nx_required) (int *nx_required);
b9d01e
 
b9d01e
 #endif /* ! GRUB_EFI_LINUX_HEADER */
b9d01e
diff --git a/include/grub/efi/pe32.h b/include/grub/efi/pe32.h
b9d01e
index 2241f6317b..45c9f8b756 100644
b9d01e
--- a/include/grub/efi/pe32.h
b9d01e
+++ b/include/grub/efi/pe32.h
b9d01e
@@ -172,6 +172,8 @@ struct grub_pe32_optional_header
b9d01e
   struct grub_pe32_data_directory reserved_entry;
b9d01e
 };
b9d01e
 
b9d01e
+#define GRUB_PE32_NX_COMPAT 0x0100
b9d01e
+
b9d01e
 struct grub_pe64_optional_header
b9d01e
 {
b9d01e
   grub_uint16_t magic;