From 781cdb1573ffb77ed6d507d8daa7c6ff92256c57 Mon Sep 17 00:00:00 2001
From: mengyingkun <mengyingkun@loongson.cn>
Date: Thu, 12 Jan 2023 14:44:13 +0800
Subject: [PATCH] port: Add LoongArch support

This patch adds support for LoongArch. From now on, we can boot up
grub as a UEFI application on LoongArch platform.

Signed-off-by: yangqiming <yangqiming@loongson.cn>
Signed-off-by: mengyingkun <mengyingkun@loongson.cn>
---
 Makefile.util.def                             |   1 +
 bootstrap.conf                                |   4 +
 configure.ac                                  |  24 +
 gentpl.py                                     |   9 +-
 grub-core/Makefile.am                         |   6 +
 grub-core/Makefile.core.def                   |  23 +
 grub-core/kern/dl.c                           |   9 +-
 grub-core/kern/efi/mm.c                       |   3 +-
 grub-core/kern/loongarch64/cache.S            |  26 +
 grub-core/kern/loongarch64/dl.c               | 150 +++++
 grub-core/kern/loongarch64/dl_helper.c        | 264 +++++++++
 grub-core/kern/loongarch64/efi/init.c         |  73 +++
 grub-core/kern/loongarch64/efi/startup.S      |  45 ++
 grub-core/kern/loongarch64/init.c             |  47 ++
 .../lib/gnulib-patches/fix-loongarch.patch    |  26 +
 grub-core/lib/loongarch64/relocator.c         | 163 ++++++
 grub-core/lib/loongarch64/relocator_asm.S     |  51 ++
 grub-core/lib/loongarch64/setjmp.S            |  68 +++
 grub-core/lib/setjmp.S                        |   2 +
 grub-core/loader/efi/chainloader.c            |   2 +
 grub-core/loader/loongarch64/linux-efi.c      | 144 +++++
 grub-core/loader/loongarch64/linux-elf.c      | 529 ++++++++++++++++++
 grub-core/loader/loongarch64/linux.c          | 398 +++++++++++++
 include/grub/efi/api.h                        |   2 +-
 include/grub/efi/efi.h                        |   6 +-
 include/grub/efi/pe32.h                       |   4 +
 include/grub/elf.h                            |  30 +
 include/grub/fdt.h                            |   4 +-
 include/grub/loongarch64/efi/loader.h         |  25 +
 include/grub/loongarch64/efi/memory.h         |  15 +
 include/grub/loongarch64/efi/time.h           |   0
 include/grub/loongarch64/io.h                 |  62 ++
 include/grub/loongarch64/linux.h              | 144 +++++
 include/grub/loongarch64/loongarch64.h        |  30 +
 include/grub/loongarch64/memory.h             |  59 ++
 include/grub/loongarch64/reloc.h              | 113 ++++
 include/grub/loongarch64/relocator.h          |  38 ++
 include/grub/loongarch64/setjmp.h             |  27 +
 include/grub/loongarch64/time.h               |  39 ++
 include/grub/loongarch64/types.h              |  34 ++
 include/grub/util/install.h                   |   1 +
 util/grub-install-common.c                    |   1 +
 util/grub-install.c                           |  16 +
 util/grub-mkimagexx.c                         |  99 +++-
 util/grub-module-verifier.c                   |  15 +
 util/mkimage.c                                |  16 +
 46 files changed, 2833 insertions(+), 14 deletions(-)
 create mode 100644 grub-core/kern/loongarch64/cache.S
 create mode 100644 grub-core/kern/loongarch64/dl.c
 create mode 100644 grub-core/kern/loongarch64/dl_helper.c
 create mode 100644 grub-core/kern/loongarch64/efi/init.c
 create mode 100644 grub-core/kern/loongarch64/efi/startup.S
 create mode 100644 grub-core/kern/loongarch64/init.c
 create mode 100644 grub-core/lib/gnulib-patches/fix-loongarch.patch
 create mode 100644 grub-core/lib/loongarch64/relocator.c
 create mode 100644 grub-core/lib/loongarch64/relocator_asm.S
 create mode 100644 grub-core/lib/loongarch64/setjmp.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/efi/loader.h
 create mode 100644 include/grub/loongarch64/efi/memory.h
 create mode 100644 include/grub/loongarch64/efi/time.h
 create mode 100644 include/grub/loongarch64/io.h
 create mode 100644 include/grub/loongarch64/linux.h
 create mode 100644 include/grub/loongarch64/loongarch64.h
 create mode 100644 include/grub/loongarch64/memory.h
 create mode 100644 include/grub/loongarch64/reloc.h
 create mode 100644 include/grub/loongarch64/relocator.h
 create mode 100644 include/grub/loongarch64/setjmp.h
 create mode 100644 include/grub/loongarch64/time.h
 create mode 100644 include/grub/loongarch64/types.h

diff --git a/Makefile.util.def b/Makefile.util.def
index b7a6311..932a8c2 100644
--- a/Makefile.util.def
+++ b/Makefile.util.def
@@ -169,6 +169,7 @@ library = {
   common = grub-core/kern/ia64/dl_helper.c;
   common = grub-core/kern/arm/dl_helper.c;
   common = grub-core/kern/arm64/dl_helper.c;
+  common = grub-core/kern/loongarch64/dl_helper.c;
   common = grub-core/lib/minilzo/minilzo.c;
   common = grub-core/lib/xzembed/xz_dec_bcj.c;
   common = grub-core/lib/xzembed/xz_dec_lzma2.c;
diff --git a/bootstrap.conf b/bootstrap.conf
index 03f1093..854d3c0 100644
--- a/bootstrap.conf
+++ b/bootstrap.conf
@@ -85,6 +85,10 @@ cp -a INSTALL INSTALL.grub
 
 bootstrap_post_import_hook () {
   set -e
+  for patchname in fix-loongarch; do
+    patch -d gnulib -p1 \
+      < "grub-core/lib/gnulib-patches/$patchname.patch"
+  done
   for patchname in \
       0001-Support-POTFILES-shell \
       0002-Handle-gettext_printf-shell-function \
diff --git a/configure.ac b/configure.ac
index 0a9522a..d03e089 100644
--- a/configure.ac
+++ b/configure.ac
@@ -149,6 +149,10 @@ case "$target_cpu" in
   riscv64*)
 		target_cpu=riscv64
 		;;
+  loongarch64)
+		target_cpu=loongarch64
+		machine_CPPFLAGS="$machine_CPPFLAGS -DGRUB_CPU_LOONGARCH64=1"
+		;;
 esac
 
 # Specify the platform (such as firmware).
@@ -167,6 +171,7 @@ if test "x$with_platform" = x; then
     powerpc64-*) platform=ieee1275 ;;
     powerpc64le-*) platform=ieee1275 ;;
     sparc64-*) platform=ieee1275 ;;
+    loongarch64-*) platform=efi ;;
     mipsel-*) platform=loongson ;;
     mips-*) platform=arc ;;
     ia64-*) platform=efi ;;
@@ -218,6 +223,7 @@ case "$target_cpu"-"$platform" in
   mipsel-yeeloong) platform=loongson ;;
   mipsel-fuloong) platform=loongson ;;
   mipsel-loongson) ;;
+  loongarch64-efi) ;;
   arm-uboot) ;;
   arm-coreboot) ;;
   arm-efi) ;;
@@ -276,6 +282,7 @@ case "$platform" in
   pc)		machine_CPPFLAGS="$machine_CPPFLAGS -DGRUB_MACHINE_PCBIOS=1" ;;
   emu)		machine_CPPFLAGS="$machine_CPPFLAGS -DGRUB_MACHINE_EMU=1" ;;
   loongson)	machine_CPPFLAGS="$machine_CPPFLAGS -DGRUB_MACHINE_MIPS_LOONGSON=1" ;;
+  loongson64)	machine_CPPFLAGS="$machine_CPPFLAGS -DGRUB_MACHINE_LOONARCH64=1" ;;
   qemu_mips)	machine_CPPFLAGS="$machine_CPPFLAGS -DGRUB_MACHINE_MIPS_QEMU_MIPS=1" ;;
   arc)	machine_CPPFLAGS="$machine_CPPFLAGS -DGRUB_MACHINE_ARC=1" ;;
 esac
@@ -890,6 +897,21 @@ if ( test "x$target_cpu" = xi386 || test "x$target_cpu" = xx86_64 ) && test "x$p
   TARGET_CFLAGS="$TARGET_CFLAGS -mno-mmx -mno-sse -mno-sse2 -mno-sse3 -mno-3dnow"
 fi
 
