[PATCH V9 20/24] LoongArch: Add efistub booting support

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



This patch adds efistub booting support, which is the standard UEFI boot
protocol for us to use.

Signed-off-by: Huacai Chen <chenhuacai@xxxxxxxxxxx>
---
 arch/loongarch/Kbuild                         |   3 +
 arch/loongarch/Kconfig                        |   8 +
 arch/loongarch/Makefile                       |  18 +-
 arch/loongarch/boot/Makefile                  |  23 +
 arch/loongarch/kernel/efi-header.S            | 100 +++++
 arch/loongarch/kernel/head.S                  |  44 +-
 arch/loongarch/kernel/image-vars.h            |  30 ++
 arch/loongarch/kernel/vmlinux.lds.S           |  23 +-
 drivers/firmware/efi/Kconfig                  |   4 +-
 drivers/firmware/efi/libstub/Makefile         |  14 +-
 drivers/firmware/efi/libstub/loongarch-stub.c | 425 ++++++++++++++++++
 include/linux/pe.h                            |   1 +
 12 files changed, 680 insertions(+), 13 deletions(-)
 create mode 100644 arch/loongarch/boot/Makefile
 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/Kbuild b/arch/loongarch/Kbuild
index 1ad35aabdd16..ab5373d0a24f 100644
--- a/arch/loongarch/Kbuild
+++ b/arch/loongarch/Kbuild
@@ -1,3 +1,6 @@
 obj-y += kernel/
 obj-y += mm/
 obj-y += vdso/
+
+# for cleaning
+subdir- += boot
diff --git a/arch/loongarch/Kconfig b/arch/loongarch/Kconfig
index 44b763046893..55225ee5f868 100644
--- a/arch/loongarch/Kconfig
+++ b/arch/loongarch/Kconfig
@@ -265,6 +265,14 @@ config EFI
 	  resultant kernel should continue to boot on existing non-EFI
 	  platforms.
 
+config EFI_STUB
+	bool "EFI boot stub support"
+	default y
+	depends on EFI
+	help
+	  This kernel feature allows the kernel to be loaded directly by
+	  EFI firmware without the use of a bootloader.
+
 config FORCE_MAX_ZONEORDER
 	int "Maximum zone order"
 	range 14 64 if PAGE_SIZE_64KB
diff --git a/arch/loongarch/Makefile b/arch/loongarch/Makefile
index c4b3f53cd276..d88a792dafbe 100644
--- a/arch/loongarch/Makefile
+++ b/arch/loongarch/Makefile
@@ -3,6 +3,14 @@
 # Author: Huacai Chen <chenhuacai@xxxxxxxxxxx>
 # Copyright (C) 2020-2022 Loongson Technology Corporation Limited
 
+boot	:= arch/loongarch/boot
+
+ifndef CONFIG_EFI_STUB
+KBUILD_IMAGE	= $(boot)/vmlinux
+else
+KBUILD_IMAGE	= $(boot)/vmlinux.efi
+endif
+
 #
 # Select the object file format to substitute into the linker script.
 #
@@ -30,8 +38,6 @@ ld-emul			= $(64bit-emul)
 cflags-y		+= -mabi=lp64s
 endif
 
-all-y			:= vmlinux
-
 #
 # GCC uses -G0 -mabicalls -fpic as default.  We don't want PIC in the kernel
 # code since it only slows down the whole thing.  At some point we might make
@@ -75,6 +81,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,12 +93,13 @@ PHONY += vdso_install
 vdso_install:
 	$(Q)$(MAKE) $(build)=arch/loongarch/vdso $@
 
-all:	$(all-y)
+all:	$(KBUILD_IMAGE)
 
-CLEAN_FILES += vmlinux
+$(KBUILD_IMAGE): vmlinux
+	$(Q)$(MAKE) $(build)=$(boot) $(bootvars-y) $@
 
 install:
-	$(Q)install -D -m 755 vmlinux $(INSTALL_PATH)/vmlinux-$(KERNELRELEASE)
+	$(Q)install -D -m 755 $(KBUILD_IMAGE) $(INSTALL_PATH)/vmlinux-$(KERNELRELEASE)
 	$(Q)install -D -m 644 .config $(INSTALL_PATH)/config-$(KERNELRELEASE)
 	$(Q)install -D -m 644 System.map $(INSTALL_PATH)/System.map-$(KERNELRELEASE)
 
