]> git.baikalelectronics.ru Git - kernel.git/commitdiff
efi/loongarch: Add efistub booting support
authorHuacai Chen <chenhuacai@loongson.cn>
Fri, 19 Aug 2022 10:20:37 +0000 (18:20 +0800)
committerArd Biesheuvel <ardb@kernel.org>
Tue, 6 Sep 2022 09:19:34 +0000 (11:19 +0200)
This patch adds efistub booting support, which is the standard UEFI boot
protocol for LoongArch 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>
[ardb: change fpic to fpie as suggested by Xi Ruoyao]
Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
15 files changed:
arch/loongarch/Kconfig
arch/loongarch/Makefile
arch/loongarch/boot/Makefile
arch/loongarch/include/asm/efi.h
arch/loongarch/kernel/efi-header.S [new file with mode: 0644]
arch/loongarch/kernel/efi.c
arch/loongarch/kernel/head.S
arch/loongarch/kernel/image-vars.h [new file with mode: 0644]
arch/loongarch/kernel/setup.c
arch/loongarch/kernel/vmlinux.lds.S
drivers/firmware/efi/Kconfig
drivers/firmware/efi/libstub/Makefile
drivers/firmware/efi/libstub/efi-stub.c
drivers/firmware/efi/libstub/loongarch-stub.c [new file with mode: 0644]
include/linux/pe.h

index 4abc9a28aba4eee5959f7a1e17f4814a945165c0..fca106a8b8af72f4f4cd15a21d2f355951ea50d3 100644 (file)
@@ -317,6 +317,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
index ec3de619127655a074ab17a95873e21f640030f5..4bc47f47cfd83379b21a0d9df7a23f6fb183f779 100644 (file)
@@ -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
@@ -86,10 +91,10 @@ PHONY += vdso_install
 vdso_install:
        $(Q)$(MAKE) $(build)=arch/loongarch/vdso $@
 
-all:   $(KBUILD_IMAGE)
+all:   $(notdir $(KBUILD_IMAGE))
 
-$(KBUILD_IMAGE): vmlinux
-       $(Q)$(MAKE) $(build)=$(boot) $(bootvars-y) $@
+vmlinux.elf vmlinux.efi: vmlinux
+       $(Q)$(MAKE) $(build)=$(boot) $(bootvars-y) $(boot)/$@
 
 install:
        $(Q)install -D -m 755 $(KBUILD_IMAGE) $(INSTALL_PATH)/vmlinux-$(KERNELRELEASE)
index 0125b17edc98c837d97854aca474b90d021c92cc..fecf34f50e5654a0352f32690e7355b9e5d34142 100644 (file)
@@ -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)
index 9d44c6948be1bdc9d45dda22ce172e16b0c4f81f..174567b00ddb907d32241ae45a7d71ff2e902050 100644 (file)
@@ -17,9 +17,16 @@ void efifb_setup_from_dmi(struct screen_info *si, const char *opt);
 #define arch_efi_call_virt_teardown()
 
 #define EFI_ALLOC_ALIGN                SZ_64K
+#define EFI_RT_VIRTUAL_OFFSET  CSR_DMW0_BASE
 
-struct screen_info *alloc_screen_info(void);
-void free_screen_info(struct screen_info *si);
+static inline struct screen_info *alloc_screen_info(void)
+{
+       return &screen_info;
+}
+
+static inline void free_screen_info(struct screen_info *si)
+{
+}
 
 static inline unsigned long efi_get_max_initrd_addr(unsigned long image_addr)
 {
diff --git a/arch/loongarch/kernel/efi-header.S b/arch/loongarch/kernel/efi-header.S
new file mode 100644 (file)
index 0000000..8c1d229
--- /dev/null
@@ -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
index a50b60c587fa0fb89a3838fe0e7b7be8767ba51d..1f1f755fb425514df023d86c219dc289258b07c9 100644 (file)
@@ -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);
 }
index c60eb66793e351cb0a91114cab9478903b44ebdb..01bac62a644273d3951e147fdc1f8077701745b9 100644 (file)
 #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 (file)
index 0000000..c901ebb
--- /dev/null
@@ -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 */
index 8f5c2f9a1a835de4435cec1a5e92439387017697..e8714b1d94c853fbb7848b4f514416347163fee7 100644 (file)
@@ -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)
index 69c76f26c1c57e03ba4ff6389111abd8cbedf03a..36d042739f3c54dfdf1ea5461a3b3bafd59bb3ff 100644 (file)
@@ -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
index 6cb7384ad2ac7062882197d4f0d6a912cbbad5c2..cbf1c55dc22474905ac6eba7c7531ac0f749106f 100644 (file)
@@ -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
index d0537573501e9353446bd45dcf6bf7da026551d1..ec2a7ba9364f0cf2b69e3e7ee02cf0c89c2745f5 100644 (file)
@@ -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)) \
+                                  -fpie
 
 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 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)
 
index f515394cce6e23295e93f018a57b4f1aa4c59a37..4bf751484e8bfd68124aeeba170639467ee6a11f 100644 (file)
 
 #ifdef CONFIG_ARM64
 # define EFI_RT_VIRTUAL_LIMIT  DEFAULT_MAP_WINDOW_64
-#elif defined(CONFIG_RISCV)
+#elif defined(CONFIG_RISCV) || defined(CONFIG_LOONGARCH)
 # define EFI_RT_VIRTUAL_LIMIT  TASK_SIZE_MIN
-#else
+#else /* Only if TASK_SIZE is a constant */
 # define EFI_RT_VIRTUAL_LIMIT  TASK_SIZE
 #endif
 
+/*
+ * Some architectures map the EFI regions into the kernel's linear map using a
+ * fixed offset.
+ */
+#ifndef EFI_RT_VIRTUAL_OFFSET
+#define EFI_RT_VIRTUAL_OFFSET  0
+#endif
+
 static u64 virtmap_base = EFI_RT_VIRTUAL_BASE;
-static bool flat_va_mapping;
+static bool flat_va_mapping = (EFI_RT_VIRTUAL_OFFSET != 0);
 
 const efi_system_table_t *efi_system_table;
 
@@ -254,8 +262,8 @@ 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 &
+       flat_va_mapping |= prop_tbl &&
+                          (prop_tbl->memory_protection_attribute &
                           EFI_PROPERTIES_RUNTIME_MEMORY_PROTECTION_NON_EXECUTABLE_PE_DATA);
 
        /* force efi_novamap if SetVirtualAddressMap() is unsupported */
@@ -338,7 +346,7 @@ 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;
+               in->virt_addr = in->phys_addr + EFI_RT_VIRTUAL_OFFSET;
                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 (file)
index 0000000..b7ef8d2
--- /dev/null
@@ -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);
+}
index daf09ffffe38992658a7ec25a5c15a6b376de389..1d3836ef9d92dcd89935faedf76899daf8cdb7e3 100644 (file)
@@ -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