diff mbox series

[V2] LoongArch: Add efistub booting support

Message ID 20220818030707.2836607-1-chenhuacai@loongson.cn
State Superseded
Headers show
Series [V2] LoongArch: Add efistub booting support | expand

Commit Message

Huacai Chen Aug. 18, 2022, 3:07 a.m. UTC
This patch adds efistub booting support, which is the standard UEFI boot
protocol for us to use.

We use generic efistub, which means we can pass boot information (i.e.,
system table, memory map, kernel command line, initrd) via a light FDT
and drop a lot of non-standard code.

We use a flat mapping to map the efi runtime in the kernel's address
space. In efi, VA = PA; in kernel, VA = PA + PAGE_OFFSET. As a result,
flat mapping is not identity mapping, SetVirtualAddressMap() is still
needed for the efi runtime.

Tested-by: Xi Ruoyao <xry111@xry111.site>
Signed-off-by: Huacai Chen <chenhuacai@loongson.cn>
---
V1 --> V2:
1, Call SetVirtualAddressMap() in stub;
2, Use core kernel data directly in alloc_screen_info();
3, Remove the magic number in MS-DOS header;
4, Disable EFI_GENERIC_STUB_INITRD_CMDLINE_LOADER;
5, Some other small changes suggested by Ard Biesheuvel.

 arch/loongarch/Kconfig                        |  9 ++
 arch/loongarch/Makefile                       |  7 +-
 arch/loongarch/boot/Makefile                  |  8 +-
 arch/loongarch/include/asm/efi.h              | 10 +-
 arch/loongarch/kernel/efi-header.S            | 99 +++++++++++++++++++
 arch/loongarch/kernel/efi.c                   |  3 +
 arch/loongarch/kernel/head.S                  | 20 ++++
 arch/loongarch/kernel/image-vars.h            | 30 ++++++
 arch/loongarch/kernel/setup.c                 | 12 +--
 arch/loongarch/kernel/vmlinux.lds.S           |  1 +
 drivers/firmware/efi/Kconfig                  |  4 +-
 drivers/firmware/efi/libstub/Makefile         | 10 ++
 drivers/firmware/efi/libstub/efi-stub.c       | 31 ++++--
 drivers/firmware/efi/libstub/loongarch-stub.c | 60 +++++++++++
 include/linux/pe.h                            |  2 +
 15 files changed, 282 insertions(+), 24 deletions(-)
 create mode 100644 arch/loongarch/kernel/efi-header.S
 create mode 100644 arch/loongarch/kernel/image-vars.h
 create mode 100644 drivers/firmware/efi/libstub/loongarch-stub.c

Comments

maobibo Aug. 18, 2022, 1:12 p.m. UTC | #1
Huacai,

This patch is much better than before.
It works well on qemu virt machine platform, and I reply inline.

