From 54a1ab3529c9294d12e1a3f4e0a7c5c8897afd0d Mon Sep 17 00:00:00 2001 From: Daniel Barlow Date: Tue, 4 Apr 2023 23:35:49 +0100 Subject: [PATCH] support jffs2, with initramfs the jffs2 filesystem contains only /nix/store and a script which is run in early init (initramfs) and is responsible for recreating "traditional" directories (/bin /etc/**/* /var &c) based on the configuration. this is tested only in qemu so far and could use some cleanup --- modules/initramfs.nix | 79 +++++++++++++++++++++++++++++++++++ modules/jffs2.nix | 49 ++++++++++++++++++++++ pkgs/default.nix | 1 + pkgs/mips-vm/mips-vm.sh | 20 +++++++-- pkgs/systemconfig/default.nix | 60 ++++++++++++++++++++++++++ 5 files changed, 206 insertions(+), 3 deletions(-) create mode 100644 modules/initramfs.nix create mode 100644 modules/jffs2.nix create mode 100644 pkgs/systemconfig/default.nix diff --git a/modules/initramfs.nix b/modules/initramfs.nix new file mode 100644 index 0000000..c963bcf --- /dev/null +++ b/modules/initramfs.nix @@ -0,0 +1,79 @@ +{ + config +, pkgs +, lib +, ... +}: +let +# inherit (lib) mkOption types concatStringsSep; + inherit (pkgs) runCommand callPackage writeText; +in +{ + config = { + kernel.config.BLK_DEV_INITRD = "y"; + + outputs = { + initramfs = + let + bb = pkgs.busybox.override { + enableStatic = true; + enableMinimal = true; + enableAppletSymlinks = false; + extraConfig = '' + CONFIG_ASH y + CONFIG_LS y + CONFIG_LN y + CONFIG_CAT y + CONFIG_MOUNT y + CONFIG_PRINTF y + CONFIG_FEATURE_MOUNT_FLAGS y + CONFIG_FEATURE_MOUNT_VERBOSE y + CONFIG_ECHO y + CONFIG_CHROOT y + CONFIG_CHMOD y + CONFIG_MKDIR y + CONFIG_MKNOD y + CONFIG_SH_IS_ASH y + CONFIG_BASH_IS_NONE y + ''; + }; + slashinit = pkgs.writeScript "init" '' + #!/bin/sh + exec >/dev/console + echo IT MOVES + mount -t proc none /proc + mount -t jffs2 mtd0 /target/persist + mount -o bind /target/persist/nix /target/nix + sh /target/persist/activate /target + cd /target + mount -o bind /target / + exec chroot . /bin/init "$@" + ''; + refs = pkgs.writeReferencesToFile bb; + gen_init_cpio = pkgs.pkgsBuildBuild.gen_init_cpio; + in runCommand "initramfs" {} '' + cat << SPECIALS | ${gen_init_cpio}/bin/gen_init_cpio /dev/stdin > $out + dir /proc 0755 0 0 + dir /dev 0755 0 0 + nod /dev/mtdblock0 0600 0 0 b 31 0 + dir /target 0755 0 0 + dir /target/persist 0755 0 0 + dir /target/nix 0755 0 0 + nod /dev/console 0600 0 0 c 5 1 + dir /bin 0755 0 0 + file /bin/busybox ${bb}/bin/busybox 0755 0 0 + slink /bin/sh /bin/busybox 0755 0 0 + slink /bin/echo /bin/busybox 0755 0 0 + slink /bin/mount /bin/busybox 0755 0 0 + slink /bin/chroot /bin/busybox 0755 0 0 + slink /bin/chmod /bin/busybox 0755 0 0 + slink /bin/ln /bin/busybox 0755 0 0 + slink /bin/mkdir /bin/busybox 0755 0 0 + slink /bin/mknod /bin/busybox 0755 0 0 + slink /bin/printf /bin/busybox 0755 0 0 + file /init ${slashinit} 0755 0 0 + SPECIALS + ''; + }; + }; +} diff --git a/modules/jffs2.nix b/modules/jffs2.nix new file mode 100644 index 0000000..2479803 --- /dev/null +++ b/modules/jffs2.nix @@ -0,0 +1,49 @@ +{ + config +, pkgs +, lib +, ... +}: +let + inherit (lib) mkOption types concatStringsSep; + inherit (pkgs) liminix callPackage writeText closureInfo; +in +{ + imports = [ + ./initramfs.nix + ]; + config = { + kernel.config.JFFS2_FS = "y"; + outputs = rec { + systemConfiguration = + let inherit (pkgs.pkgsBuildBuild) systemconfig; + in systemconfig config.filesystem.contents; + jffs2fs = + let + inherit (pkgs.pkgsBuildBuild) runCommand systemconfig mtdutils; + sysconf = systemConfiguration; + in runCommand "make-jffs2" { + depsBuildBuild = [ mtdutils ]; + } '' + mkdir -p $TMPDIR/empty/nix/store/ + cp ${sysconf}/activate $TMPDIR/empty/activate + pkgClosure=${closureInfo { rootPaths = [ sysconf ]; }} + cp $pkgClosure/registration nix-path-registration + grafts=$(sed < $pkgClosure/store-paths 's/^\(.*\)$/--graft \1:\1/g') + mkfs.jffs2 --pad --big-endian --root $TMPDIR/empty --output $out $grafts --verbose + ''; + jffs2boot = + let o = config.outputs; in + pkgs.runCommand "jffs2boot" {} '' + mkdir $out + cd $out + ln -s ${o.jffs2fs} rootfs + ln -s ${o.kernel} vmlinux + ln -s ${o.manifest} manifest + ln -s ${o.initramfs} initramfs + ''; + + + }; + }; +} diff --git a/pkgs/default.nix b/pkgs/default.nix index 2e62701..a284f53 100644 --- a/pkgs/default.nix +++ b/pkgs/default.nix @@ -12,6 +12,7 @@ }; }; writeAshScript = callPackage ./write-ash-script {}; + systemconfig = callPackage ./systemconfig {}; s6-init-bin = callPackage ./s6-init-bin {}; s6-rc-database = callPackage ./s6-rc-database {}; mips-vm = callPackage ./mips-vm {}; diff --git a/pkgs/mips-vm/mips-vm.sh b/pkgs/mips-vm/mips-vm.sh index 2de14fa..fa090fa 100755 --- a/pkgs/mips-vm/mips-vm.sh +++ b/pkgs/mips-vm/mips-vm.sh @@ -1,7 +1,13 @@ #!/usr/bin/env bash +cleanup(){ + test -n "$rootfs" && test -f $rootfs && rm $rootfs +} +trap 'exit 1' INT HUP QUIT TERM ALRM USR1 +trap 'cleanup' EXIT + usage(){ - echo "usage: $(basename $0) [--background /path/to/state_directory] kernel rootimg" + echo "usage: $(basename $0) [--background /path/to/state_directory] kernel rootimg [initramfs]" echo -e "\nWithout --background, use C-p c (not C-a c) to switch to the monitor" exit 1 } @@ -23,14 +29,22 @@ fi test -n "$2" || usage +rootfs=$(mktemp mips-vm-fs-XXXXXX) +dd if=$2 of=$rootfs bs=65536 conv=sync + +if test -n "$3"; then + initramfs="-initrd $3" +fi + INIT=${INIT-/bin/init} echo $QEMU_OPTIONS qemu-system-mips \ -M malta -m 256 \ -echr 16 \ - -append "liminix default console=ttyS0,38400n8 panic=10 oops=panic init=$INIT loglevel=8 root=/dev/mtdblock0 block2mtd.block2mtd=/dev/vda,4096" \ - -drive file=$2,format=raw,readonly=on,if=virtio \ + -append "liminix default console=ttyS0,38400n8 panic=10 oops=panic init=$INIT loglevel=8 root=/dev/mtdblock0 block2mtd.block2mtd=/dev/vda,65536" \ + -drive file=$rootfs,format=raw,readonly=on,if=virtio \ + ${initramfs} \ -netdev socket,id=access,mcast=230.0.0.1:1234,localaddr=127.0.0.1 \ -device virtio-net-pci,disable-legacy=on,disable-modern=off,netdev=access,mac=ba:ad:1d:ea:21:02 \ -netdev socket,id=lan,mcast=230.0.0.1:1235,localaddr=127.0.0.1 \ diff --git a/pkgs/systemconfig/default.nix b/pkgs/systemconfig/default.nix new file mode 100644 index 0000000..9ab33e7 --- /dev/null +++ b/pkgs/systemconfig/default.nix @@ -0,0 +1,60 @@ +# The ideal is that a Liminix system can boot with only the files in +# /nix/store. This package generates a script that is run at early +# boot (from the initramfs) to populate directories such as /etc, +# /bin, /home according to whatever the configuration says +# they should contain + +{ + writeText +, runCommand +, lib +}: +let + inherit (lib.attrsets) mapAttrsToList; + escaped = msg : builtins.replaceStrings + ["\n" "=" "\"" "$" ] + ["\\x0a" "\\x3d" "\\x22" "\\x24"] + msg; + + visit = prefix: attrset: + let makeFile = prefix : filename: { + type ? "f" + , mode ? null + , target ? null + , contents ? null + , file ? null + , major ? null + , minor ? null + }: + let + pathname = "${prefix}/${filename}"; + chmod = + let m = if mode != null then mode else + (if type == "d" then "0755" else "0644"); + in (if type == "s" + then "" + else "\nchmod ${m} ${pathname}"); + cmds = { + "f" = "printf \"${escaped file}\" > ${pathname}"; + "d" = "mkdir ${pathname}\n" + + (builtins.concatStringsSep "\n" + (visit pathname contents)); + "c" = "mknod ${pathname} c ${major} ${minor}"; + "b" = "mknod ${pathname} b ${major} ${minor}"; + "s" = "ln -s ${target} ${pathname}"; + "l" = "ln ${target} ${pathname}"; + "i" = "mknod ${pathname} p"; + }; + cmd = cmds.${type}; + in "${cmd}${chmod}"; + in mapAttrsToList (makeFile prefix) attrset; + activateScript = attrset: writeText "systemConfig" '' + #!/bin/sh + t=$1 + ${(builtins.concatStringsSep "\n" (visit "$t" attrset))} + ''; +in attrset: + runCommand "make-stuff" {} '' + mkdir -p $out + ln -s ${activateScript attrset} $out/activate + ''