diff --git a/arch/loongarch/boot/Makefile b/arch/loongarch/boot/Makefile
new file mode 100644
index 000000000000..66f2293c34b2
--- /dev/null
+++ b/arch/loongarch/boot/Makefile
@@ -0,0 +1,23 @@
+#
+# arch/loongarch/boot/Makefile
+#
+# Copyright (C) 2020-2022 Loongson Technology Corporation Limited
+#
+
+drop-sections := .comment .note .options .note.gnu.build-id
+strip-flags   := $(addprefix --remove-section=,$(drop-sections)) -S
+
+targets := vmlinux
+quiet_cmd_strip = STRIP	  $@
+      cmd_strip = $(STRIP) -s $@
+
+$(obj)/vmlinux: vmlinux FORCE
+	$(call if_changed,copy)
+	$(call if_changed,strip)
+
+targets += vmlinux.efi
+quiet_cmd_eficopy = OBJCOPY $@
+      cmd_eficopy = $(OBJCOPY) -O binary $(strip-flags) $< $@
+
+$(obj)/vmlinux.efi: $(obj)/vmlinux FORCE
+	$(call if_changed,eficopy)
diff --git a/arch/loongarch/kernel/efi-header.S b/arch/loongarch/kernel/efi-header.S
new file mode 100644
index 000000000000..ceb44524944a
--- /dev/null
+++ b/arch/loongarch/kernel/efi-header.S
@@ -0,0 +1,100 @@
+/* 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
+coff_header:
+	.short	IMAGE_FILE_MACHINE_LOONGARCH		/* Machine */
+	.short	section_count				/* NumberOfSections */
+	.long	0 					/* TimeDateStamp */
+	.long	0					/* PointerToSymbolTable */
+	.long	0					/* NumberOfSymbols */
+	.short	section_table - optional_header		/* SizeOfOptionalHeader */
+	.short	IMAGE_FILE_DEBUG_STRIPPED | \
+		IMAGE_FILE_EXECUTABLE_IMAGE | \
+		IMAGE_FILE_LINE_NUMS_STRIPPED		/* Characteristics */
+
+optional_header:
+	.short	PE_OPT_MAGIC_PE32PLUS			/* PE32+ format */
+	.byte	0x02					/* MajorLinkerVersion */
+	.byte	0x14					/* MinorLinkerVersion */
+	.long	__inittext_end - efi_header_end		/* SizeOfCode */
+	.long	_end - __initdata_begin			/* SizeOfInitializedData */
+	.long	0					/* SizeOfUninitializedData */
+	.long	__efistub_efi_pe_entry - _head		/* AddressOfEntryPoint */
+	.long	efi_header_end - _head			/* BaseOfCode */
+
+extra_header_fields:
+	.quad	0					/* ImageBase */
+	.long	PECOFF_SEGMENT_ALIGN			/* SectionAlignment */
+	.long	PECOFF_FILE_ALIGN			/* FileAlignment */
+	.short	0					/* MajorOperatingSystemVersion */
+	.short	0					/* MinorOperatingSystemVersion */
+	.short	0					/* MajorImageVersion */
+	.short	0					/* 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	efi_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	(section_table - .) / 8			/* NumberOfRvaAndSizes */
+
+	.quad	0					/* ExportTable */
+	.quad	0					/* ImportTable */
+	.quad	0					/* ResourceTable */
+	.quad	0					/* ExceptionTable */
+	.quad	0					/* CertificationTable */
+	.quad	0					/* BaseRelocationTable */
+
+	/* Section table */
+section_table:
+	.ascii	".text\0\0\0"
+	.long	__inittext_end - efi_header_end		/* VirtualSize */
+	.long	efi_header_end - _head			/* VirtualAddress */
+	.long	__inittext_end - efi_header_end		/* SizeOfRawData */
+	.long	efi_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 */
+
+	.org 0x20e
+	.word kernel_version - 512 -  _head
+
+	.set	section_count, (. - section_table) / 40
+efi_header_end:
+	.endm
diff --git a/arch/loongarch/kernel/head.S b/arch/loongarch/kernel/head.S
index b4a0b28da3e7..361b72e8bfc5 100644
--- a/arch/loongarch/kernel/head.S
+++ b/arch/loongarch/kernel/head.S
@@ -11,11 +11,53 @@
 #include <asm/regdef.h>
 #include <asm/loongarch.h>
 #include <asm/stackframe.h>
