This enables the kernel to use EFI runtime services that are
          available (such as the EFI variable services).
 
+config EFI_STUB
+       bool "EFI boot stub support"
+       default y
+       depends on EFI
+       select EFI_GENERIC_STUB
+       help
+         This kernel feature allows the kernel to be loaded directly by
+         EFI firmware without the use of a bootloader.
+
 config SMP
        bool "Multi-Processing support"
        help
 
 
 KBUILD_DEFCONFIG := loongson3_defconfig
 
-KBUILD_IMAGE   = $(boot)/vmlinux
+ifndef CONFIG_EFI_STUB
+KBUILD_IMAGE   := $(boot)/vmlinux.elf
+else
+KBUILD_IMAGE   := $(boot)/vmlinux.efi
+endif
 
 #
 # Select the object file format to substitute into the linker script.
 head-y := arch/loongarch/kernel/head.o
 
 libs-y += arch/loongarch/lib/
+libs-$(CONFIG_EFI_STUB) += $(objtree)/drivers/firmware/efi/libstub/lib.a
 
 ifeq ($(KBUILD_EXTMOD),)
 prepare: vdso_prepare
 vdso_install:
        $(Q)$(MAKE) $(build)=arch/loongarch/vdso $@
 
-all:   $(KBUILD_IMAGE)
+all:   $(notdir $(KBUILD_IMAGE))
 
-$(KBUILD_IMAGE): vmlinux
-       $(Q)$(MAKE) $(build)=$(boot) $(bootvars-y) $@
+vmlinux.elf vmlinux.efi: vmlinux
+       $(Q)$(MAKE) $(build)=$(boot) $(bootvars-y) $(boot)/$@
 
 install:
        $(Q)install -D -m 755 $(KBUILD_IMAGE) $(INSTALL_PATH)/vmlinux-$(KERNELRELEASE)
 
 strip-flags   := $(addprefix --remove-section=,$(drop-sections)) -S
 OBJCOPYFLAGS_vmlinux.efi := -O binary $(strip-flags)
 
-targets := vmlinux
 quiet_cmd_strip = STRIP          $@
       cmd_strip = $(STRIP) -s -o $@ $<
 
-$(obj)/vmlinux: vmlinux FORCE
+targets := vmlinux.elf
+$(obj)/vmlinux.elf: vmlinux FORCE
        $(call if_changed,strip)
+
+targets += vmlinux.efi
+$(obj)/vmlinux.efi: vmlinux FORCE
+       $(call if_changed,objcopy)
 
 #define arch_efi_call_virt_teardown()
 
 #define EFI_ALLOC_ALIGN                SZ_64K
+#define EFI_RT_VIRTUAL_OFFSET  CSR_DMW0_BASE
 
