Cleanup dead nixos-module code

This commit is contained in:
Ehmry - 2020-12-10 19:10:18 +01:00
parent 74b4c0df69
commit 32f1ee1fe7
15 changed files with 0 additions and 3397 deletions

View File

@ -131,10 +131,6 @@
packages = self.packages.${system};
});
nixosModules =
# Modules for composing Genode and NixOS
import ./nixos-modules { inherit self; };
checks =
# Checks for continous testing
let tests = import ./tests;

View File

@ -1,284 +0,0 @@
{ self }: {
genodeHost = { config, lib, pkgs, modulesPath, ... }:
let
apps' = self.apps.x86_64-linux-x86_64-genode;
config' = config;
lib' = self.lib.x86_64-linux-x86_64-genode;
pkgs' = self.packages.x86_64-linux-x86_64-genode;
legacyPackages' = self.legacyPackages.x86_64-linux-x86_64-genode;
modulesPath' = "${self.inputs.nixpkgs}/nixos/modules";
genodeConfig = config.genode;
console = lib'.runDhallCommand "vbox.dhall" { } ''
dhall > $out <<< '${
./dhall/console.dhall
} { bash = "${legacyPackages'.bash}", coreutils = "${legacyPackages'.coreutils}", path = "${
lib.makeSearchPathOutput "bin" "bin"
(with legacyPackages'; [ bash coreutils ])
}" }'
'';
guestChildren = lib'.runDhallCommand "vbox.dhall" { } (''
dhall > $out << END
let Genode = env:DHALL_GENODE
in [
'' + builtins.concatStringsSep "," (lib.mapAttrsToList (vmName: cfg:
let
inherit (cfg) config;
boot = {
iso = rec {
filename = "nixos.iso";
drv = pkgs.callPackage
"${modulesPath'}/../lib/make-iso9660-image.nix"
# call the ISO utility from our nixpkgs with the package set of the guest
{
isoName = filename;
inherit (config.isoImage) volumeID contents;
};
format = "< ISO | VDI >.ISO";
storeRoot = "${baseNameOf drv}/iso";
uuid = "81763434-9a51-49e8-9444-528a5a28c4bc";
};
vdi = rec {
filename = "nixos.vdi";
drv = import "${modulesPath'}/../lib/make-disk-image.nix" {
inherit config lib pkgs;
diskSize = config.virtualbox.baseImageSize;
partitionTableType = "legacy";
name = "nixos-${pkgs.stdenv.hostPlatform.system}.vdi";
format = "vdi";
};
format = "< ISO | VDI >.VDI";
storeRoot = baseNameOf drv;
uuid = ''
$(${pkgs.virtualbox}/bin/VBoxManage showmediuminfo "${boot.drv}/${boot.filename}" | awk '/^UUID:/ {print $2}')'';
};
}.${cfg.bootFormat};
in lib'.runDhallCommand "vbox.dhall" { } ''
bootUuid=${boot.uuid}
dhall > $out << END
{ mapKey = "vbox-${vmName}"
, mapValue =
${./dhall/vbox-guest.dhall}
{ bootFilename = "${boot.filename}"
, bootFormat = ${boot.format}
, bootPkg = "${boot.storeRoot}"
, bootUuid = "$bootUuid"
, memorySize = ${toString cfg.memorySize}
, vmName = "${vmName}"
}
}
END
'') config.genode.guests) + ''
] : Genode.Init.Children.Type
END
'');
initConfig = let
fbDriverConfig = {
intel = ./dhall/intel_fb_drv.dhall;
vesa = ./dhall/vesa_fb_drv.dhall;
}.${genodeConfig.fbDriver};
in ''
${
./dhall/root.dhall
} { fbDriver = ${fbDriverConfig}, guests = toMap { console = ${console} } # ${guestChildren}, inputFilterChargens = ${genodeConfig.inputFilter.extraChargen}, partitionType = ${
./dhall/partition-type
}, wm = ${./dhall/wm.dhall}, graphical-log = ${
./dhall/graphical-log.dhall
}, fs-log = ${
./dhall/fs-log.dhall
}, systemLabel = "${config.system.nixos.label}" }'';
buildBootDescription = self.legacyPackages.x86_64-linux.callPackage
./buildBootDescription.nix { lib = lib'; };
bootDescription = buildBootDescription {
inherit initConfig;
imageInputs = [ legacyPackages'.bash ] ++ map pkgs'.genodeSources.depot
([
"acpi_drv"
"ahci_drv"
"cached_fs_rom"
"chroot"
"decorator"
"event_filter"
"fs_log"
"gui_fb"
"init"
"ipxe_nic_drv"
"libc"
"libiconv"
"log_core"
"nic_router"
"nitpicker"
"part_block"
"platform_drv"
"posix"
"ps2_drv"
"report_rom"
"rom_to_file"
"rtc_drv"
"rump"
"stdcxx"
"terminal"
"terminal_log"
"usb_drv"
"vfs"
"vfs_audit"
"vfs_import"
"vfs_pipe"
"vfs_ttf"
"window_layouter"
"wm"
] ++ lib.optional (genodeConfig.guests != { }) "vbox5"
++ lib.optional (genodeConfig.fbDriver == "vesa") "vesa_drv"
++ lib.optional (genodeConfig.fbDriver == "intel") "intel_fb_drv")
++ (with pkgs'; [ base-nova block_router ]);
extraBinaries = [
"ld.lib.so"
"libc.so"
"libm.so"
"libposix.so"
"librump.so"
"librump_fs.so"
"libstdcxx.so"
"libvfs.so"
"libvfs_audit.so"
"libvfs_import.so"
"libvfs_pipe.so"
"libvfs_rump.so"
"libvfs_ttf.so"
] ++ lib.optionals (genodeConfig.guests != { }) [
"libc_pipe.so"
"libiconv.so"
"libqemu-usb.so"
];
extraRoms = {
"Inconsolata.ttf" =
"${pkgs.inconsolata}/share/fonts/truetype/inconsolata/Inconsolata-Regular.ttf";
"focus" = builtins.toFile "nitpicker-is-too-complicated.xml" ''
<focus label="focus"/>
'';
};
};
firmware = lib'.novaImage "stage0" { gzip = true; } bootDescription;
in {
options.genode = with lib; {
fbDriver = mkOption {
default = "vesa";
type = types.enum [ "intel" "vesa" ];
description = ''
Set framebuffer driver.
'';
};
guests = mkOption {
type = types.attrsOf (types.submodule
({ config, options, name, ... }: {
options = {
bootFormat = mkOption {
default = "vdi";
type = types.enum [ "iso" "vdi" ];
description = "Set boot media format.";
};
memorySize = mkOption {
type = types.int;
default = 1536;
description = ''
The amount of RAM in MiB allocated to the VirtualBox guest.
'';
};
config = mkOption {
description = ''
A specification of the desired configuration of this
guest VM, as a NixOS module.
'';
type = mkOptionType {
name = "Toplevel NixOS config";
merge = loc: defs:
(import "${modulesPath}/../lib/eval-config.nix" {
inherit (config'.nixpkgs) system;
modules = {
iso = [
"${modulesPath}/installer/cd-dvd/iso-image.nix"
];
vdi = [
"${modulesPath}/virtualisation/virtualbox-image.nix"
{
virtualbox.memorySize =
genodeConfig.guests.${name}.memorySize;
}
];
}.${genodeConfig.guests.${name}.bootFormat}
++ [{ system.nixos.tags = [ name ]; }]
++ (map (x: x.value) defs);
prefix = [ "guests" name ];
}).config;
};
};
};
}));
default = { };
};
inputFilter = {
extraChargen = mkOption {
description =
"Dhall expression of the type <code>List Prelude.XML.Type</code>";
type = types.str;
default = "${./dhall/qwerty.chargen.dhall}";
};
};
};
config = {
system.build.genode = {
inherit firmware console;
config = lib'.runDhallCommand "config.dhall" { }
''dhall > $out <<< "${initConfig}"'';
xml = lib'.runDhallCommand "config.xml" { }
''${apps'.render-init.program} <<< "${initConfig}" > $out'';
};
boot.loader.grub = {
extraEntries = ''
menuentry Genode on NOVA {
insmod multiboot2
insmod gzio
multiboot2 /bender
module2 /hypervisor hypervisor iommu novpid serial logmem
module2 /image.elf.gz image.elf
}
'';
extraFiles = {
"bender" = "${pkgs'.genodeSources}/tool/boot/bender";
"hypervisor" = "${pkgs'.NOVA}/hypervisor-x86_64";
"image.elf.gz" = "${firmware}/image.elf.gz";
};
};
};
};
workman-layout.genode.inputFilter.extraChargen =
"${./dhall/workman.chargen.dhall}";
}

View File

@ -1,201 +0,0 @@
let Genode = env:DHALL_GENODE
let Prelude = Genode.Prelude
let XML = Prelude.XML
let Init = Genode.Init
let Child = Init.Child
let Resources = Init.Resources
let ServiceRoute = Init.ServiceRoute
let Libc = Genode.Libc
let VFS = Genode.VFS
in λ(params : { bash : Text, coreutils : Text, path : Text }) →
let init =
Init::{
, verbose = True
, routes =
Prelude.List.map
Text
Init.ServiceRoute.Type
Init.ServiceRoute.parent
[ "Gui", "Rtc", "Timer" ]
, children = toMap
{ gui_fb =
Child.flat
Child.Attributes::{
, binary = "gui_fb"
, config = Init.Config::{
, attributes = toMap
{ xpos = "10"
, ypos = "10"
, initial_width = "800"
, initial_height = "600"
}
}
, exitPropagate = True
, provides = [ "Framebuffer", "Input" ]
, resources = Resources::{ ram = Genode.units.MiB 8 }
}
, terminal =
Child.flat
Child.Attributes::{
, binary = "terminal"
, config = Init.Config::{
, content =
[ XML.element
{ name = "vfs"
, attributes = XML.emptyAttributes
, content =
[ XML.element
{ name = "dir"
, attributes = toMap { name = "fonts" }
, content =
[ XML.leaf
{ name = "fs"
, attributes = toMap
{ label = "fonts" }
}
]
}
]
}
]
}
, provides = [ "Terminal" ]
, resources = Resources::{
, caps = 256
, ram = Genode.units.MiB 4
}
, routes =
[ ServiceRoute.child "Framebuffer" "gui_fb"
, ServiceRoute.child "Input" "gui_fb"
, ServiceRoute.parent "File_system"
]
}
, vfs =
Child.flat
Child.Attributes::{
, binary = "vfs"
, config = Init.Config::{
, content =
[ VFS.vfs
[ VFS.dir
"dev"
[ VFS.leaf "log"
, VFS.leaf "null"
, VFS.dir "pipe" [ VFS.leaf "pipe" ]
, VFS.leaf "rtc"
, VFS.leaf "terminal"
, VFS.leaf "zero"
]
, VFS.dir
"usr"
[ VFS.dir
"bin"
[ VFS.symlink
"${params.coreutils}/bin/env"
"env"
]
]
, VFS.dir "tmp" [ VFS.leaf "ram" ]
, VFS.dir
"nix"
[ VFS.dir
"store"
[ VFS.fs VFS.FS::{ label = "nix-store" } ]
]
]
]
, policies =
[ Init.Config.Policy::{
, service = "File_system"
, attributes = toMap
{ root = "/", writeable = "yes" }
}
]
}
, provides = [ "File_system" ]
, resources = Resources::{
, caps = 256
, ram = Genode.units.MiB 8
}
, routes =
[ Init.ServiceRoute.parent "File_system"
, Init.ServiceRoute.child "Terminal" "terminal"
]
}
, store_rom =
Child.flat
Child.Attributes::{
, binary = "cached_fs_rom"
, provides = [ "ROM" ]
, resources = Resources::{
, caps = 256
, ram = Genode.units.MiB 4
}
, routes = [ Init.ServiceRoute.child "File_system" "vfs" ]
}
, shell =
Child.flat
Child.Attributes::{
, binary = "bash"
, config =
Libc.toConfig
Libc::{
, stdin = Some "/dev/terminal"
, stdout = Some "/dev/terminal"
, stderr = Some "/dev/terminal"
, pipe = Some "/dev/pipe"
, rtc = Some "/dev/rtc"
, vfs = [ VFS.leaf "fs" ]
, env = toMap
{ TERM = "screen"
, PATH = "${params.path}"
, PS1 = "system:\$PWD"
}
, args = [ "bash" ]
}
, exitPropagate = True
, resources = Resources::{
, caps = 256
, ram = Genode.units.MiB 8
}
, routes =
[ Init.ServiceRoute.child "File_system" "vfs"
, { service =
{ name = "ROM"
, label =
Init.LabelSelector.Type.Partial
{ prefix = Some "/nix/store/"
, suffix = None Text
}
}
, route =
Init.Route.Type.Child
{ name = "store_rom"
, label = None Text
, diag = None Bool
}
}
]
}
}
}
in Init.toChild
init
Init.Attributes::{
, routes =
[ ServiceRoute.parent "File_system"
, ServiceRoute.parent "Gui"
, ServiceRoute.parent "Rtc"
, ServiceRoute.parent "Timer"
]
}

View File

@ -1,56 +0,0 @@
let Genode = env:DHALL_GENODE
let Init = Genode.Init
let Child = Init.Child
let ServiceRoute = Init.ServiceRoute
let routeLogRom =
λ(label : Text) → ServiceRoute.parentLabel "ROM" (Some "log") (Some label)
in Init::{
, verbose = True
, children = toMap
{ fs_log =
Child.flat
Child.Attributes::{
, binary = "fs_log"
, config = Init.Config::{
, defaultPolicy = Some Init.Config.DefaultPolicy::{
, attributes = toMap { merge = "yes", truncate = "yes" }
}
}
, exitPropagate = True
, provides = [ "LOG" ]
, routes = [ ServiceRoute.parent "File_system" ]
}
, log_core =
Child.flat
Child.Attributes::{
, binary = "log_core"
, routes =
[ routeLogRom "core_log"
, ServiceRoute.childLabel
"LOG"
"fs_log"
(Some "log")
(Some "core")
]
}
, log_kernel =
Child.flat
Child.Attributes::{
, binary = "log_core"
, routes =
[ routeLogRom "kernel_log"
, ServiceRoute.childLabel
"LOG"
"fs_log"
(Some "log")
(Some "kernel")
]
}
}
, routes = [ ServiceRoute.parent "Timer" ]
}

View File

@ -1,105 +0,0 @@
let Genode = env:DHALL_GENODE
let Prelude = Genode.Prelude
let XML = Prelude.XML
let Init = Genode.Init
let Child = Init.Child
let Resources = Init.Resources
let ServiceRoute = Init.ServiceRoute
let routeLogRom =
λ(label : Text) → ServiceRoute.parentLabel "ROM" (Some "log") (Some label)
in Init::{
, verbose = True
, routes =
Prelude.List.map
Text
Init.ServiceRoute.Type
Init.ServiceRoute.parent
[ "Gui", "Rtc", "Timer" ]
, children = toMap
{ gui_fb =
Child.flat
Child.Attributes::{
, binary = "gui_fb"
, config = Init.Config::{
, attributes = toMap
{ initial_width = "600", initial_height = "600" }
}
, exitPropagate = True
, provides = [ "Framebuffer", "Input" ]
, resources = Resources::{ ram = Genode.units.MiB 8 }
}
, terminal =
Child.flat
Child.Attributes::{
, binary = "terminal"
, config = Init.Config::{
, content =
[ XML.element
{ name = "vfs"
, attributes = XML.emptyAttributes
, content =
[ XML.element
{ name = "dir"
, attributes = toMap { name = "fonts" }
, content =
[ XML.leaf
{ name = "fs"
, attributes = toMap { label = "fonts" }
}
]
}
]
}
]
}
, provides = [ "Terminal" ]
, resources = Resources::{ caps = 256, ram = Genode.units.MiB 4 }
, routes =
[ ServiceRoute.child "Framebuffer" "gui_fb"
, ServiceRoute.child "Input" "gui_fb"
, ServiceRoute.parent "File_system"
]
}
, terminal_log =
Child.flat
Child.Attributes::{
, binary = "terminal_log"
, provides = [ "LOG" ]
, routes = [ ServiceRoute.child "Terminal" "terminal" ]
}
, log_core =
Child.flat
Child.Attributes::{
, binary = "log_core"
, routes =
[ routeLogRom "core_log"
, ServiceRoute.childLabel
"LOG"
"terminal_log"
(Some "log")
(Some "core")
]
}
, log_kernel =
Child.flat
Child.Attributes::{
, binary = "log_core"
, routes =
[ routeLogRom "kernel_log"
, ServiceRoute.childLabel
"LOG"
"terminal_log"
(Some "log")
(Some "kernel")
]
}
}
}

View File

@ -1,23 +0,0 @@
let Genode = env:DHALL_GENODE
let Init = Genode.Init
let Child = Init.Child
let ServiceRoute = Init.ServiceRoute
in Child.flat
Child.Attributes::{
, binary = "intel_fb_drv"
, provides = [ "Framebuffer" ]
, resources = Init.Resources::{ caps = 256, ram = Genode.units.MiB 48 }
, routes =
[ ServiceRoute.parent "IO_MEM"
, ServiceRoute.parent "IO_PORT"
, ServiceRoute.childLabel
"Platform"
"platform_drv"
(None Text)
(Some "intel_fb_drv")
]
}

View File

@ -1 +0,0 @@
"24b69406-18a1-428d-908e-d21a1437122c"

View File

@ -1,297 +0,0 @@
let Genode = env:DHALL_GENODE
let Prelude = Genode.Prelude
let XML = Prelude.XML
let Key = < Ascii : Natural | Char : Text | Code : Natural > : Type
let Map =
{ Type =
{ keys : Prelude.Map.Type Text Key
, mod1 : Bool
, mod2 : Bool
, mod3 : Bool
, mod4 : Bool
}
, default = { mod1 = False, mod2 = False, mod3 = False, mod4 = False }
}
let boolToAttr = λ(_ : Bool) → if _ then "yes" else "no"
let keyToXML =
λ(x : Prelude.Map.Entry Text Key) →
XML.leaf
{ name = "key"
, attributes =
[ merge
{ Ascii =
λ(_ : Natural) →
{ mapKey = "ascii", mapValue = Prelude.Natural.show _ }
, Char = λ(_ : Text) → { mapKey = "char", mapValue = _ }
, Code =
λ(_ : Natural) →
{ mapKey = "code", mapValue = Prelude.Natural.show _ }
}
x.mapValue
, { mapKey = "name", mapValue = x.mapKey }
]
}
let mapToXML =
λ(map : Map.Type) →
XML.element
{ name = "map"
, attributes = toMap
{ mod1 = boolToAttr map.mod1
, mod2 = boolToAttr map.mod2
, mod3 = boolToAttr map.mod3
, mod4 = boolToAttr map.mod4
}
, content =
Prelude.List.map
(Prelude.Map.Entry Text Key)
XML.Type
keyToXML
map.keys
}
let qwerty =
[ Map::{
, mod1 = False
, mod2 = False
, mod3 = False
, mod4 = False
, keys = toMap
{ KEY_1 = Key.Code 49
, KEY_2 = Key.Code 50
, KEY_3 = Key.Code 51
, KEY_4 = Key.Code 52
, KEY_5 = Key.Code 53
, KEY_6 = Key.Code 54
, KEY_7 = Key.Code 55
, KEY_8 = Key.Code 56
, KEY_9 = Key.Code 57
, KEY_0 = Key.Code 48
, KEY_MINUS = Key.Code 45
, KEY_EQUAL = Key.Code 61
, KEY_Q = Key.Code 113
, KEY_W = Key.Code 119
, KEY_E = Key.Code 101
, KEY_R = Key.Code 114
, KEY_T = Key.Code 116
, KEY_Y = Key.Code 121
, KEY_U = Key.Code 117
, KEY_I = Key.Code 105
, KEY_O = Key.Code 111
, KEY_P = Key.Code 112
, KEY_LEFTBRACE = Key.Code 91
, KEY_RIGHTBRACE = Key.Code 93
, KEY_A = Key.Code 97
, KEY_S = Key.Code 115
, KEY_D = Key.Code 100
, KEY_F = Key.Code 102
, KEY_G = Key.Code 103
, KEY_H = Key.Code 104
, KEY_J = Key.Code 106
, KEY_K = Key.Code 107
, KEY_L = Key.Code 108
, KEY_SEMICOLON = Key.Code 59
, KEY_APOSTROPHE = Key.Code 39
, KEY_GRAVE = Key.Code 96
, KEY_BACKSLASH = Key.Code 92
, KEY_Z = Key.Code 122
, KEY_X = Key.Code 120
, KEY_C = Key.Code 99
, KEY_V = Key.Code 118
, KEY_B = Key.Code 98
, KEY_N = Key.Code 110
, KEY_M = Key.Code 109
, KEY_COMMA = Key.Code 44
, KEY_DOT = Key.Code 46
, KEY_SLASH = Key.Code 47
, KEY_KPASTERISK = Key.Code 42
, KEY_SPACE = Key.Code 32
, KEY_KP7 = Key.Code 55
, KEY_KP8 = Key.Code 56
, KEY_KP9 = Key.Code 57
, KEY_KPMINUS = Key.Code 45
, KEY_KP4 = Key.Code 52
, KEY_KP5 = Key.Code 53
, KEY_KP6 = Key.Code 54
, KEY_KPPLUS = Key.Code 43
, KEY_KP1 = Key.Code 49
, KEY_KP2 = Key.Code 50
, KEY_KP3 = Key.Code 51
, KEY_KP0 = Key.Code 48
, KEY_KPDOT = Key.Code 46
, KEY_102ND = Key.Code 60
, KEY_KPSLASH = Key.Code 47
, KEY_ESC = Key.Ascii 27
, KEY_BACKSPACE = Key.Ascii 8
, KEY_TAB = Key.Ascii 9
, KEY_ENTER = Key.Ascii 10
, KEY_KPENTER = Key.Ascii 10
, KEY_DELETE = Key.Ascii 127
}
}
, Map::{
, mod1 = True
, mod2 = False
, mod3 = False
, mod4 = False
, keys = toMap
{ KEY_1 = Key.Code 33
, KEY_2 = Key.Code 64
, KEY_3 = Key.Code 35
, KEY_4 = Key.Code 36
, KEY_5 = Key.Code 37
, KEY_6 = Key.Code 94
, KEY_7 = Key.Code 38
, KEY_8 = Key.Code 42
, KEY_9 = Key.Code 40
, KEY_0 = Key.Code 41
, KEY_MINUS = Key.Code 95
, KEY_EQUAL = Key.Code 43
, KEY_Q = Key.Code 81
, KEY_W = Key.Code 87
, KEY_E = Key.Code 69
, KEY_R = Key.Code 82
, KEY_T = Key.Code 84
, KEY_Y = Key.Code 89
, KEY_U = Key.Code 85
, KEY_I = Key.Code 73
, KEY_O = Key.Code 79
, KEY_P = Key.Code 80
, KEY_LEFTBRACE = Key.Code 123
, KEY_RIGHTBRACE = Key.Code 125
, KEY_A = Key.Code 65
, KEY_S = Key.Code 83
, KEY_D = Key.Code 68
, KEY_F = Key.Code 70
, KEY_G = Key.Code 71
, KEY_H = Key.Code 72
, KEY_J = Key.Code 74
, KEY_K = Key.Code 75
, KEY_L = Key.Code 76
, KEY_SEMICOLON = Key.Code 58
, KEY_APOSTROPHE = Key.Code 34
, KEY_GRAVE = Key.Code 126
, KEY_BACKSLASH = Key.Code 124
, KEY_Z = Key.Code 90
, KEY_X = Key.Code 88
, KEY_C = Key.Code 67
, KEY_V = Key.Code 86
, KEY_B = Key.Code 66
, KEY_N = Key.Code 78
, KEY_M = Key.Code 77
, KEY_COMMA = Key.Code 60
, KEY_DOT = Key.Code 62
, KEY_SLASH = Key.Code 63
, KEY_KPASTERISK = Key.Code 42
, KEY_KPMINUS = Key.Code 45
, KEY_KPPLUS = Key.Code 43
, KEY_102ND = Key.Code 62
, KEY_KPSLASH = Key.Code 47
}
}
, Map::{
, mod1 = False
, mod2 = False
, mod3 = True
, mod4 = False
, keys = toMap { KEY_5 = Key.Code 8364, KEY_102ND = Key.Code 124 }
}
, Map::{
, mod1 = False
, mod2 = False
, mod3 = False
, mod4 = True
, keys = toMap
{ KEY_Q = Key.Code 81
, KEY_W = Key.Code 87
, KEY_E = Key.Code 69
, KEY_R = Key.Code 82
, KEY_T = Key.Code 84
, KEY_Y = Key.Code 89
, KEY_U = Key.Code 85
, KEY_I = Key.Code 73
, KEY_O = Key.Code 79
, KEY_P = Key.Code 80
, KEY_A = Key.Code 65
, KEY_S = Key.Code 83
, KEY_D = Key.Code 68
, KEY_F = Key.Code 70
, KEY_G = Key.Code 71
, KEY_H = Key.Code 72
, KEY_J = Key.Code 74
, KEY_K = Key.Code 75
, KEY_L = Key.Code 76
, KEY_Z = Key.Code 90
, KEY_X = Key.Code 88
, KEY_C = Key.Code 67
, KEY_V = Key.Code 86
, KEY_B = Key.Code 66
, KEY_N = Key.Code 78
, KEY_M = Key.Code 77
}
}
, Map::{
, mod1 = True
, mod2 = False
, mod3 = False
, mod4 = True
, keys = toMap
{ KEY_1 = Key.Code 33
, KEY_2 = Key.Code 64
, KEY_3 = Key.Code 35
, KEY_4 = Key.Code 36
, KEY_5 = Key.Code 37
, KEY_6 = Key.Code 94
, KEY_7 = Key.Code 38
, KEY_8 = Key.Code 42
, KEY_9 = Key.Code 40
, KEY_0 = Key.Code 41
, KEY_MINUS = Key.Code 95
, KEY_EQUAL = Key.Code 43
, KEY_Q = Key.Code 113
, KEY_W = Key.Code 119
, KEY_E = Key.Code 101
, KEY_R = Key.Code 114
, KEY_T = Key.Code 116
, KEY_Y = Key.Code 121
, KEY_U = Key.Code 117
, KEY_I = Key.Code 105
, KEY_O = Key.Code 111
, KEY_P = Key.Code 112
, KEY_LEFTBRACE = Key.Code 123
, KEY_RIGHTBRACE = Key.Code 125
, KEY_A = Key.Code 97
, KEY_S = Key.Code 115
, KEY_D = Key.Code 100
, KEY_F = Key.Code 102
, KEY_G = Key.Code 103
, KEY_H = Key.Code 104
, KEY_J = Key.Code 106
, KEY_K = Key.Code 107
, KEY_L = Key.Code 108
, KEY_SEMICOLON = Key.Code 58
, KEY_APOSTROPHE = Key.Code 34
, KEY_GRAVE = Key.Code 126
, KEY_BACKSLASH = Key.Code 124
, KEY_Z = Key.Code 122
, KEY_X = Key.Code 120
, KEY_C = Key.Code 99
, KEY_V = Key.Code 118
, KEY_B = Key.Code 98
, KEY_N = Key.Code 110
, KEY_M = Key.Code 109
, KEY_COMMA = Key.Code 60
, KEY_DOT = Key.Code 62
, KEY_SLASH = Key.Code 63
, KEY_102ND = Key.Code 62
}
}
]
in Prelude.List.map Map.Type XML.Type mapToXML qwerty

View File

@ -1,745 +0,0 @@
let Genode = env:DHALL_GENODE
let Prelude = Genode.Prelude
let XML = Prelude.XML
let Init = Genode.Init
let Child = Init.Child
let Resources = Init.Resources
let ServiceRoute = Init.ServiceRoute
let Policy = Init.Config.Policy
let DefaultPolicy = Init.Config.DefaultPolicy
let LabelSelector = Init.LabelSelector
let label =
λ(label : Text) →
{ local = label, route = label } : Child.Attributes.Label
let rootInit =
λ ( params
: { fbDriver : Init.Child.Type
, guests : Init.Children.Type
, inputFilterChargens : List XML.Type
, partitionType : Text
, wm : Init.Type
, graphical-log : Init.Type
, fs-log : Init.Type
, systemLabel : Text
}
) →
Init::{
, children = toMap
{ timer =
Child.flat
Child.Attributes::{
, binary = "timer_drv"
, provides = [ "Timer" ]
}
, rtc =
Child.flat
Child.Attributes::{
, binary = "rtc_drv"
, provides = [ "Rtc" ]
, routes =
[ ServiceRoute.parent "IO_PORT"
, ServiceRoute.parent "IO_MEM"
]
}
, acpi_drv =
Child.flat
Child.Attributes::{
, binary = "acpi_drv"
, priority = 1
, resources = Resources::{
, caps = 350
, ram = Genode.units.MiB 4
}
, romReports = [ label "acpi", label "smbios_table" ]
, routes = [ ServiceRoute.parent "IO_MEM" ]
}
, platform_drv =
Child.flat
Child.Attributes::{
, binary = "platform_drv"
, resources = Resources::{
, caps = 400
, ram = Genode.units.MiB 4
, constrainPhys = True
}
, reportRoms = [ label "acpi" ]
, romReports = [ label "pci" ]
, provides = [ "Acpi", "Platform" ]
, routes =
[ ServiceRoute.parent "IRQ"
, ServiceRoute.parent "IO_MEM"
, ServiceRoute.parent "IO_PORT"
, ServiceRoute.parentLabel
"ROM"
(Some "system")
(Some "system")
]
, config = Init.Config::{
, attributes = toMap { system = "yes" }
, content =
let PciPolicy/Type =
{ labelSuffix : Text, pciClass : Text }
in [ XML.text
''
<report pci="yes"/>
<policy label_suffix="ps2_drv">
<device name="PS2"/>
</policy>
<policy label_suffix="intel_fb_drv">
<pci class="VGA"/>
<pci bus="0" device="0" function="0"/>
<pci class="ISABRIDGE"/>
</policy>
<policy label_suffix="audio">
<pci class="AUDIO"/>
<pci class="HDAUDIO"/>
</policy>
<policy label="acpica"/>
''
]
# Prelude.List.map
PciPolicy/Type
XML.Type
( λ(policy : PciPolicy/Type) →
XML.element
{ name = "policy"
, attributes = toMap
{ label_suffix = policy.labelSuffix }
, content =
[ XML.leaf
{ name = "pci"
, attributes = toMap
{ class = policy.pciClass }
}
]
}
)
[ { labelSuffix = "ahci_drv"
, pciClass = "AHCI"
}
, { labelSuffix = "nic_drv"
, pciClass = "ETHERNET"
}
, { labelSuffix = "usb_drv", pciClass = "USB" }
, { labelSuffix = "vesa_fb_drv"
, pciClass = "VGA"
}
]
}
}
, framebuffer = params.fbDriver
, event_filter =
Child.flat
Child.Attributes::{
, binary = "event_filter"
, config =
let key =
λ(name : Text) →
XML.leaf
{ name = "key", attributes = toMap { name } }
let remap =
λ(name : Text) →
λ(to : Text) →
XML.leaf
{ name = "key"
, attributes = toMap { name, to }
}
in Init.Config::{
, content =
[ XML.leaf
{ name = "input"
, attributes = toMap { label = "ps2" }
}
, XML.leaf
{ name = "input"
, attributes = toMap { label = "usb" }
}
, XML.element
{ name = "output"
, attributes = XML.emptyAttributes
, content =
[ XML.element
{ name = "chargen"
, attributes = XML.emptyAttributes
, content =
[ XML.element
{ name = "remap"
, attributes =
XML.emptyAttributes
, content =
[ remap
"KEY_LEFTMETA"
"KEY_SCREEN"
, XML.element
{ name = "merge"
, attributes =
XML.emptyAttributes
, content =
[ XML.leaf
{ name = "input"
, attributes = toMap
{ name = "ps2" }
}
, XML.leaf
{ name = "input"
, attributes = toMap
{ name = "usb" }
}
]
}
]
}
, XML.element
{ name = "mod1"
, attributes =
XML.emptyAttributes
, content =
[ key "KEY_LEFTSHIFT"
, key "KEY_RIGHTSHIFT"
]
}
, XML.element
{ name = "mod2"
, attributes =
XML.emptyAttributes
, content =
[ key "KEY_LEFTCTRL"
, key "KEY_RIGHTCTRL"
]
}
, XML.element
{ name = "mod3"
, attributes =
XML.emptyAttributes
, content =
[ key "KEY_RIGHTALT" ]
}
]
# params.inputFilterChargens
}
]
}
]
}
, provides = [ "Input" ]
, routes =
[ ServiceRoute.parentLabel
"ROM"
(Some "config")
(Some "config -> event_filter.config")
, ServiceRoute.childLabel
"Input"
"ps2_drv"
(Some "ps2")
(None Text)
, ServiceRoute.childLabel
"Input"
"usb_drv"
(Some "usb")
(None Text)
, ServiceRoute.child "Capture" "nitpicker"
]
}
, ps2_drv =
Child.flat
Child.Attributes::{
, binary = "ps2_drv"
, provides = [ "Input" ]
, routes =
[ ServiceRoute.childLabel
"Platform"
"platform_drv"
(None Text)
(Some "ps2_drv")
]
}
, usb_drv =
Child.flat
Child.Attributes::{
, binary = "usb_drv"
, config = Init.Config::{
, attributes = toMap
{ uhci = "yes"
, ohci = "yes"
, ehci = "yes"
, xhci = "yes"
, bios_handoff = "yes"
}
, content =
[ XML.leaf
{ name = "hid", attributes = XML.emptyAttributes }
]
}
, provides = [ "Input" ]
, resources = Init.Resources::{
, caps = 128
, ram = Genode.units.MiB 12
}
, routes =
[ ServiceRoute.childLabel
"Platform"
"platform_drv"
(None Text)
(Some "usb_drv")
]
}
, wm =
Init.toChild
params.wm
Init.Attributes::{
, provides = [ "Capture", "Event", "Gui", "Report", "ROM" ]
, romReports = [ label "clipboard", label "shape" ]
, routes =
[ ServiceRoute.child "Framebuffer" "framebuffer"
, ServiceRoute.child "Input" "event_filter"
]
}
, fonts_fs =
Child.flat
Child.Attributes::{
, binary = "vfs"
, config = Init.Config::{
, content =
[ XML.element
{ name = "vfs"
, attributes = XML.emptyAttributes
, content =
[ XML.leaf
{ name = "rom"
, attributes = toMap
{ name = "Inconsolata.ttf" }
}
, XML.element
{ name = "dir"
, attributes = toMap { name = "fonts" }
, content =
[ XML.element
{ name = "dir"
, attributes = toMap { name = "title" }
, content =
[ XML.leaf
{ name = "ttf"
, attributes = toMap
{ name = "regular"
, path = "/Inconsolata.ttf"
, size_px = "12"
}
}
]
}
, XML.element
{ name = "dir"
, attributes = toMap { name = "text" }
, content =
[ XML.leaf
{ name = "ttf"
, attributes = toMap
{ name = "regular"
, path = "/Inconsolata.ttf"
, size_px = "12"
}
}
]
}
, XML.element
{ name = "dir"
, attributes = toMap
{ name = "annotation" }
, content =
[ XML.leaf
{ name = "ttf"
, attributes = toMap
{ name = "regular"
, path = "/Inconsolata.ttf"
, size_px = "12"
}
}
]
}
, XML.element
{ name = "dir"
, attributes = toMap
{ name = "monospace" }
, content =
[ XML.leaf
{ name = "ttf"
, attributes = toMap
{ name = "regular"
, path = "/Inconsolata.ttf"
, size_px = "12"
}
}
]
}
]
}
]
}
]
, defaultPolicy = Some DefaultPolicy::{
, attributes = toMap { root = "/fonts" }
}
}
, provides = [ "File_system" ]
, resources = Init.Resources::{
, caps = 256
, ram = Genode.units.MiB 12
}
, routes = [ ServiceRoute.child "Block" "block_router" ]
}
, graphical-log =
Init.toChild
params.graphical-log
Init.Attributes::{
, routes =
[ ServiceRoute.child "Gui" "wm"
, ServiceRoute.child "File_system" "fonts_fs"
]
}
, fs-log =
Init.toChild
params.fs-log
Init.Attributes::{
, routes =
[ ServiceRoute.childLabel
"File_system"
"chroot"
(None Text)
(Some "dump")
]
}
, rom_to_file =
Child.flat
Child.Attributes::{
, binary = "rom_to_file"
, config = Init.Config::{
, attributes = toMap { rom = "init.config" }
}
, routes =
[ ServiceRoute.childLabel
"File_system"
"chroot"
(None Text)
(Some "dump")
, ServiceRoute.parentLabel
"ROM"
(Some "init.config")
(Some "config")
]
}
, block =
Child.flat
Child.Attributes::{
, binary = "ahci_drv"
, config = Init.Config::{
, defaultPolicy = Some DefaultPolicy::{
, attributes = toMap { device = "0", writeable = "yes" }
}
}
, provides = [ "Block" ]
, resources = Init.Resources::{
, caps = 256
, ram = Genode.units.MiB 10
}
, routes =
[ ServiceRoute.childLabel
"Platform"
"platform_drv"
(None Text)
(Some "ahci_drv")
]
}
, block_partitions =
Child.flat
Child.Attributes::{
, binary = "part_block"
, config = Init.Config::{
, content =
Prelude.List.map
Natural
XML.Type
( λ(i : Natural) →
XML.leaf
{ name = "policy"
, attributes =
let partition =
Prelude.Natural.show (i + 1)
in toMap
{ label_suffix = " ${partition}"
, partition
, writeable = "yes"
}
}
)
(Prelude.Natural.enumerate 128)
# [ XML.leaf
{ name = "report"
, attributes = toMap { partitions = "yes" }
}
]
}
, resources = Resources::{
, caps = 256
, ram = Genode.units.MiB 8
}
, provides = [ "Block" ]
, routes =
[ ServiceRoute.child "Block" "block"
, ServiceRoute.child "Report" "block_router"
]
}
, block_router =
Child.flat
Child.Attributes::{
, binary = "block_router"
, config = Init.Config::{
, attributes = toMap { verbose = "yes" }
, content =
[ XML.element
{ name = "default-policy"
, attributes = XML.emptyAttributes
, content =
[ XML.leaf
{ name = "partition"
, attributes = toMap
{ type = params.partitionType
, writeable = "yes"
}
}
]
}
]
}
, resources = Resources::{
, caps = 256
, ram = Genode.units.MiB 8
}
, provides = [ "Block", "Report" ]
, routes = [ ServiceRoute.child "Block" "block_partitions" ]
}
, file_system =
Child.flat
Child.Attributes::{
, binary = "vfs"
, config = Init.Config::{
, content =
[ XML.element
{ name = "vfs"
, attributes = XML.emptyAttributes
, content =
[ XML.element
{ name = "dir"
, attributes = toMap { name = "ext2" }
, content =
[ XML.leaf
{ name = "rump"
, attributes = toMap
{ fs = "ext2fs"
, writeable = "yes"
, ram = "8M"
}
}
]
}
]
}
]
, policies =
[ Policy::{
, label = LabelSelector.suffix "nix/store"
, attributes = toMap
{ root = "/ext2/nix/store", writeable = "no" }
}
, Policy::{
, label = LabelSelector.prefix "chroot"
, attributes = toMap
{ root = "/ext2", writeable = "yes", path = "/" }
}
, Policy::{
, label = LabelSelector.prefix "init -> console"
, attributes = toMap { root = "/", writeable = "yes" }
}
]
}
, provides = [ "File_system" ]
, resources = Init.Resources::{
, caps = 256
, ram = Genode.units.MiB 12
}
, routes = [ ServiceRoute.child "Block" "block_router" ]
}
, chroot =
Child.flat
Child.Attributes::{
, binary = "chroot"
, config = Init.Config::{
, policies =
[ Policy::{
, label = LabelSelector.label "dump"
, attributes = toMap
{ path = params.systemLabel, writeable = "yes" }
}
]
, defaultPolicy = Some DefaultPolicy::{
, attributes = toMap { writeable = "yes" }
}
}
, provides = [ "File_system" ]
, routes = [ ServiceRoute.child "File_system" "file_system" ]
}
, nic_drv =
Child.flat
Child.Attributes::{
, binary = "ipxe_nic_drv"
, provides = [ "Nic" ]
, resources = Init.Resources::{
, caps = 128
, ram = Genode.units.MiB 4
}
, routes =
[ ServiceRoute.childLabel
"Platform"
"platform_drv"
(None Text)
(Some "nic_drv")
]
}
, nic_router =
Child.flat
Child.Attributes::{
, binary = "nic_router"
, config = Init.Config::{
, content =
[ XML.leaf
{ name = "uplink"
, attributes = toMap { domain = "uplink" }
}
, XML.element
{ name = "domain"
, attributes = toMap { name = "uplink" }
, content =
[ XML.leaf
{ name = "nat"
, attributes = toMap
{ domain = "default"
, tcp-ports = "1024"
, udp-ports = "1024"
, icmp-ids = "1024"
}
}
]
}
, XML.element
{ name = "domain"
, attributes = toMap
{ name = "default", interface = "10.0.1.1/24" }
, content =
[ XML.leaf
{ name = "dhcp-server"
, attributes = toMap
{ ip_first = "10.0.1.2"
, ip_last = "10.0.1.200"
, dns_server_from = "uplink"
}
}
]
# Prelude.List.map
Text
XML.Type
( λ(proto : Text) →
XML.element
{ name = proto
, attributes = toMap
{ dst = "0.0.0.0/0"
, domain = "uplink"
}
, content =
[ XML.leaf
{ name = "permit-any"
, attributes = toMap
{ domain = "uplink" }
}
]
}
)
[ "tcp", "udp", "icmp" ]
}
]
, defaultPolicy = Some DefaultPolicy::{
, attributes = toMap { domain = "default" }
}
}
, provides = [ "Nic" ]
, resources = Init.Resources::{ ram = Genode.units.MiB 8 }
, routes = [ ServiceRoute.child "Nic" "nic_drv" ]
}
, init =
Init.toChild
Init::{ children = params.guests }
Init.Attributes::{
, routes =
[ ServiceRoute.parent "VM"
, ServiceRoute.child "Gui" "wm"
, { service =
{ name = "File_system"
, label = LabelSelector.last "fonts"
}
, route =
Init.Route.Type.Child
{ name = "fonts_fs"
, label = None Text
, diag = None Bool
}
}
, { service =
{ name = "File_system"
, label = LabelSelector.suffix "nix/store"
}
, route =
Init.Route.Type.Child
{ name = "file_system"
, label = Some "nix/store"
, diag = None Bool
}
}
, { service =
{ name = "File_system"
, label = LabelSelector.prefix "console"
}
, route =
Init.Route.Type.Child
{ name = "file_system"
, label = None Text
, diag = None Bool
}
}
, ServiceRoute.child "File_system" "chroot"
, ServiceRoute.child "Nic" "nic_router"
, ServiceRoute.child "Rtc" "rtc"
, ServiceRoute.parentLabel
"ROM"
(Some "platform_info")
(Some "platform_info")
, ServiceRoute.child "Report" "_report_rom"
]
}
}
, routes = [ ServiceRoute.child "Timer" "timer" ]
}
in rootInit

View File

@ -1,41 +0,0 @@
let Genode = env:DHALL_GENODE
let Prelude = Genode.Prelude
let XML = Prelude.XML
let Init = Genode.Init
let Child = Init.Child
let Resources = Init.Resources
let ServiceRoute = Init.ServiceRoute
in Child.flat
Child.Attributes::{
, binary = "show_input"
, config = Init.Config::{
, content =
[ XML.element
{ name = "vfs"
, attributes = XML.emptyAttributes
, content =
[ XML.element
{ name = "dir"
, attributes = toMap { name = "fonts" }
, content =
[ XML.leaf
{ name = "fs"
, attributes = toMap { label = "fonts" }
}
]
}
]
}
]
}
, resources = Resources::{ ram = Genode.units.MiB 32 }
, routes =
[ ServiceRoute.parent "File_system", ServiceRoute.parent "Gui" ]
}

View File

@ -1,219 +0,0 @@
let Genode = env:DHALL_GENODE
let Prelude = Genode.Prelude
let XML = Prelude.XML
let Init = Genode.Init
let Child = Init.Child
let Resources = Init.Resources
let ServiceRoute = Init.ServiceRoute
let Libc = Genode.Libc
let VFS = Genode.VFS
let BootFormat = < ISO | VDI >
let Params
: Type
= { bootFilename : Text
, bootPkg : Text
, bootUuid : Text
, bootFormat : BootFormat
, memorySize : Natural
, vmName : Text
}
let toVbox =
λ(params : Params) →
let vboxConfig =
let hardDisks =
merge
{ ISO = XML.text ""
, VDI =
XML.leaf
{ name = "HardDisk"
, attributes = toMap
{ uuid = "{${params.bootUuid}}"
, location = "${params.bootFilename}"
, format = "VDI"
, type = "Normal"
}
}
}
params.bootFormat
let dvdImages =
merge
{ ISO =
XML.leaf
{ name = "Image"
, attributes = toMap
{ uuid = "{${params.bootUuid}}"
, location = "${params.bootFilename}"
}
}
, VDI = XML.text ""
}
params.bootFormat
let attachedDevices =
XML.element
{ name = "AttachedDevice"
, attributes =
merge
{ ISO = toMap
{ passthrough = "false"
, type = "DVD"
, port = "3"
, device = "0"
}
, VDI = toMap
{ type = "HardDisk", port = "0", device = "0" }
}
params.bootFormat
, content =
[ XML.leaf
{ name = "Image"
, attributes = toMap
{ uuid = "{${params.bootUuid}}" }
}
]
}
in ''
<VirtualBox xmlns="http://www.virtualbox.org/" version="1.14-freebsd">
<Machine uuid="{37ab43a5-38d8-4491-93f5-5b0b077f5c32}" name="${params.vmName}" OSType="Linux26_64" snapshotFolder="Snapshots" lastStateChange="2018-01-23T18:40:00Z">
<MediaRegistry>
<HardDisks>${XML.render hardDisks}</HardDisks>
<DVDImages>${XML.render dvdImages}</DVDImages>
</MediaRegistry>
<Hardware>
<CPU count="2">
<PAE enabled="true"/>
<LongMode enabled="true"/>
<HardwareVirtExLargePages enabled="false"/>
</CPU>
<Memory RAMSize="${Prelude.Natural.show
params.memorySize}"/>
<HID Pointing="USBTablet"/>
<Display VRAMSize="20"/>
<RemoteDisplay enabled="false"/>
<BIOS>
<IOAPIC enabled="true"/>
</BIOS>
<USB>
<Controllers/>
</USB>
<Network>
<Adapter slot="0" enabled="true" MACAddress="0800271D7901" cable="true" type="82540EM">
<BridgedInterface/>
</Adapter>
</Network>
<UART>
<Port slot="0" enabled="false" IOBase="0x3f8" IRQ="4" hostMode="Disconnected"/>
<Port slot="1" enabled="false" IOBase="0x2f8" IRQ="3" hostMode="Disconnected"/>
</UART>
<LPT>
<Port slot="0" enabled="false" IOBase="0x378" IRQ="7"/>
<Port slot="1" enabled="false" IOBase="0x378" IRQ="7"/>
</LPT>
<AudioAdapter controller="HDA" driver="OSS" enabled="false"/>
<RTC localOrUTC="UTC"/>
<SharedFolders/>
</Hardware>
<StorageControllers>
<StorageController name="SATA" type="AHCI" PortCount="4" useHostIOCache="true" Bootable="true" IDE0MasterEmulationPort="0" IDE0SlaveEmulationPort="1" IDE1MasterEmulationPort="2" IDE1SlaveEmulationPort="3">
${XML.render attachedDevices}
</StorageController>
</StorageControllers>
</Machine>
</VirtualBox>
''
in Child.flat
Child.Attributes::{
, binary = "virtualbox5"
, config =
( Libc.toConfig
Libc::{
, vfs =
let mutableVfs =
let fsNode =
[ XML.leaf
{ name = "fs"
, attributes = toMap
{ label = "nix/store"
, root = "${params.bootPkg}"
}
}
]
in merge
{ ISO =
[ XML.leaf
{ name = "fs"
, attributes = toMap
{ writeable = "yes" }
}
, XML.element
{ name = "import"
, attributes = toMap
{ overwrite = "no" }
, content = fsNode
}
]
, VDI =
[ XML.leaf
{ name = "fs"
, attributes = toMap
{ writeable = "yes" }
}
, XML.element
{ name = "import"
, attributes = toMap
{ overwrite = "no" }
, content = fsNode
}
]
}
params.bootFormat
in [ VFS.inline "machine.vbox" vboxConfig
, VFS.dir
"dev"
[ VFS.leaf "log"
, VFS.leaf "null"
, VFS.leaf "rtc"
]
]
# mutableVfs
}
)
with attributes = toMap
{ vbox_file = "machine.vbox", vm_name = params.vmName }
, resources = Resources::{
, caps = 1024
, ram =
Genode.units.MiB 128 + Genode.units.MiB params.memorySize
}
, routes =
[ ServiceRoute.parent "File_system"
, ServiceRoute.parent "Nic"
, ServiceRoute.parent "Gui"
, ServiceRoute.parent "Rtc"
, ServiceRoute.parent "Timer"
, ServiceRoute.parent "VM"
, ServiceRoute.parent "Report"
, ServiceRoute.parentLabel
"ROM"
(Some "platform_info")
(Some "platform_info")
]
}
in toVbox

View File

@ -1,27 +0,0 @@
let Genode = env:DHALL_GENODE
let ServiceRoute = Genode.Init.ServiceRoute
let Child = Genode.Init.Child
in Child.flat
Child.Attributes::{
, binary = "vesa_fb_drv"
, config = Genode.Init.Config::{
, attributes = toMap { width = "1024", height = "768" }
}
, provides = [ "Framebuffer" ]
, resources = Genode.Init.Resources::{
, caps = 256
, ram = Genode.units.MiB 16
}
, routes =
[ ServiceRoute.parent "IO_MEM"
, ServiceRoute.parent "IO_PORT"
, ServiceRoute.childLabel
"Platform"
"platform_drv"
(None Text)
(Some "vesa_fb_drv")
]
}

View File

@ -1,237 +0,0 @@
let Genode = env:DHALL_GENODE
let Prelude = Genode.Prelude
let XML = Prelude.XML
let Init = Genode.Init
let Child = Init.Child
let Resources = Init.Resources
let ServiceRoute = Init.ServiceRoute
let Policy = Init.Config.Policy
let DefaultPolicy = Init.Config.DefaultPolicy
let LabelSelector = Init.LabelSelector
let label =
λ(label : Text) →
{ local = label, route = label } : Child.Attributes.Label
in Init::{
, children = toMap
{ nitpicker =
Child.flat
Child.Attributes::{
, binary = "nitpicker"
, config = Init.Config::{
, attributes = toMap { focus = "rom" }
, content =
[ XML.text
''
<domain name="pointer" layer="1" content="client" label="no" origin="pointer" />
<domain name="default" layer="2" hover="always" />
<domain name="decorator" layer="2" content="client" label="no" hover="always" />
<domain name="desktop" layer="2" content="client" label="no" hover="always" />
<domain name="background" layer="3" content="client" label="no" hover="always" />
<global-key name="KEY_SCREEN" label="wm -> decorator" />
''
]
, policies =
[ Policy::{
, label = LabelSelector.prefix "pointer"
, attributes = toMap { domain = "pointer" }
}
, Policy::{
, label = LabelSelector.suffix "-> decorator"
, attributes = toMap { domain = "decorator" }
}
, Policy::{
, label = LabelSelector.prefix "wm"
, attributes = toMap { domain = "desktop" }
}
, Policy::{
, label = LabelSelector.label "backdrop"
, attributes = toMap { domain = "backdrop" }
}
]
, defaultPolicy = Some DefaultPolicy::{
, attributes = toMap { domain = "default" }
}
}
, provides = [ "Gui", "Capture", "Event" ]
, resources = Resources::{ caps = 256, ram = Genode.units.MiB 64 }
, routes =
[ ServiceRoute.parent "Framebuffer"
, ServiceRoute.parent "Input"
]
}
, pointer =
Child.flat
Child.Attributes::{
, binary = "pointer"
, routes = [ ServiceRoute.child "Gui" "nitpicker" ]
}
, wm =
Child.flat
Child.Attributes::{
, binary = "wm"
, config = Init.Config::{
, policies =
[ Policy::{
, attributes = toMap { role = "decorator" }
, label = LabelSelector.prefix "decorator"
}
, Policy::{
, attributes = toMap { role = "layouter" }
, label = LabelSelector.prefix "layouter"
}
]
, defaultPolicy = Some DefaultPolicy::{=}
}
, provides = [ "Gui", "Report", "ROM" ]
, reportRoms = [ label "focus", label "resize_request" ]
, romReports =
[ label "focus_request"
, label "pointer"
, label "shape"
, label "window_list"
]
, resources = Init.Resources::{
, caps = 256
, ram = Genode.units.MiB 8
}
, routes =
[ ServiceRoute.childLabel
"Gui"
"nitpicker"
(Some "")
(Some "focus")
, ServiceRoute.child "Gui" "nitpicker"
, ServiceRoute.parentLabel
"Report"
(Some "clipboard")
(Some "clipboard")
, ServiceRoute.parentLabel
"Report"
(Some "shape")
(Some "shape")
]
}
, layouter =
Child.flat
Child.Attributes::{
, binary = "window_layouter"
, config = Init.Config::{
, attributes = toMap { rules = "rom" }
, content =
[ XML.text
''
<report rules="yes"/>
<rules>
<screen name="screen_1"/>
<screen name="screen_2"/>
<screen name="screen_3"/>
<screen name="screen_4"/>
<screen name="screen_5"/>
<screen name="screen_6"/>
<screen name="screen_7"/>
<screen name="screen_8"/>
<screen name="screen_9"/>
<screen name="screen_0"/>
<assign label_prefix="" target="screen_1" xpos="any" ypos="any"/>
</rules>
<press key="KEY_SCREEN">
<press key="KEY_TAB" action="next_window">
<release key="KEY_TAB">
<release key="KEY_SCREEN" action="raise_window"/>
</release>
<release key="KEY_SCREEN" action="raise_window"/>
</press>
<press key="KEY_LEFTSHIFT">
<press key="KEY_TAB" action="prev_window">
<release key="KEY_TAB">
<release key="KEY_SCREEN" action="raise_window"/>
</release>
</press>
</press>
<press key="KEY_ENTER" action="toggle_fullscreen"/>
<press key="KEY_1" action="screen" target="screen_1"/>
<press key="KEY_2" action="screen" target="screen_2"/>
<press key="KEY_3" action="screen" target="screen_3"/>
<press key="KEY_4" action="screen" target="screen_4"/>
<press key="KEY_5" action="screen" target="screen_5"/>
<press key="KEY_6" action="screen" target="screen_6"/>
<press key="KEY_7" action="screen" target="screen_7"/>
<press key="KEY_8" action="screen" target="screen_8"/>
<press key="KEY_9" action="screen" target="screen_9"/>
<press key="KEY_0" action="screen" target="screen_0"/>
</press>
''
]
}
, provides = [ "Gui", "Report", "ROM" ]
, romReports =
[ label "focus"
, label "resize_request"
, label "rules"
, label "window_layout"
]
, reportRoms =
[ label "decorator_margins"
, label "focus_request"
, label "hover"
, label "rules"
, label "shape"
, label "window_list"
]
, resources = Init.Resources::{
, caps = 256
, ram = Genode.units.MiB 8
}
, routes = [ ServiceRoute.child "Gui" "wm" ]
}
, decorator =
Child.flat
Child.Attributes::{
, binary = "decorator"
, config = Init.Config::{
, content =
[ XML.element
{ name = "controls"
, attributes = XML.emptyAttributes
, content =
[ XML.leaf
{ name = "maximizer"
, attributes = XML.emptyAttributes
}
, XML.leaf
{ name = "title", attributes = XML.emptyAttributes }
]
}
]
, defaultPolicy = Some DefaultPolicy::{=}
}
, provides = [ "Gui", "Report", "ROM" ]
, reportRoms = [ label "window_layout", label "pointer" ]
, romReports = [ label "decorator_margins", label "hover" ]
, resources = Init.Resources::{
, caps = 128
, ram = Genode.units.MiB 12
}
, routes = [ ServiceRoute.child "Gui" "wm" ]
}
}
, routes = [ ServiceRoute.parent "Timer" ]
, services =
[ ServiceRoute.child "Gui" "wm"
, ServiceRoute.child "Report" "wm"
, ServiceRoute.child "ROM" "wm"
, ServiceRoute.child "Event" "Nitpicker"
, ServiceRoute.child "Capture" "Nitpicker"
]
}

View File

@ -1,245 +0,0 @@
let Genode = env:DHALL_GENODE
let Prelude = Genode.Prelude
let XML = Prelude.XML
let Key = < Ascii : Natural | Char : Text | Code : Natural > : Type
let Map =
{ Type =
{ keys : Prelude.Map.Type Text Key
, mod1 : Bool
, mod2 : Bool
, mod3 : Bool
, mod4 : Bool
}
, default = { mod1 = False, mod2 = False, mod3 = False, mod4 = False }
}
let boolToAttr = λ(_ : Bool) → if _ then "yes" else "no"
let keyToXML =
λ(x : Prelude.Map.Entry Text Key) →
XML.leaf
{ name = "key"
, attributes =
[ merge
{ Ascii =
λ(_ : Natural) →
{ mapKey = "ascii", mapValue = Prelude.Natural.show _ }
, Char = λ(_ : Text) → { mapKey = "char", mapValue = _ }
, Code =
λ(_ : Natural) →
{ mapKey = "code", mapValue = Prelude.Natural.show _ }
}
x.mapValue
, { mapKey = "name", mapValue = x.mapKey }
]
}
let mapToXML =
λ(map : Map.Type) →
XML.element
{ name = "map"
, attributes = toMap
{ mod1 = boolToAttr map.mod1
, mod2 = boolToAttr map.mod2
, mod3 = boolToAttr map.mod3
, mod4 = boolToAttr map.mod4
}
, content =
Prelude.List.map
(Prelude.Map.Entry Text Key)
XML.Type
keyToXML
map.keys
}
let workman =
[ Map::{
, keys = toMap
{ KEY_ESC = Key.Ascii 27
, KEY_1 = Key.Char "1"
, KEY_2 = Key.Char "2"
, KEY_3 = Key.Char "3"
, KEY_4 = Key.Char "4"
, KEY_5 = Key.Char "5"
, KEY_6 = Key.Char "6"
, KEY_7 = Key.Char "7"
, KEY_8 = Key.Char "8"
, KEY_9 = Key.Char "9"
, KEY_0 = Key.Char "0"
, KEY_MINUS = Key.Char "-"
, KEY_EQUAL = Key.Char "="
, KEY_BACKSPACE = Key.Ascii 8
, KEY_TAB = Key.Ascii 9
, KEY_Q = Key.Char "q"
, KEY_W = Key.Char "d"
, KEY_E = Key.Char "r"
, KEY_R = Key.Char "w"
, KEY_T = Key.Char "b"
, KEY_Y = Key.Char "j"
, KEY_U = Key.Char "f"
, KEY_I = Key.Char "u"
, KEY_O = Key.Char "p"
, KEY_P = Key.Char ";"
, KEY_LEFTBRACE = Key.Char "["
, KEY_RIGHTBRACE = Key.Char "]"
, KEY_ENTER = Key.Ascii 10
, KEY_A = Key.Char "a"
, KEY_S = Key.Char "s"
, KEY_D = Key.Char "h"
, KEY_F = Key.Char "t"
, KEY_G = Key.Char "g"
, KEY_H = Key.Char "y"
, KEY_J = Key.Char "n"
, KEY_K = Key.Char "e"
, KEY_L = Key.Char "o"
, KEY_SEMICOLON = Key.Char "i"
, KEY_APOSTROPHE = Key.Char "'"
, KEY_GRAVE = Key.Char "`"
, KEY_BACKSLASH = Key.Ascii 92
, KEY_Z = Key.Char "z"
, KEY_X = Key.Char "x"
, KEY_C = Key.Char "m"
, KEY_V = Key.Char "c"
, KEY_B = Key.Char "v"
, KEY_N = Key.Char "k"
, KEY_M = Key.Char "l"
, KEY_COMMA = Key.Char ","
, KEY_DOT = Key.Char "."
, KEY_SLASH = Key.Char "/"
, KEY_SPACE = Key.Char " "
, KEY_KP7 = Key.Char "7"
, KEY_KP8 = Key.Char "8"
, KEY_KP9 = Key.Char "9"
, KEY_KPMINUS = Key.Char "-"
, KEY_KP4 = Key.Char "4"
, KEY_KP5 = Key.Char "5"
, KEY_KP6 = Key.Char "6"
, KEY_KPPLUS = Key.Char "+"
, KEY_KP1 = Key.Char "1"
, KEY_KP2 = Key.Char "2"
, KEY_KP3 = Key.Char "3"
, KEY_KP0 = Key.Char "0"
, KEY_KPDOT = Key.Char "."
, KEY_KPENTER = Key.Ascii 10
, KEY_KPSLASH = Key.Char "/"
}
}
, Map::{
, mod1 = True
, keys = toMap
{ KEY_1 = Key.Char "!"
, KEY_2 = Key.Char "@"
, KEY_3 = Key.Char "#"
, KEY_4 = Key.Char "\$"
, KEY_5 = Key.Char "%"
, KEY_6 = Key.Char "^"
, KEY_7 = Key.Ascii 38
, KEY_8 = Key.Char "*"
, KEY_9 = Key.Char "("
, KEY_0 = Key.Char ")"
, KEY_MINUS = Key.Char "_"
, KEY_EQUAL = Key.Char "+"
, KEY_Q = Key.Char "Q"
, KEY_W = Key.Char "D"
, KEY_E = Key.Char "R"
, KEY_R = Key.Char "W"
, KEY_T = Key.Char "B"
, KEY_Y = Key.Char "J"
, KEY_U = Key.Char "F"
, KEY_I = Key.Char "U"
, KEY_O = Key.Char "P"
, KEY_P = Key.Char ":"
, KEY_LEFTBRACE = Key.Char "{"
, KEY_RIGHTBRACE = Key.Char "}"
, KEY_A = Key.Char "A"
, KEY_S = Key.Char "S"
, KEY_D = Key.Char "H"
, KEY_F = Key.Char "T"
, KEY_G = Key.Char "G"
, KEY_H = Key.Char "Y"
, KEY_J = Key.Char "N"
, KEY_K = Key.Char "E"
, KEY_L = Key.Char "O"
, KEY_SEMICOLON = Key.Char "I"
, KEY_APOSTROPHE = Key.Ascii 34
, KEY_GRAVE = Key.Char "~"
, KEY_BACKSLASH = Key.Char "|"
, KEY_Z = Key.Char "Z"
, KEY_X = Key.Char "X"
, KEY_C = Key.Char "M"
, KEY_V = Key.Char "C"
, KEY_B = Key.Char "V"
, KEY_N = Key.Char "K"
, KEY_M = Key.Char "L"
, KEY_COMMA = Key.Ascii 60
, KEY_DOT = Key.Ascii 62
, KEY_SLASH = Key.Char "?"
}
}
, Map::{
, mod2 = True
, keys = toMap
{ KEY_A = Key.Ascii 1
, KEY_B = Key.Ascii 22
, KEY_C = Key.Ascii 13
, KEY_D = Key.Ascii 8
, KEY_E = Key.Ascii 18
, KEY_F = Key.Ascii 20
, KEY_G = Key.Ascii 7
, KEY_H = Key.Ascii 25
, KEY_I = Key.Ascii 21
, KEY_J = Key.Ascii 14
, KEY_K = Key.Ascii 5
, KEY_L = Key.Ascii 15
, KEY_M = Key.Ascii 12
, KEY_N = Key.Ascii 11
, KEY_O = Key.Ascii 16
, KEY_P = Key.Ascii 9
, KEY_Q = Key.Ascii 17
, KEY_R = Key.Ascii 23
, KEY_S = Key.Ascii 19
, KEY_T = Key.Ascii 2
, KEY_U = Key.Ascii 6
, KEY_V = Key.Ascii 3
, KEY_W = Key.Ascii 4
, KEY_X = Key.Ascii 24
, KEY_Y = Key.Ascii 10
, KEY_Z = Key.Ascii 26
}
}
, Map::{
, mod3 = True
, keys = toMap
{ KEY_4 = Key.Code 8364
, KEY_A = Key.Code 228
, KEY_S = Key.Code 223
, KEY_I = Key.Code 252
, KEY_DOT = Key.Code 8230
, KEY_K = Key.Code 235
, KEY_C = Key.Code 181
, KEY_L = Key.Code 246
}
}
, Map::{
, mod1 = True
, mod3 = True
, keys = toMap
{ KEY_0 = Key.Code 8320
, KEY_1 = Key.Code 8321
, KEY_2 = Key.Code 8322
, KEY_3 = Key.Code 8323
, KEY_4 = Key.Code 8324
, KEY_5 = Key.Code 8325
, KEY_6 = Key.Code 8326
, KEY_7 = Key.Code 8327
, KEY_8 = Key.Code 8328
, KEY_9 = Key.Code 8329
}
}
]
in Prelude.List.map Map.Type XML.Type mapToXML workman

View File

@ -1,912 +0,0 @@
#! /somewhere/python3
# Copyright (c) 2003-2020 Nixpkgs/NixOS contributors
from contextlib import contextmanager, _GeneratorContextManager
from queue import Queue, Empty
from typing import Tuple, Any, Callable, Dict, Iterator, Optional, List
from xml.sax.saxutils import XMLGenerator
import _thread
import atexit
import base64
import codecs
import os
import pathlib
import ptpython.repl
import pty
import re
import shlex
import shutil
import socket
import subprocess
import sys
import tempfile
import time
import unicodedata
CHAR_TO_KEY = {
"A": "shift-a",
"N": "shift-n",
"-": "0x0C",
"_": "shift-0x0C",
"B": "shift-b",
"O": "shift-o",
"=": "0x0D",
"+": "shift-0x0D",
"C": "shift-c",
"P": "shift-p",
"[": "0x1A",
"{": "shift-0x1A",
"D": "shift-d",
"Q": "shift-q",
"]": "0x1B",
"}": "shift-0x1B",
"E": "shift-e",
"R": "shift-r",
";": "0x27",
":": "shift-0x27",
"F": "shift-f",
"S": "shift-s",
"'": "0x28",
'"': "shift-0x28",
"G": "shift-g",
"T": "shift-t",
"`": "0x29",
"~": "shift-0x29",
"H": "shift-h",
"U": "shift-u",
"\\": "0x2B",
"|": "shift-0x2B",
"I": "shift-i",
"V": "shift-v",
",": "0x33",
"<": "shift-0x33",
"J": "shift-j",
"W": "shift-w",
".": "0x34",
">": "shift-0x34",
"K": "shift-k",
"X": "shift-x",
"/": "0x35",
"?": "shift-0x35",
"L": "shift-l",
"Y": "shift-y",
" ": "spc",
"M": "shift-m",
"Z": "shift-z",
"\n": "ret",
"!": "shift-0x02",
"@": "shift-0x03",
"#": "shift-0x04",
"$": "shift-0x05",
"%": "shift-0x06",
"^": "shift-0x07",
"&": "shift-0x08",
"*": "shift-0x09",
"(": "shift-0x0A",
")": "shift-0x0B",
}
# Forward references
log: "Logger"
machines: "List[Machine]"
def eprint(*args: object, **kwargs: Any) -> None:
print(*args, file=sys.stderr, **kwargs)
def make_command(args: list) -> str:
return " ".join(map(shlex.quote, (map(str, args))))
def retry(fn: Callable) -> None:
"""Call the given function repeatedly, with 1 second intervals,
until it returns True or a timeout is reached.
"""
for _ in range(900):
if fn(False):
return
time.sleep(1)
if not fn(True):
raise Exception("action timed out")
class Logger:
def __init__(self) -> None:
self.logfile = os.environ.get("LOGFILE", "/dev/null")
self.logfile_handle = codecs.open(self.logfile, "wb")
self.xml = XMLGenerator(self.logfile_handle, encoding="utf-8")
self.queue: "Queue[Dict[str, str]]" = Queue()
self.xml.startDocument()
self.xml.startElement("logfile", attrs={})
def close(self) -> None:
self.xml.endElement("logfile")
self.xml.endDocument()
self.logfile_handle.close()
def sanitise(self, message: str) -> str:
return "".join(ch for ch in message if unicodedata.category(ch)[0] != "C")
def maybe_prefix(self, message: str, attributes: Dict[str, str]) -> str:
if "machine" in attributes:
return "{}: {}".format(attributes["machine"], message)
return message
def log_line(self, message: str, attributes: Dict[str, str]) -> None:
self.xml.startElement("line", attributes)
self.xml.characters(message)
self.xml.endElement("line")
def log(self, message: str, attributes: Dict[str, str] = {}) -> None:
eprint(self.maybe_prefix(message, attributes))
self.drain_log_queue()
self.log_line(message, attributes)
def enqueue(self, message: Dict[str, str]) -> None:
self.queue.put(message)
def drain_log_queue(self) -> None:
try:
while True:
item = self.queue.get_nowait()
attributes = {"machine": item["machine"], "type": "serial"}
self.log_line(self.sanitise(item["msg"]), attributes)
except Empty:
pass
@contextmanager
def nested(self, message: str, attributes: Dict[str, str] = {}) -> Iterator[None]:
eprint(self.maybe_prefix(message, attributes))
self.xml.startElement("nest", attrs={})
self.xml.startElement("head", attributes)
self.xml.characters(message)
self.xml.endElement("head")
tic = time.time()
self.drain_log_queue()
yield
self.drain_log_queue()
toc = time.time()
self.log("({:.2f} seconds)".format(toc - tic))
self.xml.endElement("nest")
class Machine:
def __init__(self, args: Dict[str, Any]) -> None:
if "name" in args:
self.name = args["name"]
else:
self.name = "machine"
cmd = args.get("startCommand", None)
if cmd:
match = re.search("bin/run-(.+)-vm$", cmd)
if match:
self.name = match.group(1)
self.script = args.get("startCommand", self.create_startcommand(args))
tmp_dir = os.environ.get("TMPDIR", tempfile.gettempdir())
def create_dir(name: str) -> str:
path = os.path.join(tmp_dir, name)
os.makedirs(path, mode=0o700, exist_ok=True)
return path
self.state_dir = create_dir("vm-state-{}".format(self.name))
self.shared_dir = create_dir("shared-xchg")
self.booted = False
self.connected = False
self.pid: Optional[int] = None
self.socket = None
self.monitor: Optional[socket.socket] = None
self.logger: Logger = args["log"]
self.serialQueue: "Queue[str]" = Queue()
self.allow_reboot = args.get("allowReboot", False)
@staticmethod
def create_startcommand(args: Dict[str, str]) -> str:
net_backend = "-netdev user,id=net0"
net_frontend = "-device virtio-net-pci,netdev=net0"
start_command = "qemu-system-x86_64 -m 384 $QEMU_OPTS "
if "hda" in args:
hda_path = os.path.abspath(args["hda"])
if args.get("hdaInterface", "") == "scsi":
start_command += (
"-drive id=hda,file="
+ hda_path
+ ",werror=report,if=none "
+ "-device scsi-hd,drive=hda "
)
else:
start_command += (
"-drive file="
+ hda_path
+ ",if="
+ args["hdaInterface"]
+ ",werror=report "
)
if "cdrom" in args:
start_command += "-cdrom " + args["cdrom"] + " "
if "usb" in args:
start_command += (
"-device piix3-usb-uhci -drive "
+ "id=usbdisk,file="
+ args["usb"]
+ ",if=none,readonly "
+ "-device usb-storage,drive=usbdisk "
)
if "bios" in args:
start_command += "-bios " + args["bios"] + " "
start_command += args.get("qemuFlags", "")
return start_command
def is_up(self) -> bool:
return self.booted and self.connected
def log(self, msg: str) -> None:
self.logger.log(msg, {"machine": self.name})
def nested(self, msg: str, attrs: Dict[str, str] = {}) -> _GeneratorContextManager:
my_attrs = {"machine": self.name}
my_attrs.update(attrs)
return self.logger.nested(msg, my_attrs)
def wait_for_monitor_prompt(self) -> str:
assert self.monitor is not None
answer = ""
while True:
undecoded_answer = self.monitor.recv(1024)
if not undecoded_answer:
break
answer += undecoded_answer.decode()
if answer.endswith("(qemu) "):
break
return answer
def send_monitor_command(self, command: str) -> str:
message = ("{}\n".format(command)).encode()
self.log("sending monitor command: {}".format(command))
assert self.monitor is not None
self.monitor.send(message)
return self.wait_for_monitor_prompt()
def wait_for_unit(self, unit: str, user: Optional[str] = None) -> None:
"""Wait for a systemd unit to get into "active" state.
Throws exceptions on "failed" and "inactive" states as well as
after timing out.
"""
def check_active(_: Any) -> bool:
info = self.get_unit_info(unit, user)
state = info["ActiveState"]
if state == "failed":
raise Exception('unit "{}" reached state "{}"'.format(unit, state))
if state == "inactive":
status, jobs = self.systemctl("list-jobs --full 2>&1", user)
if "No jobs" in jobs:
info = self.get_unit_info(unit, user)
if info["ActiveState"] == state:
raise Exception(
(
'unit "{}" is inactive and there ' "are no pending jobs"
).format(unit)
)
return state == "active"
retry(check_active)
def get_unit_info(self, unit: str, user: Optional[str] = None) -> Dict[str, str]:
status, lines = self.systemctl('--no-pager show "{}"'.format(unit), user)
if status != 0:
raise Exception(
'retrieving systemctl info for unit "{}" {} failed with exit code {}'.format(
unit, "" if user is None else 'under user "{}"'.format(user), status
)
)
line_pattern = re.compile(r"^([^=]+)=(.*)$")
def tuple_from_line(line: str) -> Tuple[str, str]:
match = line_pattern.match(line)
assert match is not None
return match[1], match[2]
return dict(
tuple_from_line(line)
for line in lines.split("\n")
if line_pattern.match(line)
)
def systemctl(self, q: str, user: Optional[str] = None) -> Tuple[int, str]:
if user is not None:
q = q.replace("'", "\\'")
return self.execute(
(
"su -l {} --shell /bin/sh -c "
"$'XDG_RUNTIME_DIR=/run/user/`id -u` "
"systemctl --user {}'"
).format(user, q)
)
return self.execute("systemctl {}".format(q))
def require_unit_state(self, unit: str, require_state: str = "active") -> None:
with self.nested(
"checking if unit {} has reached state '{}'".format(unit, require_state)
):
info = self.get_unit_info(unit)
state = info["ActiveState"]
if state != require_state:
raise Exception(
"Expected unit {} to to be in state ".format(unit)
+ "'{}' but it is in state {}".format(require_state, state)
)
def execute(self, command: str) -> Tuple[int, str]:
self.connect()
out_command = "( {} ); echo '|!=EOF' $?\n".format(command)
self.shell.send(out_command.encode())
output = ""
status_code_pattern = re.compile(r"(.*)\|\!=EOF\s+(\d+)")
while True:
chunk = self.shell.recv(4096).decode(errors="ignore")
match = status_code_pattern.match(chunk)
if match:
output += match[1]
status_code = int(match[2])
return (status_code, output)
output += chunk
def succeed(self, *commands: str) -> str:
"""Execute each command and check that it succeeds."""
output = ""
for command in commands:
with self.nested("must succeed: {}".format(command)):
(status, out) = self.execute(command)
if status != 0:
self.log("output: {}".format(out))
raise Exception(
"command `{}` failed (exit code {})".format(command, status)
)
output += out
return output
def fail(self, *commands: str) -> None:
"""Execute each command and check that it fails."""
for command in commands:
with self.nested("must fail: {}".format(command)):
status, output = self.execute(command)
if status == 0:
raise Exception(
"command `{}` unexpectedly succeeded".format(command)
)
def wait_until_succeeds(self, command: str) -> str:
"""Wait until a command returns success and return its output.
Throws an exception on timeout.
"""
output = ""
def check_success(_: Any) -> bool:
nonlocal output
status, output = self.execute(command)
return status == 0
with self.nested("waiting for success: {}".format(command)):
retry(check_success)
return output
def wait_until_fails(self, command: str) -> str:
"""Wait until a command returns failure.
Throws an exception on timeout.
"""
output = ""
def check_failure(_: Any) -> bool:
nonlocal output
status, output = self.execute(command)
return status != 0
with self.nested("waiting for failure: {}".format(command)):
retry(check_failure)
return output
def wait_for_shutdown(self) -> None:
if not self.booted:
return
with self.nested("waiting for the VM to power off"):
sys.stdout.flush()
self.process.wait()
self.pid = None
self.booted = False
self.connected = False
def get_tty_text(self, tty: str) -> str:
status, output = self.execute(
"fold -w$(stty -F /dev/tty{0} size | "
"awk '{{print $2}}') /dev/vcs{0}".format(tty)
)
return output
def wait_until_tty_matches(self, tty: str, regexp: str) -> None:
"""Wait until the visible output on the chosen TTY matches regular
expression. Throws an exception on timeout.
"""
matcher = re.compile(regexp)
def tty_matches(last: bool) -> bool:
text = self.get_tty_text(tty)
if last:
self.log(
f"Last chance to match /{regexp}/ on TTY{tty}, "
f"which currently contains: {text}"
)
return len(matcher.findall(text)) > 0
with self.nested("waiting for {} to appear on tty {}".format(regexp, tty)):
retry(tty_matches)
def wait_until_serial_output(self, regexp: str) -> None:
"""Wait until the serial output matches regular expression.
Throws an exception on timeout.
"""
matcher = re.compile(regexp)
def serial_matches(last: bool) -> bool:
while not self.serialQueue.empty():
text = self.serialQueue.get()
if last:
self.log(
f"Last chance to match /{regexp}/ on serial, "
f"which currently contains: {text}"
)
if len(matcher.findall(text)) > 0:
return True
return False
with self.nested("waiting for {} to appear on serial output".format(regexp)):
retry(serial_matches)
def send_chars(self, chars: List[str]) -> None:
with self.nested("sending keys {}".format(chars)):
for char in chars:
self.send_key(char)
def wait_for_file(self, filename: str) -> None:
"""Waits until the file exists in machine's file system."""
def check_file(_: Any) -> bool:
status, _ = self.execute("test -e {}".format(filename))
return status == 0
with self.nested("waiting for file {}".format(filename)):
retry(check_file)
def wait_for_open_port(self, port: int) -> None:
def port_is_open(_: Any) -> bool:
status, _ = self.execute("nc -z localhost {}".format(port))
return status == 0
with self.nested("waiting for TCP port {}".format(port)):
retry(port_is_open)
def wait_for_closed_port(self, port: int) -> None:
def port_is_closed(_: Any) -> bool:
status, _ = self.execute("nc -z localhost {}".format(port))
return status != 0
retry(port_is_closed)
def start_job(self, jobname: str, user: Optional[str] = None) -> Tuple[int, str]:
return self.systemctl("start {}".format(jobname), user)
def stop_job(self, jobname: str, user: Optional[str] = None) -> Tuple[int, str]:
return self.systemctl("stop {}".format(jobname), user)
def wait_for_job(self, jobname: str) -> None:
self.wait_for_unit(jobname)
def connect(self) -> None:
if self.connected:
return
with self.nested("waiting for the VM to finish booting"):
self.start()
tic = time.time()
self.shell.recv(1024)
# TODO: Timeout
toc = time.time()
self.log("connected to guest root shell")
self.log("(connecting took {:.2f} seconds)".format(toc - tic))
self.connected = True
def screenshot(self, filename: str) -> None:
out_dir = os.environ.get("out", os.getcwd())
word_pattern = re.compile(r"^\w+$")
if word_pattern.match(filename):
filename = os.path.join(out_dir, "{}.png".format(filename))
tmp = "{}.ppm".format(filename)
with self.nested(
"making screenshot {}".format(filename),
{"image": os.path.basename(filename)},
):
self.send_monitor_command("screendump {}".format(tmp))
ret = subprocess.run("pnmtopng {} > {}".format(tmp, filename), shell=True)
os.unlink(tmp)
if ret.returncode != 0:
raise Exception("Cannot convert screenshot")
def copy_from_host_via_shell(self, source: str, target: str) -> None:
"""Copy a file from the host into the guest by piping it over the
shell into the destination file. Works without host-guest shared folder.
Prefer copy_from_host for whenever possible.
"""
with open(source, "rb") as fh:
content_b64 = base64.b64encode(fh.read()).decode()
self.succeed(
f"mkdir -p $(dirname {target})",
f"echo -n {content_b64} | base64 -d > {target}",
)
def copy_from_host(self, source: str, target: str) -> None:
"""Copy a file from the host into the guest via the `shared_dir` shared
among all the VMs (using a temporary directory).
"""
host_src = pathlib.Path(source)
vm_target = pathlib.Path(target)
with tempfile.TemporaryDirectory(dir=self.shared_dir) as shared_td:
shared_temp = pathlib.Path(shared_td)
host_intermediate = shared_temp / host_src.name
vm_shared_temp = pathlib.Path("/tmp/shared") / shared_temp.name
vm_intermediate = vm_shared_temp / host_src.name
self.succeed(make_command(["mkdir", "-p", vm_shared_temp]))
if host_src.is_dir():
shutil.copytree(host_src, host_intermediate)
else:
shutil.copy(host_src, host_intermediate)
self.succeed("sync")
self.succeed(make_command(["mkdir", "-p", vm_target.parent]))
self.succeed(make_command(["cp", "-r", vm_intermediate, vm_target]))
# Make sure the cleanup is synced into VM
self.succeed("sync")
def copy_from_vm(self, source: str, target_dir: str = "") -> None:
"""Copy a file from the VM (specified by an in-VM source path) to a path
relative to `$out`. The file is copied via the `shared_dir` shared among
all the VMs (using a temporary directory).
"""
# Compute the source, target, and intermediate shared file names
out_dir = pathlib.Path(os.environ.get("out", os.getcwd()))
vm_src = pathlib.Path(source)
with tempfile.TemporaryDirectory(dir=self.shared_dir) as shared_td:
shared_temp = pathlib.Path(shared_td)
vm_shared_temp = pathlib.Path("/tmp/shared") / shared_temp.name
vm_intermediate = vm_shared_temp / vm_src.name
intermediate = shared_temp / vm_src.name
# Copy the file to the shared directory inside VM
self.succeed(make_command(["mkdir", "-p", vm_shared_temp]))
self.succeed(make_command(["cp", "-r", vm_src, vm_intermediate]))
self.succeed("sync")
abs_target = out_dir / target_dir / vm_src.name
abs_target.parent.mkdir(exist_ok=True, parents=True)
# Copy the file from the shared directory outside VM
if intermediate.is_dir():
shutil.copytree(intermediate, abs_target)
else:
shutil.copy(intermediate, abs_target)
# Make sure the cleanup is synced into VM
self.succeed("sync")
def dump_tty_contents(self, tty: str) -> None:
"""Debugging: Dump the contents of the TTY<n>
"""
self.execute("fold -w 80 /dev/vcs{} | systemd-cat".format(tty))
def get_screen_text(self) -> str:
if shutil.which("tesseract") is None:
raise Exception("get_screen_text used but enableOCR is false")
magick_args = (
"-filter Catrom -density 72 -resample 300 "
+ "-contrast -normalize -despeckle -type grayscale "
+ "-sharpen 1 -posterize 3 -negate -gamma 100 "
+ "-blur 1x65535"
)
tess_args = "-c debug_file=/dev/null --psm 11 --oem 2"
with self.nested("performing optical character recognition"):
with tempfile.NamedTemporaryFile() as tmpin:
self.send_monitor_command("screendump {}".format(tmpin.name))
cmd = "convert {} {} tiff:- | tesseract - - {}".format(
magick_args, tmpin.name, tess_args
)
ret = subprocess.run(cmd, shell=True, capture_output=True)
if ret.returncode != 0:
raise Exception(
"OCR failed with exit code {}".format(ret.returncode)
)
return ret.stdout.decode("utf-8")
def wait_for_text(self, regex: str) -> None:
def screen_matches(last: bool) -> bool:
text = self.get_screen_text()
matches = re.search(regex, text) is not None
if last and not matches:
self.log("Last OCR attempt failed. Text was: {}".format(text))
return matches
with self.nested("waiting for {} to appear on screen".format(regex)):
retry(screen_matches)
def send_key(self, key: str) -> None:
key = CHAR_TO_KEY.get(key, key)
self.send_monitor_command("sendkey {}".format(key))
def start(self) -> None:
if self.booted:
return
self.log("starting vm")
def create_socket(path: str) -> socket.socket:
if os.path.exists(path):
os.unlink(path)
s = socket.socket(family=socket.AF_UNIX, type=socket.SOCK_STREAM)
s.bind(path)
s.listen(1)
return s
monitor_path = os.path.join(self.state_dir, "monitor")
self.monitor_socket = create_socket(monitor_path)
shell_path = os.path.join(self.state_dir, "shell")
self.shell_socket = create_socket(shell_path)
qemu_options = (
" ".join(
[
"" if self.allow_reboot else "-no-reboot",
"-monitor unix:{}".format(monitor_path),
"-chardev socket,id=shell,path={}".format(shell_path),
"-device virtio-serial",
"-device virtconsole,chardev=shell",
"-device virtio-rng-pci",
"-serial stdio" if "DISPLAY" in os.environ else "-nographic",
]
)
+ " "
+ os.environ.get("QEMU_OPTS", "")
)
environment = dict(os.environ)
environment.update(
{
"TMPDIR": self.state_dir,
"SHARED_DIR": self.shared_dir,
"USE_TMPDIR": "1",
"QEMU_OPTS": qemu_options,
}
)
self.process = subprocess.Popen(
self.script,
bufsize=1,
stdin=subprocess.DEVNULL,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
shell=True,
cwd=self.state_dir,
env=environment,
)
self.monitor, _ = self.monitor_socket.accept()
self.shell, _ = self.shell_socket.accept()
def process_serial_output() -> None:
assert self.process.stdout is not None
for _line in self.process.stdout:
# Ignore undecodable bytes that may occur in boot menus
line = _line.decode(errors="ignore").replace("\r", "").rstrip()
eprint("{} # {}".format(self.name, line))
self.logger.enqueue({"msg": line, "machine": self.name})
self.serialQueue.put(line)
_thread.start_new_thread(process_serial_output, ())
self.wait_for_monitor_prompt()
self.pid = self.process.pid
self.booted = True
self.log("QEMU running (pid {})".format(self.pid))
def shutdown(self) -> None:
if not self.booted:
return
self.shell.send("poweroff\n".encode())
self.wait_for_shutdown()
def crash(self) -> None:
if not self.booted:
return
self.log("forced crash")
self.send_monitor_command("quit")
self.wait_for_shutdown()
def wait_for_x(self) -> None:
"""Wait until it is possible to connect to the X server. Note that
testing the existence of /tmp/.X11-unix/X0 is insufficient.
"""
def check_x(_: Any) -> bool:
cmd = (
"journalctl -b SYSLOG_IDENTIFIER=systemd | "
+ 'grep "Reached target Current graphical"'
)
status, _ = self.execute(cmd)
if status != 0:
return False
status, _ = self.execute("[ -e /tmp/.X11-unix/X0 ]")
return status == 0
with self.nested("waiting for the X11 server"):
retry(check_x)
def get_window_names(self) -> List[str]:
return self.succeed(
r"xwininfo -root -tree | sed 's/.*0x[0-9a-f]* \"\([^\"]*\)\".*/\1/; t; d'"
).splitlines()
def wait_for_window(self, regexp: str) -> None:
pattern = re.compile(regexp)
def window_is_visible(last_try: bool) -> bool:
names = self.get_window_names()
if last_try:
self.log(
"Last chance to match {} on the window list,".format(regexp)
+ " which currently contains: "
+ ", ".join(names)
)
return any(pattern.search(name) for name in names)
with self.nested("Waiting for a window to appear"):
retry(window_is_visible)
def sleep(self, secs: int) -> None:
time.sleep(secs)
def forward_port(self, host_port: int = 8080, guest_port: int = 80) -> None:
"""Forward a TCP port on the host to a TCP port on the guest.
Useful during interactive testing.
"""
self.send_monitor_command(
"hostfwd_add tcp::{}-:{}".format(host_port, guest_port)
)
def block(self) -> None:
"""Make the machine unreachable by shutting down eth1 (the multicast
interface used to talk to the other VMs). We keep eth0 up so that
the test driver can continue to talk to the machine.
"""
self.send_monitor_command("set_link virtio-net-pci.1 off")
def unblock(self) -> None:
"""Make the machine reachable.
"""
self.send_monitor_command("set_link virtio-net-pci.1 on")
def create_machine(args: Dict[str, Any]) -> Machine:
global log
args["log"] = log
args["redirectSerial"] = os.environ.get("USE_SERIAL", "0") == "1"
return Machine(args)
def start_all() -> None:
global machines
with log.nested("starting all VMs"):
for machine in machines:
machine.start()
def join_all() -> None:
global machines
with log.nested("waiting for all VMs to finish"):
for machine in machines:
machine.wait_for_shutdown()
def test_script() -> None:
exec(os.environ["testScript"])
def run_tests() -> None:
global machines
tests = os.environ.get("tests", None)
if tests is not None:
with log.nested("running the VM test script"):
try:
exec(tests, globals())
except Exception as e:
eprint("error: {}".format(str(e)))
sys.exit(1)
else:
ptpython.repl.embed(locals(), globals())
# TODO: Collect coverage data
for machine in machines:
if machine.is_up():
machine.execute("sync")
@contextmanager
def subtest(name: str) -> Iterator[None]:
with log.nested(name):
try:
yield
return True
except Exception as e:
log.log(f'Test "{name}" failed with error: "{e}"')
raise e
return False
if __name__ == "__main__":
log = Logger()
vm_scripts = sys.argv[1:]
machines = [create_machine({"startCommand": s}) for s in vm_scripts]
machine_eval = [
"{0} = machines[{1}]".format(m.name, idx) for idx, m in enumerate(machines)
]
exec("\n".join(machine_eval))
@atexit.register
def clean_up() -> None:
with log.nested("cleaning up"):
for machine in machines:
if machine.pid is None:
continue
log.log("killing {} (pid {})".format(machine.name, machine.pid))
machine.process.kill()
log.close()
tic = time.time()
run_tests()
toc = time.time()
print("test script finished in {:.2f}s".format(toc - tic))