From 35b8bad0343a4afd9ad914e377e64bd02667c563 Mon Sep 17 00:00:00 2001 From: Daniel Barlow Date: Sun, 19 Mar 2023 17:30:30 +0000 Subject: [PATCH] add --map-file option to map arbitrary files into physmem This is useful e.g. in conjunction with the MTD PHRAM device on embedded devices: a kernel can be booted with kexec and a root filesystem entirely in RAM to see if it works before writing it to flash. --- kexec/kexec.8 | 14 ++++++++++++- kexec/kexec.c | 57 +++++++++++++++++++++++++++++++++++++++++++++++++++ kexec/kexec.h | 4 +++- 3 files changed, 73 insertions(+), 2 deletions(-) diff --git a/kexec/kexec.8 b/kexec/kexec.8 index 3a344c5..3140ccb 100644 --- a/kexec/kexec.8 +++ b/kexec/kexec.8 @@ -143,10 +143,17 @@ into the current kernel. .B \-p\ (\-\-load\-panic) Load the new kernel for use on panic. .TP -.BI \-t\ (\-\-type= type ) +.BI \-m\ (\-\-type= type ) Specify that the new kernel is of this .I type. .TP +.BI \-t\ (\-\-map-file= filename@addr ) +Read +.I filename +and arrange for it to be mapped into physical memory at the address +.I addr. +This option may be repeated if there are multiple files to map. +.TP .BI \-s\ (\-\-kexec-file-syscall) Specify that the new KEXEC_FILE_LOAD syscall should be used exclusively. .TP @@ -206,6 +213,11 @@ Reuse initrd from first boot. .BI \-\-print-ckr-size Print crash kernel region size, if available. +.PP +Options taking an +.I addr +parameter will accept a memory address written in hexadecimal (with leading +0x), or octal (leading 0), or decimal (no leading sigil). .SH SUPPORTED KERNEL FILE TYPES AND OPTIONS .B Beoboot-x86 diff --git a/kexec/kexec.c b/kexec/kexec.c index 36bb2ad..c3b40a4 100644 --- a/kexec/kexec.c +++ b/kexec/kexec.c @@ -63,6 +63,13 @@ static unsigned long kexec_flags = 0; static unsigned long kexec_file_flags = 0; int kexec_debug = 0; +#define MAPPED_FILES_MAX 10 /* arbitrary number */ +struct mapped_file { + const char *filename; + unsigned long long phys_address; +} mapped_files[MAPPED_FILES_MAX] = { { .filename = NULL } }; + + void dbgprint_mem_range(const char *prefix, struct memory_range *mr, int nr_mr) { int i; @@ -771,6 +778,19 @@ static int my_load(const char *type, int fileind, int argc, char **argv, } info.kexec_flags |= native_arch; + for(struct mapped_file *m = mapped_files; m->filename; m++) { + off_t file_size = 0; + char *buf = slurp_file(m->filename, &file_size); + add_buffer(&info, + buf, file_size, file_size, sizeof (void *), + m->phys_address, + mem_max, 1); + free(m->filename); + /* do we free() memory returned by slurp_file()? + * we don't know if it was mmaped, so maybe not + */ + }; + result = file_type[i].load(argc, argv, kernel_buf, kernel_size, &info); if (result < 0) { switch (result) { @@ -1035,6 +1055,8 @@ void usage(void) " load code into.\n" " --mem-max= Specify the highest memory address to\n" " load code into.\n" + " --map-file= Map a file into memory for the\n" + " new kernel before kexec.\n" " --reuseinitrd Reuse initrd from first boot.\n" " --print-ckr-size Print crash kernel region size.\n" " --load-preserve-context Load the new kernel and preserve\n" @@ -1396,6 +1418,33 @@ static void print_crashkernel_region_size(void) printf("%" PRIu64 "\n", (start != end) ? (end - start + 1) : 0UL); } +static int add_mapped_file(char * optarg) +{ + char *at = strchr(optarg, '@'); + if(!at) + return 1; + + struct mapped_file *m = mapped_files; + struct mapped_file *m_end = mapped_files + + ((sizeof mapped_files) / (sizeof mapped_files[0])); + + while(m->filename && m < m_end) + m++; + + if(m >= m_end) + return 1; + + m->phys_address = strtoull(at + 1, NULL, 0); + if(m->phys_address == 0) + return 1; + + m->filename = strndup(optarg, at - optarg); + + (m+1)->filename = NULL; + return 0; +} + + int main(int argc, char *argv[]) { int has_opt_load = 0; @@ -1521,6 +1570,14 @@ int main(int argc, char *argv[]) kexec_file_flags |= KEXEC_FILE_ON_CRASH; kexec_flags = KEXEC_ON_CRASH; break; + case OPT_MAP_FILE: + if(add_mapped_file(optarg)) { + fprintf(stderr, + "Bad option value or too many mapped files in --mapped-file=%s\n", + optarg); + return 1; + } + break; case OPT_MEM_MIN: mem_min = strtoul(optarg, &endptr, 0); if (*endptr) { diff --git a/kexec/kexec.h b/kexec/kexec.h index 0d820ad..78022b6 100644 --- a/kexec/kexec.h +++ b/kexec/kexec.h @@ -224,6 +224,7 @@ extern int file_types; #define OPT_STATUS 'S' #define OPT_MEM_MIN 256 #define OPT_MEM_MAX 257 +#define OPT_MAP_FILE 'm' #define OPT_REUSE_INITRD 258 #define OPT_LOAD_PRESERVE_CONTEXT 259 #define OPT_LOAD_JUMP_BACK_HELPER 260 @@ -258,8 +259,9 @@ extern int file_types; { "debug", 0, 0, OPT_DEBUG }, \ { "status", 0, 0, OPT_STATUS }, \ { "print-ckr-size", 0, 0, OPT_PRINT_CKR_SIZE }, \ + { "map-file", 1, 0, OPT_MAP_FILE }, \ -#define KEXEC_OPT_STR "h?vdfixyluet:pscaS" +#define KEXEC_OPT_STR "h?vdfixyluet:pscaSm" extern void dbgprint_mem_range(const char *prefix, struct memory_range *mr, int nr_mr); extern void die(const char *fmt, ...) -- 2.38.1