-struct screen_info *alloc_screen_info(void);
-void free_screen_info(struct screen_info *si);
+static inline struct screen_info *alloc_screen_info(void)
+{
+       return &screen_info;
+}
+
+static inline void free_screen_info(struct screen_info *si)
+{
+}
 
 static inline unsigned long efi_get_max_initrd_addr(unsigned long image_addr)
 {
 
--- /dev/null
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020-2022 Loongson Technology Corporation Limited
+ */
+
+#include <linux/pe.h>
+#include <linux/sizes.h>
+
+       .macro  __EFI_PE_HEADER
+       .long   PE_MAGIC
+.Lcoff_header:
+       .short  IMAGE_FILE_MACHINE_LOONGARCH64          /* Machine */
+       .short  .Lsection_count                         /* NumberOfSections */
+       .long   0                                       /* TimeDateStamp */
+       .long   0                                       /* PointerToSymbolTable */
+       .long   0                                       /* NumberOfSymbols */
+       .short  .Lsection_table - .Loptional_header     /* SizeOfOptionalHeader */
+       .short  IMAGE_FILE_DEBUG_STRIPPED | \
+               IMAGE_FILE_EXECUTABLE_IMAGE | \
+               IMAGE_FILE_LINE_NUMS_STRIPPED           /* Characteristics */
+
+.Loptional_header:
+       .short  PE_OPT_MAGIC_PE32PLUS                   /* PE32+ format */
+       .byte   0x02                                    /* MajorLinkerVersion */
+       .byte   0x14                                    /* MinorLinkerVersion */
+       .long   __inittext_end - .Lefi_header_end       /* SizeOfCode */
+       .long   _end - __initdata_begin                 /* SizeOfInitializedData */
+       .long   0                                       /* SizeOfUninitializedData */
+       .long   __efistub_efi_pe_entry - _head          /* AddressOfEntryPoint */
+       .long   .Lefi_header_end - _head                /* BaseOfCode */
+
+.Lextra_header_fields:
+       .quad   0                                       /* ImageBase */
+       .long   PECOFF_SEGMENT_ALIGN                    /* SectionAlignment */
+       .long   PECOFF_FILE_ALIGN                       /* FileAlignment */
+       .short  0                                       /* MajorOperatingSystemVersion */
+       .short  0                                       /* MinorOperatingSystemVersion */
+       .short  LINUX_EFISTUB_MAJOR_VERSION             /* MajorImageVersion */
+       .short  LINUX_EFISTUB_MINOR_VERSION             /* MinorImageVersion */
+       .short  0                                       /* MajorSubsystemVersion */
+       .short  0                                       /* MinorSubsystemVersion */
+       .long   0                                       /* Win32VersionValue */
+
+       .long   _end - _head                            /* SizeOfImage */
+
+       /* Everything before the kernel image is considered part of the header */
+       .long   .Lefi_header_end - _head                /* SizeOfHeaders */
+       .long   0                                       /* CheckSum */
+       .short  IMAGE_SUBSYSTEM_EFI_APPLICATION         /* Subsystem */
+       .short  0                                       /* DllCharacteristics */
+       .quad   0                                       /* SizeOfStackReserve */
+       .quad   0                                       /* SizeOfStackCommit */
+       .quad   0                                       /* SizeOfHeapReserve */
+       .quad   0                                       /* SizeOfHeapCommit */
+       .long   0                                       /* LoaderFlags */
+       .long   (.Lsection_table - .) / 8               /* NumberOfRvaAndSizes */
+
+       .quad   0                                       /* ExportTable */
+       .quad   0                                       /* ImportTable */
+       .quad   0                                       /* ResourceTable */
+       .quad   0                                       /* ExceptionTable */
+       .quad   0                                       /* CertificationTable */
+       .quad   0                                       /* BaseRelocationTable */
+
+       /* Section table */
+.Lsection_table:
+       .ascii  ".text\0\0\0"
+       .long   __inittext_end - .Lefi_header_end       /* VirtualSize */
+       .long   .Lefi_header_end - _head                /* VirtualAddress */
+       .long   __inittext_end - .Lefi_header_end       /* SizeOfRawData */
+       .long   .Lefi_header_end - _head                /* PointerToRawData */
+
+       .long   0                                       /* PointerToRelocations */
+       .long   0                                       /* PointerToLineNumbers */
+       .short  0                                       /* NumberOfRelocations */
+       .short  0                                       /* NumberOfLineNumbers */
+       .long   IMAGE_SCN_CNT_CODE | \
+               IMAGE_SCN_MEM_READ | \
+               IMAGE_SCN_MEM_EXECUTE                   /* Characteristics */
+
+       .ascii  ".data\0\0\0"
+       .long   _end - __initdata_begin                 /* VirtualSize */
+       .long   __initdata_begin - _head                /* VirtualAddress */
+       .long   _edata - __initdata_begin               /* SizeOfRawData */
+       .long   __initdata_begin - _head                /* PointerToRawData */
+
+       .long   0                                       /* PointerToRelocations */
+       .long   0                                       /* PointerToLineNumbers */
+       .short  0                                       /* NumberOfRelocations */
+       .short  0                                       /* NumberOfLineNumbers */
+       .long   IMAGE_SCN_CNT_INITIALIZED_DATA | \
+               IMAGE_SCN_MEM_READ | \
+               IMAGE_SCN_MEM_WRITE                     /* Characteristics */
+
+       .set    .Lsection_count, (. - .Lsection_table) / 40
+
+       .balign 0x10000                                 /* PECOFF_SEGMENT_ALIGN */
+.Lefi_header_end:
+       .endm
 
        config_tables = early_memremap(efi_config_table, efi_nr_tables * size);
        efi_config_parse_tables(config_tables, efi_systab->nr_tables, arch_tables);
        early_memunmap(config_tables, efi_nr_tables * size);
+
+       if (screen_info.orig_video_isVGA == VIDEO_TYPE_EFI)
+               memblock_reserve(screen_info.lfb_base, screen_info.lfb_size);
 }
 
 #include <asm/loongarch.h>
 #include <asm/stackframe.h>
 
+#ifdef CONFIG_EFI_STUB
+
+#include "efi-header.S"
+
+       __HEAD
+
+_head:
+       .word   MZ_MAGIC                /* "MZ", MS-DOS header */
+       .org    0x3c                    /* 0x04 ~ 0x3b reserved */
+       .long   pe_header - _head       /* Offset to the PE header */
+
+pe_header:
+       __EFI_PE_HEADER
+
+SYM_DATA(kernel_asize, .long _end - _text);
+SYM_DATA(kernel_fsize, .long _edata - _text);
+SYM_DATA(kernel_offset, .long kernel_offset - _text);
+
+#endif
+
        __REF
 
 SYM_CODE_START(kernel_entry)                   # kernel entry point
 