在 2022/8/18 11:07, Huacai Chen 写道:
> This patch adds efistub booting support, which is the standard UEFI boot
> protocol for us to use.
> 
> We use generic efistub, which means we can pass boot information (i.e.,
> system table, memory map, kernel command line, initrd) via a light FDT
> and drop a lot of non-standard code.
> 
> We use a flat mapping to map the efi runtime in the kernel's address
> space. In efi, VA = PA; in kernel, VA = PA + PAGE_OFFSET. As a result,
> flat mapping is not identity mapping, SetVirtualAddressMap() is still
> needed for the efi runtime.
> 
> Tested-by: Xi Ruoyao <xry111@xry111.site>
> Signed-off-by: Huacai Chen <chenhuacai@loongson.cn>
> ---
> V1 --> V2:
> 1, Call SetVirtualAddressMap() in stub;
> 2, Use core kernel data directly in alloc_screen_info();
> 3, Remove the magic number in MS-DOS header;
> 4, Disable EFI_GENERIC_STUB_INITRD_CMDLINE_LOADER;
> 5, Some other small changes suggested by Ard Biesheuvel.
> 
>  arch/loongarch/Kconfig                        |  9 ++
>  arch/loongarch/Makefile                       |  7 +-
>  arch/loongarch/boot/Makefile                  |  8 +-
>  arch/loongarch/include/asm/efi.h              | 10 +-
>  arch/loongarch/kernel/efi-header.S            | 99 +++++++++++++++++++
>  arch/loongarch/kernel/efi.c                   |  3 +
>  arch/loongarch/kernel/head.S                  | 20 ++++
>  arch/loongarch/kernel/image-vars.h            | 30 ++++++
>  arch/loongarch/kernel/setup.c                 | 12 +--
>  arch/loongarch/kernel/vmlinux.lds.S           |  1 +
>  drivers/firmware/efi/Kconfig                  |  4 +-
>  drivers/firmware/efi/libstub/Makefile         | 10 ++
>  drivers/firmware/efi/libstub/efi-stub.c       | 31 ++++--
>  drivers/firmware/efi/libstub/loongarch-stub.c | 60 +++++++++++
>  include/linux/pe.h                            |  2 +
>  15 files changed, 282 insertions(+), 24 deletions(-)
>  create mode 100644 arch/loongarch/kernel/efi-header.S
>  create mode 100644 arch/loongarch/kernel/image-vars.h
>  create mode 100644 drivers/firmware/efi/libstub/loongarch-stub.c
> 
> diff --git a/arch/loongarch/Kconfig b/arch/loongarch/Kconfig
> index 9478f9646fa5..4cb412a82afa 100644
> --- a/arch/loongarch/Kconfig
> +++ b/arch/loongarch/Kconfig
> @@ -324,6 +324,15 @@ config EFI
>  	  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
> diff --git a/arch/loongarch/Makefile b/arch/loongarch/Makefile
> index ec3de6191276..2bd0a574ed73 100644
> --- a/arch/loongarch/Makefile
> +++ b/arch/loongarch/Makefile
> @@ -7,7 +7,11 @@ boot	:= arch/loongarch/boot
>  
>  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.
> @@ -75,6 +79,7 @@ endif
>  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
> diff --git a/arch/loongarch/boot/Makefile b/arch/loongarch/boot/Makefile
> index 0125b17edc98..fecf34f50e56 100644
> --- a/arch/loongarch/boot/Makefile
> +++ b/arch/loongarch/boot/Makefile
> @@ -8,9 +8,13 @@ drop-sections := .comment .note .options .note.gnu.build-id
>  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)
> diff --git a/arch/loongarch/include/asm/efi.h b/arch/loongarch/include/asm/efi.h
> index 9d44c6948be1..c7507a240f30 100644
> --- a/arch/loongarch/include/asm/efi.h
> +++ b/arch/loongarch/include/asm/efi.h
> @@ -18,8 +18,14 @@ void efifb_setup_from_dmi(struct screen_info *si, const char *opt);
>  
>  #define EFI_ALLOC_ALIGN		SZ_64K
>  
> -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)
>  {
> diff --git a/arch/loongarch/kernel/efi-header.S b/arch/loongarch/kernel/efi-header.S
> new file mode 100644
> index 000000000000..8c1d229a2afa
> --- /dev/null
> +++ b/arch/loongarch/kernel/efi-header.S
> @@ -0,0 +1,99 @@
> +/* 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
> diff --git a/arch/loongarch/kernel/efi.c b/arch/loongarch/kernel/efi.c
> index a50b60c587fa..1f1f755fb425 100644
> --- a/arch/loongarch/kernel/efi.c
> +++ b/arch/loongarch/kernel/efi.c
> @@ -69,4 +69,7 @@ void __init efi_init(void)
>  	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);
>  }
> diff --git a/arch/loongarch/kernel/head.S b/arch/loongarch/kernel/head.S
> index c60eb66793e3..01bac62a6442 100644
> --- a/arch/loongarch/kernel/head.S
> +++ b/arch/loongarch/kernel/head.S
> @@ -12,6 +12,26 @@
>  #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
> diff --git a/arch/loongarch/kernel/image-vars.h b/arch/loongarch/kernel/image-vars.h
> new file mode 100644
> index 000000000000..c901ebb903f2
> --- /dev/null
> +++ b/arch/loongarch/kernel/image-vars.h
> @@ -0,0 +1,30 @@
> +/* 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 */
> diff --git a/arch/loongarch/kernel/setup.c b/arch/loongarch/kernel/setup.c
> index 23ee293e1cd2..f938aae3e92c 100644
> --- a/arch/loongarch/kernel/setup.c
> +++ b/arch/loongarch/kernel/setup.c
> @@ -49,9 +49,7 @@
>  #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);
> @@ -122,16 +120,9 @@ static void __init parse_cpu_table(const struct dmi_header *dm)
>  
>  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)
> @@ -145,6 +136,7 @@ static void __init find_tokens(const struct dmi_header *dm, void *dummy)
>  		break;
>  	}
>  }
> +
>  static void __init smbios_parse(void)
>  {
>  	b_info.bios_vendor = (void *)dmi_get_system_info(DMI_BIOS_VENDOR);
> diff --git a/arch/loongarch/kernel/vmlinux.lds.S b/arch/loongarch/kernel/vmlinux.lds.S
> index 69c76f26c1c5..36d042739f3c 100644
> --- a/arch/loongarch/kernel/vmlinux.lds.S
> +++ b/arch/loongarch/kernel/vmlinux.lds.S
> @@ -12,6 +12,7 @@
>  #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
> diff --git a/drivers/firmware/efi/Kconfig b/drivers/firmware/efi/Kconfig
> index 6cb7384ad2ac..cbf1c55dc224 100644
> --- a/drivers/firmware/efi/Kconfig
> +++ b/drivers/firmware/efi/Kconfig
> @@ -107,7 +107,7 @@ config EFI_GENERIC_STUB
>  
>  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
> @@ -124,7 +124,7 @@ config EFI_GENERIC_STUB_INITRD_CMDLINE_LOADER
>  	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
> diff --git a/drivers/firmware/efi/libstub/Makefile b/drivers/firmware/efi/libstub/Makefile
> index d0537573501e..1588c61939e7 100644
> --- a/drivers/firmware/efi/libstub/Makefile
> +++ b/drivers/firmware/efi/libstub/Makefile
> @@ -26,6 +26,8 @@ cflags-$(CONFIG_ARM)		:= $(subst $(CC_FLAGS_FTRACE),,$(KBUILD_CFLAGS)) \
>  				   $(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)) \
> +				   -fpic
>  
>  cflags-$(CONFIG_EFI_GENERIC_STUB) += -I$(srctree)/scripts/dtc/libfdt
>  
> @@ -70,6 +72,8 @@ lib-$(CONFIG_ARM)		+= arm32-stub.o
>  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
> @@ -125,6 +129,12 @@ STUBCOPY_FLAGS-$(CONFIG_RISCV)	+= --prefix-alloc-sections=.init \
>  				   --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 doesn't 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)
>  
> diff --git a/drivers/firmware/efi/libstub/efi-stub.c b/drivers/firmware/efi/libstub/efi-stub.c
> index f515394cce6e..efb9219d8d49 100644
> --- a/drivers/firmware/efi/libstub/efi-stub.c
> +++ b/drivers/firmware/efi/libstub/efi-stub.c
> @@ -40,14 +40,19 @@
>  
>  #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
>  
> +/*
> + * 0: No flat mapping
> + * 1: Flat mapping that VA = PA
> + * 2: Flat mapping that VA = PA + PAGE_OFFSET
> + */
> +static int flat_va_mapping;
>  static u64 virtmap_base = EFI_RT_VIRTUAL_BASE;
> -static bool flat_va_mapping;
>  
>  const efi_system_table_t *efi_system_table;
>  
> @@ -121,6 +126,7 @@ efi_status_t __efiapi efi_pe_entry(efi_handle_t handle,
>  {
>  	efi_loaded_image_t *image;
>  	efi_status_t status;
> +	unsigned long attrib;
>  	unsigned long image_addr;
>  	unsigned long image_size = 0;
>  	/* addr/point and size pairs for memory management*/
> @@ -254,9 +260,11 @@ efi_status_t __efiapi efi_pe_entry(efi_handle_t handle,
>  	 * 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 &
> -			   EFI_PROPERTIES_RUNTIME_MEMORY_PROTECTION_NON_EXECUTABLE_PE_DATA);
> +	attrib = prop_tbl ? prop_tbl->memory_protection_attribute : 0;
> +	if (attrib & EFI_PROPERTIES_RUNTIME_MEMORY_PROTECTION_NON_EXECUTABLE_PE_DATA)
> +		flat_va_mapping = 1;
> +	if (IS_ENABLED(CONFIG_LOONGARCH))
> +		flat_va_mapping = 2;
>  
>  	/* force efi_novamap if SetVirtualAddressMap() is unsupported */
>  	efi_novamap |= !(get_supported_rt_services() &
> @@ -338,7 +346,16 @@ void efi_get_virtmap(efi_memory_desc_t *memory_map, unsigned long map_size,
>  		paddr = in->phys_addr;
>  		size = in->num_pages * EFI_PAGE_SIZE;
>  
> -		in->virt_addr = in->phys_addr;
> +		switch (flat_va_mapping) {
> +		case 1:
> +			in->virt_addr = in->phys_addr;
> +			break;
> +		case 2:
> +			in->virt_addr = in->phys_addr + PAGE_OFFSET;
It is useful for cachable runtime memory, however there exists uncachable runtime physical address also.

> +			break;
> +		default:
> +			in->virt_addr = in->phys_addr;
> +		}
>  		if (efi_novamap) {
>  			continue;
>  		}
> diff --git a/drivers/firmware/efi/libstub/loongarch-stub.c b/drivers/firmware/efi/libstub/loongarch-stub.c
> new file mode 100644
> index 000000000000..b7ef8d2df59e
> --- /dev/null
> +++ b/drivers/firmware/efi/libstub/loongarch-stub.c
> @@ -0,0 +1,60 @@
> +// 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);
Do we need turn off mmu and jump to physical kernel entry address ?
Or jump to direct mapped address like this?

regards
bibo,mao

> +}
> diff --git a/include/linux/pe.h b/include/linux/pe.h
> index daf09ffffe38..1d3836ef9d92 100644
> --- a/include/linux/pe.h
> +++ b/include/linux/pe.h
> @@ -65,6 +65,8 @@
>  #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
Huacai Chen Aug. 19, 2022, 8:05 a.m. UTC | #2
Hi, Bibo,

On Thu, Aug 18, 2022 at 9:12 PM maobibo <maobibo@loongson.cn> wrote:
>
> Huacai,
>
> This patch is much better than before.
> It works well on qemu virt machine platform, and I reply inline.
>
> 在 2022/8/18 11:07, Huacai Chen 写道:
> > This patch adds efistub booting support, which is the standard UEFI boot
> > protocol for us to use.
> >
> > We use generic efistub, which means we can pass boot information (i.e.,
> > system table, memory map, kernel command line, initrd) via a light FDT
> > and drop a lot of non-standard code.
> >
> > We use a flat mapping to map the efi runtime in the kernel's address
> > space. In efi, VA = PA; in kernel, VA = PA + PAGE_OFFSET. As a result,
> > flat mapping is not identity mapping, SetVirtualAddressMap() is still
> > needed for the efi runtime.
> >
> > Tested-by: Xi Ruoyao <xry111@xry111.site>
> > Signed-off-by: Huacai Chen <chenhuacai@loongson.cn>
> > ---
> > V1 --> V2:
> > 1, Call SetVirtualAddressMap() in stub;
> > 2, Use core kernel data directly in alloc_screen_info();
> > 3, Remove the magic number in MS-DOS header;
> > 4, Disable EFI_GENERIC_STUB_INITRD_CMDLINE_LOADER;
> > 5, Some other small changes suggested by Ard Biesheuvel.
> >
> >  arch/loongarch/Kconfig                        |  9 ++
> >  arch/loongarch/Makefile                       |  7 +-
> >  arch/loongarch/boot/Makefile                  |  8 +-
> >  arch/loongarch/include/asm/efi.h              | 10 +-
> >  arch/loongarch/kernel/efi-header.S            | 99 +++++++++++++++++++
> >  arch/loongarch/kernel/efi.c                   |  3 +
> >  arch/loongarch/kernel/head.S                  | 20 ++++
> >  arch/loongarch/kernel/image-vars.h            | 30 ++++++
> >  arch/loongarch/kernel/setup.c                 | 12 +--
> >  arch/loongarch/kernel/vmlinux.lds.S           |  1 +
> >  drivers/firmware/efi/Kconfig                  |  4 +-
> >  drivers/firmware/efi/libstub/Makefile         | 10 ++
> >  drivers/firmware/efi/libstub/efi-stub.c       | 31 ++++--
> >  drivers/firmware/efi/libstub/loongarch-stub.c | 60 +++++++++++
> >  include/linux/pe.h                            |  2 +
> >  15 files changed, 282 insertions(+), 24 deletions(-)
> >  create mode 100644 arch/loongarch/kernel/efi-header.S
> >  create mode 100644 arch/loongarch/kernel/image-vars.h
> >  create mode 100644 drivers/firmware/efi/libstub/loongarch-stub.c
> >
> > diff --git a/arch/loongarch/Kconfig b/arch/loongarch/Kconfig
> > index 9478f9646fa5..4cb412a82afa 100644
> > --- a/arch/loongarch/Kconfig
> > +++ b/arch/loongarch/Kconfig
> > @@ -324,6 +324,15 @@ config EFI
> >         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
> > diff --git a/arch/loongarch/Makefile b/arch/loongarch/Makefile
> > index ec3de6191276..2bd0a574ed73 100644
> > --- a/arch/loongarch/Makefile
> > +++ b/arch/loongarch/Makefile
> > @@ -7,7 +7,11 @@ boot := arch/loongarch/boot
> >
> >  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.
> > @@ -75,6 +79,7 @@ endif
> >  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
> > diff --git a/arch/loongarch/boot/Makefile b/arch/loongarch/boot/Makefile
> > index 0125b17edc98..fecf34f50e56 100644
> > --- a/arch/loongarch/boot/Makefile
> > +++ b/arch/loongarch/boot/Makefile
> > @@ -8,9 +8,13 @@ drop-sections := .comment .note .options .note.gnu.build-id
> >  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)
> > diff --git a/arch/loongarch/include/asm/efi.h b/arch/loongarch/include/asm/efi.h
> > index 9d44c6948be1..c7507a240f30 100644
> > --- a/arch/loongarch/include/asm/efi.h
> > +++ b/arch/loongarch/include/asm/efi.h
> > @@ -18,8 +18,14 @@ void efifb_setup_from_dmi(struct screen_info *si, const char *opt);
> >
> >  #define EFI_ALLOC_ALIGN              SZ_64K
> >
> > -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)
> >  {
> > diff --git a/arch/loongarch/kernel/efi-header.S b/arch/loongarch/kernel/efi-header.S
> > new file mode 100644
> > index 000000000000..8c1d229a2afa
> > --- /dev/null
> > +++ b/arch/loongarch/kernel/efi-header.S
> > @@ -0,0 +1,99 @@
> > +/* 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
> > diff --git a/arch/loongarch/kernel/efi.c b/arch/loongarch/kernel/efi.c
> > index a50b60c587fa..1f1f755fb425 100644
> > --- a/arch/loongarch/kernel/efi.c
> > +++ b/arch/loongarch/kernel/efi.c
> > @@ -69,4 +69,7 @@ void __init efi_init(void)
> >       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);
> >  }
> > diff --git a/arch/loongarch/kernel/head.S b/arch/loongarch/kernel/head.S
> > index c60eb66793e3..01bac62a6442 100644
> > --- a/arch/loongarch/kernel/head.S
> > +++ b/arch/loongarch/kernel/head.S
> > @@ -12,6 +12,26 @@
> >  #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
> > diff --git a/arch/loongarch/kernel/image-vars.h b/arch/loongarch/kernel/image-vars.h
> > new file mode 100644
> > index 000000000000..c901ebb903f2
> > --- /dev/null
> > +++ b/arch/loongarch/kernel/image-vars.h
> > @@ -0,0 +1,30 @@
> > +/* 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 */
> > diff --git a/arch/loongarch/kernel/setup.c b/arch/loongarch/kernel/setup.c
> > index 23ee293e1cd2..f938aae3e92c 100644
> > --- a/arch/loongarch/kernel/setup.c
> > +++ b/arch/loongarch/kernel/setup.c
> > @@ -49,9 +49,7 @@
> >  #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);
> > @@ -122,16 +120,9 @@ static void __init parse_cpu_table(const struct dmi_header *dm)
> >
> >  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)
> > @@ -145,6 +136,7 @@ static void __init find_tokens(const struct dmi_header *dm, void *dummy)
> >               break;
> >       }
> >  }
> > +
> >  static void __init smbios_parse(void)
> >  {
> >       b_info.bios_vendor = (void *)dmi_get_system_info(DMI_BIOS_VENDOR);
> > diff --git a/arch/loongarch/kernel/vmlinux.lds.S b/arch/loongarch/kernel/vmlinux.lds.S
> > index 69c76f26c1c5..36d042739f3c 100644
> > --- a/arch/loongarch/kernel/vmlinux.lds.S
> > +++ b/arch/loongarch/kernel/vmlinux.lds.S
> > @@ -12,6 +12,7 @@
> >  #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
> > diff --git a/drivers/firmware/efi/Kconfig b/drivers/firmware/efi/Kconfig
> > index 6cb7384ad2ac..cbf1c55dc224 100644
> > --- a/drivers/firmware/efi/Kconfig
> > +++ b/drivers/firmware/efi/Kconfig
> > @@ -107,7 +107,7 @@ config EFI_GENERIC_STUB
> >
> >  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
> > @@ -124,7 +124,7 @@ config EFI_GENERIC_STUB_INITRD_CMDLINE_LOADER
> >       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
> > diff --git a/drivers/firmware/efi/libstub/Makefile b/drivers/firmware/efi/libstub/Makefile
> > index d0537573501e..1588c61939e7 100644
> > --- a/drivers/firmware/efi/libstub/Makefile
> > +++ b/drivers/firmware/efi/libstub/Makefile
> > @@ -26,6 +26,8 @@ cflags-$(CONFIG_ARM)                := $(subst $(CC_FLAGS_FTRACE),,$(KBUILD_CFLAGS)) \
> >                                  $(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)) \
> > +                                -fpic
> >
> >  cflags-$(CONFIG_EFI_GENERIC_STUB) += -I$(srctree)/scripts/dtc/libfdt
> >
> > @@ -70,6 +72,8 @@ lib-$(CONFIG_ARM)           += arm32-stub.o
> >  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
> > @@ -125,6 +129,12 @@ STUBCOPY_FLAGS-$(CONFIG_RISCV)   += --prefix-alloc-sections=.init \
> >                                  --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 doesn't 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)
> >
> > diff --git a/drivers/firmware/efi/libstub/efi-stub.c b/drivers/firmware/efi/libstub/efi-stub.c
> > index f515394cce6e..efb9219d8d49 100644
> > --- a/drivers/firmware/efi/libstub/efi-stub.c
> > +++ b/drivers/firmware/efi/libstub/efi-stub.c
> > @@ -40,14 +40,19 @@
> >
> >  #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
> >
> > +/*
> > + * 0: No flat mapping
> > + * 1: Flat mapping that VA = PA
> > + * 2: Flat mapping that VA = PA + PAGE_OFFSET
> > + */
> > +static int flat_va_mapping;
> >  static u64 virtmap_base = EFI_RT_VIRTUAL_BASE;
> > -static bool flat_va_mapping;
> >
> >  const efi_system_table_t *efi_system_table;
> >
> > @@ -121,6 +126,7 @@ efi_status_t __efiapi efi_pe_entry(efi_handle_t handle,
> >  {
> >       efi_loaded_image_t *image;
> >       efi_status_t status;
> > +     unsigned long attrib;
> >       unsigned long image_addr;
> >       unsigned long image_size = 0;
> >       /* addr/point and size pairs for memory management*/
> > @@ -254,9 +260,11 @@ efi_status_t __efiapi efi_pe_entry(efi_handle_t handle,
> >        * 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 &
> > -                        EFI_PROPERTIES_RUNTIME_MEMORY_PROTECTION_NON_EXECUTABLE_PE_DATA);
> > +     attrib = prop_tbl ? prop_tbl->memory_protection_attribute : 0;
> > +     if (attrib & EFI_PROPERTIES_RUNTIME_MEMORY_PROTECTION_NON_EXECUTABLE_PE_DATA)
> > +             flat_va_mapping = 1;
> > +     if (IS_ENABLED(CONFIG_LOONGARCH))
> > +             flat_va_mapping = 2;
> >
> >       /* force efi_novamap if SetVirtualAddressMap() is unsupported */
> >       efi_novamap |= !(get_supported_rt_services() &
> > @@ -338,7 +346,16 @@ void efi_get_virtmap(efi_memory_desc_t *memory_map, unsigned long map_size,
> >               paddr = in->phys_addr;
> >               size = in->num_pages * EFI_PAGE_SIZE;
> >
> > -             in->virt_addr = in->phys_addr;
> > +             switch (flat_va_mapping) {
> > +             case 1:
> > +                     in->virt_addr = in->phys_addr;
> > +                     break;
> > +             case 2:
> > +                     in->virt_addr = in->phys_addr + PAGE_OFFSET;
> It is useful for cachable runtime memory, however there exists uncachable runtime physical address also.
Yes, so we will change to use the UNCACHE_BASE, as discussed.

>
> > +                     break;
> > +             default:
> > +                     in->virt_addr = in->phys_addr;
> > +             }
> >               if (efi_novamap) {
> >                       continue;
> >               }
> > diff --git a/drivers/firmware/efi/libstub/loongarch-stub.c b/drivers/firmware/efi/libstub/loongarch-stub.c
> > new file mode 100644
> > index 000000000000..b7ef8d2df59e
> > --- /dev/null
> > +++ b/drivers/firmware/efi/libstub/loongarch-stub.c
> > @@ -0,0 +1,60 @@
> > +// 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);
> Do we need turn off mmu and jump to physical kernel entry address ?
> Or jump to direct mapped address like this?
I don't think so, we will immediately turn on mmu after jumping, this
operation seems meaningless.

Huacai
>
> regards
> bibo,mao
>
> > +}
> > diff --git a/include/linux/pe.h b/include/linux/pe.h
> > index daf09ffffe38..1d3836ef9d92 100644
> > --- a/include/linux/pe.h
> > +++ b/include/linux/pe.h
> > @@ -65,6 +65,8 @@
> >  #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
>
Huacai Chen Aug. 19, 2022, 8:15 a.m. UTC | #3
Hi, Ard,

On Fri, Aug 19, 2022 at 1:04 AM Ard Biesheuvel <ardb@kernel.org> wrote:
>
> On Thu, 18 Aug 2022 at 05:07, Huacai Chen <chenhuacai@loongson.cn> wrote:
> >
> > This patch adds efistub booting support, which is the standard UEFI boot
> > protocol for us to use.
> >
> > We use generic efistub, which means we can pass boot information (i.e.,
> > system table, memory map, kernel command line, initrd) via a light FDT
> > and drop a lot of non-standard code.
> >
> > We use a flat mapping to map the efi runtime in the kernel's address
> > space. In efi, VA = PA; in kernel, VA = PA + PAGE_OFFSET. As a result,
> > flat mapping is not identity mapping, SetVirtualAddressMap() is still
> > needed for the efi runtime.
> >
> > Tested-by: Xi Ruoyao <xry111@xry111.site>
> > Signed-off-by: Huacai Chen <chenhuacai@loongson.cn>
> > ---
> > V1 --> V2:
> > 1, Call SetVirtualAddressMap() in stub;
> > 2, Use core kernel data directly in alloc_screen_info();
> > 3, Remove the magic number in MS-DOS header;
> > 4, Disable EFI_GENERIC_STUB_INITRD_CMDLINE_LOADER;
> > 5, Some other small changes suggested by Ard Biesheuvel.
> >
> >  arch/loongarch/Kconfig                        |  9 ++
> >  arch/loongarch/Makefile                       |  7 +-
> >  arch/loongarch/boot/Makefile                  |  8 +-
> >  arch/loongarch/include/asm/efi.h              | 10 +-
> >  arch/loongarch/kernel/efi-header.S            | 99 +++++++++++++++++++
> >  arch/loongarch/kernel/efi.c                   |  3 +
> >  arch/loongarch/kernel/head.S                  | 20 ++++
> >  arch/loongarch/kernel/image-vars.h            | 30 ++++++
> >  arch/loongarch/kernel/setup.c                 | 12 +--
> >  arch/loongarch/kernel/vmlinux.lds.S           |  1 +
> >  drivers/firmware/efi/Kconfig                  |  4 +-
> >  drivers/firmware/efi/libstub/Makefile         | 10 ++
> >  drivers/firmware/efi/libstub/efi-stub.c       | 31 ++++--
> >  drivers/firmware/efi/libstub/loongarch-stub.c | 60 +++++++++++
> >  include/linux/pe.h                            |  2 +
> >  15 files changed, 282 insertions(+), 24 deletions(-)
> >  create mode 100644 arch/loongarch/kernel/efi-header.S
> >  create mode 100644 arch/loongarch/kernel/image-vars.h
> >  create mode 100644 drivers/firmware/efi/libstub/loongarch-stub.c
> >
> > diff --git a/arch/loongarch/Kconfig b/arch/loongarch/Kconfig
> > index 9478f9646fa5..4cb412a82afa 100644
> > --- a/arch/loongarch/Kconfig
> > +++ b/arch/loongarch/Kconfig
> > @@ -324,6 +324,15 @@ config EFI
> >           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
> > diff --git a/arch/loongarch/Makefile b/arch/loongarch/Makefile
> > index ec3de6191276..2bd0a574ed73 100644
> > --- a/arch/loongarch/Makefile
> > +++ b/arch/loongarch/Makefile
> > @@ -7,7 +7,11 @@ boot   := arch/loongarch/boot
> >
> >  KBUILD_DEFCONFIG := loongson3_defconfig
> >
> > -KBUILD_IMAGE   = $(boot)/vmlinux
> > +ifndef CONFIG_EFI_STUB
> > +KBUILD_IMAGE   = $(boot)/vmlinux.elf
> > +else
> > +KBUILD_IMAGE   = $(boot)/vmlinux.efi
> > +endif
> >
>
> Nit: I am not 100% whether it matters or not, but all other
> architectures use := for these assignments.
>
> Also, in order to be able to use vmlinux.elf or vmlinux.efi as a make
> target directly, other architectures seem to use something like
>
> all:    $(notdir $(KBUILD_IMAGE))
>
> vmlinux.elf vmlinux.efi: vmlinux
>         $(Q)$(MAKE) $(build)=$(boot) $(bootvars-y) $(boot)/$@
>
> Adopting this will make it easier to wire up the generic zboot support too.
OK, I will change the Makefile as you suggested.

>
> >  #
> >  # Select the object file format to substitute into the linker script.
> > @@ -75,6 +79,7 @@ endif
> >  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
> > diff --git a/arch/loongarch/boot/Makefile b/arch/loongarch/boot/Makefile
> > index 0125b17edc98..fecf34f50e56 100644
> > --- a/arch/loongarch/boot/Makefile
> > +++ b/arch/loongarch/boot/Makefile
> > @@ -8,9 +8,13 @@ drop-sections := .comment .note .options .note.gnu.build-id
> >  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)
> > diff --git a/arch/loongarch/include/asm/efi.h b/arch/loongarch/include/asm/efi.h
> > index 9d44c6948be1..c7507a240f30 100644
> > --- a/arch/loongarch/include/asm/efi.h
> > +++ b/arch/loongarch/include/asm/efi.h
> > @@ -18,8 +18,14 @@ void efifb_setup_from_dmi(struct screen_info *si, const char *opt);
> >
> >  #define EFI_ALLOC_ALIGN                SZ_64K
> >
> > -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)
> >  {
> > diff --git a/arch/loongarch/kernel/efi-header.S b/arch/loongarch/kernel/efi-header.S
> > new file mode 100644
> > index 000000000000..8c1d229a2afa
> > --- /dev/null
> > +++ b/arch/loongarch/kernel/efi-header.S
> > @@ -0,0 +1,99 @@
> > +/* 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
> > diff --git a/arch/loongarch/kernel/efi.c b/arch/loongarch/kernel/efi.c
> > index a50b60c587fa..1f1f755fb425 100644
> > --- a/arch/loongarch/kernel/efi.c
> > +++ b/arch/loongarch/kernel/efi.c
> > @@ -69,4 +69,7 @@ void __init efi_init(void)
> >         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);
> >  }
> > diff --git a/arch/loongarch/kernel/head.S b/arch/loongarch/kernel/head.S
> > index c60eb66793e3..01bac62a6442 100644
> > --- a/arch/loongarch/kernel/head.S
> > +++ b/arch/loongarch/kernel/head.S
> > @@ -12,6 +12,26 @@
> >  #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);
> > +
>
> These are a bit nasty: could you perhaps add a comment that explains
> why exactly we need to emit these values like this? Using
> kernel_offset with an offset to itself just to be able to refer to
> _text from loongarch-stub.c is especially horrid, so bonus points if
> you can find a better way to do that, preferably without asm
> variables.
This is due to a link problem. Our vmlinux is not PIC, but the stub is
PIC, then the stub code cannot access _text and other symbols defined
in vmlinux.lds.S, because the link address is too far for the stub.

>
> > +#endif
> > +
> >         __REF
> >
> >  SYM_CODE_START(kernel_entry)                   # kernel entry point
> > diff --git a/arch/loongarch/kernel/image-vars.h b/arch/loongarch/kernel/image-vars.h
> > new file mode 100644
> > index 000000000000..c901ebb903f2
> > --- /dev/null
> > +++ b/arch/loongarch/kernel/image-vars.h
> > @@ -0,0 +1,30 @@
> > +/* 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 */
> > diff --git a/arch/loongarch/kernel/setup.c b/arch/loongarch/kernel/setup.c
> > index 23ee293e1cd2..f938aae3e92c 100644
> > --- a/arch/loongarch/kernel/setup.c
> > +++ b/arch/loongarch/kernel/setup.c
> > @@ -49,9 +49,7 @@
> >  #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);
> > @@ -122,16 +120,9 @@ static void __init parse_cpu_table(const struct dmi_header *dm)
> >
> >  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);
> >  }
> >
>
> Why is this taken from the SMBIOS data?
This is removing obsolete code. Before we use generic stub, we use
SMBIOS as an indication whether we have efi support.

