Skip to content

Commit

Permalink
efi/loongarch: Add efistub booting support
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Huacai Chen authored and Ard Biesheuvel committed Sep 6, 2022
1 parent 568035b commit ead384d
Show file tree
Hide file tree
Showing 15 changed files with 275 additions and 26 deletions.
9 changes: 9 additions & 0 deletions arch/loongarch/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
13 changes: 9 additions & 4 deletions arch/loongarch/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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
Expand All @@ -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)
Expand Down
8 changes: 6 additions & 2 deletions arch/loongarch/boot/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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)
11 changes: 9 additions & 2 deletions arch/loongarch/include/asm/efi.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down
99 changes: 99 additions & 0 deletions arch/loongarch/kernel/efi-header.S
Original file line number Diff line number Diff line change
@@ -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
3 changes: 3 additions & 0 deletions arch/loongarch/kernel/efi.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
20 changes: 20 additions & 0 deletions arch/loongarch/kernel/head.S
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
30 changes: 30 additions & 0 deletions arch/loongarch/kernel/image-vars.h
Original file line number Diff line number Diff line change
@@ -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 */
11 changes: 1 addition & 10 deletions arch/loongarch/kernel/setup.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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)
Expand Down
1 change: 1 addition & 0 deletions arch/loongarch/kernel/vmlinux.lds.S
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions drivers/firmware/efi/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
10 changes: 10 additions & 0 deletions drivers/firmware/efi/libstub/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)

Expand Down
20 changes: 14 additions & 6 deletions drivers/firmware/efi/libstub/efi-stub.c
Original file line number Diff line number Diff line change
Expand Up @@ -40,14 +40,22 @@

#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;

Expand Down Expand Up @@ -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 */
Expand Down Expand Up @@ -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;
}
Expand Down
Loading

0 comments on commit ead384d

Please sign in to comment.