+if test "x$target_cpu" = xloongarch64; then
+  AC_CACHE_CHECK([whether _mno_explicit_relocs works], [grub_cv_cc_mno_explicit_relocs], [
+    CFLAGS="$TARGET_CFLAGS -mno-explicit-relocs -Werror"
+    AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]], [[]])],
+       [grub_cv_cc_mno_explicit_relocs=yes],
+       [grub_cv_cc_mno_explicit_relocs=no])
+  ])
+  if test "x$grub_cv_cc_mno_explicit_relocs" = xyes; then
+    TARGET_CFLAGS="$TARGET_CFLAGS -mno-explicit-relocs -fno-plt"
+    TARGET_CCASFLAGS="$TARGET_CCASFLAGS -mno-explicit-relocs -fno-plt"
+  fi
+  TARGET_CFLAGS="$TARGET_CFLAGS -Wa,-mla-global-with-abs"
+  TARGET_CCASFLAGS="$TARGET_CCASFLAGS -Wa,-mla-global-with-abs"
+fi
+
 # Should grub utils get the host CFLAGS, or the target CFLAGS?
 AC_ARG_WITH([utils],
             AS_HELP_STRING([--with-utils=host|target|build],
@@ -2166,6 +2188,8 @@ AM_CONDITIONAL([COND_mips_arc], [test "(" x$target_cpu = xmips -o x$target_cpu =
 AM_CONDITIONAL([COND_sparc64_ieee1275], [test x$target_cpu = xsparc64 -a x$platform = xieee1275])
 AM_CONDITIONAL([COND_sparc64_emu], [test x$target_cpu = xsparc64 -a x$platform = xemu])
 AM_CONDITIONAL([COND_powerpc_ieee1275], [test x$target_cpu = xpowerpc -a x$platform = xieee1275])
+AM_CONDITIONAL([COND_loongarch64_efi], [test x$target_cpu = xloongarch64 -a x$platform = xefi])
+AM_CONDITIONAL([COND_loongarch64], [test x$target_cpu = xloongarch64])
 AM_CONDITIONAL([COND_mips], [test x$target_cpu = xmips -o x$target_cpu = xmipsel])
 AM_CONDITIONAL([COND_mipsel], [test x$target_cpu = xmipsel])
 AM_CONDITIONAL([COND_mipseb], [test x$target_cpu = xmips])
diff --git a/gentpl.py b/gentpl.py
index 59f62ef..f03aff8 100644
--- a/gentpl.py
+++ b/gentpl.py
@@ -32,7 +32,8 @@ GRUB_PLATFORMS = [ "emu", "i386_pc", "i386_efi", "i386_qemu", "i386_coreboot",
                    "mips_loongson", "sparc64_ieee1275",
                    "powerpc_ieee1275", "mips_arc", "ia64_efi",
                    "mips_qemu_mips", "arm_uboot", "arm_efi", "arm64_efi",
-                   "arm_coreboot", "riscv32_efi", "riscv64_efi" ]
+                   "arm_coreboot", "riscv32_efi", "riscv64_efi",
+                   "loongarch64_efi" ]
 
 GROUPS = {}
 
@@ -49,11 +50,12 @@ GROUPS["arm"]      = [ "arm_uboot", "arm_efi", "arm_coreboot" ]
 GROUPS["arm64"]    = [ "arm64_efi" ]
 GROUPS["riscv32"]  = [ "riscv32_efi" ]
 GROUPS["riscv64"]  = [ "riscv64_efi" ]
+GROUPS["loongarch64"]   = [ "loongarch64_efi" ]
 
 # Groups based on firmware
 GROUPS["pc"] = [ "i386_pc" ]
 GROUPS["efi"]  = [ "i386_efi", "x86_64_efi", "ia64_efi", "arm_efi", "arm64_efi",
-		   "riscv32_efi", "riscv64_efi" ]
+                   "riscv32_efi", "riscv64_efi", "loongarch64_efi" ]
 GROUPS["ieee1275"]   = [ "i386_ieee1275", "sparc64_ieee1275", "powerpc_ieee1275" ]
 GROUPS["uboot"] = [ "arm_uboot" ]
 GROUPS["xen"]  = [ "i386_xen", "x86_64_xen" ]
@@ -80,7 +82,8 @@ GROUPS["terminfomodule"]   = GRUB_PLATFORMS[:];
 for i in GROUPS["terminfoinkernel"]: GROUPS["terminfomodule"].remove(i)
 
 # Flattened Device Trees (FDT)
-GROUPS["fdt"] = [ "arm64_efi", "arm_uboot", "arm_efi", "riscv32_efi", "riscv64_efi" ]
+GROUPS["fdt"] = [ "arm64_efi", "arm_uboot", "arm_efi", "riscv32_efi",
+                  "riscv64_efi", "loongarch64_efi" ]
 
 # Needs software helpers for division
 # Must match GRUB_DIVISION_IN_SOFTWARE in misc.h
diff --git a/grub-core/Makefile.am b/grub-core/Makefile.am
index dd49939..6582f16 100644
--- a/grub-core/Makefile.am
+++ b/grub-core/Makefile.am
@@ -240,6 +240,12 @@ KERNEL_HEADER_FILES += $(top_builddir)/include/grub/machine/memory.h
 KERNEL_HEADER_FILES += $(top_builddir)/include/grub/machine/kernel.h
 endif
 
+if COND_loongarch64_efi
+KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/efi/efi.h
+KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/efi/disk.h
+KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/acpi.h
+endif
+
 if COND_powerpc_ieee1275
 KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/ieee1275/ieee1275.h
 KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/terminfo.h
diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def
index 6b00eb5..016d345 100644
--- a/grub-core/Makefile.core.def
+++ b/grub-core/Makefile.core.def
@@ -103,6 +103,9 @@ kernel = {
   arm_coreboot_ldflags       = '-Wl,-r,-d';
   arm_coreboot_stripflags    = '--strip-unneeded -K start -R .note -R .comment -R .note.gnu.gold-version';
 
+  loongarch64_efi_ldflags    = '-Wl,-r,-d';
+  loongarch64_efi_stripflags = '--strip-unneeded -K start -R .note -R .comment -R .note.gnu.gold-version -R .eh_frame';
+
   i386_pc_startup = kern/i386/pc/startup.S;
   i386_efi_startup = kern/i386/efi/startup.S;
   x86_64_efi_startup = kern/x86_64/efi/startup.S;
@@ -122,6 +125,7 @@ kernel = {
   arm64_efi_startup = kern/arm64/efi/startup.S;
   riscv32_efi_startup = kern/riscv/efi/startup.S;
   riscv64_efi_startup = kern/riscv/efi/startup.S;
+  loongarch64_efi_startup = kern/loongarch64/efi/startup.S;
 
   common = kern/buffer.c;
   common = kern/command.c;
@@ -270,6 +274,9 @@ kernel = {
   riscv64_efi = kern/riscv/efi/init.c;
   riscv64_efi = kern/efi/fdt.c;
 
+  loongarch64_efi = kern/loongarch64/efi/init.c;
+  loongarch64_efi = kern/efi/fdt.c;
+
   i386_pc = kern/i386/pc/init.c;
   i386_pc = kern/i386/pc/mmap.c;
   i386_pc = term/i386/pc/console.c;
@@ -351,6 +358,12 @@ kernel = {
   riscv64 = kern/riscv/cache_flush.S;
   riscv64 = kern/riscv/dl.c;
 
+  loongarch64 = kern/loongarch64/init.c;
+  loongarch64 = kern/loongarch64/dl.c;
+  loongarch64 = kern/loongarch64/dl_helper.c;
+  loongarch64 = kern/loongarch64/cache.S;
+  loongarch64 = kern/generic/rtc_get_time_ms.c;
+
   fdt = lib/fdt.c;
 
   emu = disk/host.c;
@@ -864,6 +877,7 @@ module = {
   enable = arm_coreboot;
   enable = riscv32_efi;
   enable = riscv64_efi;
+  enable = loongarch64_efi;
 };
 
 module = {
@@ -941,6 +955,7 @@ module = {
   i386_multiboot = commands/acpihalt.c;
   i386_efi = commands/acpihalt.c;
   x86_64_efi = commands/acpihalt.c;
+  loongarch64_efi = commands/acpihalt.c;
   i386_multiboot = lib/i386/halt.c;
   i386_coreboot = lib/i386/halt.c;
   i386_qemu = lib/i386/halt.c;
@@ -1715,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;
@@ -1724,6 +1741,7 @@ module = {
   enable = x86;
   enable = i386_xen_pvh;
   enable = xen;
+  enable = loongarch64;
 };
 
 module = {
@@ -1756,6 +1774,7 @@ module = {
   extra_dist = lib/arm/setjmp.S;
   extra_dist = lib/arm64/setjmp.S;
   extra_dist = lib/riscv/setjmp.S;
+  extra_dist = lib/loongarch64/setjmp.S;
 };
 
 module = {
@@ -1854,6 +1873,9 @@ module = {
   arm64 = loader/arm64/linux.c;
   riscv32 = loader/riscv/linux.c;
   riscv64 = loader/riscv/linux.c;
+  loongarch64 = loader/loongarch64/linux.c;
+  loongarch64 = loader/loongarch64/linux-elf.c;
+  loongarch64 = loader/loongarch64/linux-efi.c;
   emu = loader/emu/linux.c;
 
   common = loader/linux.c;
@@ -1953,6 +1975,7 @@ module = {
   enable = riscv32_efi;
   enable = riscv64_efi;
   enable = mips;
+  enable = loongarch64_efi;
 };
 
 module = {
diff --git a/grub-core/kern/dl.c b/grub-core/kern/dl.c
index f304494..8c82ea1 100644
--- a/grub-core/kern/dl.c
+++ b/grub-core/kern/dl.c
@@ -278,7 +278,8 @@ grub_dl_load_segments (grub_dl_t mod, const Elf_Ehdr *e)
   unsigned i;
   const Elf_Shdr *s;
   grub_size_t tsize = 0, talign = 1;
-#if !defined (__i386__) && !defined (__x86_64__) && !defined(__riscv)
+#if !defined (__i386__) && !defined (__x86_64__) && !defined(__riscv) && \
+  !defined(__loongarch__)
   grub_size_t tramp;
   grub_size_t got;
   grub_err_t err;
@@ -294,7 +295,8 @@ grub_dl_load_segments (grub_dl_t mod, const Elf_Ehdr *e)
 	talign = s->sh_addralign;
     }
 
-#if !defined (__i386__) && !defined (__x86_64__) && !defined(__riscv)
+#if !defined (__i386__) && !defined (__x86_64__) && !defined(__riscv) && \
+  !defined(__loongarch__)
   err = grub_arch_dl_get_tramp_got_size (e, &tramp, &got);
   if (err)
     return err;
@@ -357,7 +359,8 @@ grub_dl_load_segments (grub_dl_t mod, const Elf_Ehdr *e)
 	  mod->segment = seg;
 	}
     }
-#if !defined (__i386__) && !defined (__x86_64__) && !defined(__riscv)
+#if !defined (__i386__) && !defined (__x86_64__) && !defined(__riscv) && \
+  !defined(__loongarch__)
   ptr = (char *) ALIGN_UP ((grub_addr_t) ptr, GRUB_ARCH_DL_TRAMP_ALIGN);
   mod->tramp = ptr;
   mod->trampptr = ptr;
diff --git a/grub-core/kern/efi/mm.c b/grub-core/kern/efi/mm.c
index 8553fcd..9d723a9 100644
--- a/grub-core/kern/efi/mm.c
+++ b/grub-core/kern/efi/mm.c
@@ -699,7 +699,8 @@ grub_efi_mm_init (void)
 		       2 * BYTES_TO_PAGES (MEMORY_MAP_SIZE));
 }
 
-#if defined (__aarch64__) || defined (__arm__) || defined (__riscv)
+#if defined (__aarch64__) || defined (__arm__) || defined (__riscv) \
+  || defined (__loongarch__)
 grub_err_t
 grub_efi_get_ram_base(grub_addr_t *base_addr)
 {
diff --git a/grub-core/kern/loongarch64/cache.S b/grub-core/kern/loongarch64/cache.S
new file mode 100644
index 0000000..d291c67
--- /dev/null
+++ b/grub-core/kern/loongarch64/cache.S
@@ -0,0 +1,26 @@
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2009,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/>.
+ */
+
+#include <grub/symbol.h>
+
+FUNCTION (grub_arch_sync_caches)
+	jr	$ra
+
+FUNCTION (grub_arch_sync_dma_caches)
+	jr	$ra
+
diff --git a/grub-core/kern/loongarch64/dl.c b/grub-core/kern/loongarch64/dl.c
new file mode 100644
index 0000000..47196a2
--- /dev/null
+++ b/grub-core/kern/loongarch64/dl.c
@@ -0,0 +1,150 @@
+/*
+ *  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/dl.h>
+#include <grub/elf.h>
+#include <grub/misc.h>
+#include <grub/err.h>
+#include <grub/types.h>
+#include <grub/mm.h>
+#include <grub/i18n.h>
+#include <grub/cpu/reloc.h>
+
+/* Check if EHDR is a valid ELF header.  */
+grub_err_t
+grub_arch_dl_check_header (void *ehdr)
+{
+  Elf_Ehdr *e = ehdr;
+
+  /* Check the magic numbers.  */
+  if (e->e_ident[EI_CLASS] != ELFCLASS64
+      || e->e_ident[EI_DATA] != ELFDATA2LSB || e->e_machine != EM_LOONGARCH)
+    return grub_error (GRUB_ERR_BAD_OS, N_("invalid arch-dependent ELF magic"));
+
+  return GRUB_ERR_NONE;
+}
+
+#pragma GCC diagnostic ignored "-Wcast-align"
+
+/*
+ * Unified function for both REL and RELA.
+ */
+grub_err_t
+grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr,
+			       Elf_Shdr *s, grub_dl_segment_t seg)
+{
+  Elf_Rel *rel, *max;
+  struct grub_loongarch64_stack stack;
+  grub_loongarch64_stack_init (&stack);
+
+  for (rel = (Elf_Rel *) ((char *) ehdr + s->sh_offset),
+	 max = (Elf_Rel *) ((char *) rel + s->sh_size);
+       rel < max;
+       rel = (Elf_Rel *) ((char *) rel + s->sh_entsize))
+    {
+      Elf_Sym *sym;
+      void *place;
+      grub_uint64_t sym_addr;
+
+      if (rel->r_offset >= seg->size)
+	return grub_error (GRUB_ERR_BAD_MODULE,
+			   "reloc offset is outside the segment");
+
+      sym = (Elf_Sym *) ((char*)mod->symtab
+			 + mod->symsize * ELF_R_SYM (rel->r_info));
+
+      sym_addr = sym->st_value;
+      if (s->sh_type == SHT_RELA)
+	sym_addr += ((Elf_Rela *) rel)->r_addend;
+
+      place = (void *) ((grub_addr_t)seg->addr + rel->r_offset);
+
+      switch (ELF_R_TYPE (rel->r_info))
+	{
+	case R_LARCH_64:
+	  {
+	    grub_uint64_t *abs_place = place;
+
+	    grub_dprintf ("dl", "reloc_abs64 %p => 0x%016llx, %p\n",
+			  place, (unsigned long long) sym_addr, abs_place);
+
+	    *abs_place += (grub_uint64_t) sym_addr;
+	  }
+	  break;
+	case R_LARCH_MARK_LA:
+	  break;
+	case R_LARCH_SOP_PUSH_PCREL:
+	case R_LARCH_SOP_PUSH_PLT_PCREL:
+	  grub_loongarch64_sop_push (&stack, sym_addr - (grub_uint64_t)place);
+	  break;
+	case R_LARCH_B26:
+	  {
+	    grub_uint32_t *abs_place = place;
+	    grub_ssize_t off = sym_addr - (grub_addr_t) place;
+
+	    grub_loongarch64_b26 (abs_place, off);
+	  }
+	  break;
+	case R_LARCH_ABS_HI20:
+	  {
+	    grub_uint32_t *abs_place = place;
+	    grub_loongarch64_xxx_hi20 (abs_place, sym_addr);
+	  }
+	  break;
+	case R_LARCH_ABS64_LO20:
+	  {
+	    grub_uint32_t *abs_place = place;
+	    grub_loongarch64_xxx64_lo20 (abs_place, sym_addr);
+	  }
+	  break;
+	case R_LARCH_ABS64_HI12:
+	  {
+	    grub_uint32_t *abs_place = place;
+	    grub_loongarch64_xxx64_hi12 (abs_place, sym_addr);
+	  }
+	  break;
+	case R_LARCH_PCALA_HI20:
+	  {
+	    grub_uint32_t *abs_place = place;
+	    grub_int32_t off = (((sym_addr + 0x800) & ~0xfffULL) - ((grub_addr_t)place & ~0xfffULL));
+
+	    grub_loongarch64_xxx_hi20 (abs_place, off);
+	  }
+	  break;
+	case R_LARCH_ABS_LO12:
+	case R_LARCH_PCALA_LO12:
+	  {
+	    grub_uint32_t *abs_place = place;
+	    grub_loongarch64_xxx_lo12 (abs_place, sym_addr);
+	  }
+	  break;
+	GRUB_LOONGARCH64_RELOCATION (&stack, place, sym_addr)
+	default:
+	  {
+	    char rel_info[17]; /* log16(2^64) = 16, plus NUL.  */
+
+	    grub_snprintf (rel_info, sizeof (rel_info) - 1, "%" PRIxGRUB_UINT64_T,
+			   (grub_uint64_t) ELF_R_TYPE (rel->r_info));
+	    return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
+			       N_("relocation 0x%s is not implemented yet"), rel_info);
+	  }
+	  break;
+	}
+    }
+  return GRUB_ERR_NONE;
+}
diff --git a/grub-core/kern/loongarch64/dl_helper.c b/grub-core/kern/loongarch64/dl_helper.c
new file mode 100644
index 0000000..68275fe
--- /dev/null
+++ b/grub-core/kern/loongarch64/dl_helper.c
@@ -0,0 +1,264 @@
+/* dl_helper.c - relocation helper functions for modules and grub-mkimage */
+/*
+ *  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/dl.h>
+#include <grub/elf.h>
+#include <grub/misc.h>
+#include <grub/err.h>
+#include <grub/mm.h>
+#include <grub/i18n.h>
+#include <grub/loongarch64/reloc.h>
+
+static void grub_loongarch64_stack_push (grub_loongarch64_stack_t stack, grub_uint64_t x);
+static grub_uint64_t grub_loongarch64_stack_pop (grub_loongarch64_stack_t stack);
+
+void
+grub_loongarch64_stack_init (grub_loongarch64_stack_t stack)
+{
+  stack->top = -1;
+  stack->count = LOONGARCH64_STACK_MAX;
+}
+
+static void
+grub_loongarch64_stack_push (grub_loongarch64_stack_t stack, grub_uint64_t x)
+{
+  if (stack->top == stack->count)
+    return;
+  stack->data[++stack->top] = x;
+}
+
+static grub_uint64_t
+grub_loongarch64_stack_pop (grub_loongarch64_stack_t stack)
+{
+  if (stack->top == -1)
+    return -1;
+  return stack->data[stack->top--];
+}
+
+void
+grub_loongarch64_sop_push (grub_loongarch64_stack_t stack, grub_int64_t offset)
+{
+  grub_loongarch64_stack_push (stack, offset);
+}
+
+/* opr2 = pop (), opr1 = pop (), push (opr1 - opr2) */
+void
+grub_loongarch64_sop_sub (grub_loongarch64_stack_t stack)
+{
+  grub_uint64_t a, b;
+  b = grub_loongarch64_stack_pop (stack);
+  a = grub_loongarch64_stack_pop (stack);
+  grub_loongarch64_stack_push (stack, a - b);
+}
+
+/* opr2 = pop (), opr1 = pop (), push (opr1 << opr2) */
+void
+grub_loongarch64_sop_sl (grub_loongarch64_stack_t stack)
+{
+  grub_uint64_t a, b;
+  b = grub_loongarch64_stack_pop (stack);
+  a = grub_loongarch64_stack_pop (stack);
+  grub_loongarch64_stack_push (stack, a << b);
+}
+
+/* opr2 = pop (), opr1 = pop (), push (opr1 >> opr2) */
+void
+grub_loongarch64_sop_sr (grub_loongarch64_stack_t stack)
+{
+  grub_uint64_t a, b;
+  b = grub_loongarch64_stack_pop (stack);
+  a = grub_loongarch64_stack_pop (stack);
+  grub_loongarch64_stack_push (stack, a >> b);
+}
+
+/* opr2 = pop (), opr1 = pop (), push (opr1 + opr2) */
+void
+grub_loongarch64_sop_add (grub_loongarch64_stack_t stack)
+{
+  grub_uint64_t a, b;
+  b = grub_loongarch64_stack_pop (stack);
+  a = grub_loongarch64_stack_pop (stack);
+  grub_loongarch64_stack_push (stack, a + b);
+}
+
+/* opr2 = pop (), opr1 = pop (), push (opr1 & opr2) */
+void
+grub_loongarch64_sop_and (grub_loongarch64_stack_t stack)
+{
+  grub_uint64_t a, b;
+  b = grub_loongarch64_stack_pop (stack);
+  a = grub_loongarch64_stack_pop (stack);
+  grub_loongarch64_stack_push (stack, a & b);
+}
+
+/* opr3 = pop (), opr2 = pop (), opr1 = pop (), push (opr1 ? opr2 : opr3) */
+void
+grub_loongarch64_sop_if_else (grub_loongarch64_stack_t stack)
+{
+  grub_uint64_t a, b, c;
+  c = grub_loongarch64_stack_pop (stack);
+  b = grub_loongarch64_stack_pop (stack);
+  a = grub_loongarch64_stack_pop (stack);
+
+  if (a) {
+      grub_loongarch64_stack_push (stack, b);
+  } else {
+      grub_loongarch64_stack_push (stack, c);
+  }
+}
+
+/* opr1 = pop (), (*(uint32_t *) PC) [14 ... 10] = opr1 [4 ... 0] */
+void
+grub_loongarch64_sop_32_s_10_5 (grub_loongarch64_stack_t stack,
+				grub_uint64_t *place)
+{
+  grub_uint64_t a = grub_loongarch64_stack_pop (stack);
+  *place |= ((a & 0x1f) << 10);
+}
+
+/* opr1 = pop (), (*(uint32_t *) PC) [21 ... 10] = opr1 [11 ... 0] */
+void
+grub_loongarch64_sop_32_u_10_12 (grub_loongarch64_stack_t stack,
+				 grub_uint64_t *place)
+{
+  grub_uint64_t a = grub_loongarch64_stack_pop (stack);
+  *place = *place | ((a & 0xfff) << 10);
+}
+
+/* opr1 = pop (), (*(uint32_t *) PC) [21 ... 10] = opr1 [11 ... 0] */
+void
+grub_loongarch64_sop_32_s_10_12 (grub_loongarch64_stack_t stack,
+				 grub_uint64_t *place)
+{
+  grub_uint64_t a = grub_loongarch64_stack_pop (stack);
+  *place = (*place) | ((a & 0xfff) << 10);
+}
+
+/* opr1 = pop (), (*(uint32_t *) PC) [25 ... 10] = opr1 [15 ... 0] */
+void
+grub_loongarch64_sop_32_s_10_16 (grub_loongarch64_stack_t stack,
+				 grub_uint64_t *place)
+{
+  grub_uint64_t a = grub_loongarch64_stack_pop (stack);
+  *place = (*place) | ((a & 0xffff) << 10);
+}
+
+/* opr1 = pop (), (*(uint32_t *) PC) [25 ... 10] = opr1 [17 ... 2] */
+void
+grub_loongarch64_sop_32_s_10_16_s2 (grub_loongarch64_stack_t stack,
+				    grub_uint64_t *place)
+{
+  grub_uint64_t a = grub_loongarch64_stack_pop (stack);
+  *place = (*place) | (((a >> 2) & 0xffff) << 10);
+}
+
+/* opr1 = pop (), (*(uint32_t *) PC) [24 ... 5] = opr1 [19 ... 0] */
+void
+grub_loongarch64_sop_32_s_5_20 (grub_loongarch64_stack_t stack, grub_uint64_t *place)
+{
+  grub_uint64_t a = grub_loongarch64_stack_pop (stack);
+  *place = (*place) | ((a & 0xfffff)<<5);
+}
+
+/* opr1 = pop (),
+   (*(uint32_t *) PC) [4 ... 0] = opr1 [22 ... 18]
+   (*(uint32_t *) PC) [25 ...10] = opr1 [17 ... 2]
+ */
+void
+grub_loongarch64_sop_32_s_0_5_10_16_s2 (grub_loongarch64_stack_t stack,
+					grub_uint64_t *place)
+{
+  grub_uint64_t a = grub_loongarch64_stack_pop (stack);
+
+  *place =(*place) | (((a >> 2) & 0xffff) << 10);
+  *place =(*place) | ((a >> 18) & 0x1f);
+}
+
+/*
+   opr1 = pop ()
+   (*(uint32_t *) PC) [9 ... 0] = opr1 [27 ... 18],
+   (*(uint32_t *) PC) [25 ... 10] = opr1 [17 ... 2]
+*/
+void
+grub_loongarch64_sop_32_s_0_10_10_16_s2 (grub_loongarch64_stack_t stack,
+					 grub_uint64_t *place)
+{
+  grub_uint64_t a = grub_loongarch64_stack_pop (stack);
+  *place =(*place) | (((a >> 2) & 0xffff) << 10);
+  *place =(*place) | ((a >> 18) & 0x3ff);
+}
+
+void grub_loongarch64_b26 (grub_uint32_t *place, grub_int64_t offset)
+{
+  grub_uint32_t val;
+  const grub_uint32_t insmask = grub_cpu_to_le32_compile_time (0xfc000000);
+
+  grub_dprintf ("dl", "  reloc_xxxx64 %p %c= 0x%llx\n",
+		place, offset > 0 ? '+' : '-',
+		offset < 0 ? (long long) -(unsigned long long) offset : offset);
+
+  val = ((offset >> 18) & 0x3ff) | (((offset >> 2) & 0xffff) << 10);
+
+  *place &= insmask;
+  *place |= grub_cpu_to_le32 (val) & ~insmask;
+}
+
+void grub_loongarch64_xxx_hi20 (grub_uint32_t *place, grub_int64_t offset)
+{
+  const grub_uint32_t insmask = grub_cpu_to_le32_compile_time (0xfe00001f);
+  grub_uint32_t val;
+
+  offset >>= 12;
+  val = ((offset & 0xfffff) << 5);
+
+  *place &= insmask;
+  *place |= grub_cpu_to_le32 (val) & ~insmask;
+}
+
+void grub_loongarch64_xxx_lo12 (grub_uint32_t *place, grub_int64_t offset)
+{
+  const grub_uint32_t insmask = grub_cpu_to_le32_compile_time (0xffc003ff);
+
+  *place &= insmask;
+  *place |= grub_cpu_to_le32 (offset << 10) & ~insmask;
+}
+
+void grub_loongarch64_xxx64_hi12 (grub_uint32_t *place, grub_int64_t offset)
+{
+  const grub_uint32_t insmask = grub_cpu_to_le32_compile_time (0xffc003ff);
+  grub_uint32_t val;
+
+  offset >>= 52;
+  val = ((offset & 0xfff) << 10);
+
+  *place &= insmask;
+  *place |= grub_cpu_to_le32 (val) & ~insmask;
+}
+
+void grub_loongarch64_xxx64_lo20 (grub_uint32_t *place, grub_int64_t offset)
+{
+  const grub_uint32_t insmask = grub_cpu_to_le32_compile_time (0xfe00001f);
+  grub_uint32_t val;
+
+  offset >>= 32;
+  val = ((offset & 0xfffff) << 5);
+
+  *place &= insmask;
+  *place |= grub_cpu_to_le32 (val) & ~insmask;
+}
diff --git a/grub-core/kern/loongarch64/efi/init.c b/grub-core/kern/loongarch64/efi/init.c
new file mode 100644
index 0000000..7f7c866
--- /dev/null
+++ b/grub-core/kern/loongarch64/efi/init.c
@@ -0,0 +1,73 @@
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2013 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/env.h>
+#include <grub/kernel.h>
+#include <grub/misc.h>
+#include <grub/mm.h>
+#include <grub/time.h>
+#include <grub/cpu/time.h>
+#include <grub/efi/efi.h>
+#include <grub/loader.h>
+
+static grub_uint64_t tmr;
+static grub_efi_event_t tmr_evt;
+
+static grub_uint64_t
+grub_efi_get_time_ms (void)
+{
+  return tmr;
+}
+
+static void
+grub_loongson_increment_timer (grub_efi_event_t event __attribute__ ((unused)),
+                 void *context __attribute__ ((unused)))
+{
+  tmr += 10;
+}
+
+void
+grub_machine_init (void)
+{
+  grub_efi_boot_services_t *b;
+
+  grub_efi_init ();
+
+  b = grub_efi_system_table->boot_services;
+  efi_call_5 (b->create_event, GRUB_EFI_EVT_TIMER | GRUB_EFI_EVT_NOTIFY_SIGNAL,
+              GRUB_EFI_TPL_CALLBACK, grub_loongson_increment_timer, NULL, &tmr_evt);
+  efi_call_3 (b->set_timer, tmr_evt, GRUB_EFI_TIMER_PERIODIC, 100000);
+
+  grub_install_get_time_ms (grub_efi_get_time_ms);
+}
+
+void
+grub_machine_fini (int flags)
+{
+  grub_efi_boot_services_t *b;
+
+  if (!(flags & GRUB_LOADER_FLAG_NORETURN))
+    return;
+
+  b = grub_efi_system_table->boot_services;
+
+  efi_call_3 (b->set_timer, tmr_evt, GRUB_EFI_TIMER_CANCEL, 0);
+  efi_call_1 (b->close_event, tmr_evt);
+
+  grub_efi_fini ();
+}
diff --git a/grub-core/kern/loongarch64/efi/startup.S b/grub-core/kern/loongarch64/efi/startup.S
new file mode 100644
index 0000000..1ffff08
--- /dev/null
+++ b/grub-core/kern/loongarch64/efi/startup.S
@@ -0,0 +1,45 @@
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2013  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>
+
+	.file 	"startup.S"
+	.text
+	.globl start, _start
+	.align		4
+
+FUNCTION(start)
+FUNCTION(_start)
+	/*
+	 *  EFI_SYSTEM_TABLE and EFI_HANDLE are passed in a1/a0.
+	 */
+	addi.d		$sp, $sp, -16
+	st.d		$ra, $sp, 0
+
+	la 		$a2, grub_efi_image_handle
+	st.d		$a0, $a2, 0
+	la		$a2, grub_efi_system_table
+	st.d		$a1, $a2, 0
+
+	bl		grub_main
+
+1:
+	ld.d		$ra, $sp, 0
+	addi.d		$sp, $sp, 16
+	jr		$ra
+
diff --git a/grub-core/kern/loongarch64/init.c b/grub-core/kern/loongarch64/init.c
new file mode 100644
index 0000000..b2de930
--- /dev/null
+++ b/grub-core/kern/loongarch64/init.c
@@ -0,0 +1,47 @@
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2009,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/>.
+ */
+
+#include <grub/kernel.h>
+#include <grub/env.h>
+#include <grub/time.h>
+#include <grub/cpu/loongarch64.h>
+
+grub_uint32_t grub_arch_cpuclock;
+
+/* FIXME: use interrupt to count high.  */
+grub_uint64_t
+grub_get_rtc (void)
+{
+  static grub_uint32_t high = 0;
+  static grub_uint32_t last = 0;
+  grub_uint32_t low;
+
+  asm volatile ("csrrd %0, " GRUB_CPU_LOONGARCH_COP0_TIMER_COUNT : "=r" (low));
+  if (low < last)
+    high++;
+  last = low;
+
+  return (((grub_uint64_t) high) << 32) | low;
+}
+
+void
+grub_timer_init (grub_uint32_t cpuclock)
+{
+  grub_arch_cpuclock = cpuclock;
+  grub_install_get_time_ms (grub_rtc_get_time_ms);
+}
diff --git a/grub-core/lib/gnulib-patches/fix-loongarch.patch b/grub-core/lib/gnulib-patches/fix-loongarch.patch
new file mode 100644
index 0000000..fa0b09a
--- /dev/null
+++ b/grub-core/lib/gnulib-patches/fix-loongarch.patch
@@ -0,0 +1,26 @@
+diff --git a/build-aux/config.guess b/build-aux/config.guess
+index 8e2a58b..927581d 100755
+--- a/build-aux/config.guess
++++ b/build-aux/config.guess
+@@ -990,6 +990,9 @@ EOF
+     k1om:Linux:*:*)
+ 	echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+ 	exit ;;
++    loongarch32:Linux:*:* | loongarch64:Linux:*:* | loongarchx32:Linux:*:*)
++	echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
++	exit ;;
+     m32r*:Linux:*:*)
+ 	echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+ 	exit ;;
+diff --git a/build-aux/config.sub b/build-aux/config.sub
+index 1fc4cde..6303428 100755
+--- a/build-aux/config.sub
++++ b/build-aux/config.sub
+@@ -1184,6 +1184,7 @@ case $cpu-$vendor in
+ 			| k1om \
+ 			| le32 | le64 \
+ 			| lm32 \
++			| loongarch32 | loongarch64 | loongarchx32 \
+ 			| m32c | m32r | m32rle \
+ 			| m5200 | m68000 | m680[012346]0 | m68360 | m683?2 | m68k | v70 | w65 \
+ 			| m6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x | nvptx | picochip \
diff --git a/grub-core/lib/loongarch64/relocator.c b/grub-core/lib/loongarch64/relocator.c
new file mode 100644
index 0000000..faa4553
--- /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, 0,
+					  (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 0000000..ffdccc9
--- /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/lib/loongarch64/setjmp.S b/grub-core/lib/loongarch64/setjmp.S
new file mode 100644
index 0000000..bb09959
--- /dev/null
+++ b/grub-core/lib/loongarch64/setjmp.S
@@ -0,0 +1,68 @@
+/*
+ *  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/dl.h>
+
+	.file	"setjmp.S"
+
+GRUB_MOD_LICENSE "GPLv3+"
+
+	.text
+
+/*
+ * int grub_setjmp (jmp_buf env)
+ */
+FUNCTION(grub_setjmp)
+	st.d $s0, $a0, 0x0
+	st.d $s1, $a0, 0x8
+	st.d $s2, $a0, 0x10
+	st.d $s3, $a0, 0x18
+	st.d $s4, $a0, 0x20
+	st.d $s5, $a0, 0x28
+	st.d $s6, $a0, 0x30
+	st.d $s7, $a0, 0x38
+	st.d $s8, $a0, 0x40
+	st.d $fp, $a0, 0x48
+	st.d $sp, $a0, 0x50
+	st.d $ra, $a0, 0x58
+
+	move $a0, $zero
+	jr   $ra
+
+/*
+ * void grub_longjmp (jmp_buf env, int val)
+ */
+FUNCTION(grub_longjmp)
+	ld.d $s0, $a0, 0x0
+	ld.d $s1, $a0, 0x8
+	ld.d $s2, $a0, 0x10
+	ld.d $s3, $a0, 0x18
+	ld.d $s4, $a0, 0x20
+	ld.d $s5, $a0, 0x28
+	ld.d $s6, $a0, 0x30
+	ld.d $s7, $a0, 0x38
+	ld.d $s8, $a0, 0x40
+	ld.d $fp, $a0, 0x48
+	ld.d $sp, $a0, 0x50
+	ld.d $ra, $a0, 0x58
+
+	li.w $a0, 1
+	beqz $a1, .L0
+	move $a0, $a1
+.L0:
+	jr   $ra
diff --git a/grub-core/lib/setjmp.S b/grub-core/lib/setjmp.S
index aa297ab..da71fc7 100644
--- a/grub-core/lib/setjmp.S
+++ b/grub-core/lib/setjmp.S
@@ -15,6 +15,8 @@
 #include "./arm/setjmp.S"
 #elif defined(__aarch64__)
 #include "./arm64/setjmp.S"
+#elif defined(__loongarch64)
+#include "./loongarch64/setjmp.S"
 #elif defined(__riscv)
 #include "./riscv/setjmp.S"
 #else
diff --git a/grub-core/loader/efi/chainloader.c b/grub-core/loader/efi/chainloader.c
index fb874f1..faf60ef 100644
--- a/grub-core/loader/efi/chainloader.c
+++ b/grub-core/loader/efi/chainloader.c
@@ -345,6 +345,8 @@ static const grub_uint16_t machine_type __attribute__((__unused__)) =
   GRUB_PE32_MACHINE_I386;
 #elif defined(__ia64__)
   GRUB_PE32_MACHINE_IA64;
+#elif defined(__loongarch64)
+  GRUB_PE32_MACHINE_LOONGARCH64;
 #elif defined(__riscv) && (__riscv_xlen == 32)
   GRUB_PE32_MACHINE_RISCV32;
 #elif defined(__riscv) && (__riscv_xlen == 64)
diff --git a/grub-core/loader/loongarch64/linux-efi.c b/grub-core/loader/loongarch64/linux-efi.c
new file mode 100644
index 0000000..4dcefd9
--- /dev/null
+++ b/grub-core/loader/loongarch64/linux-efi.c
@@ -0,0 +1,144 @@
+/*
+ *  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/fdt.h>
+#include <grub/efi/efi.h>
+#include <grub/cpu/linux.h>
+#include <grub/efi/memory.h>
+#include <grub/efi/fdtload.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)
+{
+  int node, retval;
+
+  void *fdt;
+
+  fdt = grub_fdt_load (GRUB_EFI_LINUX_FDT_EXTRA_SPACE);
+
+  if (!fdt)
+    goto failure;
+
+  node = grub_fdt_find_subnode (fdt, 0, "chosen");
+  if (node < 0)
+    node = grub_fdt_add_subnode (fdt, 0, "chosen");
+
+  if (node < 1)
+    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 (fdt, node, "linux,initrd-start",
+				    kernel_params->ramdisk_addr);
+      if (retval)
+	goto failure;
+      retval = grub_fdt_set_prop64 (fdt, node, "linux,initrd-end",
+				    kernel_params->ramdisk_addr + kernel_params->ramdisk_size);
+      if (retval)
+	goto failure;
+    }
+
+  if (grub_fdt_install() != GRUB_ERR_NONE)
+    goto failure;
+
+  return GRUB_ERR_NONE;
+
+failure:
+  grub_fdt_unload();
+  return grub_error(GRUB_ERR_BAD_OS, "failed to install/update FDT");
+}
+
+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 0000000..85585b4
--- /dev/null
+++ b/grub-core/loader/loongarch64/linux-elf.c
@@ -0,0 +1,529 @@
+/*
+ *  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>
+
+#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 } \
+    }
+
+static struct grub_relocator *relocator;
+
+void grub_linux_loongarch_elf_relocator_unload (void)
+{
+  grub_relocator_unload (relocator);
+}
+
+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 ("rd_start=0xXXXXXXXXXXXXXXXX"), 4) \
+	  + ALIGN_UP (sizeof ("rd_size=0xXXXXXXXXXXXXXXXX"), 4) \
+	  + ALIGN_UP (sizeof ("initrd=0xXXXXXXXXXXXXXXXX,0xXXXXXXXXXXXXXXXX"),
+		      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 ("rd_start=0xXXXXXXXXXXXXXXXX"),
+		 "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 ("rd_start=0xXXXXXXXXXXXXXXXX"), 4);
+    kernel_params->linux_argc++;
+
+    /* rd_size */
+    grub_snprintf (linux_args,
+		 sizeof ("rd_size=0xXXXXXXXXXXXXXXXX"),
+		 "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 ("rd_size=0xXXXXXXXXXXXXXXXX"), 4);
+    kernel_params->linux_argc++;
+
+    /* initrd */
+    grub_snprintf (linux_args,
+		 sizeof ("initrd=0xXXXXXXXXXXXXXXXX,0xXXXXXXXXXXXXXXXX"),
+		 "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 ("initrd=0xXXXXXXXXXXXXXXXX,0xXXXXXXXXXXXXXXXX"), 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_printf("not find param, is fdt boot\n");
+      if (allocate_fdt_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->fdt; /* a1 = fdt */
+      state.gpr[6] = 0; /* a2 = flag */
+    } else {
+      grub_printf("find param, is 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);
+}
+
+int
+grub_linux_loongarch_elf_get_boot_params (struct boot_params_interface **boot_params)
+{
+  grub_efi_configuration_table_t *tables;
+  grub_efi_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 0000000..783054b
--- /dev/null
+++ b/grub-core/loader/loongarch64/linux.c
@@ -0,0 +1,398 @@
+/*
+ *  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)
+
+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;
+
+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);
+  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),
+					cmdline_size,
+					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;
+}
+
+/*
+ * This function returns a pointer to a legally allocated initrd buffer,
+ * or NULL if unsuccessful
+ */
+static void *
+allocate_initrd_mem (int initrd_pages)
+{
+  grub_addr_t max_addr;
+
+  if (grub_efi_get_ram_base (&max_addr) != GRUB_ERR_NONE)
+    return NULL;
+
+  max_addr += INITRD_MAX_ADDRESS_OFFSET - 1;
+
+  return grub_efi_allocate_pages_real (max_addr, initrd_pages,
+                                       GRUB_EFI_ALLOCATE_MAX_ADDRESS,
+                                       GRUB_EFI_LOADER_DATA);
+}
+
+
+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;
+
+  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");
+
+  if (is_bpi_boot == 0) {
+      grub_size_t initrd_pages;
+      initrd_pages = (GRUB_EFI_BYTES_TO_PAGES (initrd_size));
+      initrd_mem = allocate_initrd_mem (initrd_pages);
+  } else {
+      grub_err_t err;
+      initrd_mem = grub_linux_loongarch_alloc_virtual_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, argv, 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);
+  if (is_bpi_boot == 0) {
+      if (initrd_mem && !kernel_params.ramdisk_addr)
+	grub_efi_free_pages ((grub_addr_t) initrd_mem,
+			     GRUB_EFI_BYTES_TO_PAGES (initrd_size));
+  }
+  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/efi/api.h b/include/grub/efi/api.h
index f431f49..a74ce39 100644
--- a/include/grub/efi/api.h
+++ b/include/grub/efi/api.h
@@ -2104,7 +2104,7 @@ typedef struct grub_efi_ip6_config_manual_address grub_efi_ip6_config_manual_add
 
 #if (GRUB_TARGET_SIZEOF_VOID_P == 4) || defined (__ia64__) \
   || defined (__aarch64__) || defined (__MINGW64__) || defined (__CYGWIN__) \
-  || defined(__riscv)
+  || defined(__riscv) || defined (__loongarch64)
 
 #define efi_call_0(func)		func()
 #define efi_call_1(func, a)		func(a)
diff --git a/include/grub/efi/efi.h b/include/grub/efi/efi.h
index 8dfc89a..c816359 100644
--- a/include/grub/efi/efi.h
+++ b/include/grub/efi/efi.h
@@ -135,13 +135,17 @@ extern void (*EXPORT_VAR(grub_efi_net_config)) (grub_efi_handle_t hnd,
 						char **device,
 						char **path);
 
-#if defined(__arm__) || defined(__aarch64__) || defined(__riscv)
+#if defined(__arm__) || defined(__aarch64__) || defined(__riscv) || defined(__loongarch__)
 void *EXPORT_FUNC(grub_efi_get_firmware_fdt)(void);
 grub_err_t EXPORT_FUNC(grub_efi_get_ram_base)(grub_addr_t *);
 #include <grub/cpu/linux.h>
 grub_err_t grub_arch_efi_linux_check_image(struct linux_arch_kernel_header *lh);
+#if defined(__loongarch__)
+grub_err_t grub_arch_efi_linux_boot_image(grub_addr_t addr, grub_size_t size, char *args);
+#else
 grub_err_t grub_arch_efi_linux_boot_image(grub_addr_t addr, char *args);
 #endif
+#endif
 
 grub_addr_t grub_efi_section_addr (const char *section);
 
diff --git a/include/grub/efi/pe32.h b/include/grub/efi/pe32.h
index a43adf2..f8f2402 100644
--- a/include/grub/efi/pe32.h
+++ b/include/grub/efi/pe32.h
@@ -79,6 +79,8 @@ struct grub_pe32_coff_header
 #define GRUB_PE32_MACHINE_ARM64			0xAA64
 #define GRUB_PE32_MACHINE_RISCV32		0x5032
 #define GRUB_PE32_MACHINE_RISCV64		0x5064
+#define GRUB_PE32_MACHINE_LOONGARCH32     0x6232
+#define GRUB_PE32_MACHINE_LOONGARCH64     0x6264
 
 #define GRUB_PE32_RELOCS_STRIPPED		0x0001
 #define GRUB_PE32_EXECUTABLE_IMAGE		0x0002
@@ -338,6 +340,8 @@ struct grub_pe32_fixup_block
 #define GRUB_PE32_REL_BASED_ARM_MOV32T  7
 #define GRUB_PE32_REL_BASED_RISCV_LOW12I 7
 #define GRUB_PE32_REL_BASED_RISCV_LOW12S 8
+#define GRUB_PE32_REL_BASED_LOONGARCH32_MARK_LA 8
+#define GRUB_PE32_REL_BASED_LOONGARCH64_MARK_LA 8
 #define GRUB_PE32_REL_BASED_IA64_IMM64	9
 #define GRUB_PE32_REL_BASED_DIR64	10
 #define GRUB_PE32_REL_BASED_HIGH3ADJ	11
diff --git a/include/grub/elf.h b/include/grub/elf.h
index c478933..73175bd 100644
--- a/include/grub/elf.h
+++ b/include/grub/elf.h
@@ -248,6 +248,7 @@ typedef struct
 #define EM_NUM		95
 #define EM_AARCH64	183		/* ARM 64-bit architecture */
 #define EM_RISCV	243		/* RISC-V */
+#define EM_LOONGARCH	258		/* LoongArch */
 
 /* If it is necessary to assign new unofficial EM_* values, please
    pick large random numbers (0x8523, 0xa7f2, etc.) to minimize the
@@ -2531,6 +2532,35 @@ typedef Elf32_Addr Elf32_Conflict;
 #define R_RISCV_SET32           56
 #define R_RISCV_32_PCREL        57
 
+/* LoongArch relocations */
+#define R_LARCH_NONE                         0
+#define R_LARCH_64                           2
+#define R_LARCH_MARK_LA                      20
+#define R_LARCH_SOP_PUSH_PCREL               22
+#define R_LARCH_SOP_PUSH_ABSOLUTE            23
+#define R_LARCH_SOP_PUSH_PLT_PCREL           29
+#define R_LARCH_SOP_SUB                      32
+#define R_LARCH_SOP_SL                       33
+#define R_LARCH_SOP_SR                       34
+#define R_LARCH_SOP_ADD                      35
+#define R_LARCH_SOP_AND                      36
+#define R_LARCH_SOP_IF_ELSE                  37
+#define R_LARCH_SOP_POP_32_S_10_5            38
+#define R_LARCH_SOP_POP_32_U_10_12           39
+#define R_LARCH_SOP_POP_32_S_10_12           40
+#define R_LARCH_SOP_POP_32_S_10_16           41
+#define R_LARCH_SOP_POP_32_S_10_16_S2        42
+#define R_LARCH_SOP_POP_32_S_5_20            43
+#define R_LARCH_SOP_POP_32_S_0_5_10_16_S2    44
+#define R_LARCH_SOP_POP_32_S_0_10_10_16_S2   45
+#define R_LARCH_B26                          66
+#define R_LARCH_ABS_HI20                     67
+#define R_LARCH_ABS_LO12                     68
+#define R_LARCH_ABS64_LO20                   69
+#define R_LARCH_ABS64_HI12                   70
+#define R_LARCH_PCALA_HI20                   71
+#define R_LARCH_PCALA_LO12                   72
+
 #ifdef GRUB_TARGET_WORDSIZE
 #if GRUB_TARGET_WORDSIZE == 32
 
diff --git a/include/grub/fdt.h b/include/grub/fdt.h
index 3514aa4..ba2f9a9 100644
--- a/include/grub/fdt.h
+++ b/include/grub/fdt.h
@@ -20,7 +20,7 @@
 #define GRUB_FDT_HEADER	1
 
 #if !defined(GRUB_MACHINE_EMU) && \
-    (defined(__arm__) || defined(__aarch64__) || defined(__riscv))
+    (defined(__arm__) || defined(__aarch64__) || defined(__riscv) || defined(__loongarch__))
 
 #include <grub/types.h>
 #include <grub/symbol.h>
@@ -148,6 +148,6 @@ int EXPORT_FUNC(grub_fdt_set_prop) (void *fdt, unsigned int nodeoffset, const ch
 })
 
 #endif /* !defined(GRUB_MACHINE_EMU) && \
-          (defined(__arm__) || defined(__aarch64__) || defined(__riscv)) */
+          (defined(__arm__) || defined(__aarch64__) || defined(__riscv) || defined(__loongarch__)) */
 
 #endif	/* ! GRUB_FDT_HEADER */
diff --git a/include/grub/loongarch64/efi/loader.h b/include/grub/loongarch64/efi/loader.h
new file mode 100644
index 0000000..71a0159
--- /dev/null
+++ b/include/grub/loongarch64/efi/loader.h
@@ -0,0 +1,25 @@
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2002,2003,2004,2006,2007,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_LOADER_MACHINE_HEADER
+#define GRUB_LOADER_MACHINE_HEADER	1
+
+#include <grub/types.h>
+#include <grub/symbol.h>
+
+#endif /* ! GRUB_LOADER_MACHINE_HEADER */
diff --git a/include/grub/loongarch64/efi/memory.h b/include/grub/loongarch64/efi/memory.h
new file mode 100644
index 0000000..2d3f36e
--- /dev/null
+++ b/include/grub/loongarch64/efi/memory.h
@@ -0,0 +1,15 @@
+#ifndef GRUB_MEMORY_CPU_HEADER
+#include <grub/efi/memory.h>
+
+
+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/efi/time.h b/include/grub/loongarch64/efi/time.h
new file mode 100644
index 0000000..e69de29
diff --git a/include/grub/loongarch64/io.h b/include/grub/loongarch64/io.h
new file mode 100644
index 0000000..5f34103
--- /dev/null
+++ b/include/grub/loongarch64/io.h
@@ -0,0 +1,62 @@
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2009,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_IO_H
+#define	GRUB_IO_H	1
+
+#include <grub/types.h>
+
+typedef grub_addr_t grub_port_t;
+
+static __inline unsigned char
+grub_inb (grub_port_t port)
+{
+  return *(volatile grub_uint8_t *) port;
+}
+
+static __inline unsigned short int
+grub_inw (grub_port_t port)
+{
+  return *(volatile grub_uint16_t *) port;
+}
+
+static __inline unsigned int
+grub_inl (grub_port_t port)
+{
+  return *(volatile grub_uint32_t *) port;
+}
+
+static __inline void
+grub_outb (unsigned char value, grub_port_t port)
+{
+  *(volatile grub_uint8_t *) port = value;
+}
+
+static __inline void
+grub_outw (unsigned short int value, grub_port_t port)
+{
+  *(volatile grub_uint16_t *) port = value;
+}
+
+static __inline void
+grub_outl (unsigned int value, grub_port_t port)
+{
+  *(volatile grub_uint32_t *) port = value;
+}
+
+#endif /* _SYS_IO_H */
diff --git a/include/grub/loongarch64/linux.h b/include/grub/loongarch64/linux.h
new file mode 100644
index 0000000..af1f51d
--- /dev/null
+++ b/include/grub/loongarch64/linux.h
@@ -0,0 +1,144 @@
+/*
+ *  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' */
+#define linux_arch_kernel_header linux_loongarch64_kernel_header
+
+/* 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         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
+
+#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))
+
+/* 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_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/loongarch64.h b/include/grub/loongarch64/loongarch64.h
new file mode 100644
index 0000000..ea3be3d
--- /dev/null
+++ b/include/grub/loongarch64/loongarch64.h
@@ -0,0 +1,30 @@
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2010,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_REGISTERS_CPU_HEADER
+#define GRUB_REGISTERS_CPU_HEADER	1
+
+#ifdef ASM_FILE
+#define GRUB_CPU_REGISTER_WRAP(x) x
+#else
+#define GRUB_CPU_REGISTER_WRAP(x) #x
+#endif
+
+#define GRUB_CPU_LOONGARCH_COP0_TIMER_COUNT GRUB_CPU_REGISTER_WRAP(9)
+
+#endif
diff --git a/include/grub/loongarch64/memory.h b/include/grub/loongarch64/memory.h
new file mode 100644
index 0000000..cc9faef
--- /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/reloc.h b/include/grub/loongarch64/reloc.h
new file mode 100644
index 0000000..2106ba2
--- /dev/null
+++ b/include/grub/loongarch64/reloc.h
@@ -0,0 +1,113 @@
+/*
+ *  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_LOONGARCH64_RELOC_H
+#define GRUB_LOONGARCH64_RELOC_H 1
+#include <grub/types.h>
+
+#define LOONGARCH64_STACK_MAX 16
+
+struct grub_loongarch64_stack
+{
+  grub_uint64_t data[LOONGARCH64_STACK_MAX];
+  int count;
+  int top;
+};
+
+typedef struct grub_loongarch64_stack* grub_loongarch64_stack_t;
+
+void grub_loongarch64_stack_init	     (grub_loongarch64_stack_t stack);
+void grub_loongarch64_sop_push		     (grub_loongarch64_stack_t stack,
+					      grub_int64_t offset);
+void grub_loongarch64_sop_sub		     (grub_loongarch64_stack_t stack);
+void grub_loongarch64_sop_sl		     (grub_loongarch64_stack_t stack);
+void grub_loongarch64_sop_sr		     (grub_loongarch64_stack_t stack);
+void grub_loongarch64_sop_add		     (grub_loongarch64_stack_t stack);
+void grub_loongarch64_sop_and		     (grub_loongarch64_stack_t stack);
+void grub_loongarch64_sop_if_else	     (grub_loongarch64_stack_t stack);
+void grub_loongarch64_sop_32_s_10_5	     (grub_loongarch64_stack_t stack,
+					      grub_uint64_t *place);
+void grub_loongarch64_sop_32_u_10_12	     (grub_loongarch64_stack_t stack,
+					      grub_uint64_t *place);
+void grub_loongarch64_sop_32_s_10_12	     (grub_loongarch64_stack_t stack,
+					      grub_uint64_t *place);
+void grub_loongarch64_sop_32_s_10_16	     (grub_loongarch64_stack_t stack,
+					      grub_uint64_t *place);
+void grub_loongarch64_sop_32_s_10_16_s2	     (grub_loongarch64_stack_t stack,
+					      grub_uint64_t *place);
+void grub_loongarch64_sop_32_s_5_20	     (grub_loongarch64_stack_t stack,
+					      grub_uint64_t *place);
+void grub_loongarch64_sop_32_s_0_5_10_16_s2  (grub_loongarch64_stack_t stack,
+					      grub_uint64_t *place);
+void grub_loongarch64_sop_32_s_0_10_10_16_s2 (grub_loongarch64_stack_t stack,
+					      grub_uint64_t *place);
+
+void grub_loongarch64_b26	  (grub_uint32_t *place, grub_int64_t offset);
+void grub_loongarch64_xxx_hi20	  (grub_uint32_t *place, grub_int64_t offset);
+void grub_loongarch64_xxx_lo12	  (grub_uint32_t *place, grub_int64_t offset);
+void grub_loongarch64_xxx64_hi12  (grub_uint32_t *place, grub_int64_t offset);
+void grub_loongarch64_xxx64_lo20  (grub_uint32_t *place, grub_int64_t offset);
+
+#define GRUB_LOONGARCH64_RELOCATION(STACK, PLACE, OFFSET)	\
+  case R_LARCH_SOP_PUSH_ABSOLUTE:				\
+    grub_loongarch64_sop_push (STACK, OFFSET);			\
+    break;							\
+  case R_LARCH_SOP_SUB:						\
+    grub_loongarch64_sop_sub (STACK);				\
+    break;							\
+  case R_LARCH_SOP_SL:						\
+    grub_loongarch64_sop_sl (STACK);				\
+    break;							\
+  case R_LARCH_SOP_SR:						\
+    grub_loongarch64_sop_sr (STACK);				\
+    break;							\
+  case R_LARCH_SOP_ADD:						\
+    grub_loongarch64_sop_add (STACK);				\
+    break;							\
+  case R_LARCH_SOP_AND:						\
+    grub_loongarch64_sop_and (STACK);				\
+    break;							\
+  case R_LARCH_SOP_IF_ELSE:					\
+    grub_loongarch64_sop_if_else (STACK);			\
+    break;							\
+  case R_LARCH_SOP_POP_32_S_10_5:				\
+    grub_loongarch64_sop_32_s_10_5 (STACK, PLACE);		\
+    break;							\
+  case R_LARCH_SOP_POP_32_U_10_12:				\
+    grub_loongarch64_sop_32_u_10_12 (STACK, PLACE);		\
+    break;							\
+  case R_LARCH_SOP_POP_32_S_10_12:				\
+    grub_loongarch64_sop_32_s_10_12 (STACK, PLACE);		\
+    break;							\
+  case R_LARCH_SOP_POP_32_S_10_16:				\
+    grub_loongarch64_sop_32_s_10_16 (STACK, PLACE);		\
+    break;							\
+  case R_LARCH_SOP_POP_32_S_10_16_S2:				\
+    grub_loongarch64_sop_32_s_10_16_s2 (STACK, PLACE);		\
+    break;							\
+  case R_LARCH_SOP_POP_32_S_5_20:				\
+    grub_loongarch64_sop_32_s_5_20 (STACK, PLACE);		\
+    break;							\
+  case R_LARCH_SOP_POP_32_S_0_5_10_16_S2:			\
+    grub_loongarch64_sop_32_s_0_5_10_16_s2 (STACK, PLACE);	\
+    break;							\
+  case R_LARCH_SOP_POP_32_S_0_10_10_16_S2:			\
+    grub_loongarch64_sop_32_s_0_10_10_16_s2 (STACK, PLACE);	\
+    break;
+
+#endif /* GRUB_LOONGARCH64_RELOC_H */
diff --git a/include/grub/loongarch64/relocator.h b/include/grub/loongarch64/relocator.h
new file mode 100644
index 0000000..cef3aaa
--- /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 */
diff --git a/include/grub/loongarch64/setjmp.h b/include/grub/loongarch64/setjmp.h
new file mode 100644
index 0000000..d9a0776
--- /dev/null
+++ b/include/grub/loongarch64/setjmp.h
@@ -0,0 +1,27 @@
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2002,2004,2006,2007,2009,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_SETJMP_CPU_HEADER
+#define GRUB_SETJMP_CPU_HEADER	1
+
+typedef grub_uint64_t grub_jmp_buf[12];
+
+int grub_setjmp (grub_jmp_buf env) RETURNS_TWICE;
+void grub_longjmp (grub_jmp_buf env, int val) __attribute__ ((noreturn));
+
+#endif /* ! GRUB_SETJMP_CPU_HEADER */
diff --git a/include/grub/loongarch64/time.h b/include/grub/loongarch64/time.h
new file mode 100644
index 0000000..c9a7334
--- /dev/null
+++ b/include/grub/loongarch64/time.h
@@ -0,0 +1,39 @@
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2003,2004,2005,2007,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 KERNEL_CPU_TIME_HEADER
+#define KERNEL_CPU_TIME_HEADER	1
+
+#ifndef GRUB_UTIL
+
+#define GRUB_TICKS_PER_SECOND	(grub_arch_cpuclock / 2)
+
+void grub_timer_init (grub_uint32_t cpuclock);
+
+/* Return the real time in ticks.  */
+grub_uint64_t grub_get_rtc (void);
+
+extern grub_uint32_t grub_arch_cpuclock;
+#endif
+
+static inline void
+grub_cpu_idle(void)
+{
+}
+
+#endif
diff --git a/include/grub/loongarch64/types.h b/include/grub/loongarch64/types.h
new file mode 100644
index 0000000..2dbefbf
--- /dev/null
+++ b/include/grub/loongarch64/types.h
@@ -0,0 +1,34 @@
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2002,2006,2007,2009,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_TYPES_CPU_HEADER
+#define GRUB_TYPES_CPU_HEADER	1
+
+/* The size of void *.  */
+#define GRUB_TARGET_SIZEOF_VOID_P	8
+
+/* The size of long.  */
+#define GRUB_TARGET_SIZEOF_LONG	8
+
+#ifdef GRUB_CPU_LOONGARCH
+/* loongarch is little-endian.  */
+#undef GRUB_TARGET_WORDS_BIGENDIAN
+
+#endif /* ! GRUB_TYPES_CPU_HEADER */
+
+#endif
diff --git a/include/grub/util/install.h b/include/grub/util/install.h
index 51f3b13..a728afc 100644
--- a/include/grub/util/install.h
+++ b/include/grub/util/install.h
@@ -114,6 +114,7 @@ enum grub_install_plat
     GRUB_INSTALL_PLATFORM_ARM_COREBOOT,
     GRUB_INSTALL_PLATFORM_RISCV32_EFI,
     GRUB_INSTALL_PLATFORM_RISCV64_EFI,
+    GRUB_INSTALL_PLATFORM_LOONGARCH64_EFI,
     GRUB_INSTALL_PLATFORM_MAX
   };
 
diff --git a/util/grub-install-common.c b/util/grub-install-common.c
index 4833407..60d2c00 100644
--- a/util/grub-install-common.c
+++ b/util/grub-install-common.c
@@ -939,6 +939,7 @@ static struct
     [GRUB_INSTALL_PLATFORM_ARM_COREBOOT] =     { "arm",     "coreboot"  },
     [GRUB_INSTALL_PLATFORM_RISCV32_EFI] =      { "riscv32", "efi"       },
     [GRUB_INSTALL_PLATFORM_RISCV64_EFI] =      { "riscv64", "efi"       },
+    [GRUB_INSTALL_PLATFORM_LOONGARCH64_EFI] =  { "loongarch64", "efi"   },
   }; 
 
 char *
diff --git a/util/grub-install.c b/util/grub-install.c
index 5babc7a..c8f15df 100644
--- a/util/grub-install.c
+++ b/util/grub-install.c
@@ -332,6 +332,8 @@ get_default_platform (void)
 #else
    return NULL;
 #endif
+#elif defined (__loongarch64)
+  return "loongarch64-efi";
 #else
    return NULL;
 #endif
@@ -487,6 +489,7 @@ have_bootdev (enum grub_install_plat pl)
     case GRUB_INSTALL_PLATFORM_ARM64_EFI:
     case GRUB_INSTALL_PLATFORM_RISCV32_EFI:
     case GRUB_INSTALL_PLATFORM_RISCV64_EFI:
+    case GRUB_INSTALL_PLATFORM_LOONGARCH64_EFI:
     case GRUB_INSTALL_PLATFORM_I386_IEEE1275:
     case GRUB_INSTALL_PLATFORM_SPARC64_IEEE1275:
     case GRUB_INSTALL_PLATFORM_POWERPC_IEEE1275:
@@ -906,6 +909,7 @@ main (int argc, char *argv[])
     case GRUB_INSTALL_PLATFORM_I386_EFI:
     case GRUB_INSTALL_PLATFORM_IA64_EFI:
     case GRUB_INSTALL_PLATFORM_X86_64_EFI:
+    case GRUB_INSTALL_PLATFORM_LOONGARCH64_EFI:
       is_efi = 1;
       grub_util_error (_("this utility cannot be used for EFI platforms"
                          " because it does not support UEFI Secure Boot"));
@@ -933,6 +937,7 @@ main (int argc, char *argv[])
     case GRUB_INSTALL_PLATFORM_ARM64_EFI:
     case GRUB_INSTALL_PLATFORM_RISCV32_EFI:
     case GRUB_INSTALL_PLATFORM_RISCV64_EFI:
+    case GRUB_INSTALL_PLATFORM_LOONGARCH64_EFI:
     case GRUB_INSTALL_PLATFORM_IA64_EFI:
     case GRUB_INSTALL_PLATFORM_I386_IEEE1275:
     case GRUB_INSTALL_PLATFORM_SPARC64_IEEE1275:
@@ -980,6 +985,7 @@ main (int argc, char *argv[])
     case GRUB_INSTALL_PLATFORM_ARM64_EFI:
     case GRUB_INSTALL_PLATFORM_RISCV32_EFI:
     case GRUB_INSTALL_PLATFORM_RISCV64_EFI:
+    case GRUB_INSTALL_PLATFORM_LOONGARCH64_EFI:
     case GRUB_INSTALL_PLATFORM_IA64_EFI:
     case GRUB_INSTALL_PLATFORM_I386_IEEE1275:
     case GRUB_INSTALL_PLATFORM_ARM_UBOOT:
@@ -1133,6 +1139,9 @@ main (int argc, char *argv[])
 	    case GRUB_INSTALL_PLATFORM_RISCV64_EFI:
 	      efi_file = "BOOTRISCV64.EFI";
 	      break;
+            case GRUB_INSTALL_PLATFORM_LOONGARCH64_EFI:
+              efi_file = "BOOTLOONGARCH64.EFI";
+              break;
 	    default:
 	      grub_util_error ("%s", _("You've found a bug"));
 	      break;
@@ -1166,6 +1175,9 @@ main (int argc, char *argv[])
 	    case GRUB_INSTALL_PLATFORM_RISCV64_EFI:
 	      efi_file = "grubriscv64.efi";
 	      break;
+           case GRUB_INSTALL_PLATFORM_LOONGARCH64_EFI:
+             efi_file = "grubloongarch64.efi";
+             break;
 	    default:
 	      efi_file = "grub.efi";
 	      break;
@@ -1470,6 +1482,7 @@ main (int argc, char *argv[])
 		  case GRUB_INSTALL_PLATFORM_ARM64_EFI:
 		  case GRUB_INSTALL_PLATFORM_RISCV32_EFI:
 		  case GRUB_INSTALL_PLATFORM_RISCV64_EFI:
+		  case GRUB_INSTALL_PLATFORM_LOONGARCH64_EFI:
 		  case GRUB_INSTALL_PLATFORM_IA64_EFI:
 		    g = grub_util_guess_efi_drive (*curdev);
 		    break;
@@ -1614,6 +1627,7 @@ main (int argc, char *argv[])
     case GRUB_INSTALL_PLATFORM_ARM64_EFI:
     case GRUB_INSTALL_PLATFORM_RISCV32_EFI:
     case GRUB_INSTALL_PLATFORM_RISCV64_EFI:
+    case GRUB_INSTALL_PLATFORM_LOONGARCH64_EFI:
     case GRUB_INSTALL_PLATFORM_IA64_EFI:
       core_name = "core.efi";
       snprintf (mkimage_target, sizeof (mkimage_target),
@@ -1719,6 +1733,7 @@ main (int argc, char *argv[])
     case GRUB_INSTALL_PLATFORM_ARM64_EFI:
     case GRUB_INSTALL_PLATFORM_RISCV32_EFI:
     case GRUB_INSTALL_PLATFORM_RISCV64_EFI:
+    case GRUB_INSTALL_PLATFORM_LOONGARCH64_EFI:
     case GRUB_INSTALL_PLATFORM_IA64_EFI:
     case GRUB_INSTALL_PLATFORM_MIPSEL_QEMU_MIPS:
     case GRUB_INSTALL_PLATFORM_MIPS_QEMU_MIPS:
@@ -1973,6 +1988,7 @@ main (int argc, char *argv[])
     case GRUB_INSTALL_PLATFORM_ARM64_EFI:
     case GRUB_INSTALL_PLATFORM_RISCV32_EFI:
     case GRUB_INSTALL_PLATFORM_RISCV64_EFI:
+    case GRUB_INSTALL_PLATFORM_LOONGARCH64_EFI:
     case GRUB_INSTALL_PLATFORM_IA64_EFI:
       {
 	char *dst = grub_util_path_concat (2, efidir, efi_file);
diff --git a/util/grub-mkimagexx.c b/util/grub-mkimagexx.c
index 3931194..cf582a9 100644
--- a/util/grub-mkimagexx.c
+++ b/util/grub-mkimagexx.c
@@ -44,6 +44,7 @@
 #include <grub/arm/reloc.h>
 #include <grub/arm64/reloc.h>
 #include <grub/ia64/reloc.h>
+#include <grub/loongarch64/reloc.h>
 #include <grub/osdep/hostfile.h>
 #include <grub/util/install.h>
 #include <grub/util/mkimage.h>
@@ -1160,7 +1161,60 @@ SUFFIX (relocate_addrs) (Elf_Ehdr *e, struct section_metadata *smd,
 		   }
 	       break;
 	       }
-#endif
+	     case EM_LOONGARCH:
+	       {
+	       grub_int64_t pc;
+	       grub_uint32_t *t32 = (grub_uint32_t *) target;
+
+	       sym_addr += addend;
+	       pc = offset + target_section_addr + image_target->vaddr_offset;
+
+	       switch (ELF_R_TYPE (info))
+	   {
+	   case R_LARCH_64:
+	     {
+	       grub_uint64_t *t64 = (grub_uint64_t *) target;
+	       *t64 = grub_host_to_target64 (grub_target_to_host64 (*t64) + sym_addr);
+	     }
+	     break;
+	   case R_LARCH_MARK_LA:
+	     break;
+	   case R_LARCH_B26:
+	     {
+	       grub_int64_t off;
+	       off = sym_addr - target_section_addr - offset
+	               - image_target->vaddr_offset;
+	       grub_loongarch64_b26 (t32, off);
+	     }
+	     break;
+	   case R_LARCH_ABS_HI20:
+	     grub_loongarch64_xxx_hi20 (t32, sym_addr);
+	     break;
+	   case R_LARCH_ABS64_LO20:
+	     grub_loongarch64_xxx64_lo20 (t32, sym_addr);
+	     break;
+	   case R_LARCH_ABS64_HI12:
+	     grub_loongarch64_xxx64_hi12 (t32, sym_addr);
+	     break;
+	   case R_LARCH_PCALA_HI20:
+	     {
+	       grub_int32_t hi20;
+	       hi20 = (((sym_addr + 0x800) & ~0xfffULL) - (pc & ~0xfffULL));
+	       grub_loongarch64_xxx_hi20 (t32, hi20);
+	     }
+	     break;
+	   case R_LARCH_ABS_LO12:
+	   case R_LARCH_PCALA_LO12:
+	     grub_loongarch64_xxx_lo12 (t32, sym_addr);
+	     break;
+	   default:
+	     grub_util_error (_("relocation 0x%x is not implemented yet"),
+	                       (unsigned int) ELF_R_TYPE (info));
+	     break;
+	   }
+	       break;
+	       }
+#endif /* defined(MKIMAGE_ELF64) */
 #if defined(MKIMAGE_ELF32)
 	     case EM_ARM:
 	       {
@@ -1538,7 +1592,10 @@ add_fixup_entry (struct fixup_block_list **cblock, grub_uint16_t type,
 
 	  /* The spec does not mention the requirement of a Page RVA.
 	     Here, align the address with a 4K boundary for safety.  */
-	  b->page_rva = (addr & ~(0x1000 - 1));
+#ifdef GRUB_CPU_LOONGARCH64
+          if (type)
+#endif
+	    b->page_rva = (addr & ~(0x1000 - 1));
 	  b->block_size = sizeof (*b);
 	}
 
@@ -1548,7 +1605,11 @@ add_fixup_entry (struct fixup_block_list **cblock, grub_uint16_t type,
 
       /* Add a new entry.  */
       cur_index = ((b->block_size - sizeof (*b)) >> 1);
+#ifdef GRUB_CPU_LOONGARCH64
+      entry = GRUB_PE32_FIXUP_ENTRY (type, type ? (addr - b->page_rva) : addr);
+#else
       entry = GRUB_PE32_FIXUP_ENTRY (type, addr - b->page_rva);
+#endif
       b->entries[cur_index] = grub_host_to_target16 (entry);
       b->block_size += 2;
     }
@@ -1704,7 +1765,39 @@ translate_relocation_pe (struct translate_context *ctx,
 	  break;
 	}
       break;
-      break;
+#if defined(MKIMAGE_ELF64)
+	  case EM_LOONGARCH:
+	    switch (ELF_R_TYPE (info))
+	{
+	case R_LARCH_64:
+	  ctx->current_address = add_fixup_entry (&ctx->lst,
+	              GRUB_PE32_REL_BASED_DIR64, addr, 0,
+	              ctx->current_address, image_target);
+	  break;
+	case R_LARCH_MARK_LA:
+	  ctx->current_address = add_fixup_entry (&ctx->lst,
+	              GRUB_PE32_REL_BASED_LOONGARCH64_MARK_LA,
+	              addr, 0, ctx->current_address, image_target);
+	  break;
+	  /* Relative relocations do not require fixup entries. */
+	case R_LARCH_B26:
+	case R_LARCH_ABS_HI20:
+	case R_LARCH_ABS_LO12:
+	case R_LARCH_ABS64_LO20:
+	case R_LARCH_ABS64_HI12:
+	case R_LARCH_PCALA_HI20:
+	case R_LARCH_PCALA_LO12:
+	  grub_util_info ("  %s:  not adding fixup: 0x%08x : 0x%08x",
+	                  __FUNCTION__, (unsigned int) addr,
+	                  (unsigned int) ctx->current_address);
+	  break;
+	default:
+	  grub_util_error (_("relocation 0x%x is not implemented yet"),
+	                    (unsigned int) ELF_R_TYPE (info));
+	  break;
+	}
+	    break;
+#endif /* defined(MKIMAGE_ELF64) */
 #if defined(MKIMAGE_ELF32)
     case EM_ARM:
       switch (ELF_R_TYPE (info))
diff --git a/util/grub-module-verifier.c b/util/grub-module-verifier.c
index 163529c..071d1c3 100644
--- a/util/grub-module-verifier.c
+++ b/util/grub-module-verifier.c
@@ -176,6 +176,21 @@ struct grub_module_verifier_arch archs[] = {
       -1
     }
   },
+  { "loongarch64", 8, 0, EM_LOONGARCH, GRUB_MODULE_VERIFY_SUPPORTS_REL | GRUB_MODULE_VERIFY_SUPPORTS_RELA, (int[]){
+      R_LARCH_NONE,
+      R_LARCH_64,
+      R_LARCH_MARK_LA,
+      R_LARCH_B26,
+      R_LARCH_ABS_HI20,
+      R_LARCH_ABS_LO12,
+      R_LARCH_ABS64_LO20,
+      R_LARCH_ABS64_HI12,
+      R_LARCH_PCALA_HI20,
+      R_LARCH_PCALA_LO12,
+      -1
+    }, (int[]){
+      -1
+    } },
 };
 
 struct platform_whitelist {
diff --git a/util/mkimage.c b/util/mkimage.c
index 8319e8d..4e09dfc 100644
--- a/util/mkimage.c
+++ b/util/mkimage.c
@@ -654,6 +654,22 @@ static const struct grub_install_image_target_desc image_targets[] =
       .pe_target = GRUB_PE32_MACHINE_RISCV64,
       .elf_target = EM_RISCV,
     },
+    {
+      .dirname = "loongarch64-efi",
+      .names = { "loongarch64-efi", NULL },
+      .voidp_sizeof = 8,
+      .bigendian = 0,
+      .id = IMAGE_EFI,
+      .flags = PLATFORM_FLAGS_NONE,
+      .total_module_size = TARGET_NO_FIELD,
+      .decompressor_compressed_size = TARGET_NO_FIELD,
+      .decompressor_uncompressed_size = TARGET_NO_FIELD,
+      .decompressor_uncompressed_addr = TARGET_NO_FIELD,
+      .section_align = GRUB_PE32_SECTION_ALIGNMENT,
+      .vaddr_offset = EFI64_HEADER_SIZE,
+      .pe_target = GRUB_PE32_MACHINE_LOONGARCH64,
+      .elf_target = EM_LOONGARCH,
+    },
   };
 
 #include <grub/lib/LzmaEnc.h>
-- 
2.33.0

