Compare commits

...

4 Commits

Author SHA1 Message Date
Daniel Barlow 98d3336926 rewrite run-liminix-vm as a fennel program
the effect of shell quoting/word splitting rules was reaching
completely unreasonable, insofar as I was unable to reason about it
2023-12-03 22:51:39 +00:00
Daniel Barlow cb6ebbdc60 alphabetize derivations in overlay 2023-12-02 17:08:59 +00:00
Daniel Barlow bb335050fd derivation that produces /boot 2023-12-02 15:31:55 +00:00
Daniel Barlow 395f624338 think 2023-12-02 15:31:09 +00:00
9 changed files with 312 additions and 159 deletions

View File

@ -3176,6 +3176,9 @@ Turris TODO
- perhaps we need different boot "recipes" - e.g. some device
might want boot.scr and another something different
- create installable tarball and test
- gpio thingy for SFP switching
- iperf
@ -3188,7 +3191,9 @@ Turris TODO
- how to put device name into the device docs
- make config.boot.commandLine a single string
- [WONT] make config.boot.commandLine a single string
this sounds sensible but it just makes it harder to put useful comments
against command line fragments so that we know why they're there
- usefulness tiers for devices ("stable", "experimental", "wip")
- params for ubi(fs) are a mess
@ -3255,3 +3260,63 @@ we pick the name of anyway).
So maybe we don't need to rewrite ifwait, we just do it after renaming
the device
Wed Nov 29 21:28:37 GMT 2023
How do we name outputs?
fileystem image
with-boot filesystem image (e.g. ubifs for belkin)
tarball
with-boot tarball (e.g. omnia)
flashable combined image of kernel + filesystem (e.g. gl-mt*)
kernel + filesystem image + dtb + tftpboot glue
kernel + filesystem image + qemu script
we could add initramfs as a separate thing for tftpboot and qemu
(and FIT images) but it would mean not sharing a kernel with
the outputs that require embedded initramfs
we can enable with-boot variants of outputs by adding a
boot.loader config option. if we go that route, can we
use config options to drive the whole output thingy?
We could have a config option that just changes "defaultOutput"
but is that useful?
Maybe the question is: we can choose a different output at build time
rather than editing configuration - how often is this useful? I think
tftpboot vs installable is about the only case.
adding a /boot to a filesystem differs from making a combined
flashable image because it is a config change and not a composition
of two other already-built outputs.
we could have done tftpboot that way as well, but we chose to unpack
and repack the fdt so we don't have to build two kernels - and also so
that we can have both outputs from the same configuration without
editing any files.
for tftpboot we don't want to make the filesystem embed the kernel
if we need a separate kernel for booting (guessing we can't usually make
u-boot loopback mount a downloaded filesystem image). so that points to
not making it a config option, _or_ to making the inclusion logic
(hardware wants a kernel in filesystem) && !(output == tftpboot)
which itself means the output somehow needs to be injected into the
config
nix-build -I liminix-config=./examples/hello-from-mt300.nix --arg device "import ./devices/my-device" --arg output=tftpboot
let's see if we can not do that?
repacking a ubifs to add /boot is awkward and unpleasant
Thu Nov 30 14:33:08 GMT 2023
We need a new boot-in-rootfs output which calls rootfs with
(config.filesystem // { /boot ... }) when the device
type is extlinux
can conditionally augment the config.filesystem in the outputs
that produce filesystems. The condition is "device boots using the
filesystem, and this is not a tftpboot"

View File

@ -12,6 +12,7 @@ in
imports = [
./squashfs.nix
./outputs/vmroot.nix
./outputs/extlinux.nix
];
options = {
system.outputs = {

View File

@ -0,0 +1,37 @@
{
config
, pkgs
, lib
, ...
}:
let
inherit (lib) mkIf mkEnableOption mkOption types concatStringsSep;
cfg = config.boot.loader.extlinux;
o = config.system.outputs;
cmdline = concatStringsSep " " config.boot.commandLine;
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" {} ''
mkdir $out
cd $out
ln -s ${o.dtb} dtb
ln -s ${o.initramfs} initramfs
gzip -9f < ${o.kernel} > kernel.gz
cat > extlinux.conf << _EOF
menu title Liminix
timeout 100
label Liminix
kernel kernel.gz
initrd initramfs
fdt dtb
append ${cmdline}
_EOF
'';
};
}

View File

@ -54,7 +54,7 @@ in
then "${pkgs.stdenv.cc.targetPrefix}objcopy -O binary -R .comment -S ${kernel} $out"
else "cp ${kernel} $out");
phram_address = lib.toHexString (config.hardware.ram.startAddress + 256 * 1024 * 1024);
in pkgs.runCommandCC "vmroot" {} ''
in pkgs.runCommand "vmroot" {} ''
mkdir $out
cd $out
ln -s ${rootfs} rootfs
@ -64,7 +64,7 @@ in
echo ${cmdline} > commandline
cat > run.sh << EOF
#!${pkgs.runtimeShell}
CMDLINE=${cmdline} PHRAM_ADDRESS=0x${phram_address} ${pkgs.pkgsBuildBuild.run-liminix-vm}/bin/run-liminix-vm --arch ${pkgs.stdenv.hostPlatform.qemuArch} \$* ${makeBootableImage} ${config.system.outputs.rootfs}
${pkgs.pkgsBuildBuild.run-liminix-vm}/bin/run-liminix-vm --command-line ${builtins.toJSON cmdline} --arch ${pkgs.stdenv.hostPlatform.qemuArch} --phram-address 0x${phram_address} \$* ${makeBootableImage} ${config.system.outputs.rootfs}
EOF
chmod +x run.sh
'';