>
> >  static void __init find_tokens(const struct dmi_header *dm, void *dummy)
> > @@ -145,6 +136,7 @@ static void __init find_tokens(const struct dmi_header *dm, void *dummy)
> >                 break;
> >         }
> >  }
> > +
> >  static void __init smbios_parse(void)
> >  {
> >         b_info.bios_vendor = (void *)dmi_get_system_info(DMI_BIOS_VENDOR);
> > diff --git a/arch/loongarch/kernel/vmlinux.lds.S b/arch/loongarch/kernel/vmlinux.lds.S
> > index 69c76f26c1c5..36d042739f3c 100644
> > --- a/arch/loongarch/kernel/vmlinux.lds.S
> > +++ b/arch/loongarch/kernel/vmlinux.lds.S
> > @@ -12,6 +12,7 @@
> >  #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
> > diff --git a/drivers/firmware/efi/Kconfig b/drivers/firmware/efi/Kconfig
> > index 6cb7384ad2ac..cbf1c55dc224 100644
> > --- a/drivers/firmware/efi/Kconfig
> > +++ b/drivers/firmware/efi/Kconfig
> > @@ -107,7 +107,7 @@ config EFI_GENERIC_STUB
> >
> >  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
> > @@ -124,7 +124,7 @@ config EFI_GENERIC_STUB_INITRD_CMDLINE_LOADER
> >         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
> > diff --git a/drivers/firmware/efi/libstub/Makefile b/drivers/firmware/efi/libstub/Makefile
> > index d0537573501e..1588c61939e7 100644
> > --- a/drivers/firmware/efi/libstub/Makefile
> > +++ b/drivers/firmware/efi/libstub/Makefile
> > @@ -26,6 +26,8 @@ cflags-$(CONFIG_ARM)          := $(subst $(CC_FLAGS_FTRACE),,$(KBUILD_CFLAGS)) \
> >                                    $(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)) \
> > +                                  -fpic
> >
> >  cflags-$(CONFIG_EFI_GENERIC_STUB) += -I$(srctree)/scripts/dtc/libfdt
> >
> > @@ -70,6 +72,8 @@ lib-$(CONFIG_ARM)             += arm32-stub.o
> >  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
> > @@ -125,6 +129,12 @@ STUBCOPY_FLAGS-$(CONFIG_RISCV)     += --prefix-alloc-sections=.init \
> >                                    --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 doesn't 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)
> >
> > diff --git a/drivers/firmware/efi/libstub/efi-stub.c b/drivers/firmware/efi/libstub/efi-stub.c
> > index f515394cce6e..efb9219d8d49 100644
> > --- a/drivers/firmware/efi/libstub/efi-stub.c
> > +++ b/drivers/firmware/efi/libstub/efi-stub.c
> > @@ -40,14 +40,19 @@
> >
> >  #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
> >
> > +/*
> > + * 0: No flat mapping
> > + * 1: Flat mapping that VA = PA
> > + * 2: Flat mapping that VA = PA + PAGE_OFFSET
> > + */
> > +static int flat_va_mapping;
> >  static u64 virtmap_base = EFI_RT_VIRTUAL_BASE;
> > -static bool flat_va_mapping;
> >
>
> Can we change this around a bit?
>
> static bool flat_va_mapping = __is_defined(EFI_RT_FIXED_VIRTUAL_OFFSET);
>
> #ifndef EFI_RT_FIXED_VIRTUAL_OFFSET
> #define EFI_RT_FIXED_VIRTUAL_OFFSET 0
> #endif
>
> Then, in your arch's asm/efi.h, you can add
>
> #define EFI_RT_FIXED_VIRTUAL_OFFSET PAGE_OFFSET
Good idea, I will change to use the new method and send V3 asap.

