Cleanup dead nixos-module code
This commit is contained in:
parent
5488ee8a14
commit
26ba44bb54
|
@ -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;
|
||||
|
|
|
@ -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}";
|
||||
|
||||
}
|
|
@ -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"
|
||||
]
|
||||
}
|
|
@ -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" ]
|
||||
}
|
|
@ -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")
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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")
|
||||
]
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
"24b69406-18a1-428d-908e-d21a1437122c"
|
|
@ -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
|
|
@ -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
|
|
@ -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" ]
|
||||
}
|
|
@ -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
|
|
@ -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")
|
||||
]
|
||||
}
|
|
@ -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"
|
||||
]
|
||||
}
|
|
@ -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
|
|
@ -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))
|
Loading…
Reference in New Issue