From 615b6757dcab7bda01d9cb9945738b6548f97af1 Mon Sep 17 00:00:00 2001 From: Youling Tang Date: Wed, 28 Sep 2022 10:28:24 +0800 Subject: LoongArch: PE format image loading support The LoongArch kernel will mainly use the vmlinux.efi image in PE format, so add it support. I tested this on LoongArch 3A5000 machine and works as expected, kexec: $ sudo kexec -l /boot/vmlinux.efi --reuse-cmdline $ sudo kexec -e kdump: $ sudo kexec -p /boot/vmlinux-kdump.efi --reuse-cmdline --append="nr_cpus=1" # echo c > /proc/sysrq_trigger Signed-off-by: Youling Tang Signed-off-by: Simon Horman --- kexec/arch/loongarch/Makefile | 2 + kexec/arch/loongarch/image-header.h | 79 +++++++++++++++++++ kexec/arch/loongarch/kexec-elf-loongarch.c | 3 + kexec/arch/loongarch/kexec-loongarch.c | 19 +++++ kexec/arch/loongarch/kexec-loongarch.h | 9 +++ kexec/arch/loongarch/kexec-pei-loongarch.c | 117 +++++++++++++++++++++++++++++ 6 files changed, 229 insertions(+) create mode 100644 kexec/arch/loongarch/image-header.h create mode 100644 kexec/arch/loongarch/kexec-pei-loongarch.c diff --git a/kexec/arch/loongarch/Makefile b/kexec/arch/loongarch/Makefile index e5e190af..3b33b969 100644 --- a/kexec/arch/loongarch/Makefile +++ b/kexec/arch/loongarch/Makefile @@ -3,6 +3,7 @@ # loongarch_KEXEC_SRCS = kexec/arch/loongarch/kexec-loongarch.c loongarch_KEXEC_SRCS += kexec/arch/loongarch/kexec-elf-loongarch.c +loongarch_KEXEC_SRCS += kexec/arch/loongarch/kexec-pei-loongarch.c loongarch_KEXEC_SRCS += kexec/arch/loongarch/kexec-elf-rel-loongarch.c loongarch_KEXEC_SRCS += kexec/arch/loongarch/crashdump-loongarch.c @@ -16,5 +17,6 @@ loongarch_VIRT_TO_PHYS = dist += kexec/arch/loongarch/Makefile $(loongarch_KEXEC_SRCS) \ kexec/arch/loongarch/kexec-loongarch.h \ + kexec/arch/loongarch/image-header.h \ kexec/arch/loongarch/crashdump-loongarch.h \ kexec/arch/loongarch/include/arch/options.h diff --git a/kexec/arch/loongarch/image-header.h b/kexec/arch/loongarch/image-header.h new file mode 100644 index 00000000..3b757655 --- /dev/null +++ b/kexec/arch/loongarch/image-header.h @@ -0,0 +1,79 @@ +/* + * LoongArch binary image header. + */ + +#if !defined(__LOONGARCH_IMAGE_HEADER_H) +#define __LOONGARCH_IMAGE_HEADER_H + +#include +#include + +/** + * struct loongarch_image_header + * + * @pe_sig: Optional PE format 'MZ' signature. + * @reserved_1: Reserved. + * @kernel_entry: Kernel image entry pointer. + * @image_size: An estimated size of the memory image size in LSB byte order. + * @text_offset: The image load offset in LSB byte order. + * @reserved_2: Reserved. + * @reserved_3: Reserved. + * @pe_header: Optional offset to a PE format header. + **/ + +struct loongarch_image_header { + uint8_t pe_sig[2]; + uint16_t reserved_1[3]; + uint64_t kernel_entry; + uint64_t image_size; + uint64_t text_offset; + uint64_t reserved_2[3]; + uint32_t reserved_3; + uint32_t pe_header; +}; + +static const uint8_t loongarch_image_pe_sig[2] = {'M', 'Z'}; + +/** + * loongarch_header_check_pe_sig - Helper to check the loongarch image header. + * + * Returns non-zero if 'MZ' signature is found. + */ + +static inline int loongarch_header_check_pe_sig(const struct loongarch_image_header *h) +{ + if (!h) + return 0; + + return (h->pe_sig[0] == loongarch_image_pe_sig[0] + && h->pe_sig[1] == loongarch_image_pe_sig[1]); +} + +static inline uint64_t loongarch_header_text_offset( + const struct loongarch_image_header *h) +{ + if (!h) + return 0; + + return le64toh(h->text_offset); +} + +static inline uint64_t loongarch_header_image_size( + const struct loongarch_image_header *h) +{ + if (!h) + return 0; + + return le64toh(h->image_size); +} + +static inline uint64_t loongarch_header_kernel_entry( + const struct loongarch_image_header *h) +{ + if (!h) + return 0; + + return le64toh(h->kernel_entry); +} + +#endif diff --git a/kexec/arch/loongarch/kexec-elf-loongarch.c b/kexec/arch/loongarch/kexec-elf-loongarch.c index a5ec3564..2bf128f1 100644 --- a/kexec/arch/loongarch/kexec-elf-loongarch.c +++ b/kexec/arch/loongarch/kexec-elf-loongarch.c @@ -50,6 +50,7 @@ out: int elf_loongarch_load(int argc, char **argv, const char *kernel_buf, off_t kernel_size, struct kexec_info *info) { + const struct loongarch_image_header *header = NULL; unsigned long kernel_segment; struct mem_ehdr ehdr; int result; @@ -76,6 +77,8 @@ int elf_loongarch_load(int argc, char **argv, const char *kernel_buf, loongarch_mem.text_offset); dbgprintf("%s: phys_offset: %016lx\n", __func__, loongarch_mem.phys_offset); + dbgprintf("%s: PE format: %s\n", __func__, + (loongarch_header_check_pe_sig(header) ? "yes" : "no")); /* create and initialize elf core header segment */ if (info->kexec_flags & KEXEC_ON_CRASH) { diff --git a/kexec/arch/loongarch/kexec-loongarch.c b/kexec/arch/loongarch/kexec-loongarch.c index ce7db2c4..3fdba01f 100644 --- a/kexec/arch/loongarch/kexec-loongarch.c +++ b/kexec/arch/loongarch/kexec-loongarch.c @@ -165,6 +165,7 @@ int get_memory_ranges(struct memory_range **range, int *ranges, struct file_type file_type[] = { {"elf-loongarch", elf_loongarch_probe, elf_loongarch_load, elf_loongarch_usage}, + {"pei-loongarch", pei_loongarch_probe, pei_loongarch_load, pei_loongarch_usage}, }; int file_types = sizeof(file_type) / sizeof(file_type[0]); @@ -172,6 +173,24 @@ int file_types = sizeof(file_type) / sizeof(file_type[0]); struct loongarch_mem loongarch_mem; +/** + * loongarch_process_image_header - Process the loongarch image header. + */ + +int loongarch_process_image_header(const struct loongarch_image_header *h) +{ + + if (!loongarch_header_check_pe_sig(h)) + return EFAILED; + + if (h->image_size) { + loongarch_mem.text_offset = loongarch_header_text_offset(h); + loongarch_mem.image_size = loongarch_header_image_size(h); + } + + return 0; +} + void arch_usage(void) { printf(loongarch_opts_usage); diff --git a/kexec/arch/loongarch/kexec-loongarch.h b/kexec/arch/loongarch/kexec-loongarch.h index cb9b79af..5120a26f 100644 --- a/kexec/arch/loongarch/kexec-loongarch.h +++ b/kexec/arch/loongarch/kexec-loongarch.h @@ -3,6 +3,8 @@ #include +#include "image-header.h" + #define BOOT_BLOCK_VERSION 17 #define BOOT_BLOCK_LAST_COMP_VERSION 16 @@ -21,6 +23,13 @@ int elf_loongarch_load(int argc, char **argv, const char *buf, off_t len, struct kexec_info *info); void elf_loongarch_usage(void); +int pei_loongarch_probe(const char *buf, off_t len); +int pei_loongarch_load(int argc, char **argv, const char *buf, off_t len, + struct kexec_info *info); +void pei_loongarch_usage(void); + +int loongarch_process_image_header(const struct loongarch_image_header *h); + unsigned long loongarch_locate_kernel_segment(struct kexec_info *info); int loongarch_load_other_segments(struct kexec_info *info, unsigned long hole_min); diff --git a/kexec/arch/loongarch/kexec-pei-loongarch.c b/kexec/arch/loongarch/kexec-pei-loongarch.c new file mode 100644 index 00000000..f86ac61e --- /dev/null +++ b/kexec/arch/loongarch/kexec-pei-loongarch.c @@ -0,0 +1,117 @@ +/* + * LoongArch kexec PE format binary image support. + * + * Copyright (C) 2022 Loongson Technology Corporation Limited. + * Youling Tang + * + * derived from kexec-image-arm64.c + * + * This source code is licensed under the GNU General Public License, + * Version 2. See the file COPYING for more details. + */ + +#define _GNU_SOURCE + +#include +#include +#include + +#include "kexec.h" +#include "kexec-elf.h" +#include "image-header.h" +#include "kexec-syscall.h" +#include "crashdump-loongarch.h" +#include "kexec-loongarch.h" +#include "arch/options.h" + +int pei_loongarch_probe(const char *kernel_buf, off_t kernel_size) +{ + const struct loongarch_image_header *h; + + if (kernel_size < sizeof(struct loongarch_image_header)) { + dbgprintf("%s: No loongarch image header.\n", __func__); + return -1; + } + + h = (const struct loongarch_image_header *)(kernel_buf); + + if (!loongarch_header_check_pe_sig(h)) { + dbgprintf("%s: Bad loongarch PE image header.\n", __func__); + return -1; + } + + return 0; +} + +int pei_loongarch_load(int argc, char **argv, const char *buf, + off_t len, struct kexec_info *info) +{ + int result; + unsigned long hole_min = 0; + unsigned long kernel_segment, kernel_entry; + const struct loongarch_image_header *header; + + header = (const struct loongarch_image_header *)(buf); + + if (loongarch_process_image_header(header)) + return EFAILED; + + kernel_segment = loongarch_locate_kernel_segment(info); + + if (kernel_segment == ULONG_MAX) { + dbgprintf("%s: Kernel segment is not allocated\n", __func__); + result = EFAILED; + goto exit; + } + + kernel_entry = virt_to_phys(loongarch_header_kernel_entry(header)); + + dbgprintf("%s: kernel_segment: %016lx\n", __func__, kernel_segment); + dbgprintf("%s: kernel_entry: %016lx\n", __func__, kernel_entry); + dbgprintf("%s: image_size: %016lx\n", __func__, + loongarch_mem.image_size); + dbgprintf("%s: text_offset: %016lx\n", __func__, + loongarch_mem.text_offset); + dbgprintf("%s: phys_offset: %016lx\n", __func__, + loongarch_mem.phys_offset); + dbgprintf("%s: PE format: %s\n", __func__, + (loongarch_header_check_pe_sig(header) ? "yes" : "no")); + + /* Get kernel entry point */ + info->entry = (void *)kernel_entry; + + hole_min = kernel_segment + loongarch_mem.image_size; + + /* Create and initialize elf core header segment */ + if (info->kexec_flags & KEXEC_ON_CRASH) { + result = load_crashdump_segments(info); + if (result) { + dbgprintf("%s: Creating eflcorehdr failed.\n", + __func__); + goto exit; + } + } + + /* Load the kernel */ + add_segment(info, buf, len, kernel_segment, loongarch_mem.image_size); + + /* Prepare and load dtb and initrd data */ + result = loongarch_load_other_segments(info, hole_min); + if (result) { + fprintf(stderr, "kexec: Load dtb and initrd segments failed.\n"); + goto exit; + } + +exit: + if (result) + fprintf(stderr, "kexec: load failed.\n"); + + return result; +} + +void pei_loongarch_usage(void) +{ + printf( +" An LoongArch PE format binary image, uncompressed, little endian.\n" +" Typically a vmlinux.efi file.\n\n"); +} -- cgit 1.2.3-korg