+#include <generated/compile.h>
+#include <generated/utsrelease.h>
 
-SYM_ENTRY(_stext, SYM_L_GLOBAL, SYM_A_NONE)
+#ifdef CONFIG_EFI_STUB
+
+#include "efi-header.S"
+
+	__HEAD
+
+_head:
+	/* "MZ", MS-DOS header */
+	.word	MZ_MAGIC
+	.org	0x28
+	.ascii	"Loongson\0"
+	.org	0x3c
+	/* Offset to the PE header */
+	.long	pe_header - _head
+
+pe_header:
+	__EFI_PE_HEADER
+
+kernel_asize:
+	.long _end - _text
+
+kernel_fsize:
+	.long _edata - _text
+
+kernel_vaddr:
+	.quad VMLINUX_LOAD_ADDRESS
+
+kernel_offset:
+	.long kernel_offset - _text
+
+kernel_version:
+	.ascii  UTS_RELEASE " (" LINUX_COMPILE_BY "@" LINUX_COMPILE_HOST ") " UTS_VERSION "\0"
+
+SYM_L_GLOBAL(kernel_asize)
+SYM_L_GLOBAL(kernel_fsize)
+SYM_L_GLOBAL(kernel_vaddr)
+SYM_L_GLOBAL(kernel_offset)
+
+#endif
 
 	__REF
 
+SYM_ENTRY(_stext, SYM_L_GLOBAL, SYM_A_NONE)
+
 SYM_CODE_START(kernel_entry)			# kernel entry point
 
 	/* Config direct window and set PG */
diff --git a/arch/loongarch/kernel/image-vars.h b/arch/loongarch/kernel/image-vars.h
new file mode 100644
index 000000000000..0162402b6212
--- /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_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_strpbrk		= strpbrk;
+__efistub_strsep		= strsep;
+__efistub_kernel_entry		= kernel_entry;
+__efistub_kernel_asize		= kernel_asize;
+__efistub_kernel_fsize		= kernel_fsize;
+__efistub_kernel_vaddr		= kernel_vaddr;
+__efistub_kernel_offset		= kernel_offset;
+
+#endif
+
+#endif /* __LOONGARCH_KERNEL_IMAGE_VARS_H */
diff --git a/arch/loongarch/kernel/vmlinux.lds.S b/arch/loongarch/kernel/vmlinux.lds.S
index 02abfaaa4892..7da4c4d7c50d 100644
--- a/arch/loongarch/kernel/vmlinux.lds.S
+++ b/arch/loongarch/kernel/vmlinux.lds.S
@@ -12,6 +12,14 @@
 #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
+ * field of EFI application to 64K.
+ */
+PECOFF_FILE_ALIGN = 0x200;
+PECOFF_SEGMENT_ALIGN = 0x10000;
 
 OUTPUT_ARCH(loongarch)
 ENTRY(kernel_entry)
@@ -27,6 +35,9 @@ SECTIONS
 	. = VMLINUX_LOAD_ADDRESS;
 
 	_text = .;
+	HEAD_TEXT_SECTION
+
+	. = ALIGN(PECOFF_SEGMENT_ALIGN);
 	.text : {
 		TEXT_TEXT
 		SCHED_TEXT
@@ -38,11 +49,12 @@ SECTIONS
 		*(.fixup)
 		*(.gnu.warning)
 	} :text = 0
+	. = ALIGN(PECOFF_SEGMENT_ALIGN);
 	_etext = .;
 
 	EXCEPTION_TABLE(16)
 
-	. = ALIGN(PAGE_SIZE);
+	. = ALIGN(PECOFF_SEGMENT_ALIGN);
 	__init_begin = .;
 	__inittext_begin = .;
 
