diff --git a/modules/desktop/graphics/default.nix b/modules/desktop/graphics/default.nix index 2475919dc1..e97642c42c 100644 --- a/modules/desktop/graphics/default.nix +++ b/modules/desktop/graphics/default.nix @@ -11,5 +11,6 @@ ./window-manager.nix ./boot.nix ./hardware.nix + ./waypipe.nix ]; } diff --git a/modules/desktop/graphics/waypipe.nix b/modules/desktop/graphics/waypipe.nix new file mode 100644 index 0000000000..92726734b1 --- /dev/null +++ b/modules/desktop/graphics/waypipe.nix @@ -0,0 +1,39 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +# +{ + pkgs, + lib, + config, + ... +}: let + cfg = config.ghaf.waypipe; +in { + options.ghaf.waypipe = { + enable = lib.mkEnableOption "Waypipe"; + + port = lib.mkOption { + type = lib.types.int; + default = 1100; + description = '' + Waypipe port number to listen for incoming connections + ''; + }; + }; + + config = lib.mkIf cfg.enable { + systemd.user.services.waypipe = { + enable = true; + description = "waypipe"; + after = ["weston.service" "labwc.service"]; + serviceConfig = { + Type = "simple"; + ExecStart = "${pkgs.waypipe}/bin/waypipe --vsock -s ${toString cfg.port} client"; + Restart = "always"; + RestartSec = "1"; + }; + startLimitIntervalSec = 0; + wantedBy = ["ghaf-session.target"]; + }; + }; +} diff --git a/modules/microvm/default.nix b/modules/microvm/default.nix index 620ff391ff..fb16373caa 100644 --- a/modules/microvm/default.nix +++ b/modules/microvm/default.nix @@ -13,5 +13,6 @@ ./virtualization/microvm/guivm.nix ./networking.nix ./power-control.nix + ./virtualization/microvm/virtgpuvm.nix ]; } diff --git a/modules/microvm/virtualization/microvm/appvm.nix b/modules/microvm/virtualization/microvm/appvm.nix index bcbda67ae4..85fcc6a33f 100644 --- a/modules/microvm/virtualization/microvm/appvm.nix +++ b/modules/microvm/virtualization/microvm/appvm.nix @@ -42,7 +42,7 @@ runWaypipe = with pkgs; writeScriptBin "run-waypipe" '' #!${runtimeShell} -e - ${pkgs.waypipe}/bin/waypipe --vsock -s ${toString configHost.ghaf.virtualization.microvm.guivm.waypipePort} ${waypipeBorder} server $@ + ${pkgs.waypipe}/bin/waypipe --vsock -s ${toString configHost.ghaf.waypipe.port} ${waypipeBorder} server $@ ''; in { ghaf = { diff --git a/modules/microvm/virtualization/microvm/guivm.nix b/modules/microvm/virtualization/microvm/guivm.nix index ddc29521ee..6eef0c4ccd 100644 --- a/modules/microvm/virtualization/microvm/guivm.nix +++ b/modules/microvm/virtualization/microvm/guivm.nix @@ -25,6 +25,7 @@ graphics.labwc.lock.enable = false; profiles.applications.enable = false; windows-launcher.enable = false; + waypipe.enable = true; development = { ssh.daemon.enable = lib.mkDefault configHost.ghaf.development.ssh.daemon.enable; debug.tools.enable = lib.mkDefault configHost.ghaf.development.debug.tools.enable; @@ -117,21 +118,6 @@ ../../../desktop ]; - # Waypipe service runs in the GUIVM and listens for incoming connections from AppVMs - systemd.user.services.waypipe = { - enable = true; - description = "waypipe"; - after = ["weston.service" "labwc.service"]; - serviceConfig = { - Type = "simple"; - ExecStart = "${pkgs.waypipe}/bin/waypipe --vsock -s ${toString cfg.waypipePort} client"; - Restart = "always"; - RestartSec = "1"; - }; - startLimitIntervalSec = 0; - wantedBy = ["ghaf-session.target"]; - }; - # Fixed IP-address for debugging subnet systemd.network.networks."10-ethint0".addresses = [ { @@ -173,14 +159,6 @@ in { Context Identifier (CID) of the GUIVM VSOCK ''; }; - - waypipePort = lib.mkOption { - type = lib.types.int; - default = 1100; - description = '' - Waypipe port number to listen for incoming connections from AppVMs - ''; - }; }; config = lib.mkIf cfg.enable { @@ -230,7 +208,7 @@ in { Type = "simple"; }; serviceConfig = { - ExecStart = "${vsockproxy}/bin/vsockproxy ${toString cfg.waypipePort} ${toString cfg.vsockCID} ${toString cfg.waypipePort}"; + ExecStart = "${vsockproxy}/bin/vsockproxy ${toString config.ghaf.waypipe.port} ${toString cfg.vsockCID} ${toString config.ghaf.waypipe.port}"; }; wantedBy = ["multi-user.target"]; }; diff --git a/modules/microvm/virtualization/microvm/virtgpuvm.nix b/modules/microvm/virtualization/microvm/virtgpuvm.nix new file mode 100644 index 0000000000..8e429ea678 --- /dev/null +++ b/modules/microvm/virtualization/microvm/virtgpuvm.nix @@ -0,0 +1,161 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +{ + config, + lib, + pkgs, + ... +}: let + configHost = config; + vmName = "virtgpu-vm"; + # The socket is created in /tmp because it is accessible to both microvm and ghaf users + gpuSocket = "/tmp/${vmName}-gpu.sock"; + run-sommelier = with pkgs; + writeScriptBin "run-sommelier" '' + #!${runtimeShell} -e + exec ${sommelier}/bin/sommelier --virtgpu-channel -- $@ + ''; + run-wayland-proxy = with pkgs; + writeScriptBin "run-wayland-proxy" '' + #!${runtimeShell} -e + exec ${wayland-proxy-virtwl}/bin/wayland-proxy-virtwl --virtio-gpu -- $@ + ''; + run-waypipe = with pkgs; + writeScriptBin "run-waypipe" '' + #!${runtimeShell} -e + exec ${waypipe}/bin/waypipe --vsock -s 2:${toString config.ghaf.waypipe.port} server $@ + ''; + virtgpuvmBaseConfiguration = { + imports = [ + (import ./common/vm-networking.nix { + inherit vmName; + macAddress = "02:00:00:03:05:01"; + }) + ({ + lib, + pkgs, + ... + }: { + ghaf = { + users.accounts.enable = lib.mkDefault configHost.ghaf.users.accounts.enable; + + development = { + ssh.daemon.enable = lib.mkDefault configHost.ghaf.development.ssh.daemon.enable; + debug.tools.enable = lib.mkDefault configHost.ghaf.development.debug.tools.enable; + nix-setup.enable = lib.mkDefault configHost.ghaf.development.nix-setup.enable; + }; + }; + + system.stateVersion = lib.trivial.release; + + nixpkgs.buildPlatform.system = configHost.nixpkgs.buildPlatform.system; + nixpkgs.hostPlatform.system = configHost.nixpkgs.hostPlatform.system; + + environment.systemPackages = with pkgs; [ + sommelier + wayland-proxy-virtwl + waypipe + run-sommelier + run-wayland-proxy + run-waypipe + zathura + chromium + firefox + wayland-utils + ]; + + # DRM fbdev emulation is disabled to get rid of the popup console window that appears when running a VM with virtio-gpu device + boot.kernelParams = ["drm_kms_helper.fbdev_emulation=false"]; + + hardware.opengl.enable = true; + + microvm = { + optimize.enable = false; + mem = 4096; + vcpu = 4; + hypervisor = "crosvm"; + shares = [ + { + tag = "ro-store"; + source = "/nix/store"; + mountPoint = "/nix/.ro-store"; + proto = "virtiofs"; + } + ]; + + # GPU device is a separate service which is connected over vhost-user protocol + crosvm.extraArgs = ["--vhost-user" "gpu,socket=${gpuSocket}"]; + + # VSOCK is required for waypipe, 3 is the first available CID + vsock.cid = 3; + }; + + imports = [../../../common]; + }) + ]; + }; + cfg = config.ghaf.virtualization.microvm.virtgpuvm; +in { + options.ghaf.virtualization.microvm.virtgpuvm = { + enable = lib.mkEnableOption "VirtgpuVM"; + + extraModules = lib.mkOption { + description = '' + List of additional modules to be imported and evaluated as part of + VirtgpuVM's NixOS configuration. + ''; + default = []; + }; + }; + + config = lib.mkIf cfg.enable { + microvm.vms."${vmName}" = { + config = virtgpuvmBaseConfiguration // {imports = virtgpuvmBaseConfiguration.imports ++ cfg.extraModules;}; + specialArgs = {inherit lib;}; + }; + + # This service creates a crosvm backend GPU device + systemd.user.services."${vmName}-gpu" = let + preStartScript = pkgs.writeShellScriptBin "prestart-crosvmgpu" '' + if [[ -z "$WAYLAND_DISPLAY" ]]; then + echo "WAYLAND_DISPLAY is not set" + exit 1 + fi + WAYLAND_SOCK=$XDG_RUNTIME_DIR/$WAYLAND_DISPLAY + if [[ ! -r "$WAYLAND_SOCK" ]]; then + echo "Wayland socket $WAYLAND_SOCK is not readable" + exit 1 + fi + ''; + startScript = pkgs.writeShellScriptBin "start-crosvmgpu" '' + rm -f ${gpuSocket} + ${pkgs.crosvm}/bin/crosvm device gpu --socket ${gpuSocket} --wayland-sock $XDG_RUNTIME_DIR/$WAYLAND_DISPLAY --params '{"context-types":"virgl:virgl2:cross-domain","egl":true,"vulkan":true}' + ''; + postStartScript = pkgs.writeShellScriptBin "poststart-crosvmgpu" '' + while ! [ -S ${gpuSocket} ]; do + sleep .1 + done + chgrp video ${gpuSocket} + chmod 775 ${gpuSocket} + ''; + in { + enable = true; + description = "crosvm gpu device"; + after = ["weston.service" "labwc.service"]; + serviceConfig = { + Type = "simple"; + ExecStartPre = "${preStartScript}/bin/prestart-crosvmgpu"; + ExecStart = "${startScript}/bin/start-crosvmgpu"; + ExecStartPost = "${postStartScript}/bin/poststart-crosvmgpu"; + Restart = "always"; + RestartSec = "1"; + }; + startLimitIntervalSec = 0; + wantedBy = ["ghaf-session.target"]; + }; + + users.users."microvm".extraGroups = ["video"]; + + ghaf.waypipe.enable = true; + }; +} diff --git a/overlays/custom-packages/crosvm/default.nix b/overlays/custom-packages/crosvm/default.nix new file mode 100644 index 0000000000..b863a7e508 --- /dev/null +++ b/overlays/custom-packages/crosvm/default.nix @@ -0,0 +1,27 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +{ + final, + prev, +}: +prev.crosvm.overrideAttrs (_final: prevAttrs: rec { + version = "122.1"; + src = prev.fetchgit { + url = "https://chromium.googlesource.com/chromiumos/platform/crosvm"; + rev = "562d81eb28a49ed6e0d771a430c21a458cdd33f9"; + sha256 = "sha256-l5sIUInOhhkn3ernQLIEwEpRCyICDH/1k4C/aidy1/I="; + fetchSubmodules = true; + }; + + patches = []; + + cargoBuildFeatures = final.lib.lists.remove "virgl_renderer_next" prevAttrs.cargoBuildFeatures; + cargoCheckFeatures = final.lib.lists.remove "virgl_renderer_next" prevAttrs.cargoCheckFeatures; + CROSVM_USE_SYSTEM_MINIGBM = true; + + cargoDeps = prevAttrs.cargoDeps.overrideAttrs (prev.lib.const { + inherit src; + name = "crosvm-vendor.tar.gz"; + outputHash = "sha256-yTdho6lW+XqB/iGf+bT2iwnAdjz3TrrI7YAaLoenR1U="; + }); +}) diff --git a/overlays/custom-packages/default.nix b/overlays/custom-packages/default.nix index 5a61b59d6e..9b70739b85 100644 --- a/overlays/custom-packages/default.nix +++ b/overlays/custom-packages/default.nix @@ -23,4 +23,6 @@ networkmanagerapplet = import ./networkmanagerapplet {inherit prev;}; htop = import ./htop {inherit prev;}; mitmweb-ui = final.callPackage ../../packages/mitmweb-ui {}; + sommelier = import ./sommelier {inherit final prev;}; + crosvm = import ./crosvm {inherit final prev;}; }) diff --git a/overlays/custom-packages/sommelier/default.nix b/overlays/custom-packages/sommelier/default.nix new file mode 100644 index 0000000000..b8d4e6ff33 --- /dev/null +++ b/overlays/custom-packages/sommelier/default.nix @@ -0,0 +1,20 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +{ + final, + prev, +}: +prev.sommelier.overrideAttrs (_final: prevAttrs: { + version = "122.0"; + src = prev.fetchzip rec { + url = "https://chromium.googlesource.com/chromiumos/platform2/+archive/${passthru.rev}/vm_tools/sommelier.tar.gz"; + passthru.rev = "2d4f46c679da7a4e8c447c8cf68c74b80f9de3fe"; + stripRoot = false; + sha256 = "sha256-LNGA1r2IO3Ekh+dK6HUge001qC2TFvxwjhM0iaY0DbU="; + }; + + nativeBuildInputs = prevAttrs.nativeBuildInputs ++ [final.python3 final.python3.pkgs.jinja2]; + postPatch = '' + patchShebangs gen-shim.py + ''; +}) diff --git a/targets/generic-x86_64/flake-module.nix b/targets/generic-x86_64/flake-module.nix index 9ac40d98fd..a797e91a02 100644 --- a/targets/generic-x86_64/flake-module.nix +++ b/targets/generic-x86_64/flake-module.nix @@ -69,6 +69,9 @@ #graphics.compositor = "labwc"; }; windows-launcher.enable = true; + + # Uncomment this line to enable the virtgpu-vm with a virtio-gpu device: + #virtualization.microvm.virtgpuvm.enable = true; }; #TODO: how to handle the majority of laptops that need a little @@ -84,6 +87,9 @@ # Passthrough Intel WiFi card "vfio-pci.ids=8086:a0f0" ]; + + # Enable DHCP on the host to be able to access network and connect to VMs by name, e.g. net-vm.ghaf + systemd.network.networks."10-virbr0".DHCP = "yes"; } ] ++ extraModules;