View File

@ -44,20 +44,12 @@ let
lua = let s = lua_no_readline.override { self = s; }; in s;
in
extraPkgs // {
mtdutils = prev.mtdutils.overrideAttrs(o: {
patches = (if o ? patches then o.patches else []) ++ [
./pkgs/mtdutils/0001-mkfs.jffs2-add-graft-option.patch
];
});
rsyncSmall =
let r = prev.rsync.overrideAttrs(o: {
configureFlags = o.configureFlags ++ [
"--disable-openssl"
];
});
in r.override { openssl = null; };
# liminix library functions
lim = {
parseInt = s : (builtins.fromTOML "r=${s}").r;
};
# keep these alphabetical
chrony =
let chrony' = prev.chrony.overrideAttrs(o: {
configureFlags = [
@ -79,49 +71,6 @@ extraPkgs // {
};
strace = prev.strace.override { libunwind = null; };
kexec-tools-static = prev.kexec-tools.overrideAttrs(o: {
# For kexecboot we copy kexec into a ramdisk on the system being
# upgraded from. This is more likely to work if kexec is
# statically linked so doesn't have dependencies on store paths that
# may not exist on that machine. (We can't nix-copy-closure as
# the store may not be on a writable filesystem)
LDFLAGS = "-static";
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
];
});
luaFull = prev.lua;
inherit lua;
inherit s6;
s6-linux-init = prev.s6-linux-init.override {
skawarePackages = prev.skawarePackages // {
inherit s6;
};
};
s6-rc = prev.s6-rc.override {
skawarePackages = prev.skawarePackages // {
inherit s6;
};
};
nftables = prev.nftables.overrideAttrs(o: {
configureFlags = [
"--disable-debug"
"--disable-python"
"--with-mini-gmp"
"--without-cli"
];
});
dnsmasq =
let d = prev.dnsmasq.overrideAttrs(o: {
@ -134,6 +83,15 @@ extraPkgs // {
nettle = null;
};
dropbear = prev.dropbear.overrideAttrs (o: {
postPatch = ''
(echo '#define DSS_PRIV_FILENAME "/run/dropbear/dropbear_dss_host_key"'
echo '#define RSA_PRIV_FILENAME "/run/dropbear/dropbear_rsa_host_key"'
echo '#define ECDSA_PRIV_FILENAME "/run/dropbear/dropbear_ecdsa_host_key"'
echo '#define ED25519_PRIV_FILENAME "/run/dropbear/dropbear_ed25519_host_key"') > localoptions.h
'';
});
hostapd =
let
config = [
@ -161,13 +119,40 @@ extraPkgs // {
});
in h.override { openssl = null; sqlite = null; };
dropbear = prev.dropbear.overrideAttrs (o: {
postPatch = ''
(echo '#define DSS_PRIV_FILENAME "/run/dropbear/dropbear_dss_host_key"'
echo '#define RSA_PRIV_FILENAME "/run/dropbear/dropbear_rsa_host_key"'
echo '#define ECDSA_PRIV_FILENAME "/run/dropbear/dropbear_ecdsa_host_key"'
echo '#define ED25519_PRIV_FILENAME "/run/dropbear/dropbear_ed25519_host_key"') > localoptions.h
'';
kexec-tools-static = prev.kexec-tools.overrideAttrs(o: {
# For kexecboot we copy kexec into a ramdisk on the system being
# upgraded from. This is more likely to work if kexec is
# statically linked so doesn't have dependencies on store paths that
# may not exist on that machine. (We can't nix-copy-closure as
# the store may not be on a writable filesystem)
LDFLAGS = "-static";
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
];
});
luaFull = prev.lua;
inherit lua;
mtdutils = prev.mtdutils.overrideAttrs(o: {
patches = (if o ? patches then o.patches else []) ++ [
./pkgs/mtdutils/0001-mkfs.jffs2-add-graft-option.patch
];
});
nftables = prev.nftables.overrideAttrs(o: {
configureFlags = [
"--disable-debug"
"--disable-python"
"--with-mini-gmp"
"--without-cli"
];
});
openssl = prev.openssl.overrideAttrs (o: {
@ -183,6 +168,8 @@ extraPkgs // {
"\nsed -i.bak 's/linux.*-mips/linux-mops/' Configure\n");
});
pppBuild = prev.ppp;
qemu = prev.qemu.overrideAttrs (o: {
patches = o.patches ++ [
./pkgs/qemu/arm-image-friendly-load-addr.patch
@ -190,10 +177,26 @@ extraPkgs // {
];
});
pppBuild = prev.ppp;
rsyncSmall =
let r = prev.rsync.overrideAttrs(o: {
configureFlags = o.configureFlags ++ [
"--disable-openssl"
];
});
in r.override { openssl = null; };
# liminix library functions
lim = {
parseInt = s : (builtins.fromTOML "r=${s}").r;
inherit s6;
s6-linux-init = prev.s6-linux-init.override {
skawarePackages = prev.skawarePackages // {
inherit s6;
};
};
s6-rc = prev.s6-rc.override {
skawarePackages = prev.skawarePackages // {
inherit s6;
};
};
strace = prev.strace.override { libunwind = null; };
}

