diff --git a/nixos-modules/genode-core.nix b/nixos-modules/genode-core.nix
index 16cebb1..6447e2c 100644
--- a/nixos-modules/genode-core.nix
+++ b/nixos-modules/genode-core.nix
@@ -25,14 +25,22 @@ in {
prefix = mkOption {
type = types.str;
example = "hw-pc-";
+ description = "String prefix signifying the Genode core in use.";
};
supportedSystems = mkOption {
type = types.listOf types.str;
example = [ "i686-genode" "x86_64-genode" ];
+ description = "Hardware supported by this core.";
};
- basePackages = mkOption { type = types.listOf types.package; };
+ basePackages = mkOption {
+ type = types.listOf types.package;
+ description = ''
+ List of packages to make availabe before the Nix store is ready.
+ These are baked into .
+ '';
+ };
children = mkOption {
type = with types;
@@ -57,17 +65,6 @@ in {
boot = {
- kernel = mkOption {
- type = types.path;
- default = "${pkgs.genodePackages.bender}/bender";
- };
-
- initrd = mkOption {
- type = types.str;
- default = "${pkgs.genodePackages.bender}/bender";
- description = "Path to an image or a command-line arguments";
- };
-
configFile = mkOption {
type = types.path;
description = ''
@@ -84,9 +81,22 @@ in {
romModules = mkOption {
type = types.attrsOf types.path;
+ default = { };
description = "Attr set of initial ROM modules";
};
+ storeFsUuid = mkOption {
+ type = types.str;
+ default = import ./store-fs-uuid;
+ description = "Custom partition type of the nix-store file-system.";
+ };
+
+ storePartUuid = mkOption {
+ type = types.str;
+ default = import ./partition-type;
+ description = "Custom partition type of the nix-store file-system.";
+ };
+
storeBackend = mkOption {
type = types.enum [ "tarball" "usb" ]; # "parent"?
default = "tarball";
@@ -119,7 +129,6 @@ in {
storePaths = mkOption {
type = with types; listOf package;
- example = literalExample "[ pkgs.genodePackages.vfs_lwp ]";
description = ''
Derivations to be included in the Nix store in the generated boot image.
'';
@@ -148,20 +157,17 @@ in {
};
mergeManifests = inputs:
- localPackages.writeTextFile {
- name = "manifest.dhall";
- text = with builtins;
- let
- f = head: input:
- if hasAttr "manifest" input then
- ''
- ${head}, { mapKey = "${
- lib.getName input
- }", mapValue = ${input.manifest} }''
- else
- abort "${input.pname} does not have a manifest";
- in (foldl' f "[" inputs) + "]";
- };
+ with builtins;
+ let
+ f = head: input:
+ if hasAttr "manifest" input then
+ ''
+ ${head}, { mapKey = "${
+ lib.getName input
+ }", mapValue = ${input.manifest} }''
+ else
+ abort "${input.pname} does not have a manifest";
+ in (foldl' f "[" inputs) + "]";
romDirectories = filterAttrs (_: value: value != null) (mapAttrs
(name: value:
@@ -181,51 +187,85 @@ in {
message = "invalid Genode core for this system";
}];
- genode.core.children.store_fs.configFile = let
+ genode.core.basePackages =
+ lib.optional (config.genode.boot.storeBackend == "usb")
+ pkgs.genodePackages.part_block;
- storeVfsConfig = {
- tarball = ''
- VFS.vfs [ VFS.leafAttrs "tar" (toMap { name = "${config.system.build.tarball.fileName}.tar" }) ]
- '';
- usb = ''
- VFS.vfs [ VFS.leafAttrs "rump" (toMap { fs = "ext2fs", ram="12M" }) ]
- '';
- }.${config.genode.boot.storeBackend};
+ genode.core.children =
+ # Component to steer the store_fs to a specific partition
+ (if config.genode.boot.storeBackend == "usb" then {
+ part_block.configFile = builtins.toFile "part_block.dhall" ''
+ let Genode = env:DHALL_GENODE
- storeResources = {
- tarball = "Init.Resources.default";
- usb = "Init.Resources::{ caps = 256, ram = Genode.units.MiB 16 }";
- }.${config.genode.boot.storeBackend};
+ let Init = Genode.Init
- in builtins.toFile "store_fs.dhall" ''
- let Genode = env:DHALL_GENODE
-
- let Init = Genode.Init
-
- let VFS = Genode.VFS
-
- in Init.Child.flat
- Init.Child.Attributes::{
- , binary = "vfs"
- , resources = ${storeResources}
- , config = Init.Config::{
- , content = [ ${storeVfsConfig} ]
- , policies =
- [ Init.Config.Policy::{
- , service = "File_system"
- , label = Init.LabelSelector.suffix "nix-store"
- , attributes = toMap { root = "/nix/store" }
+ in Init.Child.flat
+ Init.Child.Attributes::{
+ , binary = "part_block"
+ , resources = Init.Resources::{ ram = Genode.units.MiB 8 }
+ , config = Init.Config::{
+ , attributes = toMap { ignore_mbr = "yes" }
+ , policies =
+ [ Init.Config.Policy::{
+ , service = "Block"
+ , label = Init.LabelSelector.prefix "store_fs"
+ , attributes = toMap
+ { partition = "1"
+ , writeable = "yes"
+ , TODO = "select by partition UUID"
+ }
+ }
+ ]
}
- , Init.Config.Policy::{
- , service = "File_system"
- , label = Init.LabelSelector.prefix "store_rom"
- , attributes = toMap { root = "/" }
+ }
+ '';
+ } else
+ { }) // {
+ store_fs.configFile = let
+
+ storeVfsConfig = {
+ tarball = ''
+ VFS.vfs [ VFS.leafAttrs "tar" (toMap { name = "${config.system.build.tarball.fileName}.tar" }) ]
+ '';
+ usb = ''
+ VFS.vfs [ VFS.leafAttrs "rump" (toMap { fs = "ext2fs", ram="12M" }) ]
+ '';
+ }.${config.genode.boot.storeBackend};
+
+ storeResources = {
+ tarball = "Init.Resources.default";
+ usb = "Init.Resources::{ caps = 256, ram = Genode.units.MiB 16 }";
+ }.${config.genode.boot.storeBackend};
+
+ in builtins.toFile "store_fs.dhall" ''
+ let Genode = env:DHALL_GENODE
+
+ let Init = Genode.Init
+
+ let VFS = Genode.VFS
+
+ in Init.Child.flat
+ Init.Child.Attributes::{
+ , binary = "vfs"
+ , resources = ${storeResources}
+ , config = Init.Config::{
+ , content = [ ${storeVfsConfig} ]
+ , policies =
+ [ Init.Config.Policy::{
+ , service = "File_system"
+ , label = Init.LabelSelector.suffix "nix-store"
+ , attributes = toMap { root = "/nix/store" }
+ }
+ , Init.Config.Policy::{
+ , service = "File_system"
+ , label = Init.LabelSelector.prefix "store_rom"
+ , attributes = toMap { root = "/" }
+ }
+ ]
+ }
}
- ]
- }
- , provides = [ "File_system" ]
- }
- '';
+ '';
+ };
genode.boot.configFile = let
tarball =
@@ -236,9 +276,21 @@ in {
usb = [ pkgs.genodePackages.rump ];
}.${config.genode.boot.storeBackend};
- manifest = mergeManifests (map addManifest (with pkgs.genodePackages;
- config.genode.core.basePackages ++ storeBackendInputs
- ++ [ init cached_fs_rom jitter_sponge report_rom vfs ]));
+ coreInputs = with builtins;
+ concatMap (getAttr "inputs") (attrValues config.genode.core.children);
+
+ manifest =
+ # Manifests are Dhall metadata to be attached to every
+ # package to be used for dynamically buildings enviroments
+ # using Dhall expressions. Probably not worth pursuing.
+ pkgs.writeText "manifest.dhall" (mergeManifests (map addManifest
+ (with pkgs.genodePackages;
+ config.genode.core.basePackages ++ storeBackendInputs
+ ++ [ init cached_fs_rom jitter_sponge report_rom vfs ]
+ ++ coreInputs)) + ''# [ { mapKey = "romModules", mapValue = [ ''
+ + (toString
+ (mapAttrsToList (k: v: '', { mapKey = "${k}", mapValue = "${v}" }'')
+ config.genode.boot.romModules)) + " ] } ]");
storeRomPolicies = mapAttrsToList
(name: value: '', { mapKey = "${name}", mapValue = "${value}" }'')
@@ -253,7 +305,7 @@ in {
Genode.Init.LabelSelector.Type.Partial
{ prefix = Some "nixos -> ${name}", suffix = Some "${suffix}" }
}
- , route = Genode.Init.Route.parent (Some "${suffix}")
+ , route = Genode.Init.Route.parentLabel "${suffix}"
}
'') value.coreROMs) config.genode.init.children));
@@ -282,8 +334,8 @@ in {
EOF
'';
- genode.boot.storePaths = [ config.genode.init.configFile ]
- ++ (builtins.attrValues romDirectories);
+ genode.boot.storePaths = with builtins;
+ [ config.genode.init.configFile ] ++ (attrValues romDirectories);
# Create the tarball of the store to live in core ROM
system.build.tarball =
@@ -316,17 +368,21 @@ in {
'';
system.build.bootDriveImage = let
- storeFsImage = pkgs.callPackage ./lib/make-ext2-fs.nix {
- inherit (config.genode.boot) storePaths;
- inherit (config.system.build) qemu;
- volumeLabel = "NIXOS_GENODE";
+ espImage = import ./lib/make-esp-fs.nix { inherit config pkgs; };
+ storeFsImage =
+ pkgs.callPackage ./lib/make-ext2-fs.nix { inherit config pkgs; };
+ bootDriveImage = import ./lib/make-bootable-image.nix {
+ inherit config pkgs espImage storeFsImage;
};
- in storeFsImage;
+ in bootDriveImage;
+
+ # virtualisation.useEFIBoot = config.genode.boot.storeBackend == "usb";
virtualisation.qemu.options =
lib.optionals (config.genode.boot.storeBackend == "usb") [
- "-usb"
+ "-bios ${pkgs.buildPackages.OVMF.fd}/FV/OVMF.fd"
"-drive id=usbdisk,file=${config.system.build.bootDriveImage},if=none,readonly"
+ "-usb"
"-device usb-storage,drive=usbdisk"
];
diff --git a/nixos-modules/lib/make-bootable-image.nix b/nixos-modules/lib/make-bootable-image.nix
new file mode 100644
index 0000000..3a12cbf
--- /dev/null
+++ b/nixos-modules/lib/make-bootable-image.nix
@@ -0,0 +1,69 @@
+# Builds a compressed EFI System Partition image
+{ config, pkgs, espImage, storeFsImage }:
+
+pkgs.stdenv.mkDerivation {
+ name = "boot.qcow2";
+
+ nativeBuildInputs = with pkgs.buildPackages; [
+ config.system.build.qemu
+ utillinux
+ zstd
+ ];
+
+ buildCommand = ''
+ img=./temp.raw
+ blockSize=512
+ sectorSize=$(( $blockSize * 1 ))
+ imgBytes=0
+
+ espSectorOffset=2048
+ esbByteOffset=$(( $espSectorOffset * $sectorSize ))
+
+ # Pad the front of the image
+ echo "Pad front of image with " $esbByteOffset " bytes"
+ truncate --size=$esbByteOffset $img
+
+ # Concatentenate the ESP
+ echo "Concatenate ESP ${espImage}"
+ zstdcat ${espImage} >> $img
+
+ imgBytes=$(stat --format=%s $img)
+ echo "Image is $(( $imgBytes >> 20 )) MiB with ESP partition"
+
+ nixSectorOffset=$(( ($imgBytes + $sectorSize - 1) / $sectorSize ))
+ nixByteOffset=$(( $nixSectorOffset * $sectorSize ))
+
+ # Pad the ESP
+ echo "Pad end of ESP with " $(( $nixByteOffset - $imgBytes )) " bytes"
+ truncate --size=$nixByteOffset $img
+
+ # Concatenate the nix partition
+ echo "Concatenate store ${storeFsImage}"
+ zstdcat ${storeFsImage} >> $img
+
+ imgBytes=$(stat --format=%s $img)
+ echo "Image is $(( $imgBytes >> 20 )) MiB with store partition"
+
+ endSectorOffset=$(( ($(stat --format=%s $img) + $sectorSize + 1) / $sectorSize ))
+ endByteOffset=$(( $endSectorOffset * $sectorSize ))
+
+ # Pad the end of the image
+ echo "Pad end of store with $(( $endByteOffset - $imgBytes )) bytes"
+ truncate --size=$endByteOffset $img
+
+ imgBytes=$(stat --format=%s $img)
+ echo "Image is $(( $imgBytes >> 20 )) MiB with final padding"
+
+ efiUuid=C12A7328-F81F-11D2-BA4B-00A0C93EC93B
+
+ # Create the partition table
+ sfdisk $img < embedded.cfg
+ insmod configfile
+ insmod efi_gop
+ insmod efi_uga
+ insmod ext2
+ insmod normal
+ insmod part_gpt
+ insmod search_fs_uuid
+ search.fs_uuid ${config.genode.boot.storeFsUuid} root
+ set prefix=($root)/boot/grub
+ configfile /boot/grub/grub.cfg
+ EOF
+
+ grub-script-check embedded.cfg
+
+ ${grub'}/bin/grub-mkimage \
+ --config=embedded.cfg \
+ --output=$bootdir/boot${targetArch}.efi \
+ --prefix=/boot/grub \
+ --format=${grub'.grubTarget} \
+ $MODULES
+
+ # Make the ESP image twice as large as necessary
+ imageBytes=$(du --summarize --block-size=4096 --total $bootdir | tail -1 | awk '{ print int($1 * 8192) }')
+
+ truncate --size=$imageBytes $img
+ mkfs.vfat -n EFIBOOT --invariant $img
+ mcopy -sv -i $img EFI ::
+ fsck.vfat -nv $img
+
+ zstd --verbose --no-progress ./$img -o $out
+ '';
+}
diff --git a/nixos-modules/lib/make-ext2-fs.nix b/nixos-modules/lib/make-ext2-fs.nix
index 78b273c..b2dc092 100644
--- a/nixos-modules/lib/make-ext2-fs.nix
+++ b/nixos-modules/lib/make-ext2-fs.nix
@@ -1,83 +1,77 @@
-# Builds an ext2 image containing a populated /nix/store with the closure
-# of store paths passed in the storePaths parameter, in addition to the
-# contents of a directory that can be populated with commands. The
-# generated image is sized to only fit its contents, with the expectation
-# that a script resizes the filesystem at boot time.
-{ pkgs
-, lib
-# List of derivations to be included
-, storePaths
-# Shell commands to populate the ./files directory.
-# All files in that directory are copied to the root of the FS.
-, populateImageCommands ? ""
-, volumeLabel
-, uuid ? "44444444-4444-4444-8888-888888888888"
-, e2fsprogs
-, libfaketime
-, perl
-, fakeroot
-, qemu
-}:
+{ config, pkgs }:
let
- sdClosureInfo = pkgs.buildPackages.closureInfo { rootPaths = storePaths; };
-in
-pkgs.stdenv.mkDerivation {
- name = "ext2-fs.qcow2";
+ grub' = pkgs.buildPackages.grub2_efi;
+ sdClosureInfo = pkgs.buildPackages.closureInfo {
+ rootPaths = config.genode.boot.storePaths;
+ };
+in pkgs.stdenv.mkDerivation {
+ name = "ext2-fs.img.zstd";
- nativeBuildInputs = [ e2fsprogs.bin libfaketime perl fakeroot qemu ];
+ nativeBuildInputs = with pkgs.buildPackages; [
+ e2fsprogs.bin
+ grub'
+ libfaketime
+ perl
+ fakeroot
+ zstd
+ ];
- buildCommand =
- ''
- img=temp.raw
- (
- mkdir -p ./files
- ${populateImageCommands}
- )
+ buildCommand = ''
+ img=temp.raw
- echo "Preparing store paths for image..."
+ # Create nix/store before copying path
+ mkdir -p ./rootImage/boot/grub ./rootImage/nix/store
- # Create nix/store before copying path
- mkdir -p ./rootImage/nix/store
+ cat < ./rootImage/boot/grub/grub.cfg
+ set timeout=3
+ set default=0
+ set gfxpayload=auto
- xargs -I % cp -a --reflink=auto % -t ./rootImage/nix/store/ < ${sdClosureInfo}/store-paths
- (
- GLOBIGNORE=".:.."
- shopt -u dotglob
+ ${config.boot.loader.grub.extraEntries}
+ EOF
- for f in ./files/*; do
- cp -a --reflink=auto -t ./rootImage/ "$f"
- done
- )
+ grub-script-check ./rootImage/boot/grub/grub.cfg
- # Also include a manifest of the closures in a format suitable for nix-store --load-db
- cp ${sdClosureInfo}/registration ./rootImage/nix-path-registration
+ xargs -I % cp -a --reflink=auto % -t ./rootImage/nix/store/ < ${sdClosureInfo}/store-paths
+ (
+ GLOBIGNORE=".:.."
+ shopt -u dotglob
- # Make a crude approximation of the size of the target image.
- # If the script starts failing, increase the fudge factors here.
- numInodes=$(find ./rootImage | wc -l)
- numDataBlocks=$(du -s -c -B 4096 --apparent-size ./rootImage | tail -1 | awk '{ print int($1 * 1.10) }')
- bytes=$((2 * 4096 * $numInodes + 4096 * $numDataBlocks))
- echo "Creating an EXT2 image of $bytes bytes (numInodes=$numInodes, numDataBlocks=$numDataBlocks)"
+ for f in ./files/*; do
+ cp -a --reflink=auto -t ./rootImage/ "$f"
+ done
+ )
- truncate -s $bytes $img
+ # Also include a manifest of the closures in a format suitable for nix-store --load-db
+ cp ${sdClosureInfo}/registration ./rootImage/nix-path-registration
- faketime -f "1970-01-01 00:00:01" fakeroot mkfs.ext2 -L ${volumeLabel} -U ${uuid} -d ./rootImage $img
+ # Make a crude approximation of the size of the target image.
+ # If the script starts failing, increase the fudge factors here.
+ numInodes=$(find ./rootImage | wc -l)
+ numDataBlocks=$(du -s -c -B 4096 --apparent-size ./rootImage | tail -1 | awk '{ print int($1 * 1.10) }')
+ bytes=$((2 * 4096 * $numInodes + 4096 * $numDataBlocks))
+ echo "Creating an EXT2 image of $bytes bytes (numInodes=$numInodes, numDataBlocks=$numDataBlocks)"
- export EXT2FS_NO_MTAB_OK=yes
- # I have ended up with corrupted images sometimes, I suspect that happens when the build machine's disk gets full during the build.
- if ! fsck.ext2 -n -f $img; then
- echo "--- Fsck failed for EXT2 image of $bytes bytes (numInodes=$numInodes, numDataBlocks=$numDataBlocks) ---"
- cat errorlog
- return 1
- fi
+ truncate --size=$bytes $img
- echo "Resizing to minimum allowed size"
- resize2fs -M $img
+ faketime -f "1970-01-01 00:00:01" fakeroot mkfs.ext2 -L NIXOS_GENODE -U ${config.genode.boot.storeFsUuid} -d ./rootImage $img
- # And a final fsck, because of the previous truncating.
- fsck.ext2 -n -f $img
+ export EXT2FS_NO_MTAB_OK=yes
+ # I have ended up with corrupted images sometimes, I suspect that happens when the build machine's disk gets full during the build.
+ if ! fsck.ext2 -n -f $img; then
+ echo "--- Fsck failed for EXT2 image of $bytes bytes (numInodes=$numInodes, numDataBlocks=$numDataBlocks) ---"
+ cat errorlog
+ return 1
+ fi
- qemu-img convert $img $out
- '';
+ echo "Resizing to minimum allowed size"
+ resize2fs -M $img
+
+ # And a final fsck, because of the previous truncating.
+ fsck.ext2 -n -f $img
+
+ # Compress to store
+ zstd --verbose --no-progress ./$img -o $out
+ '';
}
diff --git a/nixos-modules/nova.nix b/nixos-modules/nova.nix
index da52d0e..0ba09c4 100644
--- a/nixos-modules/nova.nix
+++ b/nixos-modules/nova.nix
@@ -7,6 +7,17 @@ let
inherit (config.nixpkgs) system localSystem crossSystem;
inherit pkgs;
};
+
+ bootDir = pkgs.runCommand "${config.system.name}-bootdir" { } ''
+ mkdir $out
+ gz() {
+ gzip --keep --to-stdout "$1" > "$2"
+ }
+ gz ${pkgs.genodePackages.genodeSources}/tool/boot/bender $out/bender.gz
+ gz ${pkgs.genodePackages.NOVA}/hypervisor-x86_64 $out/hypervisor.gz
+ gz ${config.genode.boot.image}/image.elf $out/image.elf.gz
+ '';
+
in {
genode.core = {
prefix = "nova-";
@@ -15,13 +26,27 @@ in {
};
genode.boot = {
-
- initrd =
- "'${pkgs.genodePackages.NOVA}/hypervisor-x86_64 arg=iommu novpid serial,${config.genode.boot.image}/image.elf'";
-
image =
utils.novaImage config.system.name { } config.genode.boot.configFile;
-
};
+ genode.boot.storePaths =
+ lib.optional (config.genode.boot.storeBackend == "usb") bootDir;
+
+ virtualisation.qemu.options =
+ lib.optionals (!config.virtualisation.useBootLoader) [
+ "-kernel '${pkgs.genodePackages.bender}/share/bender/bender'"
+ "-initrd '${pkgs.genodePackages.NOVA}/hypervisor-x86_64 arg=iommu logmem novpid serial,${config.genode.boot.image}/image.elf'"
+ ];
+
+ boot.loader.grub.extraEntries = ''
+ menuentry 'Genode on NOVA' {
+ insmod multiboot2
+ insmod gzio
+ multiboot2 ${bootDir}/bender.gz serial_fallback
+ module2 ${bootDir}/hypervisor.gz hypervisor iommu logmem novga novpid serial
+ module2 ${bootDir}/image.elf.gz image.elf
+ }
+ '';
+
}
diff --git a/nixos-modules/partition-type b/nixos-modules/partition-type
new file mode 100644
index 0000000..2cca25d
--- /dev/null
+++ b/nixos-modules/partition-type
@@ -0,0 +1 @@
+"24b69406-18a1-428d-908e-d21a1437122c"
diff --git a/nixos-modules/qemu-vm.nix b/nixos-modules/qemu-vm.nix
index 9f91cb0..d0b0feb 100644
--- a/nixos-modules/qemu-vm.nix
+++ b/nixos-modules/qemu-vm.nix
@@ -83,9 +83,9 @@ let
imap1 (idx: drive: drive // { device = driveDeviceName idx; });
efiPrefix = if (pkgs.stdenv.isi686 || pkgs.stdenv.isx86_64) then
- "${pkgs.OVMF.fd}/FV/OVMF"
+ "${pkgs.buildPackages.OVMF.fd}/FV/OVMF"
else if pkgs.stdenv.isAarch64 then
- "${pkgs.OVMF.fd}/FV/AAVMF"
+ "${pkgs.buildPackages.OVMF.fd}/FV/AAVMF"
else
throw "No EFI firmware available for platform";
efiFirmware = "${efiPrefix}_CODE.fd";
@@ -516,10 +516,6 @@ in {
"-device usb-kbd"
"-device usb-tablet"
])
- (mkIf (!cfg.useBootLoader) [
- "-kernel ${config.genode.boot.kernel}"
- "-initrd ${config.genode.boot.initrd}"
- ])
(mkIf cfg.useEFIBoot [
"-drive if=pflash,format=raw,unit=0,readonly,file=${efiFirmware}"
"-drive if=pflash,format=raw,unit=1,file=$NIX_EFI_VARS"
diff --git a/nixos-modules/store-fs-uuid b/nixos-modules/store-fs-uuid
new file mode 100644
index 0000000..7325548
--- /dev/null
+++ b/nixos-modules/store-fs-uuid
@@ -0,0 +1 @@
+"9668f8dd-d9a0-4398-a55a-0d499d5e5cbb"