diff --git a/NEWS b/NEWS index 9766bd8..d4ee589 100644 --- a/NEWS +++ b/NEWS @@ -132,4 +132,28 @@ and then run the generated `install.sh` script result/install.sh root@192.168.8.1 +2024-12-16 +Config options changed: if you had set config.hardware.dts.includes +(maybe in an out-of-tree device port) to specify the search paths +in which dtc finds include files, you will need to change this to +hardware.dts.includePaths. + +The "new" hardware.dts.includes option is now for dtsi files which +should be merged into the device tree. + +2024-12-19 + +Incremental updates changed again (but not massively). From hereon in, +the preferred way to do an incremental update on an installed device +with a writable filesystem is to build the updater output + + nix-build -I liminix-config=hosts/myhost.nix --argstr deviceName turris-omnia -A outputs.updater + +and then run the generated `update.sh` script. See +https://www.liminix.org/doc/admin.html#updating-an-installed-system + +2024-12-22 + +outputs.zimage is now outputs.kernel.zImage. This is unlikely to +affect many people at all but I mention it anyway. \ No newline at end of file diff --git a/THOUGHTS.txt b/THOUGHTS.txt index dfcda44..dbf996e 100644 --- a/THOUGHTS.txt +++ b/THOUGHTS.txt @@ -6539,3 +6539,134 @@ using a Makefile idea 2: when a configuration contains levitate, something similar but necessarily more "manual" to do the analogous thing + + +Sun Dec 15 18:55:55 GMT 2024 + +Where we left off with this, rotuer was crashing randomly or failing +to boot every time we tried to add log shipping, which is not very +ideal. I started doing something with logging to /dev/pmsg0 +(CONFIG_PSTORE_PMSG) but I think (there seems not to be anything +written down :-( ) that the gl-ar750 kernel needs it added to kconfig and device tree + +https://wiki.postmarketos.org/wiki/User:Knuxify/Enabling_pstore_and_ramoops + +we could add a new hardware.dts.dtsi = [] option so that any module +could add a new chunk of dts. (Ideally we'd call it `includes` +but that conflicts with the existing use of `includes` to specify +search path. Maybe rename?) + +would we ever use it except in a hardware device definition? +(Or user config?) I guess if we were consistent with names +then we could set up nodes in the device file with status="disabled" +and enable them in the module, except that dt doesn't consistently +use status and in fact there isn't one for reserved-memory + +we could use global config to enable pstore_msg and check it in +the device module to enable the needed hw support + +Tue Dec 17 23:39:28 GMT 2024 + +I think we can just stick a tee in the fallback logger pipeline that +writes to /dev/pmsg0 + +Need to check it's a circular buffer + +do we want to do anything about recovering the log on boot? +- we could just copy it to /run/log +- if we have backfilling for shipped logs (we don't yet) + then we might want to ship it - but that may result in duplicate + logs if some of it was shipped before the crash + +perhaps we should truncate pmsg0 on orderly shutdown? or maybe it's +good to see the late shutdown logs. + +Thu Dec 19 13:40:39 GMT 2024 + +although we have PSTORE_foo in the omnia kconfig, I think this might +be just because I copied it from RT3200 + +Thu Dec 19 14:15:43 GMT 2024 + +Omnia is not in ci.nix, and it's not trivial to add it because there +is no output in the ci.nix configuration that makes sense for omnia. + +... OK, fixed by adding system-configuration as an independent module +and importing in device config + +Thu Dec 19 21:59:47 GMT 2024 + +The build-system shell script in outputs.systemConfiguration +is ugly and requires we do bad things to avoid sucking build +system stuff into the config + +I propose we make it a separate derivation. + +But first maybe we could improve some names + +Sun Dec 22 14:23:02 GMT 2024 + +MT7622> echo $boot_default +if env exists flag_recover ; then else run bootcmd ; fi ; run boot_recovery ; setenv replacevol 1 ; run boot_tftp_for +ever +MT7622> echo $bootcmd +if pstore check ; then run boot_recovery ; else run boot_ubi ; fi +MT7622> echo $boot_ubi +ubi part ubi && run boot_production ; run boot_recovery +MT7622> echo $boot_production +led $bootled_pwr on ; run ubi_read_production && bootm $loadaddr#$bootconf ; led $bootled_pwr off +MT7622> echo $ubi_read_production +ubi read $loadaddr fit && iminfo $loadaddr && run ubi_prepare_rootfs +MT7622> echo $ubi_prepare_rootfs +if ubi check rootfs_data ; then else if env exists rootfs_data_max ; then ubi create rootfs_data $rootfs_data_max dynamic || ubi create rootfs_data - dynamic ; else ubi create rootfs_data - dynamic ; fi ; fi +MT7622> echo $bootconf +config-1 +MT7622> run boot_ubi +UBI partition 'ubi' already selected +No size specified -> Using max size (126976) +Read 126976 bytes from volume fit to 0000000048000000 + +## Checking Image at 48000000 ... +Unknown image format! +No size specified -> Using max size (7491584) +Read 7491584 bytes from volume recovery to 0000000048000000 +## Loading kernel from FIT Image at 48000000 ... + Using 'config-1' configuration + Trying 'kernel-1' kernel subimage + Description: ARM64 OpenWrt Linux-6.6.45 + +Sun Dec 22 17:39:56 GMT 2024 + +From the output above it looks like the device I have plugged in has +an openwrt "recovery" image but not a "production" image + +It also looks like it will be quite hard work to persuade it to boot +from usb or anything. It doesn't have any of the extlinux stuff +but it does have uefi for what that's worth + +default boot_production command reads a ubi volume called 'fit' and +calls bootm on what it finds + +we could define boot_production to ubifsmount liminix; ubifs load + (which is a fit) and bootm it. *presumably* we could +do this from the openwrt recovery image + +but could we install the whole system using said recovery image? I +expect we could do, it only requires getting a tarball onto it and +unpacking it + +however, extlinux is not going to be helpful +(actually it might be a bit, if we ask it to write the fit as +well as/instead of the individual files) + +maybe we need separate concepts of "the filesystem contains +stuff we need for boot" and "the stuff we need is the stuff +that extlinux needs" + +each bootloader makes an output called bootfiles, and +the bootablerootdir output copies from bootfiles + +Mon Dec 23 18:28:50 GMT 2024 + +it might be worth moving ubi option decls into the hardware module, if +they're hardware-dependent diff --git a/bordervm-configuration.nix b/bordervm-configuration.nix index ffd18f0..c8347d1 100644 --- a/bordervm-configuration.nix +++ b/bordervm-configuration.nix @@ -102,12 +102,18 @@ in { systemd.services.sshd.wantedBy = pkgs.lib.mkForce [ "multi-user.target" ]; virtualisation = { - forwardPorts = [ { - from = "host"; - host.port = 7654; -# guest.address = "10.0.2.15"; - guest.port =7654; - } ]; + forwardPorts = [ + { + from = "host"; + host.port = 7654; + # guest.address = "10.0.2.15"; + guest.port =7654; + } + { + host.port = 2222; + guest.address = "10.0.2.15"; + guest.port = 22; + }]; qemu = { networkingOptions = [ ]; options = diff --git a/ci.nix b/ci.nix index 1baecae..d86fd89 100644 --- a/ci.nix +++ b/ci.nix @@ -12,6 +12,7 @@ let "qemu-armv7l" "tp-archer-ax23" "zyxel-nwa50ax" + "turris-omnia" ]; vanilla = ./vanilla-configuration.nix; for-device = name: diff --git a/devices/belkin-rt3200/default.nix b/devices/belkin-rt3200/default.nix index 4b1790d..396a739 100644 --- a/devices/belkin-rt3200/default.nix +++ b/devices/belkin-rt3200/default.nix @@ -7,12 +7,10 @@ and is "work in progress" in Liminix. .. note:: The factory flash image contains ECC errors that make it - incompatible with Liminix: you need to use the `OpenWrt + incompatible with Liminix: use the `OpenWrt UBI Installer `_ to - rewrite the partition layout before you can flash - Liminix onto it (or even use it with - :ref:`system-outputs-tftpboot`, if you want the wireless - to work). + rewrite the partition layout before you can use + Liminix with it Hardware summary ================ @@ -27,8 +25,31 @@ Installation ============ - Installation is currently a manual process (you need a :ref:`serial ` conection and - TFTP) following the instructions at :ref:`system-outputs-ubimage` + Installation is currently a manual process. + + Preparation + ----------- + + To prepare the device for Liminix you first need to use the + `OpenWrt UBI Installer + `_ image to + rewrite the flash layout. You can do this in onw of two ways: + either follow the instructions to do it through the vendor web + interface, or you can drop to U-boot and use TFTP + + .. code-block:: console + + MT7622> setenv ipaddr 10.0.0.6 + MT7622> setenv serverip 10.0.0.1 + MT7622> tftpboot 0x42000000 openwrt-mediatek-mt7622-linksys_e8450-ubi-initramfs-recovery-installer.itb + MT7622> bootm 0x42000000 + + Once it's finished booted into Linux you can safely reboot + + Installing Liminix + ------------------ + + This is a manual process: you need a :ref:`serial ` conection and TFTP : follow the instructions at :ref:`system-outputs-ubimage` ''; @@ -192,7 +213,7 @@ rootDevice = "ubi0:liminix"; dts = { src = "${openwrt.src}/target/linux/mediatek/dts/mt7622-linksys-e8450-ubi.dts"; - includes = [ + includePaths = [ "${openwrt.src}/target/linux/mediatek/dts" "${config.system.outputs.kernel.modulesupport}/arch/arm64/boot/dts/mediatek/" ]; diff --git a/devices/gl-ar750/default.nix b/devices/gl-ar750/default.nix index 53de152..a7f18a6 100644 --- a/devices/gl-ar750/default.nix +++ b/devices/gl-ar750/default.nix @@ -52,8 +52,9 @@ ''; - module = {pkgs, config, lim, ... }: + module = {pkgs, config, lim, lib, ... }: let + inherit (lib) mkIf; openwrt = pkgs.openwrt; firmwareBlobs = pkgs.pkgsBuildBuild.fetchFromGitHub { owner = "kvalo"; @@ -116,9 +117,12 @@ rootDevice = "/dev/mtdblock5"; dts = { src = "${openwrt.src}/target/linux/ath79/dts/qca9531_glinet_gl-ar750.dts"; - includes = [ + includePaths = [ "${openwrt.src}/target/linux/ath79/dts" ]; + includes = mkIf config.logging.persistent.enable [ + ./pstore-ramoops.dtsi + ]; }; networkInterfaces = diff --git a/devices/gl-ar750/pstore-ramoops.dtsi b/devices/gl-ar750/pstore-ramoops.dtsi new file mode 100644 index 0000000..b81801d --- /dev/null +++ b/devices/gl-ar750/pstore-ramoops.dtsi @@ -0,0 +1,9 @@ +/ { +reserved-memory { + ramoops@03f00000 { + compatible = "ramoops"; + reg = <0x03f00000 0x10000>; + pmsg-size = <0x10000>; + }; + }; +}; diff --git a/devices/gl-mt300a/default.nix b/devices/gl-mt300a/default.nix index 63d109e..d972e44 100644 --- a/devices/gl-mt300a/default.nix +++ b/devices/gl-mt300a/default.nix @@ -81,7 +81,7 @@ dts = { src = "${openwrt.src}/target/linux/ramips/dts/mt7620a_glinet_gl-mt300a.dts"; - includes = [ + includePaths = [ "${openwrt.src}/target/linux/ramips/dts" ]; }; diff --git a/devices/gl-mt300n-v2/default.nix b/devices/gl-mt300n-v2/default.nix index 7bf2c43..901bfaa 100644 --- a/devices/gl-mt300n-v2/default.nix +++ b/devices/gl-mt300n-v2/default.nix @@ -78,7 +78,7 @@ dts = { src = "${openwrt.src}/target/linux/ramips/dts/mt7628an_glinet_gl-mt300n-v2.dts"; - includes = [ + includePaths = [ "${openwrt.src}/target/linux/ramips/dts" ]; }; diff --git a/devices/qemu/default.nix b/devices/qemu/default.nix index b0d7472..91b8050 100644 --- a/devices/qemu/default.nix +++ b/devices/qemu/default.nix @@ -66,7 +66,7 @@ # *correct* but it does at least boot dts = lib.mkForce { src = "${config.system.outputs.kernel.modulesupport}/arch/mips/boot/dts/mti/malta.dts"; - includes = [ + includePaths = [ "${config.system.outputs.kernel.modulesupport}/arch/mips/boot/dts/" ]; }; diff --git a/devices/tp-archer-ax23/default.nix b/devices/tp-archer-ax23/default.nix index d49f177..6c6519c 100644 --- a/devices/tp-archer-ax23/default.nix +++ b/devices/tp-archer-ax23/default.nix @@ -405,7 +405,7 @@ rootDevice = "/dev/mtdblock3"; dts = { src = "${openwrt.src}/target/linux/ramips/dts/mt7621_tplink_archer-ax23-v1.dts"; - includes = [ + includePaths = [ "${openwrt.src}/target/linux/ramips/dts" "${config.system.outputs.kernel.modulesupport}/arch/arm64/boot/dts/mediatek/" ]; diff --git a/devices/turris-omnia/default.nix b/devices/turris-omnia/default.nix index 3ea83ec..bcf50f2 100644 --- a/devices/turris-omnia/default.nix +++ b/devices/turris-omnia/default.nix @@ -172,7 +172,6 @@ ../../modules/arch/arm.nix ../../modules/outputs/tftpboot.nix ../../modules/outputs/mbrimage.nix - ../../modules/outputs/extlinux.nix ]; config = { @@ -224,11 +223,6 @@ # WARNING: unmet direct dependencies detected for ARCH_WANT_LIBATA_LEDS ATA = "y"; - PSTORE = "y"; - PSTORE_RAM = "y"; - PSTORE_CONSOLE = "y"; -# PSTORE_DEFLATE_COMPRESS = "n"; - BLOCK = "y"; MMC="y"; PWRSEQ_EMMC="y"; # ??? @@ -341,14 +335,14 @@ targets = ["ath9k" "ath10k_pci"]; }; in { - defaultOutput = "mtdimage"; + defaultOutput = "updater"; loadAddress = lim.parseInt "0x00800000"; # "0x00008000"; entryPoint = lim.parseInt "0x00800000"; # "0x00008000"; rootDevice = "/dev/mmcblk0p1"; dts = { src = "${config.system.outputs.kernel.modulesupport}/arch/arm/boot/dts/marvell/armada-385-turris-omnia.dts"; - includes = [ + includePaths = [ "${config.system.outputs.kernel.modulesupport}/arch/arm/boot/dts/marvell/" ]; }; diff --git a/devices/zyxel-nwa50ax/default.nix b/devices/zyxel-nwa50ax/default.nix index 2eea914..710473d 100644 --- a/devices/zyxel-nwa50ax/default.nix +++ b/devices/zyxel-nwa50ax/default.nix @@ -124,7 +124,7 @@ hash = "sha256-ifriAjWzFACrxVWCANZpUaEZgB/0pdbhnTVQytx6ddg="; }; in { - imports = [ + imports = [ # We include it to ensure the bridge functionality # is available on the target kernel. ../../modules/bridge @@ -184,7 +184,7 @@ # Actually, this is not what we want. # This DTS is insufficient. src = ./mt7621_zyxel_nwa50ax.dtsi; - includes = [ + includePaths = [ # Here's one weird trick to make `ubi` detection # out of the box. # We will write ubi on /dev/firmware_a:rootfs location @@ -233,7 +233,7 @@ services.zyxel-dual-image = config.boot.zyxel-dual-image.build { ensureActiveImage = "primary"; # TODO: use mtd names rather… - # primary and secondary are always /dev/mtd3 by virtue of the + # primary and secondary are always /dev/mtd3 by virtue of the # dtb being not too wrong… # TODO: remove this hack. primaryMtdPartition = "/dev/mtd3"; diff --git a/doc/admin.rst b/doc/admin.rst index 69d2872..9a12881 100644 --- a/doc/admin.rst +++ b/doc/admin.rst @@ -131,50 +131,54 @@ human-readable format, use :command:`s6-tai64nlocal`. 1970-01-02 21:51:48.832588765 wan.link.pppoe sent [LCP ConfReq id=0x1 ] +Log persistence +--------------- +Logs written to :file:`/run/log/` will not survive a reboot or crash, +as it is an ephemeral filesystem. -Updating an installed system (JFFS2) -************************************ +On supported hardware you can enable logging to `pstore +`_ which +means the most recent log messages will be preserved on reboot. Set +the config option ``logging.persistent.enable = true`` to log messages +to :file:`/dev/pmsg0` as well as to the regular log. This is a +circular buffer, so when it fills up newer messages will overwrite the +oldest messages. - -Adding packages -=============== - -If your device is running a JFFS2 root filesystem, you can build -extra packages for it on your build system and copy them to the -device: any package in Nixpkgs or in the Liminix overlay is available -with the ``pkgs`` prefix: +To check the previous messages after a (planned or forced) reboot, +you need to mooun the pstore filesystem. .. code-block:: console - nix-build -I liminix-config=./my-configuration.nix \ - --arg device "import ./devices/mydevice" -A pkgs.tcpdump - - nix-shell -p min-copy-closure root@the-device result/ - -Note that this only copies the package to the device: it doesn't update -any profile to add it to ``$PATH`` + # mount -t pstore pstore /sys/fs/pstore/ + # ls -l /sys/fs/pstore/ + -r--r--r-- 1 43071 pmsg-ramoops-0 + # cat /sys/fs/pstore/pmsg-ramoops-0 + @40000000000000282c997d29 mydevice klogd <6>[ 30.793756] int: port 2(wlan0) entered blocking state + [log messages from before the reboot follow] -.. _rebuilding the system: -Rebuilding the system -===================== +Updating an installed system +**************************** -Liminix has a mechanism for in-place updates of a running system which -is analogous to :command:`nixos-rebuild`, but its operation is a -bit different because it expects to run on a build machine and then -copy to the host device. To use this, build the `outputs.systemConfiguration` -target and then run the :command:`result/install.sh` script it generates. +If your system has a writable root filesystem (JFFS2, btrfs etc - +anything but squashfs), we have mechanisms for in-places updates +analogous to :command:`nixos-rebuild`, but the operation is a bit +different because it expects to run on a build machine and then copy +to the host device using :command:`ssh`. + +To use this, build the ``outputs.updater`` +target and then run the :command:`update.sh` script it generates. .. code-block:: console nix-build -I liminix-config=./my-configuration.nix \ --arg device "import ./devices/mydevice" \ - -A outputs.systemConfiguration - ./result/install.sh root@the-device + -A outputs.updater + ./result/bin/update.sh root@the-device -The install script uses min-copy-closure to copy new or changed +The update script uses min-copy-closure to copy new or changed packages to the device, then (perhaps) reboots it. The reboot behaviour can be affected by flags: @@ -187,7 +191,6 @@ behaviour can be affected by flags: the services that have updated store paths (and anything that depends on them), but will not affect services that haven't changed. - It doesn't delete old packages automatically: to do that run :command:`min-collect-garbage`, which will delete any packages not in the current system closure. Note that Liminix does not have the NixOS @@ -207,6 +210,26 @@ Caveats .. _levitate: +Adding packages +=============== + +If you simply wish to add a package without any change to services, +you can call :command:`min-copy-closure` directly to install +any package in Nixpkgs or in the Liminix overlay + +.. code-block:: console + + nix-build -I liminix-config=./my-configuration.nix \ + --arg device "import ./devices/mydevice" -A pkgs.tcpdump + + nix-shell -p min-copy-closure root@the-device result/ + +Note that this only copies the package and its dependencies to the +device: it doesn't update any profile to add it to ``$PATH`` + + +.. _rebuilding the system: + Reinstalling on a running system ******************************** diff --git a/examples/recovery.nix b/examples/recovery.nix index 9b0320e..7391a70 100644 --- a/examples/recovery.nix +++ b/examples/recovery.nix @@ -22,7 +22,6 @@ in rec { ../modules/outputs/ubimage.nix ../modules/outputs/jffs2.nix ../modules/outputs/ext4fs.nix - ../modules/outputs/extlinux.nix ]; kernel.config = { diff --git a/modules/all-modules.nix b/modules/all-modules.nix index 1e4df4e..982e4f3 100644 --- a/modules/all-modules.nix +++ b/modules/all-modules.nix @@ -30,7 +30,6 @@ ./outputs/vmroot.nix ./ppp ./ramdisk.nix - ./squashfs.nix ./ssh ./users.nix ./vlan diff --git a/modules/hardware.nix b/modules/hardware.nix index 48bdf40..056cd04 100644 --- a/modules/hardware.nix +++ b/modules/hardware.nix @@ -24,11 +24,16 @@ in only for QEMU. ''; }; - includes = mkOption { + includePaths = mkOption { default = [ ]; description = "List of directories to search for DTS includes (.dtsi files)"; type = types.listOf types.path; }; + includes = mkOption { + default = [ ]; + description = "\"dtsi\" fragments to include in the generated device tree"; + type = types.listOf types.path; + }; }; defaultOutput = mkOption { description = "\"Default\" output: what gets built for this device when outputs.default is requested. Typically this is \"mtdimage\" or \"vmroot\""; diff --git a/modules/kernel/default.nix b/modules/kernel/default.nix index ae488fa..373bfcc 100644 --- a/modules/kernel/default.nix +++ b/modules/kernel/default.nix @@ -68,19 +68,15 @@ in { }; }; config = { - system.outputs = + system.outputs.kernel = let mergedConfig = mergeConditionals config.kernel.config config.kernel.conditionalConfig; - k = liminix.builders.kernel.override { - config = mergedConfig; - inherit (config.kernel) version src extraPatchPhase; - targets = config.kernel.makeTargets; - }; - in { - kernel = k.vmlinux; - zimage = k.zImage; + in liminix.builders.kernel.override { + config = mergedConfig; + inherit (config.kernel) version src extraPatchPhase; + targets = config.kernel.makeTargets; }; kernel = rec { diff --git a/modules/outputs.nix b/modules/outputs.nix index 16742a5..6da7daa 100644 --- a/modules/outputs.nix +++ b/modules/outputs.nix @@ -11,9 +11,12 @@ let in { imports = [ - ./squashfs.nix + ./outputs/squashfs.nix ./outputs/vmroot.nix - ./outputs/extlinux.nix + ./outputs/boot-extlinux.nix + ./outputs/boot-fit.nix + ./outputs/uimage.nix + ./outputs/updater ]; options = { system.outputs = { @@ -27,17 +30,7 @@ in kernel ****** - Kernel vmlinux file (usually ELF) - ''; - }; - zimage = mkOption { - type = types.package; - internal = true; - description = '' - zimage - ****** - - Kernel in compressed self-extracting package + Kernel package (multi-output derivation) ''; }; dtb = mkOption { @@ -50,16 +43,6 @@ in Compiled device tree (FDT) for the target device ''; }; - uimage = mkOption { - type = types.package; - internal = true; - description = '' - uimage - ****** - - Combined kernel and FDT in uImage (U-Boot compatible) format - ''; - }; tplink-safeloader = mkOption { type = types.package; }; @@ -82,6 +65,12 @@ in directory of files to package into root filesystem ''; }; + bootfiles = mkOption { + type = types.nullOr types.package; + internal = true; + default = null; + # description = ""; + }; bootablerootdir = mkOption { type = types.package; internal = true; @@ -104,18 +93,11 @@ in system.outputs = rec { dtb = liminix.builders.dtb { inherit (config.boot) commandLine; - dts = config.hardware.dts.src; - includes = config.hardware.dts.includes ++ [ + dts = [config.hardware.dts.src] ++ config.hardware.dts.includes; + includes = config.hardware.dts.includePaths ++ [ "${o.kernel.headers}/include" ]; }; - uimage = liminix.builders.uimage { - commandLine = concatStringsSep " " config.boot.commandLine; - inherit (config.boot) commandLineDtbNode; - inherit (config.hardware) loadAddress entryPoint alignment; - inherit (config.boot) imageFormat; - inherit (o) kernel dtb; - }; rootdir = let inherit (pkgs.pkgsBuildBuild) runCommand; @@ -132,8 +114,8 @@ in let inherit (pkgs.pkgsBuildBuild) runCommand; in runCommand "add-slash-boot" { } '' cp -a ${o.rootdir} $out - ${if config.boot.loader.extlinux.enable - then "(cd $out && chmod -R +w . && rmdir boot && cp -a ${o.extlinux} boot)" + ${if o.bootfiles != null + then "(cd $out && chmod -R +w . && rmdir boot && cp -a ${o.bootfiles} boot)" else "" } ''; diff --git a/modules/outputs/extlinux.nix b/modules/outputs/boot-extlinux.nix similarity index 78% rename from modules/outputs/extlinux.nix rename to modules/outputs/boot-extlinux.nix index 6f73345..70b71fd 100644 --- a/modules/outputs/extlinux.nix +++ b/modules/outputs/boot-extlinux.nix @@ -12,19 +12,15 @@ let cmdline = concatStringsSep " " config.boot.commandLine; wantsDtb = config.hardware.dts ? src && config.hardware.dts.src != null; in { - options.system.outputs.extlinux = mkOption { - type = types.package; - # description = ""; - }; options.boot.loader.extlinux.enable = mkEnableOption "extlinux"; config = mkIf cfg.enable { - system.outputs.extlinux = pkgs.runCommand "extlinux" {} '' + system.outputs.bootfiles = pkgs.runCommand "extlinux" {} '' mkdir $out cd $out ${if wantsDtb then "cp ${o.dtb} dtb" else "true"} cp ${o.initramfs} initramfs - cp ${o.zimage} kernel + cp ${o.kernel.zImage} kernel mkdir extlinux cat > extlinux/extlinux.conf << _EOF menu title Liminix @@ -37,7 +33,7 @@ in { _EOF ''; filesystem = dir { - boot = symlink config.system.outputs.extlinux; + boot = symlink config.system.outputs.bootfiles; }; }; } diff --git a/modules/outputs/boot-fit.nix b/modules/outputs/boot-fit.nix new file mode 100644 index 0000000..cf998ba --- /dev/null +++ b/modules/outputs/boot-fit.nix @@ -0,0 +1,27 @@ +{ + config +, pkgs +, lib +, ... +}: +let + inherit (lib) mkIf mkEnableOption mkOption types concatStringsSep; + inherit (pkgs.pseudofile) dir symlink; + cfg = config.boot.loader.fit; + o = config.system.outputs; + cmdline = concatStringsSep " " config.boot.commandLine; + wantsDtb = config.hardware.dts ? src && config.hardware.dts.src != null; +in { + options.boot.loader.fit.enable = mkEnableOption "FIT in /boot"; + + config = mkIf cfg.enable { + system.outputs.bootfiles = pkgs.runCommand "boot-fit" {} '' + mkdir $out + cd $out + cp ${o.uimage} fit + ''; + filesystem = dir { + boot = symlink config.system.outputs.bootfiles; + }; + }; +} diff --git a/modules/outputs/initramfs.nix b/modules/outputs/initramfs.nix index 6599429..37ff60c 100644 --- a/modules/outputs/initramfs.nix +++ b/modules/outputs/initramfs.nix @@ -9,6 +9,7 @@ let inherit (pkgs) runCommand; in { + imports = [ ./system-configuration.nix ]; options = { boot.initramfs = { enable = mkEnableOption "initramfs"; @@ -22,14 +23,6 @@ in filesystem ''; }; - systemConfiguration = mkOption { - type = types.package; - description = '' - pkgs.systemconfig for the configured filesystem, - contains 'activate' and 'init' commands - ''; - internal = true; - }; }; }; config = mkIf config.boot.initramfs.enable { @@ -53,8 +46,6 @@ in file /init ${pkgs.preinit}/bin/preinit 0755 0 0 SPECIALS ''; - systemConfiguration = - pkgs.systemconfig config.filesystem.contents; }; }; } diff --git a/modules/squashfs.nix b/modules/outputs/squashfs.nix similarity index 100% rename from modules/squashfs.nix rename to modules/outputs/squashfs.nix diff --git a/modules/outputs/system-configuration.nix b/modules/outputs/system-configuration.nix new file mode 100644 index 0000000..90f87ac --- /dev/null +++ b/modules/outputs/system-configuration.nix @@ -0,0 +1,28 @@ +{ + config +, pkgs +, lib +, ... +}: +let + inherit (lib) mkEnableOption mkOption mkIf types; + inherit (pkgs) runCommand; +in +{ + options = { + system.outputs = { + systemConfiguration = mkOption { + type = types.package; + description = '' + pkgs.systemconfig for the configured filesystem, + contains 'activate' and 'init' commands + ''; + internal = true; + }; + }; + }; + config = { + system.outputs.systemConfiguration = + pkgs.systemconfig config.filesystem.contents; + }; +} diff --git a/modules/outputs/tftpboot.nix b/modules/outputs/tftpboot.nix index 63fc4ea..e4d6a03 100644 --- a/modules/outputs/tftpboot.nix +++ b/modules/outputs/tftpboot.nix @@ -61,7 +61,7 @@ in { o = config.system.outputs; image = let choices = { uimage = o.uimage; - zimage = o.zimage; + zimage = o.kernel.zImage; }; in choices.${cfg.kernelFormat}; bootCommand = let choices = { uimage = "bootm"; diff --git a/modules/outputs/uimage.nix b/modules/outputs/uimage.nix new file mode 100644 index 0000000..b36ef60 --- /dev/null +++ b/modules/outputs/uimage.nix @@ -0,0 +1,30 @@ +{ + config, + pkgs, + lib, + ... +}: +let + inherit (lib) mkOption types concatStringsSep; + inherit (pkgs) liminix writeText; + o = config.system.outputs; +in +{ + options.system.outputs.uimage = mkOption { + type = types.package; + internal = true; + description = '' + uimage + ****** + + Combined kernel and FDT in uImage (U-Boot compatible) format + ''; + }; + config.system.outputs.uimage = liminix.builders.uimage { + commandLine = concatStringsSep " " config.boot.commandLine; + inherit (config.boot) commandLineDtbNode; + inherit (config.hardware) loadAddress entryPoint alignment; + inherit (config.boot) imageFormat; + inherit (o) kernel dtb; + }; +} diff --git a/modules/outputs/updater/default.nix b/modules/outputs/updater/default.nix new file mode 100644 index 0000000..e7bb279 --- /dev/null +++ b/modules/outputs/updater/default.nix @@ -0,0 +1,36 @@ +{ + config +, pkgs +, lib +, ... +}: +let + inherit (lib) mkIf; + o = config.system.outputs; + inherit (pkgs) runCommand; + inherit (lib) mkOption types; + inherit (pkgs.buildPackages) min-copy-closure; +in +{ + imports = [ ../system-configuration.nix ]; + options.system.outputs.updater = mkOption { + type = types.package; + description = '' + updater + ****** + + For configurations with a writable filesystem, create a shell + script that runs on the build system and updates the device + over the network to the new configuration + ''; + }; + + config.system.outputs.updater = + runCommand "buildUpdater" { } '' + mkdir -p $out/bin + substitute ${./update.sh} $out/bin/update.sh \ + --subst-var-by toplevel ${o.systemConfiguration} \ + --subst-var-by min_copy_closure ${min-copy-closure} + chmod +x $out/bin/update.sh + ''; +} diff --git a/pkgs/systemconfig/build-system-install.sh b/modules/outputs/updater/update.sh similarity index 65% rename from pkgs/systemconfig/build-system-install.sh rename to modules/outputs/updater/update.sh index 7bdac64..ccea529 100755 --- a/pkgs/systemconfig/build-system-install.sh +++ b/modules/outputs/updater/update.sh @@ -1,22 +1,19 @@ #!/usr/bin/env bash -# this shell script can be run on the build system to -# min-copy-closure the system configuration onto the device -# and reboot/restart services as requested +# this shell script is run on the build system to min-copy-closure the +# system configuration onto the device and reboot/restart services as +# requested die() { echo "$@" exit 1 } -# add min-copy-closure to the path. removing junk characters -# inserted by default.nix (q.v.) -min_copy_closure=@min-copy-closure@; PATH=${min_copy_closure//_/}/bin:$PATH +PATH=@min_copy_closure@/bin:$PATH ssh_command=${SSH_COMMAND-ssh} reboot="reboot" - case "$1" in "--no-reboot") unset reboot @@ -34,10 +31,11 @@ shift test -n "$target_host" || \ die "Usage: $0 [--no-reboot] [--fast] target-host" -toplevel=$(cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && readlink `pwd` ) +toplevel=$(realpath @toplevel@) test -e $toplevel/etc/nix-store-paths || die "missing etc/nix-store-paths, is this really a system configuration?" echo installing from systemConfiguration $toplevel to host $target_host +$ssh_command $target_host uname -a || die "Can't ssh to $target_host" min-copy-closure $target_host $toplevel set -x $ssh_command $target_host $toplevel/bin/install diff --git a/modules/s6/default.nix b/modules/s6/default.nix index 1a98f41..53d7968 100644 --- a/modules/s6/default.nix +++ b/modules/s6/default.nix @@ -7,10 +7,27 @@ let s6-linux-init stdenvNoCC; inherit (lib.lists) unique concatMap; + inherit (lib) concatStrings; + inherit (builtins) map; inherit (pkgs.pseudofile) dir symlink; inherit (pkgs.liminix.services) oneshot bundle longrun; inherit (lib) mkIf mkEnableOption mkOption types; cfg = config.logging; + + logger = + let pipecmds = + ["${s6}/bin/s6-log -bpd3 -- ${cfg.script} 1"] ++ + (lib.optional cfg.persistent.enable + "/bin/tee /dev/pmsg0") ++ + (lib.optional cfg.shipping.enable + "${pkgs.logshipper}/bin/logtap ${cfg.shipping.socket} logshipper-socket-event"); + in '' + #!${execline}/bin/execlineb -P + ${execline}/bin/redirfd -w 1 /dev/null + ${execline}/bin/redirfd -rnb 0 fifo + ${concatStrings (map (l: "pipeline { ${l} }\n") pipecmds)} + ${s6}/bin/s6-log -- ${cfg.directory} + ''; s6-rc-db = let # In the default bundle we need to have all the services @@ -106,21 +123,7 @@ let mode = "0600"; }; notification-fd = { file = "3"; }; - run = { - file = '' - #!${execline}/bin/execlineb -P - ${execline}/bin/redirfd -w 1 /dev/null - ${execline}/bin/redirfd -rnb 0 fifo - ${if cfg.shipping.enable then '' - pipeline { ${s6}/bin/s6-log -bpd3 -- ${cfg.script} 1 } - pipeline { ${pkgs.logshipper}/bin/logtap ${cfg.shipping.socket} logshipper-socket-event } - ${s6}/bin/s6-log -- ${cfg.directory} - '' else '' - ${s6}/bin/s6-log -bpd3 -- ${cfg.script} ${cfg.directory} - ''} - ''; - mode = "0755"; - }; + run = { file = logger; mode = "0755"; }; }; getty = dir { run = { @@ -212,6 +215,9 @@ let in { options = { logging = { + persistent = { + enable = mkEnableOption "store logs across reboots"; + }; shipping = { enable = mkEnableOption "unix socket for log shipping"; socket = mkOption { @@ -263,6 +269,11 @@ in { )]; config = { + kernel.config = mkIf config.logging.persistent.enable { + PSTORE = "y"; + PSTORE_PMSG = "y"; + PSTORE_RAM = "y"; + }; filesystem = dir { etc = dir { s6-rc = dir { diff --git a/overlay.nix b/overlay.nix index 0f84ca5..bf302cf 100644 --- a/overlay.nix +++ b/overlay.nix @@ -218,11 +218,11 @@ extraPkgs // { ] ++ final.lib.optionals (o ? patches) o.patches; }); - mtdutils = prev.mtdutils.overrideAttrs(o: { + mtdutils = (prev.mtdutils.overrideAttrs(o: { patches = (if o ? patches then o.patches else []) ++ [ ./pkgs/mtdutils/0001-mkfs.jffs2-add-graft-option.patch ]; - }); + })).override { util-linux = final.util-linux-small ; }; nftables = prev.nftables.overrideAttrs(o: { configureFlags = [ diff --git a/pkgs/anoia/init.fnl b/pkgs/anoia/init.fnl index 8fc6585..05ddd71 100644 --- a/pkgs/anoia/init.fnl +++ b/pkgs/anoia/init.fnl @@ -84,6 +84,8 @@ (false err) (expect (string.match err "path traversal"))))] (expect= (append-path "/tmp" "hello") "/tmp/hello") (expect= (append-path "/tmp/" "hello") "/tmp/hello") + (expect= (append-path "/tmp/" "///hello") "/tmp/hello") + (expect= (append-path "/tmp/" "///hello/../fish") "/tmp/fish") (traps "/tmp/" "../hello") (expect= (append-path "/tmp/" "hello/../goodbye") "/tmp/goodbye") (traps "/tmp/" "hello/../../goodbye")) diff --git a/pkgs/kernel/dtb.nix b/pkgs/kernel/dtb.nix index f971cec..505c4a6 100644 --- a/pkgs/kernel/dtb.nix +++ b/pkgs/kernel/dtb.nix @@ -2,6 +2,8 @@ stdenv , dtc , lib +, runCommand +, writeText }: { dts , includes @@ -10,14 +12,20 @@ cppDtSearchFlags = builtins.concatStringsSep " " (map (f: "-I${f}") includes); dtcSearchFlags = builtins.concatStringsSep " " (map (f: "-i${f}") includes); cmdline = lib.concatStringsSep " " commandLine; + chosen = writeText "chosen.dtsi" "/{ chosen { bootargs = ${builtins.toJSON cmdline}; }; };"; + combined = writeText "combined-dts-fragments" + (lib.concatStrings + (builtins.map + (f: "#include \"${f}\"\n") + (dts ++ [ chosen ]))); in stdenv.mkDerivation { name = "dtb"; phases = [ "buildPhase" ]; nativeBuildInputs = [ dtc ]; buildPhase = '' - ${stdenv.cc.targetPrefix}cpp -nostdinc -x assembler-with-cpp ${cppDtSearchFlags} -undef -D__DTS__ -o dtb.tmp ${dts} - echo '/{ chosen { bootargs = ${builtins.toJSON cmdline}; }; };' >> dtb.tmp + ${stdenv.cc.targetPrefix}cpp -nostdinc -x assembler-with-cpp ${cppDtSearchFlags} -undef -D__DTS__ -o dtb.tmp ${combined} dtc ${dtcSearchFlags} -I dts -O dtb -o $out dtb.tmp + # dtc -I dtb -O dts $out test -e $out ''; } diff --git a/pkgs/systemconfig/default.nix b/pkgs/systemconfig/default.nix index 1fe2c4a..37ad3ea 100644 --- a/pkgs/systemconfig/default.nix +++ b/pkgs/systemconfig/default.nix @@ -56,38 +56,33 @@ let else ""; in "unlink(${qpathname}); ${cmd} ${chown}"; in mapAttrsToList (makeFile prefix) attrset; - activateScript = attrset: writeText "makedevs.c" '' - #include "defs.h" - int main(int argc, char* argv[]) { - chdir(argv[1]); - ${(builtins.concatStringsSep "\n" (visit "." attrset))} - } - ''; in attrset: - let makedevs = activateScript attrset; + let + activateScript = writeText "activate.c" '' + #include "defs.h" + int main(int argc, char* argv[]) { + chdir(argv[1]); + ${(builtins.concatStringsSep "\n" (visit "." attrset))} + } + ''; in stdenv.mkDerivation { - name="make-stuff"; + name="system-configuration"; src = ./.; CFLAGS = "-Os"; LDFLAGS = "-static -Xlinker -static"; postConfigure = '' - cp ${makedevs} makedevs.c + cp ${activateScript} activate.c ''; - makeFlags = ["makedevs"]; + makeFlags = ["activate"]; installPhase = '' - closure=${closureInfo { rootPaths = [ makedevs ]; }} + closure=${closureInfo { rootPaths = [ activateScript ]; }} mkdir -p $out/bin $out/etc cp $closure/store-paths $out/etc/nix-store-paths - $STRIP --remove-section=.note --remove-section=.comment --strip-all makedevs -o $out/bin/activate + $STRIP --remove-section=.note --remove-section=.comment --strip-all activate -o $out/bin/activate ln -s ${s6-init-bin}/bin/init $out/bin/init cp -p ${writeFennel "restart-services" {} ./restart-services.fnl} $out/bin/restart-services - # obfuscate the store path of min-copy-closure so that the output - # closure doesn't include a bunch of build system stuff - f=${buildPackages.min-copy-closure}; f=$(echo $f | sed 's/\(.....\)/\1_/g') - substitute ${./build-system-install.sh} $out/install.sh --subst-var-by min-copy-closure $f - chmod +x $out/install.sh cat > $out/bin/install < (# result) 0)) + (table.remove result) + (= component "..") + (error "path traversal attempt") + true + (table.insert result component))) + (.. base "/" (table.concat result "/")))) (-> (tftp:listen diff --git a/shell.nix b/shell.nix index 02b7cd8..353a52b 100644 --- a/shell.nix +++ b/shell.nix @@ -3,7 +3,6 @@ let liminix = (import ./default.nix { device = (import ./devices/qemu); liminix-config = ./vanilla-configuration.nix; - inherit nixpkgs; }); here = builtins.toString ./.; in liminix.buildEnv.overrideAttrs (o: { diff --git a/tests/jffs2/configuration.nix b/tests/jffs2/configuration.nix index 11d1f3e..010aa15 100644 --- a/tests/jffs2/configuration.nix +++ b/tests/jffs2/configuration.nix @@ -4,7 +4,7 @@ let in { imports = [ ../../vanilla-configuration.nix - ../../modules/squashfs.nix + ../../modules/outputs/squashfs.nix ../../modules/outputs/jffs2.nix ]; config.rootfsType = "jffs2";