View File

@ -1,19 +1,23 @@
{
qemu
, socat
, writeShellScriptBin
, symlinkJoin
, writeShellScript
, writeFennel
, runCommand
, lib
, lua
, pkgsBuildBuild
}: let
run-liminix-vm = writeShellScriptBin "run-liminix-vm" ''
export PATH="${lib.makeBinPath [qemu]}:$PATH"
${builtins.readFile ./run-liminix-vm.sh}
'';
connect = writeShellScriptBin "connect-vm" ''
run-liminix-vm = pkgsBuildBuild.writeFennel "run-liminix-vm" {
packages = [ qemu lua.pkgs.luaposix lua.pkgs.fennel ];
} ./run-liminix-vm.fnl;
connect = writeShellScript "connect-vm" ''
export PATH="${lib.makeBinPath [socat]}:$PATH"
socat -,raw,echo=0,icanon=0,isig=0,icrnl=0,escape=0x0f unix-connect:$1
'';
in symlinkJoin {
name = "run-liminix-vm";
paths = [ run-liminix-vm connect ];
}
in runCommand "vm" {} ''
mkdir -p $out/bin
cd $out/bin
ln -s ${connect} ./connect-vm
ln -s ${run-liminix-vm} ./run-liminix-vm
''

View File

@ -0,0 +1,120 @@
(local { : fork : execp : unlink } (require :posix.unistd))
(local { : wait } (require :posix.sys.wait))
(local { : mkstemp : setenv } (require :posix.stdlib))
(local { : fdopen } (require :posix.stdio))
(fn pad-file [name kb chr]
(let [(fd out) (mkstemp "run-vm-XXXXXXX")
pad-string (string.rep (or chr "\0") 1024)]
(with-open [f (fdopen fd :w)]
(for [i 1 kb] (f:write pad-string))
(f:seek :set 0)
(with-open [input (assert (io.open name :rb))]
(f:write (input:read "*a")))
(f:seek :end 0))
out))
(fn spawn [command args]
(match (fork)
(nil msg) (error (.. "couldn't fork: " msg))
0 (execp command args)
pid (wait pid)))
(fn appendm [t2 t1]
(table.move t1 1 (# t1) (+ 1 (# t2)) t2)
t2)
(fn merge [table1 table2]
(collect [k v (pairs table2) &into table1]
k v))
(fn assoc [tbl k v]
(tset tbl k v)
tbl)
(fn parse-args [args]
(match args
["--background" dir & rest] (assoc (parse-args rest) :background dir)
["--u-boot" bin & rest]
(assoc (parse-args rest) :u-boot (pad-file bin (* 4 1024) "\xff"))
["--arch" arch & rest] (assoc (parse-args rest) :arch arch)
["--phram-address" addr & rest] (assoc (parse-args rest) :phram-address addr)
["--lan" spec & rest] (assoc (parse-args rest) :lan spec)
["--command-line" cmd & rest] (assoc (parse-args rest) :command-line cmd)
[kernel rootfsimg]
{ :kernel kernel :rootfs (pad-file rootfsimg (* 16 1024)) }
))
(local options
(assert
(merge { :arch "mips" } (parse-args arg))
(.. "Usage: " (. arg 0) " blah bah")))
(fn background [dir]
(let [pid (.. dir "/pid")
sock (.. dir "/console")
monitor (.. dir "/monitor")]
["--daemonize"
"--pidfile" pid
"-serial" (.. "unix:" sock ",server,nowait")
"-monitor" (.. "unix:" monitor ",server,nowait")]))
(fn access-net []
[
"-netdev" "socket,id=access,mcast=230.0.0.1:1234,localaddr=127.0.0.1"
"-device" "virtio-net,disable-legacy=on,disable-modern=off,netdev=access,mac=ba:ad:1d:ea:21:02"
])
(fn local-net [override]
[
"-netdev" (.. (or override "socket,mcast=230.0.0.1:1235,localaddr=127.0.0.1")
",id=lan")
"-device" "virtio-net,disable-legacy=on,disable-modern=off,netdev=lan,mac=ba:ad:1d:ea:21:01"
])
(fn bootable [cmdline uboot]
(if uboot
(let [pflash (os.tmpname)
ffs (string.rep "\xff" 1024)]
(with-open [f (assert (io.open pflash :wb))]
(for [i 1 (* 4 1024)] (f:write ffs))
(f:seek :set 0)
(with-open [uboot-bin (assert (io.open uboot :rb))]
(f:write (uboot-bin:read "*a")))
(f:seek :end 0))
["-drive" (.. "if=pflash,format=raw,file=\"" pflash "\"")])
(let [cmdline (.. cmdline " liminix mtdparts=phram0:16M(rootfs) phram.phram=phram0," options.phram-address ",16Mi,65536 root=/dev/mtdblock0")]
["-kernel" options.kernel "-append" cmdline])))
(local bin {
:mips ["qemu-system-mips" "-M" "malta"]
:aarch64 ["qemu-system-aarch64" "-M" "virt"
"-semihosting" "-cpu" "cortex-a72"]
:arm ["qemu-system-arm" "-M" "virt,highmem=off"
"-cpu" "cortex-a15"]
})
(local exec-args
(-> []
(appendm (. bin options.arch))
(appendm ["-m" "272"
"-echr" "16"
"-device"
(.. "loader,file=" options.rootfs ",addr=" options.phram-address)
])
(appendm
(if options.background
(background options.background)
["-serial" "mon:stdio"]))
(appendm (bootable (or options.command-line "") options.u-boot))
(appendm (access-net))
(appendm (local-net options.lan))
(appendm ["-display" "none"])))
(match exec-args
[cmd & params] (print (spawn cmd params)))
(if options.rootfs (unlink options.rootfs))
(if options.u-boot (unlink options.u-boot))

View File

@ -1,78 +0,0 @@
#!/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 [initramfs]"
echo -e "\nWithout --background, use C-p c (not C-a c) to switch to the monitor"
exit 1
}
arch="mips"
if test "$1" = "--arch" ; then
arch=$2
shift;shift
fi
if test "$1" = "--background" ; then
statedir=$2
if test -z "$statedir" || ! test -d $statedir ; then
usage
fi
pid="${statedir}/pid"
socket="${statedir}/console"
monitor="${statedir}/monitor"
echo "running in background, socket is $socket, pid $pid"
flags="--daemonize --pidfile $pid -serial unix:$socket,server,nowait -monitor unix:$monitor,server,nowait"
shift;shift
else
flags="-serial mon:stdio"
fi
test -n "$2" || usage
lan=${LAN-"socket,mcast=230.0.0.1:1235,localaddr=127.0.0.1"}
rootfs=$(mktemp run-liminix-vm-fs-XXXXXX)
dd if=/dev/zero of=$rootfs bs=1M count=16 conv=sync
dd if=$2 of=$rootfs bs=65536 conv=sync,nocreat,notrunc
if test -n "$3"; then
initramfs="-initrd $3"
fi
case "$arch" in
mips)
QEMU="qemu-system-mips -M malta"
;;
aarch64)
QEMU="qemu-system-aarch64 -M virt -semihosting -cpu cortex-a72"
;;
arm)
# https://bugs.launchpad.net/qemu/+bug/1790975
QEMU="qemu-system-arm -M virt,highmem=off -cpu cortex-a15"
;;
*)
echo "unrecognised arch $arch"
exit 1;
;;
esac
phram="mtdparts=phram0:16M(rootfs) phram.phram=phram0,${PHRAM_ADDRESS},16Mi,65536 root=/dev/mtdblock0";
set -x
$QEMU \
-m 272 \
-echr 16 \
-append "$CMDLINE liminix $phram" \
-device loader,file=$rootfs,addr=$PHRAM_ADDRESS \
${initramfs} \
-netdev socket,id=access,mcast=230.0.0.1:1234,localaddr=127.0.0.1 \
-device virtio-net,disable-legacy=on,disable-modern=off,netdev=access,mac=ba:ad:1d:ea:21:02 \
-netdev ${lan},id=lan \
-device virtio-net,disable-legacy=on,disable-modern=off,netdev=lan,mac=ba:ad:1d:ea:21:01 \
-kernel $1 -display none $flags ${QEMU_OPTIONS}

View File

@ -27,6 +27,7 @@ name :
echo "#!${lua}/bin/lua ${luaFlags}"
echo "package.path = ${lib.strings.escapeShellArg (builtins.concatStringsSep "" luapath)} .. package.path"
echo "package.cpath = ${lib.strings.escapeShellArg (builtins.concatStringsSep "" luacpath)} .. package.cpath"
echo "require('posix.stdlib').setenv('PATH',${lib.escapeShellArg (lib.makeBinPath packages)} .. \":\" .. os.getenv('PATH'))"
fennel ${if correlate then "--correlate" else ""} --compile ${source}
) > ${name}.lua
'';