diff options
Diffstat (limited to 'pkgs/os-specific/linux/spectrum')
16 files changed, 838 insertions, 0 deletions
diff --git a/pkgs/os-specific/linux/spectrum/default.nix b/pkgs/os-specific/linux/spectrum/default.nix new file mode 100644 index 00000000000..c4cccab3787 --- /dev/null +++ b/pkgs/os-specific/linux/spectrum/default.nix @@ -0,0 +1,18 @@ +{ newScope, linux_cros }: + +let + self = with self; { + callPackage = newScope self; + + sys-vms = callPackage ./vm { }; + + spectrum-vm = callPackage ./spectrum-vm { linux = linux_vm; }; + + spectrum-testhost = callPackage ./testhost { }; + + linux_vm = callPackage ./linux/vm.nix { linux = linux_cros; }; + + makeRootfs = callPackage ./rootfs { }; + }; +in +self diff --git a/pkgs/os-specific/linux/spectrum/linux/vm.nix b/pkgs/os-specific/linux/spectrum/linux/vm.nix new file mode 100644 index 00000000000..c657cb443e8 --- /dev/null +++ b/pkgs/os-specific/linux/spectrum/linux/vm.nix @@ -0,0 +1,25 @@ +{ lib, linux, kernelPatches, structuredExtraConfig ? {} }: + +with lib.kernel; + +linux.override { + structuredExtraConfig = { + VIRTIO_PCI = yes; + VIRTIO_BLK = yes; + VIRTIO_WL = yes; + VIRTIO_NET = yes; + DEVTMPFS_MOUNT = yes; + SQUASHFS = yes; + + # VOP is needed to work around a Kconfig bug: + # https://lore.kernel.org/lkml/87wob4tf9b.fsf@alyssa.is/ + VOP = yes; + VOP_BUS = yes; + HW_RANDOM = yes; + HW_RANDOM_VIRTIO = yes; + + NET_9P = yes; + NET_9P_VIRTIO = yes; + "9P_FS" = yes; + } // structuredExtraConfig; +} diff --git a/pkgs/os-specific/linux/spectrum/rootfs/default.nix b/pkgs/os-specific/linux/spectrum/rootfs/default.nix new file mode 100644 index 00000000000..56f2d15b103 --- /dev/null +++ b/pkgs/os-specific/linux/spectrum/rootfs/default.nix @@ -0,0 +1,58 @@ +{ runCommandNoCC, writeScript, writeReferencesToFile, makeFontsConf, lib +, dash, execline, s6, s6-rc, s6-portable-utils, s6-linux-utils, s6-linux-init, busybox +, mesa, squashfs-tools-ng, makeDBusConf, connman +}: + +{ services, rcServices ? {}, fonts ? [], path ? [] }: + +let + stage1 = import ./stage1.nix { + inherit writeScript lib + execline s6 s6-rc s6-portable-utils s6-linux-utils s6-linux-init busybox mesa + path; + }; + + makeServicesDir = import ./services.nix { + inherit runCommandNoCC writeScript lib execline; + }; + + makeRcServicesDir = import ./rc-services.nix { + inherit runCommandNoCC lib s6-rc; + }; + + fontsConf = makeFontsConf { fontDirectories = fonts; }; + + squashfs = runCommandNoCC "root-squashfs" {} '' + cd ${rootfs} + ( + grep -v ^${rootfs} ${writeReferencesToFile rootfs} + printf "%s\n" * + ) \ + | xargs tar -cP --owner root:0 --group root:0 --hard-dereference \ + | ${squashfs-tools-ng}/bin/tar2sqfs -c gzip -X level=1 $out + ''; + + rootfs = runCommandNoCC "rootfs" { passthru = { inherit squashfs; }; } '' + mkdir $out + cd $out + + mkdir -p bin sbin dev proc run sys tmp var/lib + ln -s /run var/run + ln -s ${dash}/bin/dash bin/sh + ln -s ${stage1} sbin/init + cp -r ${./etc} etc + chmod u+w etc + ln -s ${makeDBusConf { + suidHelper = "/run/dbus-daemon-launch-helper"; + serviceDirectories = [ connman ]; + }} etc/dbus-1 + + mkdir etc/fonts + ln -s ${fontsConf} etc/fonts/fonts.conf + + touch etc/login.defs + cp -r ${makeServicesDir { inherit services; }} etc/service + cp -r ${makeRcServicesDir { services = rcServices; }} etc/s6-rc + ''; +in +rootfs diff --git a/pkgs/os-specific/linux/spectrum/rootfs/etc/group b/pkgs/os-specific/linux/spectrum/rootfs/etc/group new file mode 100644 index 00000000000..df4940a5516 --- /dev/null +++ b/pkgs/os-specific/linux/spectrum/rootfs/etc/group @@ -0,0 +1,3 @@ +root:x:0:root +messagebus:x:4:messagebus +user:x:1000:user diff --git a/pkgs/os-specific/linux/spectrum/rootfs/etc/passwd b/pkgs/os-specific/linux/spectrum/rootfs/etc/passwd new file mode 100644 index 00000000000..ddb1f854a6e --- /dev/null +++ b/pkgs/os-specific/linux/spectrum/rootfs/etc/passwd @@ -0,0 +1,3 @@ +root:x:0:0:System administrator:/:/bin/sh +messagebus:x:4:4:D-Bus system message bus daemon user:/run/dbus:/bin/sh +user:x:1000:1000:User:/:/bin/sh diff --git a/pkgs/os-specific/linux/spectrum/rootfs/etc/wayfire/wf-shell-defaults.ini b/pkgs/os-specific/linux/spectrum/rootfs/etc/wayfire/wf-shell-defaults.ini new file mode 100644 index 00000000000..7ba621225aa --- /dev/null +++ b/pkgs/os-specific/linux/spectrum/rootfs/etc/wayfire/wf-shell-defaults.ini @@ -0,0 +1,2 @@ +[panel] +widgets_right = diff --git a/pkgs/os-specific/linux/spectrum/rootfs/rc-services.nix b/pkgs/os-specific/linux/spectrum/rootfs/rc-services.nix new file mode 100644 index 00000000000..4c942189c5e --- /dev/null +++ b/pkgs/os-specific/linux/spectrum/rootfs/rc-services.nix @@ -0,0 +1,26 @@ +{ runCommandNoCC, lib, s6-rc }: + +{ services ? [] }: + +let + inherit (lib) concatStrings escapeShellArg mapAttrsToList optionalString; + + source = runCommandNoCC "s6-services-source" {} '' + mkdir $out + ${concatStrings (mapAttrsToList (name: attrs: '' + mkdir $out/${name} + ${concatStrings (mapAttrsToList (key: value: '' + cp ${value} $out/${name}/${key} + '') attrs)} + '') services)} + ''; + + s6RcCompile = { fdhuser ? null }: source: + runCommandNoCC "s6-rc-compile" {} '' + ${s6-rc}/bin/s6-rc-compile \ + ${optionalString (fdhuser != null) "-h ${escapeShellArg fdhuser}"} \ + $out ${source} + ''; +in + +s6RcCompile {} source diff --git a/pkgs/os-specific/linux/spectrum/rootfs/services.nix b/pkgs/os-specific/linux/spectrum/rootfs/services.nix new file mode 100644 index 00000000000..b2b09faa06e --- /dev/null +++ b/pkgs/os-specific/linux/spectrum/rootfs/services.nix @@ -0,0 +1,28 @@ +{ runCommandNoCC, writeScript, lib, execline }: + +{ services ? [] }: + +let + services' = { + ".s6-svscan" = { + finish = writeScript "init-stage3" '' + #! ${execline}/bin/execlineb -P + foreground { s6-nuke -th } + s6-sleep -m -- 2000 + foreground { s6-nuke -k } + wait { } + s6-linux-init-hpr -fr + ''; + } // services.".s6-svscan" or {}; + } // services; +in + +runCommandNoCC "services" {} '' + mkdir $out + ${lib.concatStrings (lib.mapAttrsToList (name: attrs: '' + mkdir $out/${name} + ${lib.concatStrings (lib.mapAttrsToList (key: value: '' + cp ${value} $out/${name}/${key} + '') attrs)} + '') services')} +'' diff --git a/pkgs/os-specific/linux/spectrum/rootfs/stage1.nix b/pkgs/os-specific/linux/spectrum/rootfs/stage1.nix new file mode 100644 index 00000000000..de10d60ffb6 --- /dev/null +++ b/pkgs/os-specific/linux/spectrum/rootfs/stage1.nix @@ -0,0 +1,44 @@ +{ writeScript, lib +, execline, s6, s6-rc, s6-portable-utils, s6-linux-utils, s6-linux-init, busybox, mesa +, path ? [] +}: + +let + path' = path ++ [ + s6 s6-rc s6-portable-utils s6-linux-utils s6-linux-init busybox execline + ]; +in + +writeScript "init-stage1" '' + #! ${execline}/bin/execlineb -P + export PATH ${lib.makeBinPath path'} + ${s6}/bin/s6-setsid -qb -- + + umask 022 + if { s6-mount -t tmpfs -o mode=0755 tmpfs /run } + if { s6-hiercopy /etc/service /run/service } + emptyenv -p + + background { + s6-setsid -- + + if { s6-rc-init -c /etc/s6-rc /run/service } + + if { s6-mkdir -p /run/user/0 /dev/pts /dev/shm } + if { install -o user -g user -d /run/user/1000 } + + if { s6-mount -t devpts -o gid=4,mode=620 none /dev/pts } + if { s6-mount -t tmpfs none /dev/shm } + if { s6-mount -t tmpfs none /var/lib } + if { s6-mount -t proc none /proc } + if { s6-mount -t sysfs none /sys } + + if { s6-ln -s ${mesa.drivers} /run/opengl-driver } + + s6-rc change ok-all + } + + unexport ! + cd /run/service + s6-svscan +'' diff --git a/pkgs/os-specific/linux/spectrum/spectrum-vm/default.nix b/pkgs/os-specific/linux/spectrum/spectrum-vm/default.nix new file mode 100644 index 00000000000..c56d2537c63 --- /dev/null +++ b/pkgs/os-specific/linux/spectrum/spectrum-vm/default.nix @@ -0,0 +1,35 @@ +{ stdenv, lib, makeWrapper, utillinux, crosvm, linux, sys-vms }: + +stdenv.mkDerivation { + name = "spectrum-vm"; + + src = ./spectrum-vm.in; + + nativeBuildInputs = [ makeWrapper ]; + + unpackPhase = '' + cp $src spectrum-vm.in + ''; + + configurePhase = '' + substituteAll spectrum-vm.in spectrum-vm + chmod +x spectrum-vm + ''; + + getopt = "${lib.getBin utillinux}/bin/getopt"; + crosvm = "${lib.getBin crosvm}/bin/crosvm"; + kernel = "${sys-vms.comp.linux}/bzImage"; + rootfs = sys-vms.comp.rootfs.squashfs; + + installPhase = '' + mkdir -p $out/bin + cp spectrum-vm $out/bin + ''; + + meta = with lib; { + description = "Utility for testing Spectrum VM components"; + maintainers = with maintainers; [ qyliss ]; + license = licenses.gpl3Plus; + inherit (crosvm.meta) platforms; + }; +} diff --git a/pkgs/os-specific/linux/spectrum/spectrum-vm/spectrum-vm.in b/pkgs/os-specific/linux/spectrum/spectrum-vm/spectrum-vm.in new file mode 100755 index 00000000000..a72c3896141 --- /dev/null +++ b/pkgs/os-specific/linux/spectrum/spectrum-vm/spectrum-vm.in @@ -0,0 +1,71 @@ +#!@shell@ +set -ue + +ex_usage() { + cat <<EOF +Usage: $(basename "$0") [OPTION]... [-- CROSVM_OPTIONS] + + -c COMMAND shell command to run inside VM + -C, --crosvm PATH path to custom crosvm executable + -k, --kernel PATH path to custom kernel image + -f, --rootfs PATH path to custom root file system image + -- CROSVM_OPTIONS extra arguments to pass to crosvm +EOF + exit "$1" +} + +args="$(@getopt@ -s sh -l crosvm:,help,kernel:,rootfs: -o c:C:hk:f: -- "$@" || exit 1)" +eval set -- "$args" + +command= +crosvm=@crosvm@ +kernel=@kernel@ +rootfs=@rootfs@ + +while : +do + case "$1" in + -c) + shift + command="$1" + shift + ;; + -C|--crosvm) + shift + crosvm="$1" + shift + ;; + -h|--help) + ex_usage 0 + ;; + -k|--kernel) + shift + kernel="$1" + shift + ;; + -f|--rootfs) + shift + rootfs="$1" + shift + ;; + --) + shift + break + ;; + esac +done + +if [ -n "${XDG_RUNTIME_DIR-}" ] +then + set -- -s "$XDG_RUNTIME_DIR" "$@" + if [ -n "${WAYLAND_DISPLAY-}" ] + then set -- --wayland-sock "$XDG_RUNTIME_DIR/$WAYLAND_DISPLAY" "$@" + fi +fi + +exec "$crosvm" run \ + -p init=/sbin/init \ + -p "spectrumcmd=$(printf %s "$command" | base64 -w0)" \ + --root "$rootfs" \ + "$@" \ + "$kernel" diff --git a/pkgs/os-specific/linux/spectrum/testhost/default.nix b/pkgs/os-specific/linux/spectrum/testhost/default.nix new file mode 100644 index 00000000000..de62f0add67 --- /dev/null +++ b/pkgs/os-specific/linux/spectrum/testhost/default.nix @@ -0,0 +1,218 @@ +{ lib, runCommandNoCC, writeScript, writeScriptBin, writeShellScript, writeText +, coreutils, cloud-hypervisor, crosvm, curl, execline, gnutar, gnused, iproute +, iptables, jq, kmod, mktuntap, rsync, s6, s6-rc, sys-vms, utillinux +}: + +let + inherit (lib) concatStrings escapeShellArg makeBinPath mapAttrsToList + optionalString; + + compose2 = f: g: a: b: f (g a b); + + concatMapAttrs = compose2 concatStrings mapAttrsToList; + + makeServicesDir = { services }: + runCommandNoCC "services" {} '' + mkdir $out + ${concatMapAttrs (name: attrs: '' + mkdir $out/${name} + ${concatMapAttrs (key: value: '' + cp -r ${value} $out/${name}/${key} + '') attrs} + '') services} + ''; + + s6RcCompile = { fdhuser ? null }: source: + runCommandNoCC "s6-rc-compile" {} '' + ${s6-rc}/bin/s6-rc-compile \ + ${optionalString (fdhuser != null) "-h ${escapeShellArg fdhuser}"} \ + dest ${source} + tar -C dest -cf $out . + ''; + + compiledRcServicesDir = s6RcCompile {} (makeServicesDir { + services = { + vm-app = { + run = writeScript "app-run" '' + #! ${execline}/bin/execlineb -S0 + # fdclose 0 + + # Checking the return value of the bridge creation is + # important, because if it fails due to the bridge already + # existing that means something else could already be using + # this bridge. + if { ip link add name br0 type bridge } + if { ip link set br0 up } + + # Calculate the MACs for our TAP and the router's TAP. + # MAC address format, by octet: + # + # 0-3 Static OUI for Spectrum + # 4 Most significant bit is used to differentiate + # routers from clients. Other bits are reserved. + # 5-6 Last two octets of client's IP (in 100.64.0.0/16). + # + backtick -i router_mac { + pipeline { printf %.4x ${toString sys-vms.app.vmID} } + sed s/^\\(..\\)\\(..\\)$/0A:B3:EC:80:\\1:\\2/ + } + backtick -i client_mac { + pipeline { printf %.4x ${toString sys-vms.app.vmID} } + sed s/^\\(..\\)\\(..\\)$/0A:B3:EC:00:\\1:\\2/ + } + multisubstitute { + importas -iu router_mac router_mac + importas -iu client_mac client_mac + } + + # Create the net VM end, and attach it to the net VM. + # + # Use a hardcoded name for now because if we use a dynamic + # one iproute2 has no way of telling us the name that was + # chosen: + # https://lore.kernel.org/netdev/20210406134240.wwumpnrzfjbttnmd@eve.qyliss.net/ + define other_tap_name vmtapnet + # Try to delete the device in case the VM was powered off + # (as the finish script wouldn't have been run in that + # case.) Since we check the return value of ip tuntap add, + # in the case of a race condition between deleting the + # device and creating it again, we'll just fail and try + # again. + foreground { ip link delete $other_tap_name } + if { ip tuntap add name $other_tap_name mode tap } + if { ip link set $other_tap_name master br0 } + if { ip link set $other_tap_name up } + if { + pipeline { + jq -n "$ARGS.named" + --arg tap $other_tap_name + --arg mac $router_mac + } + curl -iX PUT + -H "Accept: application/json" + -H "Content-Type: application/json" + --data-binary @- + --unix-socket ../vm-net/env/cloud-hypervisor.sock + http://localhost/api/v1/vm.add-net + } + + mktuntap -pvBi vmtap%d 6 + importas -iu tap_name TUNTAP_NAME + if { ip link set $tap_name master br0 } + if { ip link set $tap_name up } + if { iptables -t nat -A POSTROUTING -o $tap_name -j MASQUERADE } + + ${crosvm}/bin/crosvm run -p init=/sbin/init -p notifyport=''${port} + # --serial type=file,path=/tmp/app.log + --cid 4 + --tap-fd 6,mac=''${client_mac} + --root ${sys-vms.app.rootfs.squashfs} ${sys-vms.app.linux}/bzImage + ''; + finish = writeScript "app-finish" '' + #! ${execline}/bin/execlineb -S0 + # TODO: remove from vm-net + foreground { ip link delete vmtapnet } + ip link delete br0 + ''; + type = writeText "app-type" '' + longrun + ''; + dependencies = writeText "app-dependencies" '' + vm-net + ''; + }; + + vm-net = { + run = writeScript "net-run" '' + #! ${execline}/bin/execlineb -S0 + # This is only necessary for when running s6 from a tty. + # (i.e. when debugging or running the demo). + redirfd -w 0 /dev/null + + define PCI_LOCATION 0000:00:19.0 + define PCI_PATH /sys/bus/pci/devices/''${PCI_LOCATION} + + # Unbind the network device from the driver it's already + # attached to, if any. + foreground { + redirfd -w 1 ''${PCI_PATH}/driver/unbind + printf "%s" $PCI_LOCATION + } + + # Tell the VFIO driver it should support our device. This + # is allowed to fail because it might already know that, in + # which case it'll return EEXIST. + if { modprobe vfio-pci } + backtick -in device_id { + if { dd bs=2 skip=1 count=2 status=none if=''${PCI_PATH}/vendor } + if { printf " " } + dd bs=2 skip=1 count=2 status=none if=''${PCI_PATH}/device + } + importas -iu device_id device_id + foreground { + redirfd -w 1 /sys/bus/pci/drivers/vfio-pci/new_id + printf "%s" $device_id + } + + # Bind the device to the VFIO driver. This is allowed to + # fail because the new_id operation we just tried will have + # bound it automatically for us if it succeeded. In such a + # case, the kernel will return ENODEV (conistency!). + foreground { + redirfd -w 1 /sys/bus/pci/drivers/vfio-pci/bind + printf "%s" $PCI_LOCATION + } + + # Because we allow both new_id and bind to fail, we need to + # manually make sure now that at least one of them succeeded + # and the device is actually attached to the vfio-driver. + if { test -e /sys/bus/pci/drivers/vfio-pci/''${PCI_LOCATION} } + + foreground { mkdir env } + + ${cloud-hypervisor}/bin/cloud-hypervisor + --api-socket env/cloud-hypervisor.sock + --console off + # --serial tty + --cmdline "console=ttyS0 panic=30 root=/dev/vda" + --device path=''${PCI_PATH} + --disk path=${sys-vms.net.rootfs.squashfs},readonly=on + --kernel ${sys-vms.net.linux.dev}/vmlinux + ''; + type = writeText "net-type" '' + longrun + ''; + }; + }; + }); + + servicesDir = makeServicesDir { + services = { + ".s6-svscan" = { + finish = writeShellScript ".s6-svscan-finish" ""; + }; + }; + }; +in + +writeScriptBin "spectrum-testhost" '' + #! ${execline}/bin/execlineb -S0 + export PATH ${makeBinPath [ + coreutils curl execline gnused gnutar iproute iptables jq kmod mktuntap rsync + s6 s6-rc + ]} + + if { redirfd -w 1 /proc/sys/net/ipv4/ip_forward echo 1 } + + importas -iu runtime_dir XDG_RUNTIME_DIR + backtick -in TOP { mktemp -dp $runtime_dir spectrum.XXXXXXXXXX } + importas -iu top TOP + if { echo $top } + if { rsync -r --chmod=Du+w ${servicesDir}/ ''${top}/service } + background { + if { mkdir -p ''${top}/s6-rc/compiled } + if { tar -C ''${top}/s6-rc/compiled -xf ${compiledRcServicesDir} } + s6-rc-init -c ''${top}/s6-rc/compiled -l ''${top}/s6-rc/live ''${top}/service + } + s6-svscan ''${top}/service +'' diff --git a/pkgs/os-specific/linux/spectrum/vm/app/default.nix b/pkgs/os-specific/linux/spectrum/vm/app/default.nix new file mode 100644 index 00000000000..f8ff480932c --- /dev/null +++ b/pkgs/os-specific/linux/spectrum/vm/app/default.nix @@ -0,0 +1,58 @@ +{ runCommand, writeScript, writeText, makeRootfs +, busybox, execline, linux_vm, jq, iproute +}: + +runCommand "vm-app" rec { + linux = linux_vm; + + login = writeScript "login" '' + #! ${execline}/bin/execlineb -s0 + unexport ! + ${busybox}/bin/login -p -f root $@ + ''; + + rootfs = makeRootfs { + rcServices.ok-all = { + type = writeText "ok-all-type" '' + bundle + ''; + contents = writeText "ok-all-contents" '' + net + ''; + }; + + rcServices.net = { + type = writeText "net-type" '' + oneshot + ''; + up = writeText "net-up" '' + backtick -i LOCAL_IP { + pipeline { ip -j link show eth0 } + pipeline { jq -r ".[0].address | split(\":\") | .[4:6] | \"0x\" + .[]" } + xargs printf "100.64.%d.%d" + } + importas -iu LOCAL_IP LOCAL_IP + + if { ip address add ''${LOCAL_IP}/32 dev eth0 } + if { ip link set eth0 up } + if { ip route add 169.254.0.1 dev eth0 } + ip route add default via 169.254.0.1 dev eth0 + ''; + }; + + services.getty.run = writeScript "getty-run" '' + #! ${execline}/bin/execlineb -P + ${busybox}/bin/getty -i -n -l ${login} 38400 ttyS0 + ''; + + path = [ iproute jq ]; + }; + + inherit (rootfs) squashfs; + vmID = 0; +} '' + mkdir $out + echo "$vmID" > $out/vm-id + ln -s $linux/bzImage $out/kernel + ln -s $squashfs $out/squashfs +'' diff --git a/pkgs/os-specific/linux/spectrum/vm/comp/default.nix b/pkgs/os-specific/linux/spectrum/vm/comp/default.nix new file mode 100644 index 00000000000..eb6317ed6f2 --- /dev/null +++ b/pkgs/os-specific/linux/spectrum/vm/comp/default.nix @@ -0,0 +1,73 @@ +{ lib, makeRootfs, runCommand, writeScript, writeText +, busybox, emacs-nox, execline, gcc, linux_vm, s6, sommelier, source-code-pro +, tinywl, westonLite, zsh +}: + +runCommand "vm-comp" rec { + linux = linux_vm; + + path = [ + busybox emacs-nox execline gcc s6 sommelier tinywl westonLite zsh + ]; + + login = writeScript "login" '' + #! ${execline}/bin/execlineb -s0 + unexport ! + ${busybox}/bin/login -p -f root $@ + ''; + + rootfs = makeRootfs { + services.getty.run = writeScript "getty-run" '' + #! ${execline}/bin/execlineb -P + ${busybox}/bin/getty -i -n -l ${login} 38400 ttyS0 + ''; + + rcServices.ok-all = { + type = writeText "ok-all-type" '' + bundle + ''; + contents = writeText "ok-all-contents" '' + compositor + ''; + }; + + rcServices.compositor = { + type = writeText "compositor-type" '' + longrun + ''; + run = writeScript "compositor-run" '' + #! ${execline}/bin/execlineb -S0 + + s6-applyuidgid -u 1000 -g 1000 + + export HOME / + export PATH ${lib.makeBinPath path} + export XDG_RUNTIME_DIR /run/user/1000 + export XKB_DEFAULT_LAYOUT dvorak + + ${sommelier}/bin/sommelier + ${tinywl}/bin/tinywl -s "weston-terminal --shell $(command -v zsh)" + ''; + dependencies = writeText "compositor-dependencies" '' + wl0 + ''; + }; + + rcServices.wl0 = { + type = writeText "wl0-type" '' + oneshot + ''; + up = writeText "wl0-run" '' + chown user /dev/wl0 + ''; + }; + + fonts = [ source-code-pro ]; + }; + + inherit (rootfs) squashfs; +} '' + mkdir $out + ln -s $linux/bzImage $out/kernel + ln -s $squashfs $out/squashfs +'' diff --git a/pkgs/os-specific/linux/spectrum/vm/default.nix b/pkgs/os-specific/linux/spectrum/vm/default.nix new file mode 100644 index 00000000000..f5d591a960a --- /dev/null +++ b/pkgs/os-specific/linux/spectrum/vm/default.nix @@ -0,0 +1,9 @@ +{ callPackage }: + +{ + app = callPackage ./app { }; + + comp = callPackage ./comp { }; + + net = callPackage ./net { }; +} diff --git a/pkgs/os-specific/linux/spectrum/vm/net/default.nix b/pkgs/os-specific/linux/spectrum/vm/net/default.nix new file mode 100644 index 00000000000..5921b62fcf7 --- /dev/null +++ b/pkgs/os-specific/linux/spectrum/vm/net/default.nix @@ -0,0 +1,167 @@ +{ lib, makeRootfs, runCommand, writeScript, writeText +, busybox, connman, dbus, execline, iptables, iproute, jq, linux_vm, mdevd +}: + +runCommand "vm-net" rec { + linux = linux_vm.override { + structuredExtraConfig = with lib.kernel; { + E1000E = yes; + IGB = yes; + PACKET = yes; + + IP_NF_NAT = yes; + IP_NF_IPTABLES = yes; + IP_NF_TARGET_MASQUERADE = yes; + NF_CONNTRACK = yes; + }; + }; + + login = writeScript "login" '' + #! ${execline}/bin/execlineb -s0 + unexport ! + ${busybox}/bin/login -p -f root $@ + ''; + + rootfs = makeRootfs { + rcServices.ok-all = { + type = writeText "ok-all-type" '' + bundle + ''; + contents = writeText "ok-all-contents" '' + mdevd-coldplug + ''; + }; + + rcServices.mdevd = { + type = writeText "mdevd-type" '' + longrun + ''; + run = writeScript "mdevd-run" '' + #! ${execline}/bin/execlineb -P + ${mdevd}/bin/mdevd -D3 -f ${writeText "mdevd.conf" '' + $INTERFACE=.* 0:0 660 ! @${writeScript "interface" '' + #! ${execline}/bin/execlineb -S0 + + multisubstitute { + importas -i DEVPATH DEVPATH + importas -i INTERFACE INTERFACE + } + + ifte + + { + # This interface is connected to another VM. + + # Our IP is encoded in the NIC-specific portion of the + # interface's MAC address. + backtick -i CLIENT_IP { + pipeline { ip -j link show $INTERFACE } + pipeline { jq -r ".[0].address | split(\":\") | .[4:6] | \"0x\" + .[]" } + xargs printf "100.64.%d.%d" + } + importas -iu CLIENT_IP CLIENT_IP + + if { ip address add 169.254.0.1/32 dev $INTERFACE } + if { ip link set $INTERFACE up } + ip route add $CLIENT_IP dev $INTERFACE + } + + { + if { test $INTERFACE != lo } + # This is a physical connection to a network device. + if { iptables -t nat -A POSTROUTING -o $INTERFACE -j MASQUERADE } + s6-rc -u change connman + } + + grep -iq ^0A:B3:EC: /sys/class/net/''${INTERFACE}/address + ''} + ''} + ''; + notification-fd = writeText "mdevd-notification-fd" '' + 3 + ''; + dependencies = writeText "mdevd-dependencies" '' + sysctl + ''; + }; + + rcServices.mdevd-coldplug = { + type = writeText "mdevd-coldplug-type" '' + oneshot + ''; + up = writeText "mdevd-run" '' + ${mdevd}/bin/mdevd-coldplug + ''; + dependencies = writeText "mdevd-coldplug-dependencies" '' + mdevd + ''; + }; + + rcServices.dbus = { + type = writeText "dbus-daemon" '' + longrun + ''; + run = writeScript "dbus-daemon-run" '' + #! ${execline}/bin/execlineb -S0 + foreground { mkdir /run/dbus } + # Busybox cp doesn't have -n to avoid copying to paths that + # already exist, but we can abuse -u for the same effect, + # since every file in the store is from Jan 1 1970. + foreground { cp -u ${dbus}/libexec/dbus-daemon-launch-helper /run } + foreground { chgrp messagebus /run/dbus-daemon-launch-helper } + foreground { chmod 4550 /run/dbus-daemon-launch-helper } + ${dbus}/bin/dbus-daemon + --nofork --nosyslog --nopidfile --config-file=/etc/dbus-1/system.conf + ''; + }; + + rcServices.connman = { + type = writeText "connman-type" '' + longrun + ''; + run = writeScript "connman-run" '' + #! ${execline}/bin/execlineb -S0 + backtick -in HARDWARE_INTERFACES { + pipeline { + find -L /sys/class/net -mindepth 2 -maxdepth 2 -name address -print0 + } + + # Filter out other VMs and the loopback device. + pipeline { xargs -0 grep -iL ^\\(0A:B3:EC:\\|00:00:00:00:00:00$\\) } + + # Extract the interface names from the address file paths. + awk -F/ "{if (NR > 1) printf \",\"; printf \"%s\", $5}" + } + importas -iu HARDWARE_INTERFACES HARDWARE_INTERFACES + + ${connman}/bin/connmand -ni $HARDWARE_INTERFACES + ''; + dependencies = writeText "connman-dependencies" '' + dbus + ''; + }; + + rcServices.sysctl = { + type = writeText "sysctl-type" '' + oneshot + ''; + up = writeText "sysctl-up" '' + redirfd -w 1 /proc/sys/net/ipv4/ip_forward + echo 1 + ''; + }; + + services.getty.run = writeScript "getty-run" '' + #! ${execline}/bin/execlineb -P + ${busybox}/bin/getty -i -n -l ${login} 38400 ttyS0 + ''; + + path = [ iproute iptables jq ]; + }; + + inherit (rootfs) squashfs; +} '' + mkdir $out + ln -s $linux/bzImage $out/kernel + ln -s $squashfs $out/squashfs +'' |