Skip to content

Commit

Permalink
LoongArch: PE format image loading support
Browse files Browse the repository at this point in the history
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 <[email protected]>
Signed-off-by: Simon Horman <[email protected]>
  • Loading branch information
Youling Tang authored and horms committed Oct 10, 2022
1 parent 1c8bf2d commit 615b675
Show file tree
Hide file tree
Showing 6 changed files with 229 additions and 0 deletions.
2 changes: 2 additions & 0 deletions kexec/arch/loongarch/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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
79 changes: 79 additions & 0 deletions kexec/arch/loongarch/image-header.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/*
* LoongArch binary image header.
*/

#if !defined(__LOONGARCH_IMAGE_HEADER_H)
#define __LOONGARCH_IMAGE_HEADER_H

#include <endian.h>
#include <stdint.h>

/**
* 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
3 changes: 3 additions & 0 deletions kexec/arch/loongarch/kexec-elf-loongarch.c
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ int elf_loongarch_probe(const char *kernel_buf, off_t kernel_size)
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;
Expand All @@ -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) {
Expand Down
19 changes: 19 additions & 0 deletions kexec/arch/loongarch/kexec-loongarch.c
Original file line number Diff line number Diff line change
Expand Up @@ -165,13 +165,32 @@ 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]);

/* loongarch global varables. */

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);
Expand Down
9 changes: 9 additions & 0 deletions kexec/arch/loongarch/kexec-loongarch.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@

#include <sys/types.h>

#include "image-header.h"

#define BOOT_BLOCK_VERSION 17
#define BOOT_BLOCK_LAST_COMP_VERSION 16

Expand All @@ -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);
Expand Down
117 changes: 117 additions & 0 deletions kexec/arch/loongarch/kexec-pei-loongarch.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
/*
* LoongArch kexec PE format binary image support.
*
* Copyright (C) 2022 Loongson Technology Corporation Limited.
* Youling Tang <[email protected]>
*
* 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 <limits.h>
#include <errno.h>
#include <elf.h>

#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");
}

0 comments on commit 615b675

Please sign in to comment.