Huacai
>
> ...
> > @@ -254,9 +260,11 @@ efi_status_t __efiapi efi_pe_entry(efi_handle_t handle,
> >          * 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 &
> > -                          EFI_PROPERTIES_RUNTIME_MEMORY_PROTECTION_NON_EXECUTABLE_PE_DATA);
> > +       attrib = prop_tbl ? prop_tbl->memory_protection_attribute : 0;
> > +       if (attrib & EFI_PROPERTIES_RUNTIME_MEMORY_PROTECTION_NON_EXECUTABLE_PE_DATA)
> > +               flat_va_mapping = 1;
> > +       if (IS_ENABLED(CONFIG_LOONGARCH))
> > +               flat_va_mapping = 2;
> >
>
> Keep the original code but change it into
>
> flat_va_mapping |= ....
>
> (and drop the rest of the change)
>
> >         /* force efi_novamap if SetVirtualAddressMap() is unsupported */
> >         efi_novamap |= !(get_supported_rt_services() &
> > @@ -338,7 +346,16 @@ void efi_get_virtmap(efi_memory_desc_t *memory_map, unsigned long map_size,
> >                 paddr = in->phys_addr;
> >                 size = in->num_pages * EFI_PAGE_SIZE;
> >
> > -               in->virt_addr = in->phys_addr;
>
> Change this into
>
>                in->virt_addr = in->phys_addr + EFI_RT_FIXED_VIRTUAL_OFFSET;
>
> > +               switch (flat_va_mapping) {
> > +               case 1:
> > +                       in->virt_addr = in->phys_addr;
> > +                       break;
> > +               case 2:
> > +                       in->virt_addr = in->phys_addr + PAGE_OFFSET;
> > +                       break;
> > +               default:
> > +                       in->virt_addr = in->phys_addr;
> > +               }
>
> And drop the switch
>
> >                 if (efi_novamap) {
> >                         continue;
> >                 }
> > diff --git a/drivers/firmware/efi/libstub/loongarch-stub.c b/drivers/firmware/efi/libstub/loongarch-stub.c
> > new file mode 100644
> > index 000000000000..b7ef8d2df59e
> > --- /dev/null
> > +++ b/drivers/firmware/efi/libstub/loongarch-stub.c
> > @@ -0,0 +1,60 @@
> > +// 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);
> > +}
> > diff --git a/include/linux/pe.h b/include/linux/pe.h
> > index daf09ffffe38..1d3836ef9d92 100644
> > --- a/include/linux/pe.h
> > +++ b/include/linux/pe.h
> > @@ -65,6 +65,8 @@
> >  #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
>
> This is looking really good now! Thanks for taking my feedback so seriously.
>
> We will have to figure out how to queue this up, given that I intend
> to queue the generic EFI zboot changes for v6.1 as well.
diff mbox series

Patch

diff --git a/arch/loongarch/Kconfig b/arch/loongarch/Kconfig
index 9478f9646fa5..4cb412a82afa 100644
--- a/arch/loongarch/Kconfig
+++ b/arch/loongarch/Kconfig
@@ -324,6 +324,15 @@  config EFI
 	  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
diff --git a/arch/loongarch/Makefile b/arch/loongarch/Makefile
index ec3de6191276..2bd0a574ed73 100644
--- a/arch/loongarch/Makefile
+++ b/arch/loongarch/Makefile
@@ -7,7 +7,11 @@  boot	:= arch/loongarch/boot
 
 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.
@@ -75,6 +79,7 @@  endif
 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
diff --git a/arch/loongarch/boot/Makefile b/arch/loongarch/boot/Makefile
index 0125b17edc98..fecf34f50e56 100644
--- a/arch/loongarch/boot/Makefile
+++ b/arch/loongarch/boot/Makefile
@@ -8,9 +8,13 @@  drop-sections := .comment .note .options .note.gnu.build-id
 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)
