{ 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 [ "fs" "memory" ]; # "parent"? default = "memory"; description = '' Backend for the initial /nix/store file-system. fs Store backed by a File_system session. tarball An in-memory tarball. ''; }; 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= "%p",mapValue="%p"}' >> $out ${if builtins.elem "lib" drv.outputs then '' find ${drv.lib}/ -type f -printf ',{mapKey= "%p",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); tarball = "${config.system.build.tarball}/tarball/${config.system.build.tarball.fileName}.tar"; in { assertions = [{ assertion = builtins.any (s: s == config.nixpkgs.system || s == config.nixpkgs.crossSystem.system) config.genode.core.supportedSystems; message = "invalid Genode core for this system"; }]; genode.core.basePackages = lib.optional (config.genode.boot.storeBackend != "memory") pkgs.genodePackages.part_block; genode.boot.romModules = let getBin = name: "${builtins.getAttr name pkgs.genodePackages}/bin/${name}"; in { cached_fs_rom = getBin "cached_fs_rom"; init = "${pkgs.genodePackages.init}/bin/init"; jitter_sponge = "${pkgs.genodePackages.jitter_sponge}/bin/jitter_sponge"; report_rom = "${pkgs.genodePackages.report_rom}/bin/report_rom"; rtc_drv = "${pkgs.genodePackages.rtc_drv}/bin/rtc_drv"; vfs = "${pkgs.genodePackages.vfs.unpatched}/bin/vfs"; "vfs.lib.so" = "${pkgs.genodePackages.vfs.unpatched}/lib/vfs.lib.so"; }; genode.core.children = # Component to steer the main fs to a specific partition (if config.genode.boot.storeBackend != "memory" then { part_block.configFile = pkgs.writeText "part_block.dhall" '' let Genode = env:DHALL_GENODE let Init = Genode.Init in Init.Child.flat Init.Child.Attributes::{ , binary = "${pkgs.genodePackages.part_block}/bin/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 "fs" , attributes = toMap { partition = "${ toString config.fileSystems."/".block.partition }" , writeable = "yes" , TODO = "select by partition UUID" } } ] } } ''; } else { }) // { fs.configFile = let vfsConfig = if config.genode.boot.storeBackend == "memory" then '' VFS.vfs [ VFS.leafAttrs "tar" (toMap { name = "${tarball}" }) ] '' else let rumpExt2 = '' VFS.vfs [ VFS.leafAttrs "plugin" ( toMap { load = "${pkgs.genodePackages.rump}/lib/vfs_rump.lib.so" , fs = "ext2fs" , ram = "12M" } ) ] ''; in { ahci = rumpExt2; usb = rumpExt2; }.${config.fileSystems."/".block.driver}; storeResources = let rumpExt2 = "Init.Resources::{ caps = 256, ram = Genode.units.MiB 16 }"; in { fs = rumpExt2; memory = "Init.Resources.default"; }.${config.genode.boot.storeBackend}; persistencePolicies = lib.mapAttrsToList (name: _: '' , Init.Config.Policy::{ , service = "File_system" , label = Genode.Init.LabelSelector.prefix "nixos -> ${name}" , attributes = toMap { root = "/services", writeable = "yes" } } '') (filterAttrs (_: child: child.fsPersistence) config.genode.init.children); fsPolicies = map (name: '' , Init.Config.Policy::{ , service = "File_system" , label = Init.LabelSelector.Type.Partial { prefix = Some "nixos -> ${name}", suffix = Some "nix-store" } , attributes = toMap { root = "/nix/store", writeable = "no" } } '') (builtins.attrNames config.genode.init.children); in pkgs.writeText "fs.dhall" '' let Genode = env:DHALL_GENODE let Init = Genode.Init let VFS = Genode.VFS let persistencePolicies = [ ${ toString persistencePolicies } ] : List Init.Config.Policy.Type let fsPolicies = [ ${ toString fsPolicies } ] : List Init.Config.Policy.Type in Init.Child.flat Init.Child.Attributes::{ , binary = "vfs" , resources = ${storeResources} , provides = [ "File_system" ] , config = Init.Config::{ , content = [ ${vfsConfig} ] , policies = [ Init.Config.Policy::{ , service = "File_system" , label = Init.LabelSelector.prefix "store_rom" , attributes = toMap { root = "/" } } ] # persistencePolicies # fsPolicies } } ''; }; genode.boot.configFile = let storeBackendInputs = { fs = [ pkgs.genodePackages.rump ]; memory = [ config.system.build.tarball ]; }.${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 ++ [ 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.parent (Some "${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 ''; virtualisation.diskImage = 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 lib.mkIf (config.genode.boot.storeBackend != "memory") bootDriveImage; virtualisation.useBootLoader = config.genode.boot.storeBackend != "memory"; virtualisation.qemu.options = let blockCommon = [ "-bios ${pkgs.buildPackages.buildPackages.OVMF.fd}/FV/OVMF.fd" ]; in { fs = blockCommon; memory = [ ]; }.${config.genode.boot.storeBackend}; }; }