--- /dev/null
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2020-2022 Loongson Technology Corporation Limited
+ */
+#ifndef __LOONGARCH_KERNEL_IMAGE_VARS_H
+#define __LOONGARCH_KERNEL_IMAGE_VARS_H
+
+#ifdef CONFIG_EFI_STUB
+
+__efistub_memcmp               = memcmp;
+__efistub_memchr               = memchr;
+__efistub_memcpy               = memcpy;
+__efistub_memmove              = memmove;
+__efistub_memset               = memset;
+__efistub_strcat               = strcat;
+__efistub_strcmp               = strcmp;
+__efistub_strlen               = strlen;
+__efistub_strncat              = strncat;
+__efistub_strnstr              = strnstr;
+__efistub_strnlen              = strnlen;
+__efistub_strrchr              = strrchr;
+__efistub_kernel_entry         = kernel_entry;
+__efistub_kernel_asize         = kernel_asize;
+__efistub_kernel_fsize         = kernel_fsize;
+__efistub_kernel_offset                = kernel_offset;
+__efistub_screen_info          = screen_info;
+
+#endif
+
+#endif /* __LOONGARCH_KERNEL_IMAGE_VARS_H */
 
 #define SMBIOS_CORE_PACKAGE_OFFSET     0x23
 #define LOONGSON_EFI_ENABLE            (1 << 3)
 
-#ifdef CONFIG_VT
-struct screen_info screen_info;
-#endif
+struct screen_info screen_info __section(".data");
 
 unsigned long fw_arg0, fw_arg1;
 DEFINE_PER_CPU(unsigned long, kernelsp);
 
 static void __init parse_bios_table(const struct dmi_header *dm)
 {
-       int bios_extern;
        char *dmi_data = (char *)dm;
 
-       bios_extern = *(dmi_data + SMBIOS_BIOSEXTERN_OFFSET);
        b_info.bios_size = (*(dmi_data + SMBIOS_BIOSSIZE_OFFSET) + 1) << 6;
-
-       if (bios_extern & LOONGSON_EFI_ENABLE)
-               set_bit(EFI_BOOT, &efi.flags);
-       else
-               clear_bit(EFI_BOOT, &efi.flags);
 }
 
 static void __init find_tokens(const struct dmi_header *dm, void *dummy)
 
 #define BSS_FIRST_SECTIONS *(.bss..swapper_pg_dir)
 
 #include <asm-generic/vmlinux.lds.h>
+#include "image-vars.h"
 
 /*
  * Max avaliable Page Size is 64K, so we set SectionAlignment
 
 
 config EFI_ARMSTUB_DTB_LOADER
        bool "Enable the DTB loader"
-       depends on EFI_GENERIC_STUB && !RISCV
+       depends on EFI_GENERIC_STUB && !RISCV && !LOONGARCH
        default y
        help
          Select this config option to add support for the dtb= command
        bool "Enable the command line initrd loader" if !X86
        depends on EFI_STUB && (EFI_GENERIC_STUB || X86)
        default y if X86
-       depends on !RISCV
+       depends on !RISCV && !LOONGARCH
        help
          Select this config option to add support for the initrd= command
          line parameter, allowing an initrd that resides on the same volume
 
                                   $(call cc-option,-mno-single-pic-base)
 cflags-$(CONFIG_RISCV)         := $(subst $(CC_FLAGS_FTRACE),,$(KBUILD_CFLAGS)) \
                                   -fpic
+cflags-$(CONFIG_LOONGARCH)     := $(subst $(CC_FLAGS_FTRACE),,$(KBUILD_CFLAGS)) \
+                                  -fpie
 
 cflags-$(CONFIG_EFI_GENERIC_STUB) += -I$(srctree)/scripts/dtc/libfdt
 
 lib-$(CONFIG_ARM64)            += arm64-stub.o
 lib-$(CONFIG_X86)              += x86-stub.o
 lib-$(CONFIG_RISCV)            += riscv-stub.o
+lib-$(CONFIG_LOONGARCH)                += loongarch-stub.o
+
 CFLAGS_arm32-stub.o            := -DTEXT_OFFSET=$(TEXT_OFFSET)
 
 # Even when -mbranch-protection=none is set, Clang will generate a
                                   --prefix-symbols=__efistub_
 STUBCOPY_RELOC-$(CONFIG_RISCV) := R_RISCV_HI20
 
+# For LoongArch, keep all the symbols in .init section and make sure that no
+# absolute symbols references exist.
+STUBCOPY_FLAGS-$(CONFIG_LOONGARCH)     += --prefix-alloc-sections=.init \
+                                          --prefix-symbols=__efistub_
+STUBCOPY_RELOC-$(CONFIG_LOONGARCH)     := R_LARCH_MARK_LA
+
 $(obj)/%.stub.o: $(obj)/%.o FORCE
        $(call if_changed,stubcopy)
 
 
 
 #ifdef CONFIG_ARM64
 # define EFI_RT_VIRTUAL_LIMIT  DEFAULT_MAP_WINDOW_64
-#elif defined(CONFIG_RISCV)
+#elif defined(CONFIG_RISCV) || defined(CONFIG_LOONGARCH)
 # define EFI_RT_VIRTUAL_LIMIT  TASK_SIZE_MIN
-#else
+#else /* Only if TASK_SIZE is a constant */
 # define EFI_RT_VIRTUAL_LIMIT  TASK_SIZE
 #endif
 