diff --git a/arch/loongarch/include/asm/efi.h b/arch/loongarch/include/asm/efi.h
index 9d44c6948be1..c7507a240f30 100644
--- a/arch/loongarch/include/asm/efi.h
+++ b/arch/loongarch/include/asm/efi.h
@@ -18,8 +18,14 @@  void efifb_setup_from_dmi(struct screen_info *si, const char *opt);
 
 #define EFI_ALLOC_ALIGN		SZ_64K
 
-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)
 {
diff --git a/arch/loongarch/kernel/efi-header.S b/arch/loongarch/kernel/efi-header.S
new file mode 100644
index 000000000000..8c1d229a2afa
--- /dev/null
+++ b/arch/loongarch/kernel/efi-header.S
@@ -0,0 +1,99 @@ 
+/* 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
diff --git a/arch/loongarch/kernel/efi.c b/arch/loongarch/kernel/efi.c
index a50b60c587fa..1f1f755fb425 100644
--- a/arch/loongarch/kernel/efi.c
+++ b/arch/loongarch/kernel/efi.c
@@ -69,4 +69,7 @@  void __init efi_init(void)
 	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);
 }
diff --git a/arch/loongarch/kernel/head.S b/arch/loongarch/kernel/head.S
index c60eb66793e3..01bac62a6442 100644
--- a/arch/loongarch/kernel/head.S
+++ b/arch/loongarch/kernel/head.S
@@ -12,6 +12,26 @@ 
 #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
diff --git a/arch/loongarch/kernel/image-vars.h b/arch/loongarch/kernel/image-vars.h
new file mode 100644
index 000000000000..c901ebb903f2
--- /dev/null
+++ b/arch/loongarch/kernel/image-vars.h
@@ -0,0 +1,30 @@ 
+/* 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 */
diff --git a/arch/loongarch/kernel/setup.c b/arch/loongarch/kernel/setup.c
index 23ee293e1cd2..f938aae3e92c 100644
--- a/arch/loongarch/kernel/setup.c
+++ b/arch/loongarch/kernel/setup.c
@@ -49,9 +49,7 @@ 
 #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);
@@ -122,16 +120,9 @@  static void __init parse_cpu_table(const struct dmi_header *dm)
 
 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)
