From: Vladimir Serbinenko <phco...@gmail.com>

This is a replacement workaround for EFIs that do not map memory above 4G
and allows to increase maximum available address to 128TiB
---
 grub-core/Makefile.core.def    |  1 +
 grub-core/kern/efi/mm.c        | 10 ++++
 grub-core/kern/x86_64/efi/mm.c | 84 ++++++++++++++++++++++++++++++++++
 include/grub/efi/memory.h      |  5 ++
 4 files changed, 100 insertions(+)
 create mode 100644 grub-core/kern/x86_64/efi/mm.c

diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def
index 7f8cb3f7d..57e76dda5 100644
--- a/grub-core/Makefile.core.def
+++ b/grub-core/Makefile.core.def
@@ -219,6 +219,7 @@ kernel = {
   efi = kern/efi/acpi.c;
   efi = kern/efi/sb.c;
   efi = kern/lockdown.c;
+  x86_64_efi = kern/x86_64/efi/mm.c;
   i386_coreboot = kern/i386/pc/acpi.c;
   i386_multiboot = kern/i386/pc/acpi.c;
   i386_coreboot = kern/acpi.c;
diff --git a/grub-core/kern/efi/mm.c b/grub-core/kern/efi/mm.c
index df238b165..60ac7ed00 100644
--- a/grub-core/kern/efi/mm.c
+++ b/grub-core/kern/efi/mm.c
@@ -160,6 +160,16 @@ grub_efi_allocate_pages_real (grub_efi_physical_address_t 
address,
        }
     }
 
+#ifdef __x86_64__
+  status = grub_efi_arch_ensure_mapping (address, pages);
+  if (status != GRUB_EFI_SUCCESS)
+    {
+      b->free_pages (address, pages);
+      grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("out of memory"));
+      return NULL;
+    }
+#endif
+
   grub_efi_store_alloc (address, pages);
 
   return (void *) ((grub_addr_t) address);
diff --git a/grub-core/kern/x86_64/efi/mm.c b/grub-core/kern/x86_64/efi/mm.c
new file mode 100644
index 000000000..01e2b9ac4
--- /dev/null
+++ b/grub-core/kern/x86_64/efi/mm.c
@@ -0,0 +1,84 @@
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2025  Free Software Foundation, Inc.
+ *
+ *  GRUB is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  GRUB is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/types.h>
+#include <grub/misc.h>
+#include <grub/mm.h>
+#include <grub/err.h>
+#include <grub/dl.h>
+#include <grub/cache.h>
+#include <grub/kernel.h>
+#include <grub/efi/efi.h>
+
+static grub_efi_status_t
+create_paging_entry(grub_uint64_t *entry)
+{
+  grub_efi_status_t status;
+  grub_efi_boot_services_t *b;
+
+  grub_uint64_t address = 0xffffffff;
+
+  b = grub_efi_system_table->boot_services;
+  /* TODO: Which type should it be?  */
+  status = b->allocate_pages (GRUB_EFI_ALLOCATE_MAX_ADDRESS, 
GRUB_EFI_LOADER_DATA, 1, &address);
+  if (status != GRUB_EFI_SUCCESS)
+    return status;
+  *entry = address | 7;
+  return GRUB_EFI_SUCCESS;
+}
+
+grub_efi_status_t
+grub_efi_arch_ensure_mapping (grub_efi_physical_address_t address,
+                             grub_efi_uintn_t pages)
+{
+  grub_uint64_t cr3;
+  asm volatile("movq   %%cr3, %0\n" : "=r"(cr3));
+  for (grub_uint64_t page = 0; page < pages; page++)
+    {
+      grub_uint64_t pageidx = (address >> 12) + page;
+      grub_uint64_t *pt4 = (grub_uint64_t *) cr3;
+      if (!(pt4[pageidx >> 27] & 1)) {
+       grub_efi_status_t status = create_paging_entry(&pt4[pageidx >> 27]);
+       if (status != GRUB_EFI_SUCCESS)
+         return status;
+      }
+      grub_uint64_t *pt3 = (grub_uint64_t *) (pt4[pageidx >> 27] & ~0xfff);
+      if (!(pt3[pageidx >> 18] & 1)) {
+       grub_efi_status_t status = create_paging_entry(&pt3[pageidx >> 18]);
+       if (status != GRUB_EFI_SUCCESS)
+         return status;
+      }
+      if (pt3[pageidx >> 18] & 0x80)
+       continue;
+      grub_uint64_t *pt2 = (grub_uint64_t *) (pt3[pageidx >> 18] & ~0xfff);
+      if (!(pt2[pageidx >> 9] & 1)) {
+       grub_efi_status_t status = create_paging_entry(&pt3[pageidx >> 9]);
+       if (status != GRUB_EFI_SUCCESS)
+         return status;
+      }
+      if (pt2[pageidx >> 9] & 0x80)
+       continue;
+
+      grub_uint64_t *pt1 = (grub_uint64_t *) (pt2[pageidx >> 9] & ~0xfff);
+      if (!(pt1[pageidx] & 1)) {
+       pt1[pageidx] = (pageidx << 12) | 7;
+      }
+    }
+
+  return GRUB_EFI_SUCCESS;
+}
diff --git a/include/grub/efi/memory.h b/include/grub/efi/memory.h
index 08fe62277..2e5e4f84a 100644
--- a/include/grub/efi/memory.h
+++ b/include/grub/efi/memory.h
@@ -21,6 +21,7 @@
 
 #include <grub/err.h>
 #include <grub/types.h>
+#include <grub/efi/api.h>
 
 /* The term "page" in UEFI refers only to a 4 KiB-aligned 4 KiB size region of
    memory. It is not concerned with underlying translation management concepts,
@@ -35,4 +36,8 @@ grub_err_t grub_machine_mmap_register (grub_uint64_t start, 
grub_uint64_t size,
                                       int type, int handle);
 grub_err_t grub_machine_mmap_unregister (int handle);
 
+grub_efi_status_t
+grub_efi_arch_ensure_mapping (grub_efi_physical_address_t address,
+                             grub_efi_uintn_t pages);
+
 #endif /* ! GRUB_MEMORY_MACHINE_HEADER */
-- 
2.49.0


_______________________________________________
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel

Reply via email to