diff --git a/nixos-modules/genode-core.nix b/nixos-modules/genode-core.nix
index ab0fee3..16cebb1 100644
--- a/nixos-modules/genode-core.nix
+++ b/nixos-modules/genode-core.nix
@@ -1,9 +1,25 @@
{ config, pkgs, lib, modulesPath, ... }:
with lib;
-let localPackages = pkgs.buildPackages;
+let
+ localPackages = pkgs.buildPackages;
+ coreROMs = mkOption {
+ type = with types; listOf str;
+ default = [ ];
+ description = ''
+ List of label suffixes that when matched against
+ ROM requests shall be forwared to the core.
+ '';
+ example = [ "platform_info" ];
+ };
+ inputs = mkOption {
+ description = "List of packages to build a ROM store with.";
+ default = [ ];
+ type = types.listOf types.package;
+ };
in {
options.genode = {
+
core = {
prefix = mkOption {
@@ -18,6 +34,25 @@ in {
basePackages = mkOption { type = types.listOf types.package; };
+ children = mkOption {
+ type = with types;
+ attrsOf (submodule {
+ options = {
+ inherit coreROMs inputs;
+ configFile = mkOption {
+ type = types.path;
+ description = ''
+ Set of children at the lowest init level, these children must not
+ have any dependency on a Nix store.
+ Configuration format is a Dhall configuration of type
+ Genode.Init.Child.Type.
+ See https://git.sr.ht/~ehmry/dhall-genode/tree/master/Init/Child/Type
+ '';
+ };
+ };
+ });
+ };
+
};
boot = {
@@ -52,6 +87,44 @@ in {
description = "Attr set of initial ROM modules";
};
+ storeBackend = mkOption {
+ type = types.enum [ "tarball" "usb" ]; # "parent"?
+ default = "tarball";
+ description = ''
+ Backend for the initial /nix/store file-system.
+
+
+
+
+ tarball
+
+
+ An in-memory tarball.
+
+
+
+
+
+ usb
+
+
+ An EXT2 file-system backed by USB storage.
+
+
+
+
+
+ '';
+ };
+
+ 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.
+ '';
+ };
+
};
};
@@ -108,18 +181,64 @@ in {
message = "invalid Genode core for this system";
}];
+ genode.core.children.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 =
"${config.system.build.tarball}/tarball/${config.system.build.tarball.fileName}.tar";
- manifest = mergeManifests (map addManifest
- (config.genode.core.basePackages ++ [ config.system.build.tarball ]
- ++ (with pkgs.genodePackages; [
- init
- cached_fs_rom
- jitter_sponge
- report_rom
- vfs
- ])));
+
+ storeBackendInputs = {
+ tarball = [ config.system.build.tarball ];
+ 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 ]));
storeRomPolicies = mapAttrsToList
(name: value: '', { mapKey = "${name}", mapValue = "${value}" }'')
@@ -138,19 +257,34 @@ in {
}
'') value.coreROMs) config.genode.init.children));
+ extraCoreChildren = "[ ${
+ toString (lib.mapAttrsToList (name: value:
+ '', { mapKey = "${name}", mapValue = ${value.configFile} }'')
+ config.genode.core.children)
+ } ]";
+
in localPackages.runCommand "boot.dhall" { } ''
cat > $out << EOF
let Genode = env:DHALL_GENODE in
+ let VFS = Genode.VFS
+ let XML = Genode.Prelude.XML
+ in
${./store-wrapper.dhall}
- (${config.genode.init.configFile})
- "${config.system.build.tarball.fileName}.tar"
- $(stat --format '%s' ${tarball})
- ([${toString storeRomPolicies} ] : Genode.Prelude.Map.Type Text Text)
- ([${extraRoutes} ] : List Genode.Init.ServiceRoute.Type)
- ${manifest}
+ { extraCoreChildren = ${extraCoreChildren}
+ , subinit = ${config.genode.init.configFile}
+ , storeSize = $(stat --format '%s' ${tarball})
+ , storeRomPolicies = [${
+ toString storeRomPolicies
+ } ] : Genode.Prelude.Map.Type Text Text
+ , routes = [${extraRoutes} ] : List Genode.Init.ServiceRoute.Type
+ , bootManifest = ${manifest}
+ }
EOF
'';
+ genode.boot.storePaths = [ config.genode.init.configFile ]
+ ++ (builtins.attrValues romDirectories);
+
# Create the tarball of the store to live in core ROM
system.build.tarball =
pkgs.callPackage "${modulesPath}/../lib/make-system-tarball.nix" {
@@ -181,6 +315,21 @@ in {
xmllint --noout $out
'';
+ 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";
+ };
+ in storeFsImage;
+
+ virtualisation.qemu.options =
+ lib.optionals (config.genode.boot.storeBackend == "usb") [
+ "-usb"
+ "-drive id=usbdisk,file=${config.system.build.bootDriveImage},if=none,readonly"
+ "-device usb-storage,drive=usbdisk"
+ ];
+
};
}
diff --git a/nixos-modules/hardware.nix b/nixos-modules/hardware.nix
index c2d7b24..9ad92c3 100644
--- a/nixos-modules/hardware.nix
+++ b/nixos-modules/hardware.nix
@@ -32,6 +32,9 @@ with lib;
hardware.usb.genode.enable = lib.mkEnableOption "USB driver";
+ hardware.usb.genode.storage.enable =
+ lib.mkEnableOption "USB mass storage driver";
+
};
config = {
@@ -49,13 +52,18 @@ with lib;
in lib.mapAttrsToList addrCheck config.networking.interfaces
++ lib.mapAttrsToList routeCheck config.networking.interfaces;
+ hardware.usb.genode.storage.enable = config.genode.boot.storeBackend
+ == "usb";
+
+ hardware.usb.genode.enable = config.hardware.usb.genode.storage.enable;
+
hardware.genode.platform.policies = lib.lists.imap0 (i: name:
builtins.toFile (name + ".platform-policy.dhall") ''
let Genode = env:DHALL_GENODE
in Genode.Init.Config.Policy::{
, service = "Platform"
- , label = Genode.Init.LabelSelector.prefix "${name}.driver"
+ , label = Genode.Init.LabelSelector.prefix "nixos -> ${name}.driver"
, content =
[ Genode.Prelude.XML.leaf
{ name = "pci"
@@ -66,12 +74,25 @@ with lib;
}
]
}
- '') (builtins.attrNames config.networking.interfaces);
+ '') (builtins.attrNames config.networking.interfaces)
+ ++ lib.optional config.hardware.usb.genode.enable
+ (builtins.toFile ("usb.platform-policy.dhall") ''
+ let Genode = env:DHALL_GENODE
- genode.core.basePackages = with pkgs.genodePackages; [
- acpi_drv
- platform_drv
- ];
+ in Genode.Init.Config.Policy::{
+ , service = "Platform"
+ , label = Genode.Init.LabelSelector.prefix "usb_drv"
+ , content =
+ [ Genode.Prelude.XML.leaf
+ { name = "pci", attributes = toMap { class = "USB" } }
+ ]
+ }
+ '');
+
+ genode.core.basePackages = with pkgs.genodePackages;
+ [ acpi_drv platform_drv ]
+ ++ lib.optional config.hardware.usb.genode.enable
+ pkgs.genodePackages.usb_drv;
genode.init.children = let
@@ -110,7 +131,10 @@ with lib;
, caps = 128
, ram = Genode.units.MiB 4
}
- , routes = [ Init.ServiceRoute.parent "IO_MEM" ]
+ , routes = [
+ , Init.ServiceRoute.parent "IO_MEM"
+ , Init.ServiceRoute.parent "Platform"
+ ]
, config = Init.Config::{
, attributes = toMap { verbose = "true" }
, policies = ${policies}
@@ -201,7 +225,9 @@ with lib;
};
}) config.networking.interfaces;
- in lib.filterAttrs (n: v: v != null) (nics // sockets // {
+ in lib.filterAttrs (n: v: v != null) (nics // sockets);
+
+ genode.core.children = {
acpi_drv = {
coreROMs = [ "acpi_drv" ];
@@ -261,33 +287,52 @@ with lib;
}
'';
};
+ } // (if config.hardware.usb.genode.enable then {
- usb_drv = if config.hardware.usb.genode.enable then {
- inputs = [ pkgs.genodePackages.usb_drv ];
- configFile = pkgs.writeText "${name'}.dhall" ''
+ usb_drv = {
+ coreROMs = [ "usb_drv" ];
+ configFile = builtins.toFile "usb_drv.dhall" ''
let Genode = env:DHALL_GENODE
let XML = Genode.Prelude.XML
let Init = Genode.Init
+ let storageEnable = ${
+ if config.hardware.usb.genode.storage.enable then
+ "True"
+ else
+ "False"
+ }
+
in Init.Child.flat
Init.Child.Attributes::{
, binary = "usb_drv"
- , provides = [ "Usb" ]
+ , provides = [ "Block", "Usb" ]
, resources = Init.Resources::{ caps = 256, ram = Genode.units.MiB 12 }
, routes = [ Init.ServiceRoute.parent "IO_MEM" ]
, config = Init.Config::{
, attributes = toMap { uhci = "yes", ehci = "yes", xhci = "yes" }
, content =
- [ XML.leaf { name = "raw", attributes = XML.emptyAttributes } ]
+ if storageEnable
+ then [ XML.leaf
+ { name = "storage", attributes = XML.emptyAttributes }
+ ]
+ else [] : List XML.Type
+ , policies =
+ if storageEnable
+ then [ Init.Config.Policy::{
+ , service = "Block"
+ , label = Init.LabelSelector.prefix "store_fs"
+ }
+ ]
+ else [] : List Init.Config.Policy.Type
}
}
'';
- } else
- null;
-
- });
+ };
+ } else
+ { });
};
diff --git a/nixos-modules/lib/make-ext2-fs.nix b/nixos-modules/lib/make-ext2-fs.nix
new file mode 100644
index 0000000..78b273c
--- /dev/null
+++ b/nixos-modules/lib/make-ext2-fs.nix
@@ -0,0 +1,83 @@
+# 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
+}:
+
+let
+ sdClosureInfo = pkgs.buildPackages.closureInfo { rootPaths = storePaths; };
+in
+pkgs.stdenv.mkDerivation {
+ name = "ext2-fs.qcow2";
+
+ nativeBuildInputs = [ e2fsprogs.bin libfaketime perl fakeroot qemu ];
+
+ buildCommand =
+ ''
+ img=temp.raw
+ (
+ mkdir -p ./files
+ ${populateImageCommands}
+ )
+
+ echo "Preparing store paths for image..."
+
+ # Create nix/store before copying path
+ mkdir -p ./rootImage/nix/store
+
+ xargs -I % cp -a --reflink=auto % -t ./rootImage/nix/store/ < ${sdClosureInfo}/store-paths
+ (
+ GLOBIGNORE=".:.."
+ shopt -u dotglob
+
+ for f in ./files/*; do
+ cp -a --reflink=auto -t ./rootImage/ "$f"
+ done
+ )
+
+ # Also include a manifest of the closures in a format suitable for nix-store --load-db
+ cp ${sdClosureInfo}/registration ./rootImage/nix-path-registration
+
+ # 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)"
+
+ truncate -s $bytes $img
+
+ faketime -f "1970-01-01 00:00:01" fakeroot mkfs.ext2 -L ${volumeLabel} -U ${uuid} -d ./rootImage $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
+
+ echo "Resizing to minimum allowed size"
+ resize2fs -M $img
+
+ # And a final fsck, because of the previous truncating.
+ fsck.ext2 -n -f $img
+
+ qemu-img convert $img $out
+ '';
+}
diff --git a/nixos-modules/store-wrapper.dhall b/nixos-modules/store-wrapper.dhall
index 15f089a..dbc0db0 100644
--- a/nixos-modules/store-wrapper.dhall
+++ b/nixos-modules/store-wrapper.dhall
@@ -2,195 +2,184 @@ let Genode = env:DHALL_GENODE
let Prelude = Genode.Prelude
+let XML = Prelude.XML
+
let Init = Genode.Init
let Child = Init.Child
let TextMapType = Prelude.Map.Type Text
+let ChildMapType = TextMapType Child.Type
+
let Manifest/Type = TextMapType (TextMapType Text)
-in λ(subinit : Init.Type) →
- λ(storeName : Text) →
- λ(storeSize : Natural) →
- λ(storeRomPolicies : Prelude.Map.Type Text Text) →
- λ(routes : List Init.ServiceRoute.Type) →
- λ(bootManifest : Manifest/Type) →
+in λ ( params
+ : { extraCoreChildren : ChildMapType
+ , subinit : Init.Type
+ , storeSize : Natural
+ , storeRomPolicies : Prelude.Map.Type Text Text
+ , routes : List Init.ServiceRoute.Type
+ , bootManifest : Manifest/Type
+ }
+ ) →
Genode.Boot::{
, config = Init::{
- , routes
+ , routes = params.routes
, children =
let child = Prelude.Map.keyValue Child.Type
- in [ child
- "timer"
- ( Child.flat
- Child.Attributes::{
- , binary = "timer_drv"
- , provides = [ "Timer" ]
- }
- )
- , child
- "rtc"
- ( Child.flat
- Child.Attributes::{
- , binary = "rtc_drv"
- , provides = [ "Rtc" ]
- , routes = [ Init.ServiceRoute.parent "IO_PORT" ]
- }
- )
- , child
- "jitter_sponge"
- ( Child.flat
- Child.Attributes::{
- , binary = "jitter_sponge"
- , provides = [ "Terminal" ]
- , config = Init.Config::{
- , policies =
- [ Init.Config.Policy::{
- , service = "Terminal"
- , label = Init.LabelSelector.suffix "entropy"
- }
- ]
+ in [ child
+ "timer"
+ ( Child.flat
+ Child.Attributes::{
+ , binary = "timer_drv"
+ , provides = [ "Timer" ]
+ , config = Init.Config::{
+ , policies =
+ [ Init.Config.Policy::{
+ , service = "Timer"
+ , label = Init.LabelSelector.none
+ }
+ ]
+ }
}
- }
- )
- , child
- "store_fs"
- ( Child.flat
- Child.Attributes::{
- , binary = "vfs"
- , config = Init.Config::{
- , content =
- let VFS = Genode.VFS
-
- in [ VFS.vfs
- [ VFS.leafAttrs
- "tar"
- (toMap { name = storeName })
- ]
+ )
+ , child
+ "rtc"
+ ( Child.flat
+ Child.Attributes::{
+ , binary = "rtc_drv"
+ , provides = [ "Rtc" ]
+ , routes = [ Init.ServiceRoute.parent "IO_PORT" ]
+ }
+ )
+ , child
+ "jitter_sponge"
+ ( Child.flat
+ Child.Attributes::{
+ , binary = "jitter_sponge"
+ , provides = [ "Terminal" ]
+ , config = Init.Config::{
+ , policies =
+ [ Init.Config.Policy::{
+ , service = "Terminal"
+ , label = Init.LabelSelector.suffix "entropy"
+ }
+ ]
+ }
+ }
+ )
+ , child
+ "store_rom"
+ ( Child.flat
+ Child.Attributes::{
+ , binary = "cached_fs_rom"
+ , provides = [ "ROM" ]
+ , resources = Init.Resources::{
+ , ram = params.storeSize + Genode.units.MiB 1
+ }
+ , config = Init.Config::{
+ , policies =
+ [ Init.Config.Policy::{
+ , service = "ROM"
+ , label =
+ Init.LabelSelector.prefix
+ "nixos -> /nix/store"
+ , diag = Some True
+ }
]
- , 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" ]
- }
- )
- , child
- "store_rom"
- ( Child.flat
- Child.Attributes::{
- , binary = "cached_fs_rom"
- , provides = [ "ROM" ]
- , resources = Init.Resources::{
- , ram = storeSize + Genode.units.MiB 1
- }
- , config = Init.Config::{
- , policies =
- [ Init.Config.Policy::{
- , service = "ROM"
- , label =
- Init.LabelSelector.prefix
- "nixos -> /nix/store"
- }
- ]
- # ( let Entry = Prelude.Map.Entry Text Text
+ # ( let Entry = Prelude.Map.Entry Text Text
- in Prelude.List.concatMap
- Entry
- Init.Config.Policy.Type
- ( λ(e : Entry) →
- [ Init.Config.Policy::{
- , service = "ROM"
- , label =
- Init.LabelSelector.prefix
- "nixos -> ${e.mapKey}"
- , attributes = toMap
- { directory =
- "${e.mapValue}/bin"
- }
- }
- , Init.Config.Policy::{
- , service = "ROM"
- , label =
- Init.LabelSelector.Type.Partial
- { prefix = Some
- "nixos -> ${e.mapKey}"
- , suffix = Some ".lib.so"
+ in Prelude.List.concatMap
+ Entry
+ Init.Config.Policy.Type
+ ( λ(e : Entry) →
+ [ Init.Config.Policy::{
+ , service = "ROM"
+ , diag = Some True
+ , label =
+ Init.LabelSelector.prefix
+ "nixos -> ${e.mapKey}"
+ , attributes = toMap
+ { directory =
+ "${e.mapValue}/bin"
}
- , attributes = toMap
- { directory =
- "${e.mapValue}/lib"
- }
- }
- ]
- )
- storeRomPolicies
- )
- }
- }
- )
- , child
- "nixos"
- ( Init.toChild
- subinit
- Init.Attributes::{
- , exitPropagate = True
- , resources = Init.Resources::{
- , ram = Genode.units.MiB 4
- }
- , routes =
- let parentROMs =
- Prelude.List.concatMap
- Text
- Init.ServiceRoute.Type
- ( λ(suffix : Text) →
- Prelude.List.map
- Text
- Init.ServiceRoute.Type
- ( λ(prefix : Text) →
- { service =
- { name = "ROM"
+ }
+ , Init.Config.Policy::{
+ , service = "ROM"
+ , diag = Some True
, label =
Init.LabelSelector.Type.Partial
- { prefix = Some prefix
- , suffix = Some suffix
+ { prefix = Some
+ "nixos -> ${e.mapKey}"
+ , suffix = Some ".so"
}
+ , attributes = toMap
+ { directory =
+ "${e.mapValue}/lib"
+ }
}
- , route =
- Init.Route.parent
- (Some suffix)
- }
+ ]
)
- ( Prelude.Map.keys
- Text
- Init.Child.Type
- subinit.children
- )
- )
+ params.storeRomPolicies
+ )
+ }
+ }
+ )
+ ]
+ # params.extraCoreChildren
+ # [ child
+ "nixos"
+ ( Init.toChild
+ params.subinit
+ Init.Attributes::{
+ , exitPropagate = True
+ , resources = Init.Resources::{
+ , ram = Genode.units.MiB 4
+ }
+ , routes =
+ let parentROMs =
+ Prelude.List.concatMap
+ Text
+ Init.ServiceRoute.Type
+ ( λ(suffix : Text) →
+ Prelude.List.map
+ Text
+ Init.ServiceRoute.Type
+ ( λ(prefix : Text) →
+ { service =
+ { name = "ROM"
+ , label =
+ Init.LabelSelector.Type.Partial
+ { prefix = Some prefix
+ , suffix = Some suffix
+ }
+ }
+ , route =
+ Init.Route.parent
+ (Some suffix)
+ }
+ )
+ ( Prelude.Map.keys
+ Text
+ Init.Child.Type
+ params.subinit.children
+ )
+ )
- in parentROMs
- [ "ld.lib.so", "vfs.lib.so", "init" ]
- # [ Init.ServiceRoute.parent "IO_MEM"
- , Init.ServiceRoute.parent "IO_PORT"
- , Init.ServiceRoute.parent "IRQ"
- , Init.ServiceRoute.parent "VM"
- , Init.ServiceRoute.child "Timer" "timer"
- , Init.ServiceRoute.child "Rtc" "rtc"
- ]
- }
- )
- ]
+ in parentROMs
+ [ "ld.lib.so", "vfs.lib.so", "init" ]
+ # [ Init.ServiceRoute.parent "IO_MEM"
+ , Init.ServiceRoute.parent "IO_PORT"
+ , Init.ServiceRoute.parent "IRQ"
+ , Init.ServiceRoute.parent "VM"
+ , Init.ServiceRoute.child "Timer" "timer"
+ , Init.ServiceRoute.child "Rtc" "rtc"
+ ]
+ }
+ )
+ ]
}
, rom =
Genode.BootModules.toRomPaths
@@ -199,7 +188,7 @@ in λ(subinit : Init.Type) →
( Prelude.Map.values
Text
(Prelude.Map.Type Text Text)
- bootManifest
+ params.bootManifest
)
)
}
diff --git a/tests/lib/build-vms.nix b/tests/lib/build-vms.nix
index 1e3db3f..fcc32a6 100644
--- a/tests/lib/build-vms.nix
+++ b/tests/lib/build-vms.nix
@@ -29,6 +29,7 @@ rec {
baseModules = (import "${modulesPath}/module-list.nix") ++ [
../../nixos-modules/genode-core.nix
../../nixos-modules/genode-init.nix
+ ../../nixos-modules/hardware.nix
../../nixos-modules/qemu-vm.nix
{
key = "no-manual";