From 28dcf484821b95d7de85de33dbd41ee7682e5778 Mon Sep 17 00:00:00 2001
From: Xue Liu <liuxue@loongson.cn>
Date: Fri, 24 May 2024 14:16:02 +0800
Subject: [PATCH] LoongArch: Add back-compatibility for linux kernel.

---
 configure.ac                              |   4 +-
 grub-core/Makefile.core.def               |   7 +-
 grub-core/kern/efi/mm.c                   |  33 +-
 grub-core/lib/loongarch64/relocator.c     | 163 ++++
 grub-core/lib/loongarch64/relocator_asm.S |  51 ++
 grub-core/loader/loongarch64/linux-efi.c  | 102 +++
 grub-core/loader/loongarch64/linux-elf.c  | 896 ++++++++++++++++++++++
 grub-core/loader/loongarch64/linux.c      | 437 +++++++++++
 include/grub/loongarch64/efi/memory.h     |   9 +-
 include/grub/loongarch64/linux.h          | 215 ++++++
 include/grub/loongarch64/memory.h         |  59 ++
 include/grub/loongarch64/relocator.h      |  38 +
 12 files changed, 2008 insertions(+), 6 deletions(-)
 create mode 100644 grub-core/lib/loongarch64/relocator.c
 create mode 100644 grub-core/lib/loongarch64/relocator_asm.S
 create mode 100644 grub-core/loader/loongarch64/linux-efi.c
 create mode 100644 grub-core/loader/loongarch64/linux-elf.c
 create mode 100644 grub-core/loader/loongarch64/linux.c
 create mode 100644 include/grub/loongarch64/linux.h
 create mode 100644 include/grub/loongarch64/memory.h
 create mode 100644 include/grub/loongarch64/relocator.h

diff --git a/configure.ac b/configure.ac
index 71a2424a140d..96f4f5e9e554 100644
--- a/configure.ac
+++ b/configure.ac
@@ -146,7 +146,9 @@ case "$target_cpu" in
 		;;
   arm*)		target_cpu=arm ;;
   aarch64*)	target_cpu=arm64 ;;
-  loongarch64)	target_cpu=loongarch64 ;;
+  loongarch64)	target_cpu=loongarch64
+		machine_CPPFLAGS="$machine_CPPFLAGS -DGRUB_CPU_LOONGARCH64=1"
+		;;
   riscv32*)	target_cpu=riscv32 ;;
   riscv64*)	target_cpu=riscv64 ;;
 esac
diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def
index cc33ca57af8b..7f025e5cfcd6 100644
--- a/grub-core/Makefile.core.def
+++ b/grub-core/Makefile.core.def
@@ -1730,6 +1730,8 @@ module = {
   x86_64_xen = lib/x86_64/xen/relocator.S;
   xen = lib/i386/relocator_common_c.c;
   x86_64_efi = lib/x86_64/efi/relocator.c;
+  loongarch64 = lib/loongarch64/relocator_asm.S;
+  loongarch64 = lib/loongarch64/relocator.c;
 
   extra_dist = lib/i386/relocator_common.S;
   extra_dist = kern/powerpc/cache_flush.S;
@@ -1739,6 +1741,7 @@ module = {
   enable = x86;
   enable = i386_xen_pvh;
   enable = xen;
+  enable = loongarch64;
 };
 
 module = {
@@ -1875,7 +1878,9 @@ module = {
   arm_efi = loader/efi/linux.c;
   arm_uboot = loader/arm/linux.c;
   arm64 = loader/arm64/efi/linux.c;
-  loongarch64 = loader/efi/linux.c;
+  loongarch64 = loader/loongarch64/linux.c;
+  loongarch64 = loader/loongarch64/linux-efi.c;
+  loongarch64 = loader/loongarch64/linux-elf.c;
   riscv32 = loader/efi/linux.c;
   riscv64 = loader/efi/linux.c;
   emu = loader/emu/linux.c;
diff --git a/grub-core/kern/efi/mm.c b/grub-core/kern/efi/mm.c
index 9b1d3add715b..31bffb534414 100644
--- a/grub-core/kern/efi/mm.c
+++ b/grub-core/kern/efi/mm.c
@@ -39,7 +39,11 @@
 #define MEMORY_MAP_SIZE	0x3000
 
 /* The default heap size for GRUB itself in bytes.  */
+#ifdef GRUB_CPU_LOONGARCH64
+#define DEFAULT_HEAP_SIZE	0x10000000
+#else
 #define DEFAULT_HEAP_SIZE	0x2000000
+#endif
 
 static void *finish_mmap_buf = 0;
 static grub_efi_uintn_t finish_mmap_size = 0;
@@ -156,7 +160,11 @@ grub_efi_allocate_pages_real (grub_efi_physical_address_t address,
   grub_efi_physical_address_t ret = address;
 
   /* Limit the memory access to less than 4GB for 32-bit platforms.  */
+#ifdef GRUB_CPU_LOONGARCH64
+  if (address > grub_efi_max_usable_address())
+#else
   if (address > GRUB_EFI_MAX_USABLE_ADDRESS)
+#endif
     {
       char inv_addr[17], max_addr[17]; /* log16(2^64) = 16, plus NUL. */
 
@@ -184,7 +192,11 @@ grub_efi_allocate_pages_real (grub_efi_physical_address_t address,
     {
       /* Uggh, the address 0 was allocated... This is too annoying,
 	 so reallocate another one.  */
+#ifdef GRUB_CPU_LOONGARCH64
+      ret = grub_efi_max_usable_address();
+#else
       ret = address;
+#endif
       status = b->allocate_pages (alloctype, memtype, pages, &ret);
       grub_efi_free_pages (0, pages);
       if (status != GRUB_EFI_SUCCESS)
@@ -202,9 +214,15 @@ grub_efi_allocate_pages_real (grub_efi_physical_address_t address,
 void *
 grub_efi_allocate_any_pages (grub_efi_uintn_t pages)
 {
+#ifdef GRUB_CPU_LOONGARCH64
+  return grub_efi_allocate_pages_real (grub_efi_max_usable_address(),
+				       pages, GRUB_EFI_ALLOCATE_MAX_ADDRESS,
+				       GRUB_EFI_LOADER_DATA);
+#else
   return grub_efi_allocate_pages_real (GRUB_EFI_MAX_USABLE_ADDRESS,
 				       pages, GRUB_EFI_ALLOCATE_MAX_ADDRESS,
 				       GRUB_EFI_LOADER_DATA);
+#endif
 }
 
 void *
@@ -480,8 +498,10 @@ filter_memory_map (grub_efi_memory_descriptor_t *memory_map,
        desc = NEXT_MEMORY_DESCRIPTOR (desc, desc_size))
     {
       if (desc->type == GRUB_EFI_CONVENTIONAL_MEMORY
-#if 1
-	  && desc->physical_start <= GRUB_EFI_MAX_ALLOCATION_ADDRESS
+#ifdef GRUB_CPU_LOONGARCH64
+          && desc->physical_start <= grub_efi_max_usable_address()
+#else
+	  && desc->physical_start <= GRUB_EFI_MAX_USABLE_ADDRESS
 #endif
 	  && desc->physical_start + PAGES_TO_BYTES (desc->num_pages) > 0x100000
 	  && desc->num_pages != 0)
@@ -496,7 +516,14 @@ filter_memory_map (grub_efi_memory_descriptor_t *memory_map,
 	      desc->physical_start = 0x100000;
 	    }
 
-#if 1
+#ifdef GRUB_CPU_LOONGARCH64
+	  if (BYTES_TO_PAGES (filtered_desc->physical_start)
+	      + filtered_desc->num_pages
+	      > BYTES_TO_PAGES_DOWN (grub_efi_max_usable_address()))
+	    filtered_desc->num_pages
+	      = (BYTES_TO_PAGES_DOWN (grub_efi_max_usable_address())
+		 - BYTES_TO_PAGES (filtered_desc->physical_start));
+#else
 	  if (BYTES_TO_PAGES (filtered_desc->physical_start)
 	      + filtered_desc->num_pages
 	      > BYTES_TO_PAGES_DOWN (GRUB_EFI_MAX_ALLOCATION_ADDRESS))
diff --git a/grub-core/lib/loongarch64/relocator.c b/grub-core/lib/loongarch64/relocator.c
new file mode 100644
index 000000000000..587fc585ab7a
--- /dev/null
+++ b/grub-core/lib/loongarch64/relocator.c
@@ -0,0 +1,163 @@
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2022  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/mm.h>
+#include <grub/misc.h>
+
+#include <grub/types.h>
+#include <grub/types.h>
+#include <grub/err.h>
+#include <grub/cache.h>
+
+#include <grub/loongarch64/relocator.h>
+#include <grub/relocator_private.h>
+
+extern grub_uint8_t grub_relocator_forward_start;
+extern grub_uint8_t grub_relocator_forward_end;
+extern grub_uint8_t grub_relocator_backward_start;
+extern grub_uint8_t grub_relocator_backward_end;
+
+#define REGW_SIZEOF (4 * sizeof (grub_uint32_t))
+#define JUMP_SIZEOF (2 * sizeof (grub_uint32_t))
+
+#define RELOCATOR_SRC_SIZEOF(x) (&grub_relocator_##x##_end \
+				 - &grub_relocator_##x##_start)
+#define RELOCATOR_SIZEOF(x)	(RELOCATOR_SRC_SIZEOF(x) \
+				 + REGW_SIZEOF * 3)
+grub_size_t grub_relocator_align = sizeof (grub_uint64_t);
+grub_size_t grub_relocator_forward_size;
+grub_size_t grub_relocator_backward_size;
+grub_size_t grub_relocator_jumper_size = JUMP_SIZEOF + REGW_SIZEOF;
+
+void
+grub_cpu_relocator_init (void)
+{
+  grub_relocator_forward_size = RELOCATOR_SIZEOF(forward);
+  grub_relocator_backward_size = RELOCATOR_SIZEOF(backward);
+}
+
+static void
+write_reg (int regn, grub_uint64_t val, void **target)
+{
+  grub_uint32_t lu12iw=0x14000000;
+  grub_uint32_t ori=0x03800000;
+  grub_uint32_t lu32id=0x16000000;
+  grub_uint32_t lu52id=0x03000000;
+
+  *(grub_uint32_t *) *target = (lu12iw | (grub_uint32_t)((val & 0xfffff000)>>12<<5) | (grub_uint32_t)regn);;
+  *target = ((grub_uint32_t *) *target) + 1;
+  *(grub_uint32_t *) *target = (ori | (grub_uint32_t)((val & 0xfff)<<10) | (grub_uint32_t)(regn | regn<<5));
+  *target = ((grub_uint32_t *) *target) + 1;
+  *(grub_uint32_t *) *target = (lu32id | (grub_uint32_t)((val & 0xfffff00000000)>>32<<5) | (grub_uint32_t)regn);;
+  *target = ((grub_uint32_t *) *target) + 1;
+  *(grub_uint32_t *) *target = (lu52id | (grub_uint32_t)((val & 0xfff0000000000000)>>52<<10) | (grub_uint32_t)(regn | regn<<5));;
+  *target = ((grub_uint32_t *) *target) + 1;
+}
+
+static void
+write_jump (int regn, void **target)
+{
+  grub_uint32_t jirl=0x4c000000;
+
+  *(grub_uint32_t *) *target = (jirl | (grub_uint32_t)(regn<<5));
+  *target = ((grub_uint32_t *) *target) + 1;
+}
+
+void
+grub_cpu_relocator_jumper (void *rels, grub_addr_t addr)
+{
+  write_reg (1, addr, &rels);
+  write_jump (1, &rels);
+}
+
+void
+grub_cpu_relocator_backward (void *ptr0, void *src, void *dest,
+			     grub_size_t size)
+{
+  void *ptr = ptr0;
+  write_reg (8, (grub_uint64_t) src, &ptr);
+  write_reg (9, (grub_uint64_t) dest, &ptr);
+  write_reg (10, (grub_uint64_t) size, &ptr);
+  grub_memcpy (ptr, &grub_relocator_backward_start,
+	       RELOCATOR_SRC_SIZEOF (backward));
+}
+
+void
+grub_cpu_relocator_forward (void *ptr0, void *src, void *dest,
+			     grub_size_t size)
+{
+  void *ptr = ptr0;
+  write_reg (8, (grub_uint64_t) src, &ptr);
+  write_reg (9, (grub_uint64_t) dest, &ptr);
+  write_reg (10, (grub_uint64_t) size, &ptr);
+  grub_memcpy (ptr, &grub_relocator_forward_start,
+	       RELOCATOR_SRC_SIZEOF (forward));
+}
+
+grub_err_t
+grub_relocator64_boot (struct grub_relocator *rel,
+		       struct grub_relocator64_state state)
+{
+  grub_relocator_chunk_t ch;
+  void *ptr;
+  grub_err_t err;
+  void *relst;
+  grub_size_t relsize;
+  grub_size_t stateset_size = 31 * REGW_SIZEOF + JUMP_SIZEOF;
+  unsigned i;
+  grub_addr_t vtarget;
+
+  err = grub_relocator_alloc_chunk_align (rel, &ch, 0x3000000,
+					  (0xffffffff - stateset_size)
+					  + 1, stateset_size,
+					  grub_relocator_align,
+					  GRUB_RELOCATOR_PREFERENCE_NONE, 0);
+  if (err)
+    return err;
+
+  ptr = get_virtual_current_address (ch);
+  for (i = 1; i < 32; i++)
+    write_reg (i, state.gpr[i], &ptr);
+  write_jump (state.jumpreg, &ptr);
+
+  vtarget = (grub_addr_t) grub_map_memory (get_physical_target_address (ch),
+					   stateset_size);
+
+  err = grub_relocator_prepare_relocs (rel, vtarget, &relst, &relsize);
+  if (err)
+    return err;
+
+  grub_arch_sync_caches ((void *) relst, relsize);
+
+  asm volatile (
+                 "ibar 0 \n");
+
+  grub_uint64_t val;
+  __asm__ __volatile__(
+		  "li.w    %0, 0x4\n\t"
+		  "csrxchg $r0, %0, 0x0\n\t"
+		  : "=r"(val)
+		  :
+		  :
+		  );
+
+  ((void (*) (void)) relst) ();
+
+  /* Not reached.  */
+  return GRUB_ERR_NONE;
+}
diff --git a/grub-core/lib/loongarch64/relocator_asm.S b/grub-core/lib/loongarch64/relocator_asm.S
new file mode 100644
index 000000000000..ffdccc903e5f
--- /dev/null
+++ b/grub-core/lib/loongarch64/relocator_asm.S
@@ -0,0 +1,51 @@
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2022  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/symbol.h>
+
+	.p2align	4	/* force 16-byte alignment */
+
+VARIABLE (grub_relocator_forward_start)
+
+copycont1:
+	ld.d $r11,$r8,0
+	st.d $r11,$r9,0
+	addi.d $r8, $r8, 8
+	addi.d $r10, $r10, -8
+	addi.d $r9, $r9, 8
+	bne $r10, $r0, copycont1
+
+VARIABLE (grub_relocator_forward_end)
+
+VARIABLE (grub_relocator_backward_start)
+
+	add.d $r9, $r9, $r10
+	add.d $r8, $r8, $r10
+	/* Backward movsl is implicitly off-by-one.  compensate that.  */
+	addi.d $r9, $r9, -8
+	addi.d $r8, $r8, -8
+copycont2:
+	ld.w $r11,$r8,0
+	st.w $r11,$r9,0
+	addi.d $r8, $r8, -8
+	addi.d $r10, $r10, -8
+	addi.d $r9, $r9, -8
+	bne $r10, $r0, copycont2
+
+VARIABLE (grub_relocator_backward_end)
+
diff --git a/grub-core/loader/loongarch64/linux-efi.c b/grub-core/loader/loongarch64/linux-efi.c
new file mode 100644
index 000000000000..8e2726163725
--- /dev/null
+++ b/grub-core/loader/loongarch64/linux-efi.c
@@ -0,0 +1,102 @@
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2021 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/linux.h>
+#include <grub/efi/efi.h>
+#include <grub/cpu/linux.h>
+#include <grub/efi/memory.h>
+#include <grub/charset.h>
+
+#define GRUB_EFI_PE_MAGIC	0x5A4D
+
+grub_err_t
+finalize_efi_params_linux (struct linux_loongarch64_kernel_params *kernel_params)
+{
+  return grub_loongarch_setup_initrd_params();
+}
+
+grub_err_t
+grub_arch_efi_linux_check_image (struct linux_arch_kernel_header * lh)
+{
+  if ((lh->code0 & 0xffff) == GRUB_EFI_PE_MAGIC)
+	return GRUB_ERR_NONE;
+  else
+	return 1;
+
+  grub_dprintf ("linux", "UEFI stub kernel:\n");
+  grub_dprintf ("linux", "PE/COFF header @ %08x\n", lh->hdr_offset);
+
+  return GRUB_ERR_NONE;
+}
+
+grub_err_t
+grub_arch_efi_linux_boot_image (grub_addr_t addr, grub_size_t size, char *args)
+{
+  grub_efi_memory_mapped_device_path_t *mempath;
+  grub_efi_handle_t image_handle;
+  grub_efi_boot_services_t *b;
+  grub_efi_status_t status;
+  grub_efi_loaded_image_t *loaded_image;
+  int len;
+
+  mempath = grub_malloc (2 * sizeof (grub_efi_memory_mapped_device_path_t));
+  if (!mempath)
+    return grub_errno;
+
+  mempath[0].header.type = GRUB_EFI_HARDWARE_DEVICE_PATH_TYPE;
+  mempath[0].header.subtype = GRUB_EFI_MEMORY_MAPPED_DEVICE_PATH_SUBTYPE;
+  mempath[0].header.length = grub_cpu_to_le16_compile_time (sizeof (*mempath));
+  mempath[0].memory_type = GRUB_EFI_LOADER_DATA;
+  mempath[0].start_address = addr;
+  mempath[0].end_address = addr + size;
+
+  mempath[1].header.type = GRUB_EFI_END_DEVICE_PATH_TYPE;
+  mempath[1].header.subtype = GRUB_EFI_END_ENTIRE_DEVICE_PATH_SUBTYPE;
+  mempath[1].header.length = sizeof (grub_efi_device_path_t);
+
+  b = grub_efi_system_table->boot_services;
+  status = b->load_image (0, grub_efi_image_handle,
+			  (grub_efi_device_path_t *) mempath,
+			  (void *) addr, size, &image_handle);
+  if (status != GRUB_EFI_SUCCESS)
+    return grub_error (GRUB_ERR_BAD_OS, "cannot load image");
+
+  grub_dprintf ("linux", "linux command line: '%s'\n", args);
+
+  /* Convert command line to UCS-2 */
+  loaded_image = grub_efi_get_loaded_image (image_handle);
+  loaded_image->load_options_size = len =
+    (grub_strlen (args) + 1) * sizeof (grub_efi_char16_t);
+  loaded_image->load_options =
+    grub_efi_allocate_any_pages (GRUB_EFI_BYTES_TO_PAGES (loaded_image->load_options_size));
+  if (!loaded_image->load_options)
+    return grub_errno;
+
+  loaded_image->load_options_size =
+    2 * grub_utf8_to_utf16 (loaded_image->load_options, len,
+			    (grub_uint8_t *) args, len, NULL);
+
+  grub_dprintf ("linux", "starting image %p\n", image_handle);
+  status = b->start_image (image_handle, 0, NULL);
+
+  /* When successful, not reached */
+  b->unload_image (image_handle);
+  grub_efi_free_pages ((grub_addr_t) loaded_image->load_options,
+		       GRUB_EFI_BYTES_TO_PAGES (loaded_image->load_options_size));
+
+  return grub_errno;
+}
diff --git a/grub-core/loader/loongarch64/linux-elf.c b/grub-core/loader/loongarch64/linux-elf.c
new file mode 100644
index 000000000000..c5d943437ed8
--- /dev/null
+++ b/grub-core/loader/loongarch64/linux-elf.c
@@ -0,0 +1,896 @@
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2021 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/cpu/linux.h>
+#include <grub/linux.h>
+#include <grub/fdt.h>
+#include <grub/efi/efi.h>
+#include <grub/elfload.h>
+#include <grub/cpu/relocator.h>
+#include <grub/efi/memory.h>
+#include <grub/efi/graphics_output.h>
+
+#define GRUB_EFI_MMAP_NR_SLACK_SLOTS 8
+
+#define GRUB_ADDRESS_TYPE_SYSRAM	1
+#define GRUB_ADDRESS_TYPE_RESERVED	2
+#define GRUB_ADDRESS_TYPE_ACPI		3
+#define GRUB_ADDRESS_TYPE_NVS		4
+#define GRUB_ADDRESS_TYPE_PMEM		5
+
+#define GRUB_EFI_LOONGSON_BPI_TABLE_GUID	\
+    { 0x4660f721, 0x2ec5, 0x416a, \
+	{ 0x89, 0x9a, 0x43, 0x18, 0x02, 0x50, 0xa0, 0xc9 } \
+    }
+
+#define GRUB_EFI_LARCH_SCREEN_INFO_GUID \
+  { 0x07fd51a6, 0x9532, 0x926f, \
+    { 0x51, 0xdc, 0x6a, 0x63, 0x60, 0x2f, 0x84, 0xb4 } \
+  }
+
+#define GRUB_EFI_LARCH_CONSOLE_OUT_DEVICE_GUID \
+  { 0xd3b36f2c, 0xd551, 0x11d4,  \
+    { 0x9a, 0x46, 0x00, 0x90, 0x27, 0x3f, 0xc1, 0x4d } \
+  }
+
+#define GRUB_EFI_LARCH_BOOT_MEMMAP_GUID \
+  { 0x800f683f, 0xd08b, 0x423a,  \
+    { 0xa2, 0x93, 0x96, 0x5c, 0x3c, 0x6f, 0xe2, 0xb4 } \
+  }
+
+#define GRUB_EFI_LARCH_INITRD_MEDIA_GUID \
+  { 0x5568e427, 0x68fc, 0x4f3d,  \
+    { 0xac, 0x74, 0xca, 0x55, 0x52, 0x31, 0xcc, 0x68 } \
+  }
+
+#define GRUB_EFI_SCREEN_INFO_GUID \
+  { 0xe03fc20a, 0x85dc, 0x406e,  \
+    { 0xb9, 0x0e, 0x4a, 0xb5, 0x02, 0x37, 0x1d, 0x95 } \
+  }
+
+static struct grub_relocator *relocator;
+static grub_guid_t compat_screen_info_guid = GRUB_EFI_LARCH_SCREEN_INFO_GUID;
+static grub_guid_t screen_info_guid = GRUB_EFI_SCREEN_INFO_GUID;
+
+void grub_linux_loongarch_elf_relocator_unload (void)
+{
+  grub_relocator_unload (relocator);
+}
+
+static void
+find_bits (unsigned long mask, grub_efi_uint8_t *pos, grub_efi_uint8_t *size)
+{
+  grub_efi_uint8_t first, len;
+
+  first = 0;
+  len = 0;
+
+  if (mask)
+    {
+      while (!(mask & 0x1))
+        {
+    mask = mask >> 1;
+    first++;
+        }
+
+      while (mask & 0x1)
+        {
+    mask = mask >> 1;
+    len++;
+        }
+    }
+
+  *pos = first;
+  *size = len;
+}
+
+static void
+setup_pixel_info (struct screen_info *si, grub_efi_uint32_t pixels_per_scan_line,
+                  struct grub_efi_gop_pixel_bitmask pixel_info, int pixel_format)
+{
+  if (pixel_format == GRUB_EFI_GOT_RGBA8)
+    {
+      si->lfb_depth = 32;
+      si->lfb_linelength = pixels_per_scan_line * 4;
+      si->red_size = 8;
+      si->red_pos = 0;
+      si->green_size = 8;
+      si->green_pos = 8;
+      si->blue_size = 8;
+      si->blue_pos = 16;
+      si->rsvd_size = 8;
+      si->rsvd_pos = 24;
+    }
+  else if (pixel_format == GRUB_EFI_GOT_BGRA8)
+    {
+      si->lfb_depth = 32;
+      si->lfb_linelength = pixels_per_scan_line * 4;
+      si->red_size = 8;
+      si->red_pos = 16;
+      si->green_size = 8;
+      si->green_pos = 8;
+      si->blue_size = 8;
+      si->blue_pos = 0;
+      si->rsvd_size = 8;
+      si->rsvd_pos = 24;
+    }
+  else if (pixel_format == GRUB_EFI_GOT_BITMASK)
+    {
+      find_bits(pixel_info.r, &si->red_pos, &si->red_size);
+      find_bits(pixel_info.g, &si->green_pos, &si->green_size);
+      find_bits(pixel_info.b, &si->blue_pos, &si->blue_size);
+      find_bits(pixel_info.a, &si->rsvd_pos, &si->rsvd_size);
+      si->lfb_depth = si->red_size + si->green_size +
+                      si->blue_size + si->rsvd_size;
+      si->lfb_linelength = (pixels_per_scan_line * si->lfb_depth) / 8;
+    }
+  else
+    {
+      si->lfb_depth = 4;
+      si->lfb_linelength = si->lfb_width / 2;
+      si->red_size = 0;
+      si->red_pos = 0;
+      si->green_size = 0;
+      si->green_pos = 0;
+      si->blue_size = 0;
+      si->blue_pos = 0;
+      si->rsvd_size = 0;
+      si->rsvd_pos = 0;
+    }
+}
+
+static struct screen_info *
+alloc_screen_info (void)
+{
+  grub_efi_status_t status;
+  grub_efi_boot_services_t *b;
+  struct screen_info *si;
+
+  b = grub_efi_system_table->boot_services;
+  status = b->allocate_pool (GRUB_EFI_RUNTIME_SERVICES_DATA,
+                           sizeof(*si), (void**)&si);
+  if (status != GRUB_EFI_SUCCESS)
+    return NULL;
+
+  status = b->install_configuration_table (&compat_screen_info_guid, si);
+  if (status != GRUB_EFI_SUCCESS)
+    goto free_mem;
+
+  status = b->install_configuration_table (&screen_info_guid, si);
+  if (status == GRUB_EFI_SUCCESS)
+    return si;
+
+free_table:
+  b->install_configuration_table (&compat_screen_info_guid, NULL);
+free_mem:
+  b->free_pool (si);
+
+  return NULL;
+}
+
+static struct screen_info *
+setup_screen_info (void)
+{
+  grub_efi_boot_services_t *b;
+  grub_efi_handle_t gop_handle;
+  struct screen_info *si = NULL;
+  struct grub_efi_gop *gop, *first_gop;
+  grub_efi_handle_t *handles;
+  grub_efi_uintn_t num_handles, i;
+  grub_guid_t graphics_output_guid = GRUB_EFI_GOP_GUID;
+  grub_efi_uint16_t width, height;
+  grub_efi_uint32_t ext_lfb_base, pixels_per_scan_line;
+  grub_efi_uint64_t fb_base;
+  struct grub_efi_gop_pixel_bitmask pixel_info;
+  grub_efi_gop_pixel_format_t pixel_format;
+
+  si = alloc_screen_info();
+  if (!si)
+    return NULL;
+
+  handles = grub_efi_locate_handle (GRUB_EFI_BY_PROTOCOL,
+                    &graphics_output_guid, NULL, &num_handles);
+  if (!handles || num_handles == 0)
+    goto free_screen_info;
+
+  gop = NULL;
+  first_gop = NULL;
+
+  for (i = 0; i < num_handles; i++)
+    {
+      struct grub_efi_gop_mode *mode;
+      struct grub_efi_gop_mode_info *info = NULL;
+      grub_guid_t conout_proto = GRUB_EFI_LARCH_CONSOLE_OUT_DEVICE_GUID;
+      void *dummy = NULL;
+      grub_efi_uint8_t conout_found = 0;
+      grub_efi_uint64_t current_fb_base;
+
+      gop_handle = handles[i];
+      gop = grub_efi_open_protocol (gop_handle, &graphics_output_guid,
+            GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL);
+
+      dummy = grub_efi_open_protocol (gop_handle, &conout_proto,
+                GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL);
+      if (dummy != NULL)
+        conout_found = 1;
+
+      mode = gop->mode;
+      info = mode->info;
+      current_fb_base = mode->fb_base;
+
+      if ((!first_gop || conout_found) &&
+        info->pixel_format != GRUB_EFI_GOT_BLT_ONLY)
+        {
+    /*
+    * Systems that use the UEFI Console Splitter may
+    * provide multiple GOP devices, not all of which are
+    * backed by real hardware. The workaround is to search
+    * for a GOP implementing the ConOut protocol, and if
+    * one isn't found, to just fall back to the first GOP.
+    */
+    width = info->width;
+    height = info->height;
+    pixel_format = info->pixel_format;
+    pixel_info = info->pixel_bitmask;
+    pixels_per_scan_line = info->pixels_per_scanline;
+    fb_base = current_fb_base;
+
+    /*
+    * Once we've found a GOP supporting ConOut,
+    * don't bother looking any further.
+    */
+    first_gop = gop;
+    if (conout_found)
+      break;
+        }
+    }
+
+  /* Did we find any GOPs? */
+  if (!first_gop)
+    goto free_screen_info;
+
+  /* EFI framebuffer */
+  si->orig_video_isVGA = GRUB_VIDEO_TYPE_EFI;
+
+  si->lfb_width = width;
+  si->lfb_height = height;
+  si->lfb_base = fb_base;
+  grub_dprintf ("loongson", "Screen info fb base: 0x%"PRIxGRUB_UINT32_T"\n",
+                si->lfb_base);
+
+  ext_lfb_base = (grub_uint64_t)fb_base >> 32;
+  if (ext_lfb_base) {
+    si->capabilities |= GRUB_VIDEO_CAPABILITY_64BIT_BASE;
+    si->ext_lfb_base = ext_lfb_base;
+  }
+  si->pages = 1;
+
+  setup_pixel_info(si, pixels_per_scan_line, pixel_info, pixel_format);
+
+  si->lfb_size = si->lfb_linelength * si->lfb_height;
+  si->capabilities |= GRUB_VIDEO_CAPABILITY_SKIP_QUIRKS;
+
+  return si;
+
+free_screen_info:
+  b = grub_efi_system_table->boot_services;
+  b->install_configuration_table (&compat_screen_info_guid, NULL);
+  b->install_configuration_table (&screen_info_guid, NULL);
+  if (si)
+    b->free_pool (si);
+
+  grub_dprintf ("loongson", "No screen info\n");
+  return NULL;
+}
+
+static grub_err_t
+allocate_memmap_and_exit_boot (struct linux_loongarch64_kernel_params *kernel_params)
+{
+  grub_err_t err;
+  grub_efi_status_t status;
+  grub_efi_uintn_t mmap_size, desc_size, size;
+  grub_efi_uint32_t desc_version;
+  grub_efi_memory_descriptor_t *mmap_buf;
+  grub_efi_boot_services_t *b;
+  struct efi_boot_memmap *m, tmp;
+  grub_guid_t boot_memmap_guid = GRUB_EFI_LARCH_BOOT_MEMMAP_GUID;
+
+  setup_screen_info();
+
+  grub_dprintf ("loongson", "ramdisk_addr:0x%"PRIxGRUB_UINT64_T", \
+          size:0x%"PRIxGRUB_UINT64_T"\n",
+          kernel_params->ramdisk_addr,
+          kernel_params->ramdisk_size);
+
+  /* Set initrd info to system table*/
+  err = grub_loongarch_setup_initrd_params();
+  if (err != GRUB_ERR_NONE)
+    {
+      grub_error(GRUB_ERR_IO, "failed to install initrd media");
+      return err;
+    }
+
+  tmp.map_size = 0;
+  status = grub_efi_get_memory_map (&tmp.map_size, NULL, &tmp.map_key,
+                    &tmp.desc_size, &tmp.desc_ver);
+  if (status != 0) {
+    grub_error (GRUB_ERR_IO, "cannot get memory map");
+    goto uninstall_initrd_table;
+  }
+  size = tmp.map_size + tmp.desc_size * GRUB_EFI_MMAP_NR_SLACK_SLOTS;
+  m = grub_efi_allocate_any_pages (GRUB_EFI_BYTES_TO_PAGES (sizeof(*m) + size));
+  if (!m) {
+    grub_error (GRUB_ERR_IO, "cannot allocate m memory");
+    goto uninstall_initrd_table;
+  }
+
+  b = grub_efi_system_table->boot_services;
+  status = b->install_configuration_table (&boot_memmap_guid, m);
+  if (status != GRUB_EFI_SUCCESS) {
+    grub_error (GRUB_ERR_IO, "failed to install boot memmap");
+    goto free_m;
+  }
+
+  m->buff_size = m->map_size = size;
+  if (grub_efi_get_memory_map (&m->map_size, m->map,
+                   &m->map_key, &m->desc_size,
+                   &m->desc_ver) <= 0)
+    {
+      grub_error (GRUB_ERR_IO, "cannot get EFI memory map");
+      goto uninstall_mem_table;
+    }
+
+  mmap_size = grub_efi_find_mmap_size ();
+  if (! mmap_size)
+    goto uninstall_mem_table;
+
+  mmap_buf = grub_efi_allocate_any_pages (GRUB_EFI_BYTES_TO_PAGES (mmap_size));
+  err = grub_efi_finish_boot_services (&mmap_size, mmap_buf, NULL,
+                                         &desc_size, &desc_version);
+  if (err) {
+    grub_error (GRUB_ERR_IO, "failed to finish boot services");
+    goto free_map;
+  }
+
+  return 0;
+
+free_map:
+  if (mmap_buf)
+    grub_efi_free_pages ((grub_addr_t) mmap_buf,
+               GRUB_EFI_BYTES_TO_PAGES (mmap_size));
+
+uninstall_mem_table:
+  b->install_configuration_table (&boot_memmap_guid, NULL);
+
+free_m:
+  if (m)
+    grub_efi_free_pages ((grub_addr_t) m,
+               GRUB_EFI_BYTES_TO_PAGES (sizeof(*m) + size));
+
+uninstall_initrd_table:
+  grub_loongarch_remove_initrd_params();
+
+  return grub_error(GRUB_ERR_BAD_OS, "failed to V40 boot");
+}
+
+static grub_err_t
+allocate_fdt_and_exit_boot (struct linux_loongarch64_kernel_params *kernel_params)
+{
+  int node, retval;
+  grub_err_t err;
+  unsigned int size;
+  grub_efi_uintn_t mmap_size;
+  grub_efi_uintn_t desc_size;
+  grub_efi_uint32_t desc_version;
+  grub_efi_memory_descriptor_t *mmap_buf;
+
+  size = GRUB_FDT_EMPTY_TREE_SZ + FDT_ADDR_SIZE_EXTRA + GRUB_EFI_LINUX_FDT_EXTRA_SPACE;
+
+  kernel_params->fdt = grub_efi_allocate_any_pages (GRUB_EFI_BYTES_TO_PAGES (size));
+  if (!kernel_params->fdt)
+    return GRUB_ERR_OUT_OF_MEMORY;
+
+  grub_fdt_create_empty_tree (kernel_params->fdt, size);
+  grub_fdt_set_prop32 (kernel_params->fdt, 0, FDT_ADDR_CELLS_STRING, 2);
+  grub_fdt_set_prop32 (kernel_params->fdt, 0, FDT_SIZE_CELLS_STRING, 2);
+
+  node = grub_fdt_find_subnode (kernel_params->fdt, 0, "chosen");
+  if (node < 0)
+    node = grub_fdt_add_subnode (kernel_params->fdt, 0, "chosen");
+  if (node < 1)
+    goto failure;
+
+  grub_dprintf ("loongson", "command_line %s, len %ld\n",
+                (char *)kernel_params->linux_args,
+                grub_strlen(kernel_params->linux_args) + 1);
+  if ((kernel_params->linux_args != NULL) && (grub_strlen(kernel_params->linux_args) > 0)) {
+    retval = grub_fdt_set_prop (kernel_params->fdt, node, "bootargs", kernel_params->linux_args,
+				grub_strlen(kernel_params->linux_args) + 1);
+    if (retval)
+      goto failure;
+  }
+
+  /* Set initrd info */
+  if (kernel_params->ramdisk_addr && kernel_params->ramdisk_size)
+  {
+    grub_dprintf ("linux", "Initrd @ %p-%p\n",
+		    (void *) kernel_params->ramdisk_addr,
+		    (void *) (kernel_params->ramdisk_addr + kernel_params->ramdisk_size));
+
+    retval = grub_fdt_set_prop64 (kernel_params->fdt, node, "linux,initrd-start",
+				    kernel_params->ramdisk_addr);
+    if (retval)
+	  goto failure;
+    retval = grub_fdt_set_prop64 (kernel_params->fdt, node, "linux,initrd-end",
+		                  (grub_uint64_t) (kernel_params->ramdisk_addr + kernel_params->ramdisk_size));
+    if (retval)
+	  goto failure;
+  }
+
+  node = grub_fdt_find_subnode (kernel_params->fdt, 0, "chosen");
+  retval = grub_fdt_set_prop64 (kernel_params->fdt, node, "linux,uefi-system-table",
+		                (grub_uint64_t)grub_efi_system_table);
+  if (retval)
+	goto failure;
+
+  mmap_size = grub_efi_find_mmap_size ();
+  if (! mmap_size)
+    return grub_errno;
+  mmap_buf = grub_efi_allocate_any_pages (GRUB_EFI_BYTES_TO_PAGES (mmap_size));
+  if (! mmap_buf)
+    return grub_error (GRUB_ERR_IO, "cannot allocate memory map");
+  err = grub_efi_finish_boot_services (&mmap_size, mmap_buf, NULL,
+                                       &desc_size, &desc_version);
+  if (err)
+    return err;
+
+  if (!mmap_buf || !mmap_size || !desc_size)
+    return GRUB_ERR_BAD_ARGUMENT;
+
+  retval = grub_fdt_set_prop64 (kernel_params->fdt, node, "linux,uefi-mmap-start",
+		                (grub_uint64_t)mmap_buf);
+  if (retval)
+	goto failure;
+
+  retval = grub_fdt_set_prop32 (kernel_params->fdt, node, "linux,uefi-mmap-size",
+		                mmap_size);
+  if (retval)
+	goto failure;
+
+  retval = grub_fdt_set_prop32 (kernel_params->fdt, node, "linux,uefi-mmap-desc-size",
+		                desc_size);
+  if (retval)
+	goto failure;
+
+  retval = grub_fdt_set_prop32 (kernel_params->fdt, node, "linux,uefi-mmap-desc-ver",
+		                desc_version);
+  if (retval)
+	goto failure;
+
+  return GRUB_ERR_NONE;
+
+failure:
+  if (!kernel_params->fdt) {
+    return GRUB_ERR_BAD_OS;
+  }
+  grub_efi_free_pages ((grub_addr_t) kernel_params->fdt,
+		       GRUB_EFI_BYTES_TO_PAGES (grub_fdt_get_totalsize (kernel_params->fdt)));
+  kernel_params->fdt = NULL;
+  return grub_error(GRUB_ERR_BAD_OS, "failed to install/update FDT");
+}
+
+static void
+grub_linux_loongarch_elf_make_argv (struct linux_loongarch64_kernel_params *kernel_params)
+{
+  static void* linux_args_addr;
+  int size;
+  grub_uint64_t *linux_argv;
+  char *args, *p, *linux_args;
+  int i, argc;
+  grub_err_t err;
+
+  argc = kernel_params->linux_argc;
+  args = kernel_params->linux_args;
+
+  /* new size */
+  p = args;
+  size = (argc + 3 + 1) * sizeof (grub_uint64_t);  /* orig arguments */
+  for (i = 0; i < argc; i++)
+    {
+      size += ALIGN_UP (grub_strlen (p) + 1, 4);
+      p += grub_strlen (p) + 1;
+    }
+
+  if (kernel_params->ramdisk_addr && kernel_params->ramdisk_size)
+    {
+      size += ALIGN_UP (sizeof (GRUB_RD_START_STRING), 4)
+	            + ALIGN_UP (sizeof (GRUB_RD_SIZE_STRING), 4)
+	            + ALIGN_UP (sizeof (GRUB_INITRD_STRING), 4);
+    }
+  size = ALIGN_UP (size, 8);
+
+  /* alloc memory */
+  linux_args_addr = grub_linux_loongarch_alloc_virtual_mem_align (size, 8, &err);
+
+  linux_argv = linux_args_addr;
+  linux_args = (char *)(linux_argv + (argc + 1 + 3));
+  p = args;
+  for (i = 0; i < argc; i++)
+    {
+      grub_memcpy (linux_args, p, grub_strlen (p) + 1);
+      *linux_argv = (grub_uint64_t) (grub_addr_t) linux_args;
+      linux_argv++;
+      linux_args += ALIGN_UP (grub_strlen (p) + 1, 4);
+      p += grub_strlen (p) + 1;
+    }
+
+  if (kernel_params->ramdisk_addr && kernel_params->ramdisk_size)
+  {
+    /* rd_start */
+    grub_snprintf (linux_args,
+		 sizeof (GRUB_RD_START_STRING),
+		 "rd_start=0x%lx",
+		 (grub_uint64_t) kernel_params->ramdisk_addr);
+    *linux_argv = (grub_uint64_t) (grub_addr_t) linux_args;
+    linux_argv++;
+    linux_args += ALIGN_UP (sizeof (GRUB_RD_START_STRING), 4);
+    kernel_params->linux_argc++;
+
+    /* rd_size */
+    grub_snprintf (linux_args,
+		 sizeof (GRUB_RD_SIZE_STRING),
+		 "rd_size=0x%lx",
+		 (grub_uint64_t) kernel_params->ramdisk_size);
+    *linux_argv = (grub_uint64_t) (grub_addr_t) linux_args;
+    linux_argv++;
+    linux_args += ALIGN_UP (sizeof (GRUB_RD_SIZE_STRING), 4);
+    kernel_params->linux_argc++;
+
+    /* initrd */
+    grub_snprintf (linux_args,
+		 sizeof (GRUB_INITRD_STRING),
+		 "initrd=0x%lx,0x%lx",
+		 ((grub_uint64_t) kernel_params->ramdisk_addr & 0xffffffff),
+		 (grub_uint64_t) kernel_params->ramdisk_size);
+    *linux_argv = (grub_uint64_t) (grub_addr_t) linux_args;
+    linux_argv++;
+    linux_args += ALIGN_UP (sizeof (GRUB_INITRD_STRING), 4);
+    kernel_params->linux_argc++;
+  }
+
+  /* Reserve space for initrd arguments.  */
+  *linux_argv = 0;
+
+  grub_free (kernel_params->linux_args);
+  kernel_params->linux_argv = (grub_addr_t) linux_args_addr;
+}
+
+grub_err_t
+grub_linux_loongarch_elf_linux_boot_image (struct linux_loongarch64_kernel_params
+					   *kernel_params)
+{
+  struct boot_params_interface *boot_params = NULL;
+  struct grub_relocator64_state state;
+  grub_err_t err;
+
+  /* linux kernel type is ELF */
+  grub_memset (&state, 0, sizeof (state));
+
+  state.jumpreg = 1;
+  state.gpr[1] = kernel_params->kernel_addr;  /* ra */
+  if (grub_linux_loongarch_elf_get_boot_params (&boot_params) == 0)
+    {
+      grub_dprintf("loongson", "V4.0 boot\n");
+      if (allocate_memmap_and_exit_boot (kernel_params) != GRUB_ERR_NONE)
+        return grub_errno;
+      state.gpr[4] = 1 << FLAGS_EFI_SUPPORT_BIT;   /* a0 = flag */
+      state.gpr[5] = (grub_uint64_t)kernel_params->linux_args; /* a1 = cmdline */
+      state.gpr[6] = (grub_uint64_t)grub_efi_system_table; /* a2 = system_table */
+    } else {
+      grub_dprintf("loongson", "BPI boot\n");
+      grub_linux_loongarch_elf_make_argv (kernel_params);
+      state.gpr[4] = kernel_params->linux_argc;   /* a0 = argc */
+      state.gpr[5] = kernel_params->linux_argv; /* a1 = args */
+      state.gpr[6] = (grub_uint64_t) boot_params; /* a2 = envp */
+      err = grub_linux_loongarch_elf_boot_params (boot_params);
+      if (err)
+        return err;
+    }
+
+  /* Boot the ELF kernel */
+  grub_relocator64_boot (relocator, state);
+
+  return GRUB_ERR_NONE;
+}
+
+void*
+grub_linux_loongarch_alloc_virtual_mem_addr (grub_addr_t addr,
+					     grub_size_t size,
+					     grub_err_t *err)
+{
+  relocator = grub_relocator_new ();
+  if (!relocator)
+    return NULL;
+
+  grub_relocator_chunk_t ch;
+  *err = grub_relocator_alloc_chunk_addr (relocator, &ch,
+					 grub_vtop ((void *) addr),
+					 size);
+  if (*err)
+    return NULL;
+  return get_virtual_current_address (ch);
+}
+
+void*
+grub_linux_loongarch_alloc_virtual_mem_align (grub_size_t size,
+					      grub_size_t align,
+					      grub_err_t *err)
+{
+  grub_relocator_chunk_t ch;
+
+  *err = grub_relocator_alloc_chunk_align (relocator, &ch,
+					  0, (0xffffffff - size) + 1,
+					  size, align,
+					  GRUB_RELOCATOR_PREFERENCE_LOW, 0);
+  return get_virtual_current_address (ch);
+}
+
+void*
+grub_linux_loongarch_alloc_initrd_mem_align (grub_size_t size,
+                 grub_size_t align,
+                 grub_err_t *err)
+{
+  grub_relocator_chunk_t ch;
+
+  /* Firstly try to allocate from memory higher than 256MB */
+  *err = grub_relocator_alloc_chunk_align (relocator, &ch,
+              0x10000000, (0xffffffff - size) + 1, size, align,
+              GRUB_RELOCATOR_PREFERENCE_LOW, 0);
+  if (*err != GRUB_ERR_NONE)
+    {
+      /* Failed, try to allocate in range 0 ~ 256MB */
+      *err = grub_relocator_alloc_chunk_align (relocator, &ch,
+                  0, (0xfffffff - size) + 1, size, align,
+                  GRUB_RELOCATOR_PREFERENCE_HIGH, 0);
+    }
+  return get_virtual_current_address (ch);
+}
+
+int
+grub_linux_loongarch_elf_get_boot_params (struct boot_params_interface **boot_params)
+{
+  grub_efi_configuration_table_t *tables;
+  grub_guid_t bpi_guid = GRUB_EFI_LOONGSON_BPI_TABLE_GUID;
+  unsigned int i;
+  int found = 0;
+
+  /* Look for Loongson BPI in UEFI config tables. */
+  tables = grub_efi_system_table->configuration_table;
+
+  for (i = 0; i < grub_efi_system_table->num_table_entries; i++)
+    if (grub_memcmp (&tables[i].vendor_guid, &bpi_guid, sizeof (bpi_guid)) == 0)
+      {
+	*boot_params = tables[i].vendor_table;
+	char *p = (char*) &((*boot_params)->signature);
+	if (grub_strncmp (p, "BPI", 3) == 0)
+	  {
+	    found = 1;
+	    break;
+	  }
+      }
+  return found;
+}
+
+static grub_uint8_t
+grub_kernel_update_checksum (const grub_uint8_t *buffer, grub_efi_uintn_t length)
+{
+  grub_uint8_t sum;
+  grub_efi_uintn_t count;
+
+  for (sum = 0, count = 0; count < length; count++)
+  {
+    sum = (grub_uint8_t) (sum + *(buffer + count));
+  }
+
+  return (grub_uint8_t) (0x100 - sum);
+}
+
+static grub_uint32_t
+grub_efi_loongarch64_memmap_sort (struct memmap array[],
+				  grub_uint32_t length,
+				  struct loongsonlist_mem_map* bpmem,
+				  grub_uint32_t index,
+				  grub_uint32_t memtype)
+{
+  grub_uint64_t tempmemsize = 0;
+  grub_uint32_t j = 0;
+  grub_uint32_t t = 0;
+
+  for(j = 0; j < length;)
+  {
+    tempmemsize = array[j].mem_size;
+    for(t = j + 1; t < length; t++)
+    {
+      if(array[j].mem_start + tempmemsize == array[t].mem_start)
+      {
+        tempmemsize += array[t].mem_size;
+      }
+      else
+      {
+        break;
+      }
+    }
+    bpmem->map[index].mem_type = memtype;
+    bpmem->map[index].mem_start = array[j].mem_start;
+    bpmem->map[index].mem_size = tempmemsize;
+    grub_printf("map[%d]:type %"PRIuGRUB_UINT32_T", start 0x%"
+		PRIxGRUB_UINT64_T", end 0x%"PRIxGRUB_UINT64_T"\n",
+		index,
+		bpmem->map[index].mem_type,
+		bpmem->map[index].mem_start,
+		bpmem->map[index].mem_start+ bpmem->map[index].mem_size
+	       );
+    j = t;
+    index++;
+  }
+  return index;
+}
+
+grub_err_t
+grub_linux_loongarch_elf_boot_params (struct boot_params_interface *boot_params)
+{
+  grub_int8_t checksum = 0;
+  grub_err_t err;
+
+  struct loongsonlist_mem_map *loongson_mem_map = NULL;
+  struct _extention_list_hdr * listpointer = NULL;
+  grub_uint32_t tmp_index = 0;
+  grub_efi_memory_descriptor_t * lsdesc = NULL;
+
+  grub_uint32_t free_index = 0;
+  grub_uint32_t reserve_index = 0;
+  grub_uint32_t acpi_table_index = 0;
+  grub_uint32_t acpi_nvs_index = 0;
+
+  grub_efi_uintn_t mmap_size;
+  grub_efi_uintn_t desc_size;
+  grub_efi_memory_descriptor_t *mmap_buf;
+
+  struct memmap reserve_mem[GRUB_LOONGSON3_BOOT_MEM_MAP_MAX];
+  struct memmap free_mem[GRUB_LOONGSON3_BOOT_MEM_MAP_MAX];
+  struct memmap acpi_table_mem[GRUB_LOONGSON3_BOOT_MEM_MAP_MAX];
+  struct memmap acpi_nvs_mem[GRUB_LOONGSON3_BOOT_MEM_MAP_MAX];
+
+  grub_memset (reserve_mem, 0, sizeof(struct memmap) * GRUB_LOONGSON3_BOOT_MEM_MAP_MAX);
+  grub_memset (free_mem, 0, sizeof(struct memmap) * GRUB_LOONGSON3_BOOT_MEM_MAP_MAX);
+  grub_memset (acpi_table_mem, 0, sizeof(struct memmap) * GRUB_LOONGSON3_BOOT_MEM_MAP_MAX);
+  grub_memset (acpi_nvs_mem, 0, sizeof(struct memmap) * GRUB_LOONGSON3_BOOT_MEM_MAP_MAX);
+
+  /* Check extlist headers */
+  listpointer = boot_params->extlist;
+  for( ;listpointer != NULL; listpointer = listpointer->next)
+    {
+      char *pl= (char *)&(listpointer->signature);
+      if(grub_strncmp(pl, "MEM", 3) == 0)
+        {
+          loongson_mem_map = (struct loongsonlist_mem_map *)listpointer;
+          break;
+        }
+    }
+
+  mmap_size = grub_efi_find_mmap_size ();
+  if (! mmap_size)
+    return grub_errno;
+  mmap_buf = grub_efi_allocate_any_pages (GRUB_EFI_BYTES_TO_PAGES (mmap_size));
+  if (! mmap_buf)
+    return grub_error (GRUB_ERR_IO, "cannot allocate memory map");
+
+  err = grub_efi_finish_boot_services (&mmap_size, mmap_buf, NULL,
+				       &desc_size, NULL);
+  if (err)
+    return err;
+
+  if (!mmap_buf || !mmap_size || !desc_size)
+    return -1;
+
+  /*
+     According to UEFI SPEC,mmap_buf is the accurate Memory Map array \
+     now we can fill platform specific memory structure.
+     */
+  for (lsdesc = mmap_buf; lsdesc < (grub_efi_memory_descriptor_t *)((char *)mmap_buf + mmap_size);
+      lsdesc = (grub_efi_memory_descriptor_t *)((char *)lsdesc + desc_size))
+    {
+      /* System RAM */
+      if((lsdesc->type != GRUB_EFI_ACPI_RECLAIM_MEMORY) && \
+	 (lsdesc->type != GRUB_EFI_ACPI_MEMORY_NVS) && \
+	 (lsdesc->type != GRUB_EFI_RUNTIME_SERVICES_DATA) && \
+	 (lsdesc->type != GRUB_EFI_RUNTIME_SERVICES_CODE) && \
+	 (lsdesc->type != GRUB_EFI_RESERVED_MEMORY_TYPE) && \
+	 (lsdesc->type != GRUB_EFI_PAL_CODE))
+	{
+	  free_mem[free_index].mem_type = GRUB_ADDRESS_TYPE_SYSRAM;
+	  free_mem[free_index].mem_start = (lsdesc->physical_start) & GRUB_EFI_MAX_PHY_ADDRESS;
+	  free_mem[free_index].mem_size = lsdesc->num_pages * GRUB_EFI_PAGE_SIZE;
+	  free_index++;
+
+	  /*ACPI*/
+	}else if((lsdesc->type == GRUB_EFI_ACPI_RECLAIM_MEMORY)){
+	    acpi_table_mem[acpi_table_index].mem_type = GRUB_ADDRESS_TYPE_ACPI;
+	    acpi_table_mem[acpi_table_index].mem_start = (lsdesc->physical_start) & GRUB_EFI_MAX_PHY_ADDRESS;
+	    acpi_table_mem[acpi_table_index].mem_size = lsdesc->num_pages * GRUB_EFI_PAGE_SIZE;
+	    acpi_table_index++;
+	}else if((lsdesc->type == GRUB_EFI_ACPI_MEMORY_NVS)){
+	    acpi_nvs_mem[acpi_nvs_index].mem_type = GRUB_ADDRESS_TYPE_NVS;
+	    acpi_nvs_mem[acpi_nvs_index].mem_start = (lsdesc->physical_start) & GRUB_EFI_MAX_PHY_ADDRESS;
+	    acpi_nvs_mem[acpi_nvs_index].mem_size = lsdesc->num_pages * GRUB_EFI_PAGE_SIZE;
+	    acpi_nvs_index++;
+
+	/* Reserve */
+	}else{
+	    reserve_mem[reserve_index].mem_type = GRUB_ADDRESS_TYPE_RESERVED;
+	    reserve_mem[reserve_index].mem_start = (lsdesc->physical_start) & GRUB_EFI_MAX_PHY_ADDRESS;
+	    reserve_mem[reserve_index].mem_size = lsdesc->num_pages * GRUB_EFI_PAGE_SIZE;
+	    reserve_index++;
+	}
+    }
+
+  tmp_index = loongson_mem_map->map_count;
+  /*System RAM Sort*/
+  tmp_index = grub_efi_loongarch64_memmap_sort(free_mem,
+						free_index,
+						loongson_mem_map,
+						tmp_index,
+						GRUB_ADDRESS_TYPE_SYSRAM);
+  /*ACPI Sort*/
+  tmp_index = grub_efi_loongarch64_memmap_sort(acpi_table_mem,
+						acpi_table_index,
+						loongson_mem_map,
+						tmp_index,
+						GRUB_ADDRESS_TYPE_ACPI);
+  tmp_index = grub_efi_loongarch64_memmap_sort(acpi_nvs_mem,
+						acpi_nvs_index,
+						loongson_mem_map,
+						tmp_index,
+						GRUB_ADDRESS_TYPE_NVS);
+
+  /*Reserve Sort*/
+  {
+    grub_uint64_t loongarch_addr;
+    asm volatile ("csrrd %0, 0x181" : "=r" (loongarch_addr));
+    if ((loongarch_addr & 0xff00000000000000) == 0x9000000000000000)
+      tmp_index = grub_efi_loongarch64_memmap_sort(reserve_mem,
+						reserve_index,
+						loongson_mem_map,
+						tmp_index,
+						GRUB_ADDRESS_TYPE_RESERVED);
+    else
+      tmp_index = grub_efi_loongarch64_memmap_sort(reserve_mem,
+						reserve_index,
+						loongson_mem_map,
+						tmp_index,
+						GRUB_ADDRESS_TYPE_RESERVED + 1);
+  }
+  loongson_mem_map->map_count = tmp_index;
+  loongson_mem_map->header.checksum = 0;
+
+  checksum = grub_kernel_update_checksum ((grub_uint8_t *) loongson_mem_map,
+					  loongson_mem_map->header.length);
+  loongson_mem_map->header.checksum = checksum;
+
+  return grub_errno;
+}
diff --git a/grub-core/loader/loongarch64/linux.c b/grub-core/loader/loongarch64/linux.c
new file mode 100644
index 000000000000..7c8cd36fca15
--- /dev/null
+++ b/grub-core/loader/loongarch64/linux.c
@@ -0,0 +1,437 @@
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2021 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/loader.h>
+#include <grub/misc.h>
+#include <grub/command.h>
+#include <grub/i18n.h>
+#include <grub/lib/cmdline.h>
+#include <grub/linux.h>
+#include <grub/cpu/linux.h>
+#include <grub/efi/memory.h>
+
+GRUB_MOD_LICENSE ("GPLv3+");
+
+#define INITRD_MAX_ADDRESS_OFFSET (32ULL * 1024 * 1024 * 1024)
+
+#define GRUB_EFI_LARCH_INITRD_MEDIA_GUID \
+  { 0x5568e427, 0x68fc, 0x4f3d,  \
+      { 0xac, 0x74, 0xca, 0x55, 0x52, 0x31, 0xcc, 0x68 } \
+  }
+
+static struct linux_loongarch64_kernel_params kernel_params;
+
+static grub_addr_t phys_addr;
+static grub_dl_t my_mod;
+static int loaded;
+static int is_bpi_boot;
+static int grub_loongarch_linux_type = GRUB_LOONGARCH_LINUX_BAD;
+struct efi_initrd *initrd_tbl;
+
+grub_err_t
+grub_loongarch_setup_initrd_params (void)
+{
+  grub_efi_boot_services_t *b;
+  grub_guid_t initrd_media_guid = GRUB_EFI_LARCH_INITRD_MEDIA_GUID;
+  grub_efi_status_t status;
+
+  if (!kernel_params.ramdisk_addr || !kernel_params.ramdisk_size)
+    {
+      grub_error (GRUB_ERR_BAD_ARGUMENT,
+		  N_("you need to load the initrd first"));
+      return GRUB_ERR_BAD_ARGUMENT;
+    }
+
+  /* Set initrd info to system table*/
+  b = grub_efi_system_table->boot_services;
+  initrd_tbl = grub_efi_allocate_any_pages (GRUB_EFI_BYTES_TO_PAGES (sizeof(struct efi_initrd)));
+  if (!initrd_tbl)
+    return grub_error (GRUB_ERR_IO, "cannot allocate tbl memory");
+
+  initrd_tbl->base = kernel_params.ramdisk_addr;
+  initrd_tbl->size = kernel_params.ramdisk_size;
+  status = b->install_configuration_table (&initrd_media_guid, initrd_tbl);
+  if (status != GRUB_EFI_SUCCESS)
+    {
+      grub_error (GRUB_ERR_IO, "failed to install initrd media");
+      goto free_tbl;
+    }
+
+  return GRUB_ERR_NONE;
+
+free_tbl:
+  if (initrd_tbl)
+    {
+      grub_efi_free_pages ((grub_addr_t) initrd_tbl,
+               GRUB_EFI_BYTES_TO_PAGES (sizeof(struct efi_initrd)));
+    }
+
+  return GRUB_ERR_IO;
+}
+
+void
+grub_loongarch_remove_initrd_params (void)
+{
+  grub_efi_boot_services_t *b;
+  grub_guid_t initrd_media_guid = GRUB_EFI_LARCH_INITRD_MEDIA_GUID;
+
+  if (!initrd_tbl)
+    return;
+
+  b = grub_efi_system_table->boot_services;
+  b->install_configuration_table (&initrd_media_guid, NULL);
+
+  grub_efi_free_pages ((grub_addr_t) initrd_tbl,
+		       GRUB_EFI_BYTES_TO_PAGES (sizeof(struct efi_initrd)));
+
+  initrd_tbl = NULL;
+}
+
+
+static grub_err_t
+grub_linux_boot (void)
+{
+
+  if (grub_loongarch_linux_type == GRUB_LOONGARCH_LINUX_EFI) {
+      if (finalize_efi_params_linux (&kernel_params) != GRUB_ERR_NONE)
+        return grub_errno;
+      return (grub_arch_efi_linux_boot_image((grub_addr_t) kernel_params.kernel_addr,
+					     kernel_params.kernel_size,
+					     kernel_params.linux_args));
+  }
+  if (grub_loongarch_linux_type == GRUB_LOONGARCH_LINUX_ELF) {
+      return grub_linux_loongarch_elf_linux_boot_image (&kernel_params);
+  }
+
+  return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+grub_linux_unload (void)
+{
+
+  if (grub_loongarch_linux_type == GRUB_LOONGARCH_LINUX_EFI) {
+      if (kernel_params.ramdisk_addr)
+	grub_efi_free_pages ((grub_efi_physical_address_t) kernel_params.ramdisk_addr,
+			     GRUB_EFI_BYTES_TO_PAGES (kernel_params.ramdisk_size));
+      kernel_params.ramdisk_size = 0;
+
+      if (kernel_params.kernel_addr)
+	grub_efi_free_pages ((grub_addr_t) kernel_params.kernel_addr,
+			     GRUB_EFI_BYTES_TO_PAGES (kernel_params.kernel_size));
+      kernel_params.kernel_addr = 0;
+  }
+
+  if (grub_loongarch_linux_type == GRUB_LOONGARCH_LINUX_ELF) {
+      grub_free (kernel_params.linux_args);
+      kernel_params.linux_args = 0;
+      grub_linux_loongarch_elf_relocator_unload ();
+  }
+
+  grub_dl_unref (my_mod);
+  loaded = 0;
+  grub_loongarch_linux_type = GRUB_LOONGARCH_LINUX_BAD;
+
+  return GRUB_ERR_NONE;
+}
+
+grub_err_t
+grub_linux_loongarch_elf_load_kernel (grub_elf_t elf, const char *filename)
+{
+  Elf64_Addr base;
+  grub_err_t err;
+  grub_uint8_t *playground;
+  grub_uint64_t addr;
+  int flag;
+
+  /* Linux's entry point incorrectly contains a virtual address.  */
+  kernel_params.kernel_addr = elf->ehdr.ehdr64.e_entry;
+  kernel_params.kernel_size = grub_elf64_size (elf, &base, 0);
+
+  if (kernel_params.kernel_size == 0)
+    return grub_errno;
+
+  phys_addr = base;
+  kernel_params.kernel_size = ALIGN_UP (base + kernel_params.kernel_size - base, 8);
+
+  asm volatile ("csrrd %0, 0x181" : "=r" (addr));
+  if (addr & 0x1) {
+    flag = GRUB_ELF_LOAD_FLAGS_NONE;
+  } else {
+    flag = GRUB_ELF_LOAD_FLAGS_30BITS;
+    base &= ~ELF64_LOADMASK;
+    kernel_params.kernel_addr &= ~ELF64_LOADMASK;
+  }
+
+  playground = grub_linux_loongarch_alloc_virtual_mem_addr (phys_addr,
+							    kernel_params.kernel_size,
+							    &err);
+  if (playground == NULL)
+    return err;
+
+  /* Now load the segments into the area we claimed.  */
+  return grub_elf64_load (elf, filename, playground - base,
+			  flag, 0, 0);
+}
+
+static grub_err_t
+grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
+		int argc, char *argv[])
+{
+  grub_file_t file = 0;
+  struct linux_arch_kernel_header lh;
+  struct boot_params_interface *boot_params = NULL;
+  grub_elf_t elf = NULL;
+  grub_err_t err;
+  grub_size_t cmdline_size;
+  int i;
+
+  grub_dl_ref (my_mod);
+
+  /* Release the previously used memory.  */
+  grub_loader_unset ();
+
+  if (argc == 0)
+    {
+      grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
+      goto fail;
+    }
+
+  file = grub_file_open (argv[0], GRUB_FILE_TYPE_LINUX_KERNEL);
+  if (!file)
+    goto fail;
+
+  kernel_params.kernel_size = grub_file_size (file);
+  grub_dprintf ("linux", "kernel file size: %" PRIuGRUB_SIZE "\n",
+		kernel_params.kernel_size);
+
+  if (grub_file_read (file, &lh, sizeof (lh)) < (long) sizeof (lh))
+    return grub_errno;
+
+  if (grub_arch_efi_linux_check_image (&lh) == GRUB_ERR_NONE) {
+    grub_loongarch_linux_type = GRUB_LOONGARCH_LINUX_EFI;
+  }
+
+  if (grub_loongarch_linux_type != GRUB_LOONGARCH_LINUX_EFI) {
+    elf = grub_elf_file (file, argv[0]);
+    if (elf != NULL)
+    {
+      /* linux kernel type is ELF */
+      grub_loongarch_linux_type = GRUB_LOONGARCH_LINUX_ELF;
+      if (elf->ehdr.ehdr64.e_type != ET_EXEC)
+	{
+	  grub_error (GRUB_ERR_UNKNOWN_OS,
+		      N_("this ELF file is not of the right type"));
+	  goto fail;
+	}
+      if (elf->ehdr.ehdr64.e_machine != EM_LOONGARCH)
+	{
+	  grub_error (GRUB_ERR_BAD_OS, "invalid magic number");
+	  goto fail;
+	}
+
+      if (grub_elf_is_elf64 (elf))
+	{
+	  err = grub_linux_loongarch_elf_load_kernel (elf, argv[0]);
+	  if (err)
+	    goto fail;
+	} else {
+	    grub_error (GRUB_ERR_BAD_OS, N_("invalid arch-dependent ELF magic"));
+	    goto fail;
+	}
+      grub_dprintf ("linux", "kernel @ %p\n", (void*) elf->ehdr.ehdr64.e_entry);
+    }
+  } else {
+      if (grub_file_seek (file, 0) == (grub_off_t) -1)
+	goto fail;
+
+      if (grub_file_read (file, &lh, sizeof (lh)) < (grub_ssize_t) sizeof (lh))
+	{
+	  if (!grub_errno)
+	    grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"),
+			argv[0]);
+	  goto fail;
+	}
+
+      if (grub_arch_efi_linux_check_image (&lh) != GRUB_ERR_NONE)
+	{
+	  goto fail;
+	}
+      /* linux kernel type is EFI */
+      grub_loongarch_linux_type = GRUB_LOONGARCH_LINUX_EFI;
+      kernel_params.kernel_addr = (grub_addr_t) grub_efi_allocate_any_pages (
+				GRUB_EFI_BYTES_TO_PAGES (kernel_params.kernel_size));
+      grub_dprintf ("linux", "kernel numpages: %" PRIuGRUB_SIZE "\n",
+		    GRUB_EFI_BYTES_TO_PAGES (kernel_params.kernel_size));
+      if (!kernel_params.kernel_addr)
+	{
+	  grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("out of memory"));
+	  goto fail;
+	}
+
+      grub_file_seek (file, 0);
+      if (grub_file_read (file, (void*) kernel_params.kernel_addr, kernel_params.kernel_size)
+	  < (grub_int64_t) kernel_params.kernel_size)
+	{
+	  if (!grub_errno)
+	    grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"), argv[0]);
+	  goto fail;
+	}
+
+      grub_dprintf ("linux", "kernel @ %p\n", (void*) kernel_params.kernel_addr);
+    }
+
+  cmdline_size = grub_loader_cmdline_size (argc, argv) + sizeof (LINUX_IMAGE)
+                 + sizeof (GRUB_INITRD_STRING);
+  kernel_params.ramdisk_args_len = grub_loader_cmdline_size (argc, argv)
+                                   + sizeof (LINUX_IMAGE);
+  kernel_params.linux_argc = argc;
+  kernel_params.linux_args = grub_malloc (cmdline_size);
+  if (!kernel_params.linux_args)
+    {
+      grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("out of memory"));
+      goto fail;
+    }
+
+  grub_memcpy (kernel_params.linux_args, LINUX_IMAGE, sizeof (LINUX_IMAGE));
+
+  if (grub_linux_loongarch_elf_get_boot_params (&boot_params) == 1)
+    is_bpi_boot = 1;
+  else
+    is_bpi_boot = 0;
+
+  if (is_bpi_boot == 0)
+    {
+      err = grub_create_loader_cmdline (argc, argv,
+					(char*) ((grub_addr_t) kernel_params.linux_args + sizeof (LINUX_IMAGE) - 1),
+					kernel_params.ramdisk_args_len,
+					GRUB_VERIFY_KERNEL_CMDLINE);
+      if (err)
+	goto fail;
+    } else {
+      /* save args from linux cmdline */
+      char *p = kernel_params.linux_args;
+
+      p += sizeof (LINUX_IMAGE) - 1;
+      for (i=0; i < argc; i++)
+	{
+	  grub_memcpy (p, argv[i], grub_strlen(argv[i]) + 1);
+	  p += grub_strlen(argv[i]) + 1;
+	}
+  }
+
+  if (grub_errno == GRUB_ERR_NONE)
+    {
+      grub_loader_set (grub_linux_boot, grub_linux_unload, 0);
+      loaded = 1;
+    }
+
+fail:
+  if (elf != NULL) {
+      /* grub_elf_close will call grub_file_close() */
+      grub_elf_close (elf);
+  } else {
+      if (file)
+	grub_file_close (file);
+  }
+
+  if (grub_errno != GRUB_ERR_NONE)
+    {
+      grub_dl_unref (my_mod);
+      loaded = 0;
+    }
+
+  if (kernel_params.linux_args && !loaded)
+    grub_free (kernel_params.linux_args);
+
+  if (grub_loongarch_linux_type == GRUB_LOONGARCH_LINUX_EFI) {
+      if (kernel_params.kernel_addr && !loaded)
+	grub_efi_free_pages ((grub_addr_t) kernel_params.kernel_addr,
+			     GRUB_EFI_BYTES_TO_PAGES (kernel_params.kernel_size));
+  }
+
+  return grub_errno;
+}
+
+static grub_err_t
+grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)),
+		 int argc, char *argv[])
+{
+  struct grub_linux_initrd_context initrd_ctx = { 0, 0, 0 };
+  grub_size_t initrd_size;
+  void *initrd_mem = NULL;
+  grub_err_t err;
+
+  if (argc == 0)
+    {
+      return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
+      goto fail;
+    }
+
+  if (!loaded)
+    {
+      return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("you need to load the kernel first"));
+      goto fail;
+    }
+
+  if (grub_initrd_init (argc, argv, &initrd_ctx))
+    goto fail;
+
+  initrd_size = grub_get_initrd_size (&initrd_ctx);
+  grub_dprintf ("linux", "Loading initrd\n");
+
+  initrd_mem = grub_linux_loongarch_alloc_initrd_mem_align (initrd_size, 0x10000, &err);
+  if (err)
+    goto fail;
+
+  if (!initrd_mem)
+    {
+      grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("out of memory"));
+      goto fail;
+    }
+
+  if (grub_initrd_load (&initrd_ctx, initrd_mem))
+    goto fail;
+
+  /* save ramdisk addr and size */
+  kernel_params.ramdisk_addr = (grub_addr_t) initrd_mem;
+  kernel_params.ramdisk_size = initrd_size;
+  grub_dprintf ("linux", "ramdisk [addr=%p, size=0x%lx]\n",
+                 (void *) initrd_mem, initrd_size);
+fail:
+  grub_initrd_close (&initrd_ctx);
+
+  return grub_errno;
+}
+
+static grub_command_t cmd_linux, cmd_initrd;
+
+GRUB_MOD_INIT(linux)
+{
+  cmd_linux = grub_register_command ("linux", grub_cmd_linux,
+				     N_("FILE [ARGS...]"), N_("Load Linux."));
+  cmd_initrd = grub_register_command ("initrd", grub_cmd_initrd,
+				      N_("FILE"), N_("Load initrd."));
+  my_mod = mod;
+}
+
+GRUB_MOD_FINI(linux)
+{
+  grub_unregister_command (cmd_linux);
+  grub_unregister_command (cmd_initrd);
+}
diff --git a/include/grub/loongarch64/efi/memory.h b/include/grub/loongarch64/efi/memory.h
index 792258b2af20..fbe35a4b6bf4 100644
--- a/include/grub/loongarch64/efi/memory.h
+++ b/include/grub/loongarch64/efi/memory.h
@@ -19,7 +19,14 @@
 #ifndef GRUB_MEMORY_CPU_HEADER
 #include <grub/efi/memory.h>
 