@@ -51,6 +63,7 @@ SECTIONS
 		EXIT_TEXT
 	}
 
+	. = ALIGN(PECOFF_SEGMENT_ALIGN);
 	__inittext_end = .;
 
 	__initdata_begin = .;
@@ -60,6 +73,10 @@ SECTIONS
 		EXIT_DATA
 	}
 
+	.init.bss : {
+		*(.init.bss)
+	}
+	. = ALIGN(PECOFF_SEGMENT_ALIGN);
 	__initdata_end = .;
 
 	__init_end = .;
@@ -71,11 +88,11 @@ SECTIONS
 	.sdata : {
 		*(.sdata)
 	}
-
-	. = ALIGN(SZ_64K);
+	.edata_padding : { BYTE(0); . = ALIGN(PECOFF_FILE_ALIGN); }
 	_edata =  .;
 
 	BSS_SECTION(0, SZ_64K, 8)
+	. = ALIGN(PECOFF_SEGMENT_ALIGN);
 
 	_end = .;
 
diff --git a/drivers/firmware/efi/Kconfig b/drivers/firmware/efi/Kconfig
index 2c3dac5ecb36..ecb4e0b1295a 100644
--- a/drivers/firmware/efi/Kconfig
+++ b/drivers/firmware/efi/Kconfig
@@ -121,9 +121,9 @@ config EFI_ARMSTUB_DTB_LOADER
 
 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 EFI_STUB && (EFI_GENERIC_STUB || X86 || LOONGARCH)
