[PATCH RFC] add --map-file option to map arbitrary files into physmem

dan at telent.net dan at telent.net
Sun Mar 19 11:00:47 PDT 2023


This is useful e.g. in conjunction with the MTD "phram" driver 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. Something like this:

   kexec --map-file ./squashfs at 104857600 --dtb ./dtb  --command-line "mtdparts=phram0:9945088(rootfs) phram.phram=phram0,104857600,9945088 memmap=9945088$104857600" vmlinux

My use case might be quite niche :-) but the implementation is
I hope general enough that it could be used for other things as
well. Or maybe it's impossibly general and shouldn't be offered
at all without safety warnings.

Feedback welcome on any or all of style, correctness, general
desirability and/or other.  This is clearly not ever going to work with
KEXEC_FILE_LOAD.


Daniel

---
 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 at 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=<addr> Specify the highest memory address to\n"
 	       "                      load code into.\n"
+	       "     --map-file=<filename at addr> 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




More information about the kexec mailing list