commit 4b19568f1bc16407fb472fe09ba36decbe75eb51
parent 429ffa8e7dcd93aef12148d765adbf612ee2e85b
Author: Daniel Barlow <dan@telent.net>
Date: Sun, 19 Mar 2023 09:49:32 +0000
add kexecboot
This allows booting a new image from a running OS, creating a
phram mtd for the root squashfs
* enable CONFIG_KEXEC
* add modules/kexecboot
* patch kexec-tools to add --map-file option for the squashfs
* patch kernel kexec code to call new kernel with DTB
Diffstat:
6 files changed, 243 insertions(+), 0 deletions(-)
diff --git a/modules/base.nix b/modules/base.nix
@@ -74,6 +74,7 @@ in {
IKCONFIG_PROC = "y";
PROC_FS = "y";
+ KEXEC = "y";
MODULES = "y";
MODULE_SIG = "y";
DEBUG_FS = "y";
diff --git a/modules/kexecboot.nix b/modules/kexecboot.nix
@@ -0,0 +1,50 @@
+{
+ config
+, pkgs
+, lib
+, ...
+}:
+let
+ inherit (lib) mkOption mkForce types concatStringsSep;
+in {
+ imports = [ ./ramdisk.nix ];
+ config = {
+ boot.ramdisk.enable = true;
+
+ kernel.config.MIPS_CMDLINE_FROM_DTB = "y";
+ kernel.config.MIPS_CMDLINE_FROM_BOOTLOADER = mkForce "n";
+
+ outputs.kexecboot =
+ let o = config.outputs; in
+ pkgs.runCommand "kexecboot" {} ''
+ mkdir $out
+ cd $out
+ ln -s ${o.squashfs} squashfs
+ ln -s ${o.kernel} kernel
+ ln -s ${o.manifest} manifest
+ ln -s ${o.boot-sh} boot.sh
+ ln -s ${pkgs.kexec-tools}/bin/kexec ./kexec
+ ln -s ${o.dtb} dtb
+ '';
+
+ outputs.boot-sh =
+ let
+ inherit (pkgs) kexec-tools;
+ inherit (pkgs.lib.trivial) toHexString;
+ inherit (config.outputs) squashfs kernel;
+ cmdline = concatStringsSep " " config.boot.commandLine;
+ in
+ pkgs.buildPackages.runCommand "boot.sh.sh" {
+ } ''
+ squashfsStart=${toString (100 * 1024 * 1024)}
+ squashfsBytes=$(stat -L -c %s ${squashfs})
+ append_cmd="mtdparts=phram0:''${squashfsBytes}(rootfs) phram.phram=phram0,''${squashfsStart},''${squashfsBytes} memmap=''${squashfsBytes}\$''${squashfsStart}";
+ cat > $out <<EOF
+ #!/bin/sh
+ test -d \$1
+ cd \$1
+ ./kexec -f -d --map-file squashfs@$squashfsStart --dtb dtb --command-line '${cmdline} $append_cmd' kernel
+ EOF
+ '';
+ };
+}
diff --git a/overlay.nix b/overlay.nix
@@ -6,6 +6,17 @@ in
extraPkgs // {
strace = prev.strace.override { libunwind = null; };
+ kexec-tools = prev.kexec-tools.overrideAttrs(o: {
+ patches = o.patches ++ [
+ (fetchpatch {
+ # merge user command line options into DTB chosen
+ url = "https://patch-diff.githubusercontent.com/raw/horms/kexec-tools/pull/3.patch";
+ hash = "sha256-MvlJhuex9dlawwNZJ1sJ33YPWn1/q4uKotqkC/4d2tk=";
+ })
+ pkgs/kexec-map-file.patch
+ ];
+ });
+
s6 = prev.s6.overrideAttrs(o:
let patch = fetchpatch {
# add "p" directive in s6-log
diff --git a/pkgs/kernel/default.nix b/pkgs/kernel/default.nix
@@ -61,6 +61,11 @@ stdenv.mkDerivation rec {
'';
patchScripts = ''
+ # Make kexec pass dtb in register when invoking new kernel. The
+ # code to do this is already present, but bracketed by UHI_BOOT
+ # which we can't enable.
+ sed -i arch/mips/kernel/machine_kexec.c -e 's/CONFIG_UHI_BOOT/CONFIG_MIPS/g'
+
patchShebangs scripts/
'';
diff --git a/pkgs/kexec-map-file.patch b/pkgs/kexec-map-file.patch
@@ -0,0 +1,172 @@
+From 35b8bad0343a4afd9ad914e377e64bd02667c563 Mon Sep 17 00:00:00 2001
+From: Daniel Barlow <dan@telent.net>
+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=<addr> Specify the highest memory address to\n"
+ " load code into.\n"
++ " --map-file=<filename@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
+
diff --git a/pkgs/openwrt/default.nix b/pkgs/openwrt/default.nix
@@ -20,6 +20,10 @@ let
}
patches ${src}/target/linux/generic/backport-5.15/*.patch
patches ${src}/target/linux/generic/pending-5.15/*.patch
+ # This patch breaks passing the DTB to kexeced kernel, so let's
+ # get rid of it. It's not needed anyway as we pass the cmdline
+ # in the dtb
+ patch --batch -p1 --reverse < ${src}/target/linux/generic/pending-5.15/330-MIPS-kexec-Accept-command-line-parameters-from-users.patch
patches ${src}/target/linux/generic/hack-5.15/*.patch
patches ${src}/target/linux/${family}/patches-5.15/*.patch
'';