+	default y if (X86 || 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..663e9d317299 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
 
@@ -55,7 +57,7 @@ KCOV_INSTRUMENT			:= n
 lib-y				:= efi-stub-helper.o gop.o secureboot.o tpm.o \
 				   file.o mem.o random.o randomalloc.o pci.o \
 				   skip_spaces.o lib-cmdline.o lib-ctype.o \
-				   alignedmem.o relocate.o vsprintf.o
+				   alignedmem.o relocate.o string.o vsprintf.o
 
 # include the stub's generic dependencies from lib/ when building for ARM/arm64
 efi-deps-y := fdt_rw.c fdt_ro.c fdt_wip.c fdt.c fdt_empty_tree.c fdt_sw.c
@@ -63,13 +65,15 @@ efi-deps-y := fdt_rw.c fdt_ro.c fdt_wip.c fdt.c fdt_empty_tree.c fdt_sw.c
 $(obj)/lib-%.o: $(srctree)/lib/%.c FORCE
 	$(call if_changed_rule,cc_o_c)
 
-lib-$(CONFIG_EFI_GENERIC_STUB)	+= efi-stub.o fdt.o string.o \
+lib-$(CONFIG_EFI_GENERIC_STUB)	+= efi-stub.o fdt.o \
 				   $(patsubst %.c,lib-%.o,$(efi-deps-y))
 
 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/loongarch-stub.c b/drivers/firmware/efi/libstub/loongarch-stub.c
new file mode 100644
index 000000000000..399641a0b0cb
--- /dev/null
+++ b/drivers/firmware/efi/libstub/loongarch-stub.c
@@ -0,0 +1,425 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Author: Yun Liu <liuyun@xxxxxxxxxxx>
+ *         Huacai Chen <chenhuacai@xxxxxxxxxxx>
+ * Copyright (C) 2020-2022 Loongson Technology Corporation Limited
+ */
+
+#include <linux/efi.h>
+#include <linux/sort.h>
+#include <asm/efi.h>
+#include <asm/addrspace.h>
+#include <asm/boot_param.h>
+#include "efistub.h"
+
+#define MAX_ARG_COUNT		128
+#define CMDLINE_MAX_SIZE	0x200
+
+static int argc;
+static char **argv;
+const efi_system_table_t *efi_system_table;
+static efi_guid_t screen_info_guid = LINUX_EFI_LARCH_SCREEN_INFO_TABLE_GUID;
+static unsigned int map_entry[LOONGSON3_BOOT_MEM_MAP_MAX];
+static struct efi_mmap mmap_array[EFI_MAX_MEMORY_TYPE][LOONGSON3_BOOT_MEM_MAP_MAX];
+
+struct exit_boot_struct {
+	struct boot_params *bp;
+	unsigned int *runtime_entry_count;
+};
+
+typedef void (*kernel_entry_t)(int argc, char *argv[], struct boot_params *boot_p);
+
+extern int kernel_asize;
+extern int kernel_fsize;
+extern int kernel_offset;
+extern unsigned long kernel_vaddr;
+extern kernel_entry_t kernel_entry;
+
+unsigned char efi_crc8(char *buff, int size)
+{
+	int sum, cnt;
+
+	for (sum = 0, cnt = 0; cnt < size; cnt++)
+		sum = (char) (sum + *(buff + cnt));
+
+	return (char)(0x100 - sum);
+}
+
+struct screen_info *alloc_screen_info(void)
+{
+	efi_status_t status;
+	struct screen_info *si;
+
+	status = efi_bs_call(allocate_pool,
+			EFI_RUNTIME_SERVICES_DATA, sizeof(*si), (void **)&si);
+	if (status != EFI_SUCCESS)
+		return NULL;
+
+	status = efi_bs_call(install_configuration_table, &screen_info_guid, si);
+	if (status == EFI_SUCCESS)
+		return si;
+
+	efi_bs_call(free_pool, si);
+
+	return NULL;
+}
+
+static void setup_graphics(void)
+{
+	unsigned long size;
+	efi_status_t status;
+	efi_guid_t gop_proto = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID;
+	void **gop_handle = NULL;
+	struct screen_info *si = NULL;
+
+	size = 0;
+	status = efi_bs_call(locate_handle, EFI_LOCATE_BY_PROTOCOL,
+				&gop_proto, NULL, &size, gop_handle);
+	if (status == EFI_BUFFER_TOO_SMALL) {
+		si = alloc_screen_info();
+		efi_setup_gop(si, &gop_proto, size);
+	}
+}
+
+struct boot_params *bootparams_init(efi_system_table_t *sys_table)
+{
+	efi_status_t status;
+	struct boot_params *p;
+	unsigned char sig[8] = {'B', 'P', 'I', '0', '1', '0', '0', '2'};
+
+	status = efi_bs_call(allocate_pool, EFI_RUNTIME_SERVICES_DATA, SZ_64K, (void **)&p);
+	if (status != EFI_SUCCESS)
+		return NULL;
+
+	memset(p, 0, SZ_64K);
+	memcpy(&p->signature, sig, sizeof(long));
+
+	return p;
+}
+
+static unsigned long convert_priv_cmdline(char *cmdline_ptr,
+		unsigned long rd_addr, unsigned long rd_size)
+{
+	unsigned int rdprev_size;
+	unsigned int cmdline_size;
+	efi_status_t status;
+	char *pstr, *substr;
+	char *initrd_ptr = NULL;
+	char convert_str[CMDLINE_MAX_SIZE];
+	static char cmdline_array[CMDLINE_MAX_SIZE];
+
+	cmdline_size = strlen(cmdline_ptr);
+	snprintf(cmdline_array, CMDLINE_MAX_SIZE, "kernel ");
+
+	initrd_ptr = strstr(cmdline_ptr, "initrd=");
+	if (!initrd_ptr) {
+		snprintf(cmdline_array, CMDLINE_MAX_SIZE, "kernel %s", cmdline_ptr);
+		goto completed;
+	}
+	snprintf(convert_str, CMDLINE_MAX_SIZE, " initrd=0x%lx,0x%lx", rd_addr, rd_size);
+	rdprev_size = cmdline_size - strlen(initrd_ptr);
+	strncat(cmdline_array, cmdline_ptr, rdprev_size);
+
+	cmdline_ptr = strnstr(initrd_ptr, " ", CMDLINE_MAX_SIZE);
+	strcat(cmdline_array, convert_str);
+	if (!cmdline_ptr)
+		goto completed;
+
+	strcat(cmdline_array, cmdline_ptr);
+
+completed:
+	status = efi_allocate_pages((MAX_ARG_COUNT + 1) * (sizeof(char *)),
+					(unsigned long *)&argv, ULONG_MAX);
+	if (status != EFI_SUCCESS) {
+		efi_err("Alloc argv mmap_array error\n");
+		return status;
+	}
+
+	argc = 0;
+	pstr = cmdline_array;
+
+	substr = strsep(&pstr, " \t");
+	while (substr != NULL) {
+		if (strlen(substr)) {
+			argv[argc++] = substr;
+			if (argc == MAX_ARG_COUNT) {
+				efi_err("Argv mmap_array full!\n");
+				break;
+			}
+		}
+		substr = strsep(&pstr, " \t");
+	}
+
+	return EFI_SUCCESS;
+}
+
+unsigned int efi_memmap_sort(struct loongsonlist_mem_map *memmap,
+			unsigned int index, unsigned int mem_type)
+{
+	unsigned int i, t;
+	unsigned long msize;
+
+	for (i = 0; i < map_entry[mem_type]; i = t) {
+		msize = mmap_array[mem_type][i].mem_size;
+		for (t = i + 1; t < map_entry[mem_type]; t++) {
+			if (mmap_array[mem_type][i].mem_start + msize <
+					mmap_array[mem_type][t].mem_start)
+				break;
+
+			msize += mmap_array[mem_type][t].mem_size;
+		}
+		memmap->map[index].mem_type = mem_type;
+		memmap->map[index].mem_start = mmap_array[mem_type][i].mem_start;
+		memmap->map[index].mem_size = msize;
+		memmap->map[index].attribute = mmap_array[mem_type][i].attribute;
+		index++;
+	}
+
+	return index;
+}
+
+static efi_status_t mk_mmap(struct efi_boot_memmap *map, struct boot_params *p)
+{
+	char checksum;
+	unsigned int i;
+	unsigned int nr_desc;
+	unsigned int mem_type;
+	unsigned long count;
+	efi_memory_desc_t *mem_desc;
+	struct loongsonlist_mem_map *mhp = NULL;
+
+	memset(map_entry, 0, sizeof(map_entry));
+	memset(mmap_array, 0, sizeof(mmap_array));
+
+	if (!strncmp((char *)p, "BPI", 3)) {
+		p->flags |= BPI_FLAGS_UEFI_SUPPORTED;
+		p->systemtable = (efi_system_table_t *)efi_system_table;
+		p->extlist_offset = sizeof(*p) + sizeof(unsigned long);
+		mhp = (struct loongsonlist_mem_map *)((char *)p + p->extlist_offset);
+
+		memcpy(&mhp->header.signature, "MEM", sizeof(unsigned long));
+		mhp->header.length = sizeof(*mhp);
+		mhp->desc_version = *map->desc_ver;
+		mhp->map_count = 0;
+	}
+	if (!(*(map->map_size)) || !(*(map->desc_size)) || !mhp) {
+		efi_err("get memory info error\n");
+		return EFI_INVALID_PARAMETER;
+	}
+	nr_desc = *(map->map_size) / *(map->desc_size);
+
+	/*
+	 * According to UEFI SPEC, mmap_buf is the accurate Memory Map
+	 * mmap_array now we can fill platform specific memory structure.
+	 */
+	for (i = 0; i < nr_desc; i++) {
+		mem_desc = (efi_memory_desc_t *)((void *)(*map->map) + (i * (*(map->desc_size))));
+		switch (mem_desc->type) {
+		case EFI_RESERVED_TYPE:
+		case EFI_RUNTIME_SERVICES_CODE:
+		case EFI_RUNTIME_SERVICES_DATA:
+		case EFI_MEMORY_MAPPED_IO:
+		case EFI_MEMORY_MAPPED_IO_PORT_SPACE:
+		case EFI_UNUSABLE_MEMORY:
+		case EFI_PAL_CODE:
+			mem_type = ADDRESS_TYPE_RESERVED;
+			break;
+
+		case EFI_ACPI_MEMORY_NVS:
+			mem_type = ADDRESS_TYPE_NVS;
+			break;
+
+		case EFI_ACPI_RECLAIM_MEMORY:
+			mem_type = ADDRESS_TYPE_ACPI;
+			break;
+
+		case EFI_LOADER_CODE:
+		case EFI_LOADER_DATA:
+		case EFI_PERSISTENT_MEMORY:
+		case EFI_BOOT_SERVICES_CODE:
+		case EFI_BOOT_SERVICES_DATA:
+		case EFI_CONVENTIONAL_MEMORY:
+			mem_type = ADDRESS_TYPE_SYSRAM;
+			break;
+
+		default:
+			continue;
+		}
+
+		mmap_array[mem_type][map_entry[mem_type]].mem_type = mem_type;
+		mmap_array[mem_type][map_entry[mem_type]].mem_start =
+						mem_desc->phys_addr & TO_PHYS_MASK;
+		mmap_array[mem_type][map_entry[mem_type]].mem_size =
+						mem_desc->num_pages << EFI_PAGE_SHIFT;
+		mmap_array[mem_type][map_entry[mem_type]].attribute =
+						mem_desc->attribute;
+		map_entry[mem_type]++;
+	}
+
+	count = mhp->map_count;
+	/* Sort EFI memmap and add to BPI for kernel */
+	for (i = 0; i < LOONGSON3_BOOT_MEM_MAP_MAX; i++) {
+		if (!map_entry[i])
+			continue;
+		count = efi_memmap_sort(mhp, count, i);
+	}
+
+	mhp->map_count = count;
+	mhp->header.checksum = 0;
+
+	checksum = efi_crc8((char *)mhp, mhp->header.length);
+	mhp->header.checksum = checksum;
+
+	return EFI_SUCCESS;
+}
+
+static efi_status_t exit_boot_func(struct efi_boot_memmap *map, void *priv)
+{
+	efi_status_t status;
+	struct exit_boot_struct *p = priv;
+
+	status = mk_mmap(map, p->bp);
+	if (status != EFI_SUCCESS) {
+		efi_err("Make kernel memory map failed!\n");
+		return status;
+	}
+
+	return EFI_SUCCESS;
+}
+
+static efi_status_t exit_boot_services(struct boot_params *boot_params, void *handle)
+{
+	unsigned int desc_version;
+	unsigned int runtime_entry_count = 0;
+	unsigned long map_size, key, desc_size, buff_size;
+	efi_status_t status;
+	efi_memory_desc_t *mem_map;
+	struct efi_boot_memmap map;
+	struct exit_boot_struct priv;
+
+	map.map			= &mem_map;
+	map.map_size		= &map_size;
+	map.desc_size		= &desc_size;
+	map.desc_ver		= &desc_version;
+	map.key_ptr		= &key;
+	map.buff_size		= &buff_size;
+	status = efi_get_memory_map(&map);
+	if (status != EFI_SUCCESS) {
+		efi_err("Unable to retrieve UEFI memory map.\n");
+		return status;
+	}
+
+	priv.bp = boot_params;
+	priv.runtime_entry_count = &runtime_entry_count;
+
+	/* Might as well exit boot services now */
+	status = efi_exit_boot_services(handle, &map, &priv, exit_boot_func);
+	if (status != EFI_SUCCESS)
+		return status;
+
+	return EFI_SUCCESS;
+}
+
+/*
+ * EFI entry point for the LoongArch EFI stub.
+ */
+efi_status_t __efiapi efi_pe_entry(efi_handle_t handle, efi_system_table_t *sys_table)
+{
+	unsigned int cmdline_size = 0;
+	unsigned long kernel_addr = 0;
+	unsigned long initrd_addr = 0;
+	unsigned long initrd_size = 0;
+	enum efi_secureboot_mode secure_boot;
+	char *cmdline_ptr = NULL;
+	struct boot_params *boot_p;
+	efi_status_t status;
+	efi_loaded_image_t *image;
+	efi_guid_t loaded_image_proto;
+	kernel_entry_t real_kernel_entry;
+
+	/* Config Direct Mapping */
+	csr_writeq(CSR_DMW0_INIT, LOONGARCH_CSR_DMWIN0);
+	csr_writeq(CSR_DMW1_INIT, LOONGARCH_CSR_DMWIN1);
+
+	efi_system_table = sys_table;
+	loaded_image_proto = LOADED_IMAGE_PROTOCOL_GUID;
+	kernel_addr = (unsigned long)&kernel_offset - kernel_offset;
+	real_kernel_entry = (kernel_entry_t)
+		((unsigned long)&kernel_entry - kernel_addr + kernel_vaddr);
+
+	/* Check if we were booted by the EFI firmware */
+	if (sys_table->hdr.signature != EFI_SYSTEM_TABLE_SIGNATURE)
+		goto fail;
+
+	/*
+	 * Get a handle to the loaded image protocol.  This is used to get
+	 * information about the running image, such as size and the command
+	 * line.
+	 */
+	status = sys_table->boottime->handle_protocol(handle,
+					&loaded_image_proto, (void *)&image);
+	if (status != EFI_SUCCESS) {
+		efi_err("Failed to get loaded image protocol\n");
+		goto fail;
+	}
+
+	/* Get the command line from EFI, using the LOADED_IMAGE protocol. */
+	cmdline_ptr = efi_convert_cmdline(image, &cmdline_size);
+	if (!cmdline_ptr) {
+		efi_err("Getting command line failed!\n");
+		goto fail_free_cmdline;
+	}
+
+#ifdef CONFIG_CMDLINE_BOOL
+	if (cmdline_size == 0)
+		efi_parse_options(CONFIG_CMDLINE);
+#endif
+	if (!IS_ENABLED(CONFIG_CMDLINE_OVERRIDE) && cmdline_size > 0)
+		efi_parse_options(cmdline_ptr);
+
+	efi_info("Booting Linux Kernel...\n");
+
+	efi_relocate_kernel(&kernel_addr, kernel_fsize, kernel_asize,
+			    PHYSADDR(kernel_vaddr), SZ_2M, PHYSADDR(kernel_vaddr));
+
+	setup_graphics();
+	secure_boot = efi_get_secureboot();
+	efi_enable_reset_attack_mitigation();
+
+	status = efi_load_initrd(image, &initrd_addr, &initrd_size, SZ_4G, ULONG_MAX);
+	if (status != EFI_SUCCESS) {
+		efi_err("Failed get initrd addr!\n");
+		goto fail_free;
+	}
+
+	status = convert_priv_cmdline(cmdline_ptr, initrd_addr, initrd_size);
+	if (status != EFI_SUCCESS) {
+		efi_err("Covert cmdline failed!\n");
+		goto fail_free;
+	}
+
+	boot_p = bootparams_init(sys_table);
+	if (!boot_p) {
+		efi_err("Create BPI struct error!\n");
+		goto fail;
+	}
+
+	status = exit_boot_services(boot_p, handle);
+	if (status != EFI_SUCCESS) {
+		efi_err("exit_boot services failed!\n");
+		goto fail_free;
+	}
+
+	real_kernel_entry(argc, argv, boot_p);
+
+	return EFI_SUCCESS;
+
+fail_free:
+	efi_free(initrd_size, initrd_addr);
+
+fail_free_cmdline:
+	efi_free(cmdline_size, (unsigned long)cmdline_ptr);
+
+fail:
+	return status;
+}
diff --git a/include/linux/pe.h b/include/linux/pe.h
index daf09ffffe38..f4bb0b6a416d 100644
--- a/include/linux/pe.h
+++ b/include/linux/pe.h
@@ -65,6 +65,7 @@
 #define	IMAGE_FILE_MACHINE_SH5		0x01a8
 #define	IMAGE_FILE_MACHINE_THUMB	0x01c2
 #define	IMAGE_FILE_MACHINE_WCEMIPSV2	0x0169
+#define	IMAGE_FILE_MACHINE_LOONGARCH	0x6264
 
 /* flags */
 #define IMAGE_FILE_RELOCS_STRIPPED           0x0001
-- 
2.27.0




[Index of Archives]     [Kernel Newbies]     [Security]     [Netfilter]     [Bugtraq]     [Linux FS]     [Yosemite Forum]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Samba]     [Video 4 Linux]     [Device Mapper]     [Linux Resources]

  Powered by Linux