{ config, pkgs, lib, modulesPath, ... }: with lib; 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 { 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; description = '' List of packages to make availabe before the Nix store is ready. These are baked into . ''; }; 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 = { configFile = mkOption { type = types.path; description = '' Dhall boot configuration. See https://git.sr.ht/~ehmry/dhall-genode/tree/master/Boot/package.dhall ''; }; image = mkOption { type = types.path; description = "Boot image containing the base component binaries and configuration."; }; 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"; 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; description = '' Derivations to be included in the Nix store in the generated boot image. ''; }; }; }; config = let addManifest = drv: drv // { manifest = localPackages.runCommand "${drv.name}.dhall" { inherit drv; } '' set -eu echo -n '[' >> $out find $drv/ -type f -printf ',{mapKey= "%f",mapValue="%p"}' >> $out ${if builtins.elem "lib" drv.outputs then '' find ${drv.lib}/ -type f -printf ',{mapKey= "%f",mapValue="%p"}' >> $out'' else ""} echo -n ']' >> $out ''; }; mergeManifests = 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: if value.inputs == [ ] then null else pkgs.symlinkJoin { name = "${name}-rom"; paths = value.inputs; }) config.genode.init.children); in { assertions = [{ assertion = builtins.any (s: s == config.nixpkgs.system) config.genode.core.supportedSystems; message = "invalid Genode core for this system"; }]; genode.core.basePackages = lib.optional (config.genode.boot.storeBackend == "usb") pkgs.genodePackages.part_block; 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 let Init = Genode.Init 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" } } ] } } ''; } 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 = "/" } } ] } } ''; }; genode.boot.configFile = let tarball = "${config.system.build.tarball}/tarball/${config.system.build.tarball.fileName}.tar"; storeBackendInputs = { tarball = [ config.system.build.tarball ]; usb = [ pkgs.genodePackages.rump ]; }.${config.genode.boot.storeBackend}; 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)) + lib.optionalString (config.genode.boot.romModules != { }) '' # [ { mapKey = "romModules", mapValue = [ ${ toString (mapAttrsToList (k: v: '', { mapKey = "${k}", mapValue = "${v}" }'') config.genode.boot.romModules) }] } ]''); storeRomPolicies = mapAttrsToList (name: value: '', { mapKey = "${name}", mapValue = "${value}" }'') romDirectories; extraRoutes = lib.concatStringsSep ", " (lib.lists.flatten (lib.mapAttrsToList (name: value: map (suffix: '' { service = { name = "ROM" , label = Genode.Init.LabelSelector.Type.Partial { prefix = Some "nixos -> ${name}", suffix = Some "${suffix}" } } , route = Genode.Init.Route.parentLabel "${suffix}" } '') 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} { 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 = with builtins; [ config.genode.init.configFile ] ++ (attrValues romDirectories); # Create the tarball of the store to live in core ROM system.build.tarball = pkgs.callPackage "${modulesPath}/../lib/make-system-tarball.nix" { contents = [ ]; storeContents = let romDirs = mapAttrsToList (name: object: { symlink = "rom/${name}"; inherit object; }) romDirectories; configFiles = mapAttrsToList (name: child: { symlink = "config/${name}.dhall"; object = child.configFile; }) config.genode.init.children; in romDirs ++ configFiles; compressCommand = "cat"; compressionExtension = ""; }; system.build.initXml = pkgs.buildPackages.runCommand "init.xml" { nativeBuildInputs = with pkgs.buildPackages; [ dhall xorg.lndir libxml2 ]; DHALL_GENODE = "${pkgs.genodePackages.dhallGenode}/binary.dhall"; } '' export XDG_CACHE_HOME=$NIX_BUILD_TOP lndir -silent \ ${pkgs.genodePackages.dhallGenode}/.cache \ $XDG_CACHE_HOME dhall text <<< "(env:DHALL_GENODE).Init.render (${config.genode.boot.configFile}).config" > $out xmllint --noout $out ''; system.build.bootDriveImage = let 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 bootDriveImage; # virtualisation.useEFIBoot = config.genode.boot.storeBackend == "usb"; virtualisation.qemu.options = lib.optionals (config.genode.boot.storeBackend == "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" ]; }; }