+/*
+ * Some architectures map the EFI regions into the kernel's linear map using a
+ * fixed offset.
+ */
+#ifndef EFI_RT_VIRTUAL_OFFSET
+#define EFI_RT_VIRTUAL_OFFSET  0
+#endif
+
 static u64 virtmap_base = EFI_RT_VIRTUAL_BASE;
-static bool flat_va_mapping;
+static bool flat_va_mapping = (EFI_RT_VIRTUAL_OFFSET != 0);
 
 const efi_system_table_t *efi_system_table;
 
         * The easiest way to achieve that is to simply use a 1:1 mapping.
         */
        prop_tbl = get_efi_config_table(EFI_PROPERTIES_TABLE_GUID);
-       flat_va_mapping = prop_tbl &&
-                         (prop_tbl->memory_protection_attribute &
+       flat_va_mapping |= prop_tbl &&
+                          (prop_tbl->memory_protection_attribute &
                           EFI_PROPERTIES_RUNTIME_MEMORY_PROTECTION_NON_EXECUTABLE_PE_DATA);
 
        /* force efi_novamap if SetVirtualAddressMap() is unsupported */
                paddr = in->phys_addr;
                size = in->num_pages * EFI_PAGE_SIZE;
 
-               in->virt_addr = in->phys_addr;
+               in->virt_addr = in->phys_addr + EFI_RT_VIRTUAL_OFFSET;
                if (efi_novamap) {
                        continue;
                }
 
--- /dev/null
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Author: Yun Liu <liuyun@loongson.cn>
+ *         Huacai Chen <chenhuacai@loongson.cn>
+ * Copyright (C) 2020-2022 Loongson Technology Corporation Limited
+ */
+
+#include <asm/efi.h>
+#include <asm/addrspace.h>
+#include "efistub.h"
+
+typedef void __noreturn (*kernel_entry_t)(bool efi, unsigned long fdt);
+
+extern int kernel_asize;
+extern int kernel_fsize;
+extern int kernel_offset;
+extern kernel_entry_t kernel_entry;
+
+efi_status_t check_platform_features(void)
+{
+       return EFI_SUCCESS;
+}
+
+efi_status_t handle_kernel_image(unsigned long *image_addr,
+                                unsigned long *image_size,
+                                unsigned long *reserve_addr,
+                                unsigned long *reserve_size,
+                                efi_loaded_image_t *image,
+                                efi_handle_t image_handle)
+{
+       efi_status_t status;
+       unsigned long kernel_addr = 0;
+
+       kernel_addr = (unsigned long)&kernel_offset - kernel_offset;
+
+       status = efi_relocate_kernel(&kernel_addr, kernel_fsize, kernel_asize,
+                                    PHYSADDR(VMLINUX_LOAD_ADDRESS), SZ_2M, 0x0);
+
+       *image_addr = kernel_addr;
+       *image_size = kernel_asize;
+
+       return status;
+}
+
+void __noreturn efi_enter_kernel(unsigned long entrypoint, unsigned long fdt, unsigned long fdt_size)
+{
+       kernel_entry_t real_kernel_entry;
+
+       /* Config Direct Mapping */
+       csr_write64(CSR_DMW0_INIT, LOONGARCH_CSR_DMWIN0);
+       csr_write64(CSR_DMW1_INIT, LOONGARCH_CSR_DMWIN1);
+
+       real_kernel_entry = (kernel_entry_t)
+               ((unsigned long)&kernel_entry - entrypoint + VMLINUX_LOAD_ADDRESS);
+
+       if (!efi_novamap)
+               real_kernel_entry(true, fdt);
+       else
+               real_kernel_entry(false, fdt);
+}
 
 #define        IMAGE_FILE_MACHINE_SH5          0x01a8
 #define        IMAGE_FILE_MACHINE_THUMB        0x01c2
 #define        IMAGE_FILE_MACHINE_WCEMIPSV2    0x0169
+#define        IMAGE_FILE_MACHINE_LOONGARCH32  0x6232
+#define        IMAGE_FILE_MACHINE_LOONGARCH64  0x6264
 
 /* flags */
 #define IMAGE_FILE_RELOCS_STRIPPED           0x0001