sigil/nixos-modules/genode-core.nix
Emery Hemingway 8379dccf12 Refer to program and library ROMs by store path
Retrieve ROMs in the common case by full store path. This reduces
the need for route policies for driving relative requests into
absolute package paths.

Making library requests by absolute path required libraries to be
stored in the core image as such, and it follows that program
binaries should be handled in the same way. This makes requests
to core and to a file-system store more consistent, and makes
dependency detection more robust.
2021-02-16 15:46:14 +01:00

447 lines
15 KiB
Nix

{ 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 <option>config.genode.core.image</option>.
'';
};
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
<literal>Genode.Init.Child.Type</literal>.
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.
<variablelist>
<varlistentry>
<term>
<literal>fs</literal>
</term>
<listitem>
<para>Store backed by a File_system session.</para>
</listitem>
</varlistentry>
<varlistentry>
<term>
<literal>tarball</literal>
</term>
<listitem>
<para>An in-memory tarball.</para>
</listitem>
</varlistentry>
</variablelist>
'';
};
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}/bin/vfs";
"vfs.lib.so" = "${pkgs.genodePackages.vfs.lib}/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};
};
}