Emery Hemingway
2afd2d08cc
Require that all init children declared by Nix be declared with an absolute path to the program binary.
455 lines
15 KiB
Nix
455 lines
15 KiB
Nix
{ config, pkgs, lib, modulesPath, ... }:
|
|
|
|
with lib;
|
|
let
|
|
localPackages = pkgs.buildPackages;
|
|
|
|
binary = mkOption {
|
|
description = "Program binary for this child.";
|
|
type = types.path;
|
|
};
|
|
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" ];
|
|
};
|
|
extraInputs = 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.";
|
|
};
|
|
|
|
children = mkOption {
|
|
type = with types;
|
|
attrsOf (submodule {
|
|
options = {
|
|
inherit binary coreROMs extraInputs;
|
|
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.extraInputs == [ ] then
|
|
null
|
|
else
|
|
pkgs.symlinkJoin {
|
|
name = "${name}-rom";
|
|
paths = value.extraInputs;
|
|
}) 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.boot.romModules = with pkgs.genodePackages;
|
|
let
|
|
getBin = name:
|
|
"${builtins.getAttr name pkgs.genodePackages}/bin/${name}";
|
|
in {
|
|
cached_fs_rom = getBin "cached_fs_rom";
|
|
init = "${init}/bin/init";
|
|
jitter_sponge = "${jitter_sponge}/bin/jitter_sponge";
|
|
report_rom = "${report_rom}/bin/report_rom";
|
|
rtc_drv = "${rtc_drv}/bin/rtc_drv";
|
|
vfs = "${vfs}/bin/vfs";
|
|
"vfs.lib.so" = "${vfs}/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 = {
|
|
binary = "${pkgs.genodePackages.part_block}/bin/part_block";
|
|
configFile = pkgs.writeText "part_block.dhall" ''
|
|
let Genode = env:DHALL_GENODE
|
|
|
|
let Init = Genode.Init
|
|
|
|
in λ(binary : Text) -> Init.Child.flat
|
|
Init.Child.Attributes::{
|
|
, binary
|
|
, 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 = {
|
|
binary = "${pkgs.genodePackages.vfs}/bin/vfs";
|
|
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 λ(binary : Text) -> Init.Child.flat
|
|
Init.Child.Attributes::{
|
|
, binary
|
|
, 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 "extraInputs")
|
|
(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;
|
|
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) ++ (lib.attrsets.mapAttrsToList
|
|
(name: value:
|
|
''
|
|
, { mapKey = "${value.binary}", mapValue = "${value.binary}" }'')
|
|
config.genode.core.children))
|
|
}] } ]'');
|
|
|
|
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} "${value.binary}" }'')
|
|
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 = pkgs.writeText "${name}.dhall"
|
|
"${child.configFile} ${child.binary}";
|
|
}) 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};
|
|
|
|
};
|
|
|
|
}
|