@@ -145,6 +136,7 @@  static void __init find_tokens(const struct dmi_header *dm, void *dummy)
 		break;
 	}
 }
+
 static void __init smbios_parse(void)
 {
 	b_info.bios_vendor = (void *)dmi_get_system_info(DMI_BIOS_VENDOR);
diff --git a/arch/loongarch/kernel/vmlinux.lds.S b/arch/loongarch/kernel/vmlinux.lds.S
index 69c76f26c1c5..36d042739f3c 100644
--- a/arch/loongarch/kernel/vmlinux.lds.S
+++ b/arch/loongarch/kernel/vmlinux.lds.S
@@ -12,6 +12,7 @@ 
 #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
diff --git a/drivers/firmware/efi/Kconfig b/drivers/firmware/efi/Kconfig
index 6cb7384ad2ac..cbf1c55dc224 100644
--- a/drivers/firmware/efi/Kconfig
+++ b/drivers/firmware/efi/Kconfig
@@ -107,7 +107,7 @@  config EFI_GENERIC_STUB
 
 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
@@ -124,7 +124,7 @@  config EFI_GENERIC_STUB_INITRD_CMDLINE_LOADER
 	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
diff --git a/drivers/firmware/efi/libstub/Makefile b/drivers/firmware/efi/libstub/Makefile
index d0537573501e..1588c61939e7 100644
--- a/drivers/firmware/efi/libstub/Makefile
+++ b/drivers/firmware/efi/libstub/Makefile
@@ -26,6 +26,8 @@  cflags-$(CONFIG_ARM)		:= $(subst $(CC_FLAGS_FTRACE),,$(KBUILD_CFLAGS)) \
 				   $(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)) \