-#define GRUB_EFI_MAX_USABLE_ADDRESS 0xfffffffffffULL
+static inline grub_uint64_t grub_efi_max_usable_address(void)
+{
+  grub_uint64_t addr;
+  asm volatile ("csrrd %0, 0x181" : "=r" (addr));
+  return addr |= 0xffffffffffUL;
+}
+
+#define GRUB_EFI_MAX_USABLE_ADDRESS (grub_efi_max_usable_address())
 #define GRUB_EFI_MAX_ALLOCATION_ADDRESS GRUB_EFI_MAX_USABLE_ADDRESS
 
 #endif /* ! GRUB_MEMORY_CPU_HEADER */
diff --git a/include/grub/loongarch64/linux.h b/include/grub/loongarch64/linux.h
new file mode 100644
index 000000000000..73876b8383bb
--- /dev/null
+++ b/include/grub/loongarch64/linux.h
@@ -0,0 +1,215 @@
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2021  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/>.
+ */
+
+#ifndef GRUB_LOONGARCH64_LINUX_HEADER
+#define GRUB_LOONGARCH64_LINUX_HEADER 1
+
+#include <grub/types.h>
+
+/* LoongArch linux kernel type */
+#define GRUB_LOONGARCH_LINUX_BAD 0
+#define GRUB_LOONGARCH_LINUX_ELF 1
+#define GRUB_LOONGARCH_LINUX_EFI 2
+
+#define GRUB_LOONGSON3_BOOT_MEM_MAP_MAX 128
+
+#define GRUB_LINUX_LOONGARCH_MAGIC_SIGNATURE  0x6E6F73676E6F6F4C /* 'Loongson' */
+
+/* From linux/Documentation/loongarch/booting.txt
+ *
+ * 0-1: MZ
+ * 0x28: LoongArch\0
+ * 0x3c: PE/COFF头偏移
+ * 0x20e:内核版本号偏移-512
+ * riscv的version字段在0x20偏移处，现在LoongArch没有使用，是0
+ */
+struct linux_loongarch64_kernel_header
+{
+  grub_uint32_t code0;		/* Executable code */
+  grub_uint32_t code1;		/* Executable code */
+  grub_uint64_t text_offset;	/* Image load offset */
+  grub_uint64_t res0;		/* reserved */
+  grub_uint64_t res1;		/* reserved */
+  grub_uint64_t res2;		/* reserved */
+  grub_uint64_t magic;		/* Magic number, little endian, "Loongson" */
+  grub_uint64_t res3;		/* reserved */
+  grub_uint32_t res4;		/* reserved */
+  grub_uint32_t hdr_offset;	/* Offset of PE/COFF header */
+};
+
+struct linux_loongarch64_kernel_params
+{
+  grub_addr_t kernel_addr; 	/* kernel entry address */
+  grub_size_t kernel_size;	/* kernel size */
+  grub_addr_t ramdisk_addr;	/* initrd load address */
+  grub_size_t ramdisk_size;	/* initrd size */
+  int         ramdisk_args_len;  /* position of initrd in linux_args */
+  int         linux_argc;	/* cmdline parameters number*/
+  grub_addr_t linux_argv;	/* cmdline parameters address*/
+  void*       linux_args;
+  void*       fdt;
+};
+
+#include <grub/efi/efi.h>
+#include <grub/elfload.h>
+
+#define GRUB_EFI_MAX_PHY_ADDRESS 0xffffffffffffULL
+#define ELF32_LOADMASK (0xf0000000UL)
+#define ELF64_LOADMASK (0xf000000000000000ULL)
+#define FLAGS_EFI_SUPPORT_BIT 0
+
+/*initrd info*/
+#define GRUB_RD_START_STRING "rd_start=0xXXXXXXXXXXXXXXXX"
+#define GRUB_RD_SIZE_STRING  "rd_size=0xXXXXXXXXXXXXXXXX"
+#define GRUB_INITRD_STRING   "initrd=0xXXXXXXXXXXXXXXXX,0xXXXXXXXXXXXXXXXX"
+
+#define FDT_ADDR_CELLS_STRING "#address-cells"
+#define FDT_SIZE_CELLS_STRING "#size-cells"
+#define FDT_ADDR_SIZE_EXTRA ((2 * grub_fdt_prop_entry_size (sizeof(grub_uint32_t))) + \
+                             sizeof (FDT_ADDR_CELLS_STRING) + \
+                             sizeof (FDT_SIZE_CELLS_STRING))
+
+struct efi_boot_memmap {
+  grub_efi_uintn_t  map_size;
+  grub_efi_uintn_t  desc_size;
+  grub_efi_uint32_t desc_ver;
+  grub_efi_uintn_t  map_key;
+  grub_efi_uintn_t  buff_size;
+  grub_efi_memory_descriptor_t map[];
+};
+
+struct efi_initrd {
+  grub_efi_uintn_t  base;
+  grub_efi_uintn_t  size;
+};
+
+/*
+ * These are set up by the setup-routine at boot-time:
+ */
+struct screen_info {
+  grub_efi_uint8_t  orig_x;       /* 0x00 */
+  grub_efi_uint8_t  orig_y;       /* 0x01 */
+  grub_efi_uint16_t ext_mem_k;    /* 0x02 */
+  grub_efi_uint16_t orig_video_page;  /* 0x04 */
+  grub_efi_uint8_t  orig_video_mode;  /* 0x06 */
+  grub_efi_uint8_t  orig_video_cols;  /* 0x07 */
+  grub_efi_uint8_t  flags;        /* 0x08 */
+  grub_efi_uint8_t  unused2;      /* 0x09 */
+  grub_efi_uint16_t orig_video_ega_bx;/* 0x0a */
+  grub_efi_uint16_t unused3;      /* 0x0c */
+  grub_efi_uint8_t  orig_video_lines; /* 0x0e */
+  grub_efi_uint8_t  orig_video_isVGA; /* 0x0f */
+  grub_efi_uint16_t orig_video_points;/* 0x10 */
+
+  /* VESA graphic mode -- linear frame buffer */
+  grub_efi_uint16_t lfb_width;    /* 0x12 */
+  grub_efi_uint16_t lfb_height;   /* 0x14 */
+  grub_efi_uint16_t lfb_depth;    /* 0x16 */
+  grub_efi_uint32_t lfb_base;     /* 0x18 */
+  grub_efi_uint32_t lfb_size;     /* 0x1c */
+  grub_efi_uint16_t cl_magic, cl_offset; /* 0x20 */
+  grub_efi_uint16_t lfb_linelength;   /* 0x24 */
+  grub_efi_uint8_t  red_size;     /* 0x26 */
+  grub_efi_uint8_t  red_pos;      /* 0x27 */
+  grub_efi_uint8_t  green_size;   /* 0x28 */
+  grub_efi_uint8_t  green_pos;    /* 0x29 */
+  grub_efi_uint8_t  blue_size;    /* 0x2a */
+  grub_efi_uint8_t  blue_pos;     /* 0x2b */
+  grub_efi_uint8_t  rsvd_size;    /* 0x2c */
+  grub_efi_uint8_t  rsvd_pos;     /* 0x2d */
+  grub_efi_uint16_t vesapm_seg;   /* 0x2e */
+  grub_efi_uint16_t vesapm_off;   /* 0x30 */
+  grub_efi_uint16_t pages;        /* 0x32 */
+  grub_efi_uint16_t vesa_attributes;  /* 0x34 */
+  grub_efi_uint32_t capabilities;     /* 0x36 */
+  grub_efi_uint32_t ext_lfb_base; /* 0x3a */
+  grub_efi_uint8_t  _reserved[2]; /* 0x3e */
+} __attribute__((packed));
+
+#define GRUB_VIDEO_TYPE_EFI     0x70
+#define GRUB_VIDEO_CAPABILITY_SKIP_QUIRKS    (1 << 0)
+#define GRUB_VIDEO_CAPABILITY_64BIT_BASE     (1 << 1)    /* Frame buffer base is 64-bit */
+
+/* From arch/loongarch/include/asm/mach-loongson64/boot_param.h */
+struct _extention_list_hdr {
+    grub_uint64_t		signature;
+    grub_uint32_t  		length;
+    grub_uint8_t   		revision;
+    grub_uint8_t   		checksum;
+	union {
+      struct  _extention_list_hdr *next;
+      grub_uint64_t  next_offset;
+    };
+
+} GRUB_PACKED;
+
+struct boot_params_interface {
+    grub_uint64_t		signature;  /* {"B", "P", "I", "0", "1", ... } */
+    grub_efi_system_table_t *systemtable;
+    union {
+      struct _extention_list_hdr	*extlist;
+      grub_uint64_t  extlist_offset;
+    };
+    grub_uint64_t flags;
+}GRUB_PACKED;
+
+struct loongsonlist_mem_map {
+    struct _extention_list_hdr	header;	/* {"M", "E", "M"} */
+    grub_uint8_t  map_count;
+    struct memmap {
+	grub_uint32_t mem_type;
+	grub_uint64_t mem_start;
+	grub_uint64_t mem_size;
+    } GRUB_PACKED map[GRUB_LOONGSON3_BOOT_MEM_MAP_MAX];
+}GRUB_PACKED;
+
+grub_err_t
+finalize_efi_params_linux (struct linux_loongarch64_kernel_params *kernel_params);
+
+grub_err_t
+grub_linux_loongarch_elf_linux_boot_image (struct linux_loongarch64_kernel_params
+					   *kernel_params);
+
+void*
+grub_linux_loongarch_alloc_virtual_mem_addr (grub_addr_t addr,
+					     grub_size_t size,
+					     grub_err_t *err);
+
+void*
+grub_linux_loongarch_alloc_virtual_mem_align (grub_size_t size,
+					      grub_size_t align,
+					      grub_err_t *err);
+
+void*
+grub_linux_loongarch_alloc_initrd_mem_align (grub_size_t size,
+                          grub_size_t align,
+                          grub_err_t *err);
+
+void
+grub_linux_loongarch_elf_relocator_unload (void);
+
+int
+grub_linux_loongarch_elf_get_boot_params (struct boot_params_interface **boot_params);
+
+grub_err_t
+grub_linux_loongarch_elf_boot_params (struct boot_params_interface *boot_params);
+
+grub_err_t
+grub_linux_loongarch_elf_load_kernel (grub_elf_t elf, const char *filename);
+
+#endif /* ! GRUB_LOONGARCH64_LINUX_HEADER */
diff --git a/include/grub/loongarch64/memory.h b/include/grub/loongarch64/memory.h
new file mode 100644
index 000000000000..cc9faefc1d9d
--- /dev/null
+++ b/include/grub/loongarch64/memory.h
@@ -0,0 +1,59 @@
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2017 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/>.
+ */
+
+#ifndef GRUB_MEMORY_CPU_HEADER
+#define GRUB_MEMORY_CPU_HEADER	1
+
+#ifndef ASM_FILE
+#include <grub/symbol.h>
+#include <grub/err.h>
+#include <grub/types.h>
+#endif
+
+#ifndef ASM_FILE
+
+typedef grub_addr_t grub_phys_addr_t;
+
+static inline grub_phys_addr_t
+grub_vtop (void *a)
+{
+  if (-1 == ((grub_int64_t) a >> 32))
+    return ((grub_phys_addr_t) a) & 0x1fffffffUL;
+  return ((grub_phys_addr_t) a) & 0xffffffffffffUL;
+}
+
+static inline void *
+grub_map_memory (grub_phys_addr_t a, grub_size_t size)
+{
+  grub_uint64_t addr;
+  asm volatile ("csrrd %0, 0x181" : "=r" (addr));
+  if (addr & 0x1)
+    return (void *) (a | (addr & 0xffffffffffffff00UL));
+  else
+	return (void *) a;
+}
+
+static inline void
+grub_unmap_memory (void *a __attribute__ ((unused)),
+		   grub_size_t size __attribute__ ((unused)))
+{
+}
+
+#endif
+
+#endif
diff --git a/include/grub/loongarch64/relocator.h b/include/grub/loongarch64/relocator.h
new file mode 100644
index 000000000000..cef3aaaf77ba
--- /dev/null
+++ b/include/grub/loongarch64/relocator.h
@@ -0,0 +1,38 @@
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2022  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/>.
+ */
+
+#ifndef GRUB_RELOCATOR_CPU_HEADER
+#define GRUB_RELOCATOR_CPU_HEADER	1
+
+#include <grub/types.h>
+#include <grub/err.h>
+#include <grub/relocator.h>
+
+struct grub_relocator64_state
+{
+  /* gpr[0] is ignored since it's hardwired to 0.  */
+  grub_uint64_t gpr[32];
+  /* Register holding target $pc.  */
+  int jumpreg;
+};
+
+grub_err_t
+grub_relocator64_boot (struct grub_relocator *rel,
+		       struct grub_relocator64_state state);
+
+#endif /* ! GRUB_RELOCATOR_CPU_HEADER */
-- 
2.43.0