+				   -fpic
 
 cflags-$(CONFIG_EFI_GENERIC_STUB) += -I$(srctree)/scripts/dtc/libfdt
 
@@ -70,6 +72,8 @@  lib-$(CONFIG_ARM)		+= arm32-stub.o
 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
@@ -125,6 +129,12 @@  STUBCOPY_FLAGS-$(CONFIG_RISCV)	+= --prefix-alloc-sections=.init \
 				   --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 doesn't 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)
 
diff --git a/drivers/firmware/efi/libstub/efi-stub.c b/drivers/firmware/efi/libstub/efi-stub.c
index f515394cce6e..efb9219d8d49 100644
--- a/drivers/firmware/efi/libstub/efi-stub.c
+++ b/drivers/firmware/efi/libstub/efi-stub.c
@@ -40,14 +40,19 @@ 
 
 #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
 
+/*
+ * 0: No flat mapping
+ * 1: Flat mapping that VA = PA
+ * 2: Flat mapping that VA = PA + PAGE_OFFSET
+ */
+static int flat_va_mapping;
 static u64 virtmap_base = EFI_RT_VIRTUAL_BASE;
-static bool flat_va_mapping;
 
 const efi_system_table_t *efi_system_table;
 
@@ -121,6 +126,7 @@  efi_status_t __efiapi efi_pe_entry(efi_handle_t handle,
 {
 	efi_loaded_image_t *image;
 	efi_status_t status;
+	unsigned long attrib;
 	unsigned long image_addr;
 	unsigned long image_size = 0;
 	/* addr/point and size pairs for memory management*/
@@ -254,9 +260,11 @@  efi_status_t __efiapi efi_pe_entry(efi_handle_t handle,
 	 * 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 &
-			   EFI_PROPERTIES_RUNTIME_MEMORY_PROTECTION_NON_EXECUTABLE_PE_DATA);
+	attrib = prop_tbl ? prop_tbl->memory_protection_attribute : 0;
+	if (attrib & EFI_PROPERTIES_RUNTIME_MEMORY_PROTECTION_NON_EXECUTABLE_PE_DATA)
+		flat_va_mapping = 1;
+	if (IS_ENABLED(CONFIG_LOONGARCH))
+		flat_va_mapping = 2;
 
 	/* force efi_novamap if SetVirtualAddressMap() is unsupported */
 	efi_novamap |= !(get_supported_rt_services() &
@@ -338,7 +346,16 @@  void efi_get_virtmap(efi_memory_desc_t *memory_map, unsigned long map_size,
 		paddr = in->phys_addr;
 		size = in->num_pages * EFI_PAGE_SIZE;
 
-		in->virt_addr = in->phys_addr;
+		switch (flat_va_mapping) {
+		case 1:
+			in->virt_addr = in->phys_addr;
+			break;
+		case 2:
+			in->virt_addr = in->phys_addr + PAGE_OFFSET;
+			break;
+		default:
+			in->virt_addr = in->phys_addr;
+		}
 		if (efi_novamap) {
 			continue;
 		}
diff --git a/drivers/firmware/efi/libstub/loongarch-stub.c b/drivers/firmware/efi/libstub/loongarch-stub.c
new file mode 100644
index 000000000000..b7ef8d2df59e
--- /dev/null
+++ b/drivers/firmware/efi/libstub/loongarch-stub.c
@@ -0,0 +1,60 @@ 
+// 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);
+}
diff --git a/include/linux/pe.h b/include/linux/pe.h
index daf09ffffe38..1d3836ef9d92 100644
--- a/include/linux/pe.h
+++ b/include/linux/pe.h
@@ -65,6 +65,8 @@ 
 #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