From ae4850076ac70b716c4a33f7dd1a22fd9a02cf42 Mon Sep 17 00:00:00 2001 From: Manuel Bluhm Date: Mon, 24 Jun 2024 09:38:11 +0400 Subject: [PATCH] Refactor guivm Changes: - remove guivmExtraModules - separate functionality, mostly to desktop and openpdf services - add kernel config, allows now to centrally handle kernel configs As kernel modules and parameters will have different sources such as hardware, security, etc. this allows to join configurations and pass them to VMs - add qemu config, for required extra configurations (here gui-vm) - add namespace bridge, here to share a list of configured VMs - continue work on reference programs, re-work windows-launcher - fix to guard x86-generic Signed-off-by: Manuel Bluhm --- modules/common/services/default.nix | 3 + modules/common/services/desktop.nix | 128 +++++++++++++ modules/common/services/fprint.nix | 107 +++++------ modules/common/services/namespaces.nix | 25 +++ modules/common/services/pdfopen.nix | 58 ++++++ modules/hardware/common/default.nix | 4 +- .../common/{passthru.nix => devices.nix} | 52 +----- modules/hardware/common/kernel.nix | 82 ++++++++ modules/hardware/common/qemu.nix | 38 ++++ modules/hardware/definition.nix | 84 ++++++--- modules/hardware/lenovo-x1/default.nix | 7 +- .../lenovo-x1/definitions/x1-gen10.nix | 28 +-- .../lenovo-x1/definitions/x1-gen11.nix | 28 +-- .../microvm/virtualization/microvm/guivm.nix | 31 ++-- .../virtualization/microvm/modules.nix | 114 +++++++++--- modules/reference/appvms/chromium.nix | 2 +- modules/reference/appvms/zathura.nix | 2 +- modules/reference/programs/chromium.nix | 4 +- .../reference/programs/windows-launcher.nix | 4 +- modules/reference/programs/zathura.nix | 4 +- targets/generic-x86_64/flake-module.nix | 2 +- targets/lenovo-x1/everything.nix | 22 +-- targets/lenovo-x1/guivmExtraModules.nix | 175 ------------------ targets/nvidia-jetson-orin/flake-module.nix | 2 +- 24 files changed, 602 insertions(+), 404 deletions(-) create mode 100644 modules/common/services/desktop.nix create mode 100644 modules/common/services/namespaces.nix create mode 100644 modules/common/services/pdfopen.nix rename modules/hardware/common/{passthru.nix => devices.nix} (65%) create mode 100644 modules/hardware/common/kernel.nix create mode 100644 modules/hardware/common/qemu.nix delete mode 100644 targets/lenovo-x1/guivmExtraModules.nix diff --git a/modules/common/services/default.nix b/modules/common/services/default.nix index 781f6e58b..874a2e8bb 100644 --- a/modules/common/services/default.nix +++ b/modules/common/services/default.nix @@ -6,5 +6,8 @@ ./audio.nix ./wifi.nix ./firmware.nix + ./desktop.nix + ./pdfopen.nix + ./namespaces.nix ]; } diff --git a/modules/common/services/desktop.nix b/modules/common/services/desktop.nix new file mode 100644 index 000000000..bbcb9478c --- /dev/null +++ b/modules/common/services/desktop.nix @@ -0,0 +1,128 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +{ + config, + lib, + pkgs, + ... +}: let + inherit (builtins) filter map hasAttr; + inherit (lib) mkIf mkEnableOption head any optionals optionalAttrs; + cfg = config.ghaf.services.desktop; + + winConfig = + if (hasAttr "reference" config.ghaf) + then + if (hasAttr "programs" config.ghaf.reference) + then config.ghaf.reference.programs.windows-launcher + else {} + else {}; + isIdsvmEnabled = any (vm: vm == "ids-vm") config.ghaf.namespaces.vms; + # TODO: The desktop configuration needs to be re-worked. +in { + options.ghaf.services.desktop = { + enable = mkEnableOption "Enable the desktop configuration"; + }; + + config = mkIf cfg.enable { + ghaf = optionalAttrs (hasAttr "graphics" config.ghaf) { + profiles.graphics.compositor = "labwc"; + graphics = { + launchers = let + hostEntry = filter (x: x.name == "ghaf-host-debug") config.ghaf.networking.hosts.entries; + hostAddress = head (map (x: x.ip) hostEntry); + powerControl = pkgs.callPackage ../../../packages/powercontrol {}; + privateSshKeyPath = config.ghaf.security.sshKeys.sshKeyPath; + in + [ + { + # The SPKI fingerprint is calculated like this: + # $ openssl x509 -noout -in mitmproxy-ca-cert.pem -pubkey | openssl asn1parse -noout -inform pem -out public.key + # $ openssl dgst -sha256 -binary public.key | openssl enc -base64 + name = "Chromium"; + path = + if isIdsvmEnabled + then "${pkgs.openssh}/bin/ssh -i ${privateSshKeyPath} -o StrictHostKeyChecking=no chromium-vm run-waypipe chromium --enable-features=UseOzonePlatform --ozone-platform=wayland --user-data-dir=/home/${config.ghaf.users.accounts.user}/.config/chromium/Default --ignore-certificate-errors-spki-list=Bq49YmAq1CG6FuBzp8nsyRXumW7Dmkp7QQ/F82azxGU=" + else "${pkgs.openssh}/bin/ssh -i ${privateSshKeyPath} -o StrictHostKeyChecking=no chromium-vm run-waypipe chromium --enable-features=UseOzonePlatform --ozone-platform=wayland"; + icon = "${pkgs.icon-pack}/chromium.svg"; + } + + { + name = "GALA"; + path = "${pkgs.openssh}/bin/ssh -i ${privateSshKeyPath} -o StrictHostKeyChecking=no gala-vm run-waypipe gala --enable-features=UseOzonePlatform --ozone-platform=wayland"; + icon = "${pkgs.icon-pack}/distributor-logo-android.svg"; + } + + { + name = "PDF Viewer"; + path = "${pkgs.openssh}/bin/ssh -i ${privateSshKeyPath} -o StrictHostKeyChecking=no zathura-vm run-waypipe zathura"; + icon = "${pkgs.icon-pack}/document-viewer.svg"; + } + + { + name = "Element"; + path = "${pkgs.openssh}/bin/ssh -i ${privateSshKeyPath} -o StrictHostKeyChecking=no element-vm run-waypipe element-desktop --enable-features=UseOzonePlatform --ozone-platform=wayland"; + icon = "${pkgs.icon-pack}/element-desktop.svg"; + } + + { + name = "AppFlowy"; + path = "${pkgs.openssh}/bin/ssh -i ${privateSshKeyPath} -o StrictHostKeyChecking=no appflowy-vm run-waypipe appflowy"; + icon = "${pkgs.appflowy}/opt/data/flutter_assets/assets/images/flowy_logo.svg"; + } + + { + name = "Network Settings"; + path = "${pkgs.nm-launcher}/bin/nm-launcher"; + icon = "${pkgs.icon-pack}/preferences-system-network.svg"; + } + + { + name = "Shutdown"; + path = "${powerControl.makePowerOffCommand { + inherit hostAddress; + inherit privateSshKeyPath; + }}"; + icon = "${pkgs.icon-pack}/system-shutdown.svg"; + } + + { + name = "Reboot"; + path = "${powerControl.makeRebootCommand { + inherit hostAddress; + inherit privateSshKeyPath; + }}"; + icon = "${pkgs.icon-pack}/system-reboot.svg"; + } + + # Temporarly disabled as it fails to turn off display when suspended + # { + # name = "Suspend"; + # path = "${powerControl.makeSuspendCommand { + # inherit hostAddress; + # inherit privateSshKeyPath; + # }}"; + # icon = "${pkgs.icon-pack}/system-suspend.svg"; + # } + + # Temporarly disabled as it doesn't work at all + # { + # name = "Hibernate"; + # path = "${powerControl.makeHibernateCommand { + # inherit hostAddress; + # inherit privateSshKeyPath; + # }}"; + # icon = "${pkgs.icon-pack}/system-suspend-hibernate.svg"; + # } + ] + ++ optionals (hasAttr "spice-host" winConfig) [ + { + name = "Windows"; + path = "${pkgs.virt-viewer}/bin/remote-viewer -f spice://${winConfig.spice-host}:${toString winConfig.spice-port}"; + icon = "${pkgs.icon-pack}/distributor-logo-windows.svg"; + } + ]; + }; + }; + }; +} diff --git a/modules/common/services/fprint.nix b/modules/common/services/fprint.nix index debe35489..958b5aeeb 100644 --- a/modules/common/services/fprint.nix +++ b/modules/common/services/fprint.nix @@ -6,81 +6,60 @@ pkgs, ... }: let + inherit (lib) mkEnableOption mkIf; cfg = config.ghaf.services.fprint; - inherit (lib) mkEnableOption mkOption types mkIf; in { options.ghaf.services.fprint = { enable = mkEnableOption "Enable fingerprint reader support"; - qemuExtraArgs = mkOption { - type = types.listOf types.str; - default = []; - description = '' - Extra arguments to pass to qemu when enabling the fingerprint reader. - ''; - }; - extraConfigurations = mkOption { - type = types.attrsOf types.anything; - default = {}; - description = '' - Extra configurations when enabling the fingerprint reader in a guest. - ''; - }; }; config = mkIf cfg.enable { - ghaf.services.fprint = { - # Use qemu arguments generated for the device - qemuExtraArgs = config.ghaf.hardware.usb.internal.qemuExtraArgs.fprint-reader; + # Enable service and package for fingerprint reader + services.fprintd.enable = true; + environment.systemPackages = [pkgs.fprintd]; - extraConfigurations = { - # Enable service and package for fingerprint reader - services.fprintd.enable = true; - environment.systemPackages = [pkgs.fprintd]; - - # Enable polkit and add rules - ghaf.systemd.withPolkit = true; - security = { - polkit = { - enable = true; - debug = true; - # Polkit rules for fingerprint reader - extraConfig = '' - // Allow user to verify fingerprints - polkit.addRule(function(action, subject) { - if (action.id == "net.reactivated.fprint.device.verify" && - subject.user == "ghaf") { - return polkit.Result.YES; - } - }); - // Allow user to enroll fingerprints - polkit.addRule(function(action, subject) { - if (action.id == "net.reactivated.fprint.device.enroll" && - subject.user == "ghaf") { - return polkit.Result.YES; - } - }); - ''; - }; - # PAM rules for swaylock fingerprint reader - pam.services = { - swaylock.text = '' - # Account management. - account required pam_unix.so + # Enable polkit and add rules + ghaf.systemd.withPolkit = true; + security = { + polkit = { + enable = true; + debug = true; + # Polkit rules for fingerprint reader + extraConfig = '' + // Allow user to verify fingerprints + polkit.addRule(function(action, subject) { + if (action.id == "net.reactivated.fprint.device.verify" && + subject.user == "ghaf") { + return polkit.Result.YES; + } + }); + // Allow user to enroll fingerprints + polkit.addRule(function(action, subject) { + if (action.id == "net.reactivated.fprint.device.enroll" && + subject.user == "ghaf") { + return polkit.Result.YES; + } + }); + ''; + }; + # PAM rules for swaylock fingerprint reader + pam.services = { + swaylock.text = '' + # Account management. + account required pam_unix.so - # Authentication management. - auth sufficient pam_unix.so likeauth try_first_pass - auth sufficient ${pkgs.fprintd}/lib/security/pam_fprintd.so - auth required pam_deny.so + # Authentication management. + auth sufficient pam_unix.so likeauth try_first_pass + auth sufficient ${pkgs.fprintd}/lib/security/pam_fprintd.so + auth required pam_deny.so - # Password management. - password sufficient pam_unix.so nullok sha512 + # Password management. + password sufficient pam_unix.so nullok sha512 - # Session management. - session required pam_env.so conffile=/etc/pam/environment readenv=0 - session required pam_unix.so - ''; - }; - }; + # Session management. + session required pam_env.so conffile=/etc/pam/environment readenv=0 + session required pam_unix.so + ''; }; }; }; diff --git a/modules/common/services/namespaces.nix b/modules/common/services/namespaces.nix new file mode 100644 index 000000000..6597c6209 --- /dev/null +++ b/modules/common/services/namespaces.nix @@ -0,0 +1,25 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +{ + config, + lib, + ... +}: let + inherit (builtins) attrNames hasAttr; + inherit (lib) mkOption types optionalAttrs; +in { + options.ghaf.namespaces = { + vms = mkOption { + type = types.listOf types.str; + default = []; + description = "List of VMs currently enabled."; + }; + }; + config = { + ghaf = optionalAttrs (hasAttr "microvm" config) { + namespaces = optionalAttrs (hasAttr "vms" config.microvm) { + vms = attrNames config.microvm.vms; + }; + }; + }; +} diff --git a/modules/common/services/pdfopen.nix b/modules/common/services/pdfopen.nix new file mode 100644 index 000000000..f8ecadaa1 --- /dev/null +++ b/modules/common/services/pdfopen.nix @@ -0,0 +1,58 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +{ + config, + lib, + pkgs, + ... +}: let + inherit (builtins) toString; + inherit (lib) mkEnableOption mkOption mkIf types; + cfg = config.ghaf.services.pdfopener; + + # TODO: Fix the path to get the sshKeyPath so that + # openPdf can be exported as a normal package from + # packaged/flake-module.nix and hence easily imported + # into all targets + openPdf = pkgs.callPackage ../../../packages/openPdf { + inherit (config.ghaf.security.sshKeys) sshKeyPath; + }; +in { + options.ghaf.services.pdfopener = { + enable = mkEnableOption "Enable the pdf opening service"; + xdgPdfPort = mkOption { + type = types.int; + default = 1200; + description = "TCP port for the PDF XDG socket"; + }; + }; + + config = mkIf cfg.enable { + # PDF XDG handler service receives a PDF file path from the chromium-vm and executes the openpdf script + systemd.user = { + sockets."pdf" = { + unitConfig = { + Description = "PDF socket"; + }; + socketConfig = { + ListenStream = "${toString cfg.xdgPdfPort}"; + Accept = "yes"; + }; + wantedBy = ["sockets.target"]; + }; + + services."pdf@" = { + description = "PDF opener"; + serviceConfig = { + ExecStart = "${openPdf}/bin/openPdf"; + StandardInput = "socket"; + StandardOutput = "journal"; + StandardError = "journal"; + }; + }; + }; + + # Open TCP port for the PDF XDG socket. + networking.firewall.allowedTCPPorts = [cfg.xdgPdfPort]; + }; +} diff --git a/modules/hardware/common/default.nix b/modules/hardware/common/default.nix index a6ffa7b9d..12c4dfa7c 100644 --- a/modules/hardware/common/default.nix +++ b/modules/hardware/common/default.nix @@ -4,6 +4,8 @@ imports = [ ./usb/internal.nix ./usb/external.nix - ./passthru.nix + ./devices.nix + ./kernel.nix + ./qemu.nix ]; } diff --git a/modules/hardware/common/passthru.nix b/modules/hardware/common/devices.nix similarity index 65% rename from modules/hardware/common/passthru.nix rename to modules/hardware/common/devices.nix index b2a1f732e..8605abc3f 100644 --- a/modules/hardware/common/passthru.nix +++ b/modules/hardware/common/devices.nix @@ -6,17 +6,8 @@ ... }: let inherit (lib) mkOption types mkForce; - - # PCI device passthroughs for vfio - filterDevices = builtins.filter (d: d.vendorId != null && d.productId != null); - mapPciIdsToString = builtins.map (d: "${d.vendorId}:${d.productId}"); - vfioPciIds = mapPciIdsToString (filterDevices ( - config.ghaf.hardware.definition.network.pciDevices - ++ config.ghaf.hardware.definition.gpu.pciDevices - ++ config.ghaf.hardware.definition.audio.pciDevices - )); in { - options.ghaf.hardware.passthrough = { + options.ghaf.hardware.devices = { netvmPCIPassthroughModule = mkOption { type = types.attrsOf types.anything; default = {}; @@ -45,24 +36,10 @@ in { Virtio evdev paths' to passthrough to the guivm. ''; }; - guivmQemuExtraArgs = mkOption { - type = types.listOf types.str; - default = []; - description = '' - Extra arguments to pass to qemu when enabling the guivm. - ''; - }; - audiovmKernelParams = mkOption { - type = types.attrsOf types.anything; - default = {}; - description = '' - Sound hardware specific kernel parameters to configure audiovm. - ''; - }; }; config = { - ghaf.hardware.passthrough = { + ghaf.hardware.devices = { netvmPCIPassthroughModule = { microvm.devices = mkForce ( builtins.map (d: { @@ -110,31 +87,6 @@ in { # TODO: Remove this once wifi-signal-strength is changed ghaf.hardware.definition.network.pciDevices = config.ghaf.hardware.definition.network.pciDevices; }; - - guivmQemuExtraArgs = [ - # Button - "-device" - "button" - # Battery - "-device" - "battery" - # AC adapter - "-device" - "acad" - ]; - - audiovmKernelParams = { - microvm = { - inherit (config.ghaf.hardware.definition.audio) kernelParams; - }; - }; - }; - - # Enable VFIO for PCI devices - boot = { - kernelParams = [ - "vfio-pci.ids=${builtins.concatStringsSep "," vfioPciIds}" - ]; }; }; } diff --git a/modules/hardware/common/kernel.nix b/modules/hardware/common/kernel.nix new file mode 100644 index 000000000..28e1c1831 --- /dev/null +++ b/modules/hardware/common/kernel.nix @@ -0,0 +1,82 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +# +# Module for Kernel Configuration Definitions +# +{ + config, + lib, + pkgs, + ... +}: let + inherit (lib) mkOption types optionalAttrs; + inherit (builtins) concatStringsSep filter map hasAttr; + + # Only x86 targets with hw definition supported at the moment + inherit (pkgs.stdenv.hostPlatform) isx86; + fullVirtualization = isx86 && (hasAttr "hardware" config.ghaf); +in { + options.ghaf.kernel = { + host = mkOption { + type = types.attrs; + default = {}; + description = "Host kernel configuration"; + }; + guivm = mkOption { + type = types.attrs; + default = {}; + description = "GuiVM kernel configuration"; + }; + audiovm = mkOption { + type = types.attrs; + default = {}; + description = "AudioVM kernel configuration"; + }; + }; + + config = { + # Host kernel configuration + boot = optionalAttrs fullVirtualization { + initrd = { + inherit (config.ghaf.hardware.definition.host.kernelConfig.stage1) kernelModules; + }; + inherit (config.ghaf.hardware.definition.host.kernelConfig.stage2) kernelModules; + kernelParams = let + # PCI device passthroughs for vfio + filterDevices = filter (d: d.vendorId != null && d.productId != null); + mapPciIdsToString = map (d: "${d.vendorId}:${d.productId}"); + vfioPciIds = mapPciIdsToString (filterDevices ( + config.ghaf.hardware.definition.network.pciDevices + ++ config.ghaf.hardware.definition.gpu.pciDevices + ++ config.ghaf.hardware.definition.audio.pciDevices + )); + in + config.ghaf.hardware.definition.host.kernelConfig.kernelParams + ++ [ + "vfio-pci.ids=${concatStringsSep "," vfioPciIds}" + ]; + }; + + # Guest kernel configurations + ghaf.kernel = optionalAttrs fullVirtualization { + guivm = { + boot = { + initrd = { + inherit (config.ghaf.hardware.definition.gpu.kernelConfig.stage1) kernelModules; + }; + inherit (config.ghaf.hardware.definition.gpu.kernelConfig.stage2) kernelModules; + inherit (config.ghaf.hardware.definition.gpu.kernelConfig) kernelParams; + }; + }; + audiovm = { + boot = { + initrd = { + inherit (config.ghaf.hardware.definition.audio.kernelConfig.stage1) kernelModules; + }; + inherit (config.ghaf.hardware.definition.audio.kernelConfig.stage2) kernelModules; + inherit (config.ghaf.hardware.definition.audio.kernelConfig) kernelParams; + }; + }; + }; + }; +} diff --git a/modules/hardware/common/qemu.nix b/modules/hardware/common/qemu.nix new file mode 100644 index 000000000..290390c17 --- /dev/null +++ b/modules/hardware/common/qemu.nix @@ -0,0 +1,38 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +# +{ + config, + lib, + ... +}: let + inherit (builtins) hasAttr; + inherit (lib) mkOption types optionals optionalAttrs; +in { + options.ghaf.qemu = { + guivm = mkOption { + type = types.attrs; + default = {}; + description = "Extra qemu arguments for GuiVM"; + }; + }; + + config = { + ghaf.qemu.guivm = optionalAttrs (hasAttr "hardware" config.ghaf) { + microvm.qemu.extraArgs = + [ + # Button + "-device" + "button" + # Battery + "-device" + "battery" + # AC adapter + "-device" + "acad" + ] + ++ optionals (hasAttr "fprint-reader" config.ghaf.hardware.usb.internal.qemuExtraArgs) + config.ghaf.hardware.usb.internal.qemuExtraArgs.fprint-reader; + }; + }; +} diff --git a/modules/hardware/definition.nix b/modules/hardware/definition.nix index 22b2c7883..39ebe324b 100644 --- a/modules/hardware/definition.nix +++ b/modules/hardware/definition.nix @@ -90,14 +90,58 @@ in { type = types.listOf types.str; default = []; description = '' - + List of input device names. ''; }; evdev = mkOption { type = types.listOf types.str; default = []; description = '' + List of event devices. + ''; + }; + }; + }; + # Kernel configuration submodule + kernelConfig = types.submodule { + options = { + stage1 = { + kernelModules = mkOption { + description = "Hardware specific kernel modules"; + type = types.listOf types.str; + default = []; + example = literalExpression '' + [ + "i915" + ] + ''; + }; + }; + stage2 = { + kernelModules = mkOption { + description = "Hardware specific kernel modules"; + type = types.listOf types.str; + default = []; + example = literalExpression '' + [ + "i915" + ] + ''; + }; + }; + kernelParams = mkOption { + description = "Hardware specific kernel parameters"; + type = types.listOf types.str; + default = []; + example = literalExpression '' + [ + "intel_iommu=on,sm_on" + "iommu=pt" + "module_blacklist=i915" + "acpi_backlight=vendor" + "acpi_osi=linux" + ] ''; }; }; @@ -109,20 +153,11 @@ in { default = ""; }; - generic = { - kernelParams = mkOption { - description = "Hardware specific kernel parameters for the host"; - type = types.listOf types.str; - default = []; - example = literalExpression '' - [ - "intel_iommu=on,sm_on" - "iommu=pt" - "module_blacklist=i915" - "acpi_backlight=vendor" - "acpi_osi=linux" - ] - ''; + host = { + kernelConfig = mkOption { + description = "Host kernel configuration"; + type = kernelConfig; + default = {}; }; }; @@ -204,6 +239,11 @@ in { }] ''; }; + kernelConfig = mkOption { + description = "Hardware specific kernel configuration for gpu devices"; + type = kernelConfig; + default = {}; + }; }; udevRules = mkOption { @@ -251,16 +291,10 @@ in { ] ''; }; - kernelParams = mkOption { - description = "Hardware specific kernel parameters for audio devices"; - type = types.listOf types.str; - default = []; - example = literalExpression '' - [ - "snd_intel_dspcfg.dsp_driver=3" - "snd_sof_intel_hda_common.dmic_num=4" - ] - ''; + kernelConfig = mkOption { + description = "Hardware specific kernel configuration for audio devices"; + type = kernelConfig; + default = {}; }; }; diff --git a/modules/hardware/lenovo-x1/default.nix b/modules/hardware/lenovo-x1/default.nix index 61225f80e..a0a21ffd3 100644 --- a/modules/hardware/lenovo-x1/default.nix +++ b/modules/hardware/lenovo-x1/default.nix @@ -22,7 +22,7 @@ in { config = { # Hardware definition ghaf.hardware.definition = { - inherit (hwDefinition) generic; + inherit (hwDefinition) host; inherit (hwDefinition) input; inherit (hwDefinition) disks; inherit (hwDefinition) network; @@ -34,11 +34,6 @@ in { # Disk configuration disko.devices.disk = hwDefinition.disks; - # Hardware specific kernel parameters - boot = { - inherit (hwDefinition.generic) kernelParams; - }; - # Host udev rules services.udev.extraRules = '' # Keyboard diff --git a/modules/hardware/lenovo-x1/definitions/x1-gen10.nix b/modules/hardware/lenovo-x1/definitions/x1-gen10.nix index 81c833763..87db6dd9a 100644 --- a/modules/hardware/lenovo-x1/definitions/x1-gen10.nix +++ b/modules/hardware/lenovo-x1/definitions/x1-gen10.nix @@ -4,8 +4,8 @@ { name = "Lenovo X1 Carbon Gen 10"; - generic = { - kernelParams = [ + host = { + kernelConfig.kernelParams = [ "intel_iommu=on,sm_on" "iommu=pt" "module_blacklist=i915" # Prevent i915 module from being accidentally used by host @@ -66,14 +66,20 @@ } ]; - gpu.pciDevices = [ - { - # Passthrough Intel Iris GPU - path = "0000:00:02.0"; - vendorId = "8086"; - productId = "46a6"; - } - ]; + gpu = { + pciDevices = [ + { + # Passthrough Intel Iris GPU + path = "0000:00:02.0"; + vendorId = "8086"; + productId = "46a6"; + } + ]; + kernelConfig = { + stage1.kernelModules = ["i915"]; + kernelParams = ["earlykms"]; + }; + }; # With the current implementation, the whole PCI IOMMU group 13: # 00:1f.x in the Lenovo X1 Carbon 10 gen @@ -105,7 +111,7 @@ productId = "51a4"; } ]; - kernelParams = [ + kernelConfig.kernelParams = [ "snd_intel_dspcfg.dsp_driver=3" "snd_sof_intel_hda_common.dmic_num=4" ]; diff --git a/modules/hardware/lenovo-x1/definitions/x1-gen11.nix b/modules/hardware/lenovo-x1/definitions/x1-gen11.nix index 0f174dd6b..e7491730c 100644 --- a/modules/hardware/lenovo-x1/definitions/x1-gen11.nix +++ b/modules/hardware/lenovo-x1/definitions/x1-gen11.nix @@ -4,8 +4,8 @@ { name = "Lenovo X1 Carbon Gen 11"; - generic = { - kernelParams = [ + host = { + kernelConfig.kernelParams = [ "intel_iommu=on,sm_on" "iommu=pt" "module_blacklist=i915" # Prevent i915 module from being accidentally used by host @@ -68,14 +68,20 @@ } ]; - gpu.pciDevices = [ - { - # Passthrough Intel Iris GPU - path = "0000:00:02.0"; - vendorId = "8086"; - productId = "a7a1"; - } - ]; + gpu = { + pciDevices = [ + { + # Passthrough Intel Iris GPU + path = "0000:00:02.0"; + vendorId = "8086"; + productId = "a7a1"; + } + ]; + kernelConfig = { + stage1.kernelModules = ["i915"]; + kernelParams = ["earlykms"]; + }; + }; # With the current implementation, the whole PCI IOMMU group 14: # 00:1f.x in the example from Lenovo X1 Carbon @@ -107,7 +113,7 @@ productId = "51a4"; } ]; - kernelParams = [ + kernelConfig.kernelParams = [ "snd_intel_dspcfg.dsp_driver=3" "snd_sof_intel_hda_common.dmic_num=4" ]; diff --git a/modules/microvm/virtualization/microvm/guivm.nix b/modules/microvm/virtualization/microvm/guivm.nix index 9c86177fc..af8db612e 100644 --- a/modules/microvm/virtualization/microvm/guivm.nix +++ b/modules/microvm/virtualization/microvm/guivm.nix @@ -6,7 +6,6 @@ pkgs, ... }: let - configHost = config; vmName = "gui-vm"; macAddress = "02:00:00:02:02:02"; inherit (import ../../../../lib/launcher.nix {inherit pkgs lib;}) rmDesktopEntries; @@ -22,19 +21,18 @@ ... }: { ghaf = { - users.accounts.enable = lib.mkDefault configHost.ghaf.users.accounts.enable; + users.accounts.enable = lib.mkDefault config.ghaf.users.accounts.enable; profiles = { - debug.enable = lib.mkDefault configHost.ghaf.profiles.debug.enable; + debug.enable = lib.mkDefault config.ghaf.profiles.debug.enable; applications.enable = false; graphics.enable = true; }; # To enable screen locking set to true graphics.labwc.autolock.enable = false; - windows-launcher.enable = false; 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; + ssh.daemon.enable = lib.mkDefault config.ghaf.development.ssh.daemon.enable; + debug.tools.enable = lib.mkDefault config.ghaf.development.debug.tools.enable; + nix-setup.enable = lib.mkDefault config.ghaf.development.nix-setup.enable; }; systemd = { enable = true; @@ -42,7 +40,7 @@ withNss = true; withResolved = true; withTimesyncd = true; - withDebug = configHost.ghaf.profiles.debug.enable; + withDebug = config.ghaf.profiles.debug.enable; withHardenedConfigs = true; }; }; @@ -79,14 +77,15 @@ pkgs.nm-launcher pkgs.pamixer ] - ++ (lib.optional (configHost.ghaf.profiles.debug.enable && configHost.ghaf.virtualization.microvm.idsvm.mitmproxy.enable) pkgs.mitmweb-ui); + ++ (lib.optional (config.ghaf.profiles.debug.enable && config.ghaf.virtualization.microvm.idsvm.mitmproxy.enable) pkgs.mitmweb-ui); }; + time.timeZone = config.time.timeZone; system.stateVersion = lib.trivial.release; nixpkgs = { - buildPlatform.system = configHost.nixpkgs.buildPlatform.system; - hostPlatform.system = configHost.nixpkgs.hostPlatform.system; + buildPlatform.system = config.nixpkgs.buildPlatform.system; + hostPlatform.system = config.nixpkgs.hostPlatform.system; }; # Suspend inside Qemu causes segfault @@ -101,8 +100,8 @@ shares = [ { tag = "rw-waypipe-ssh-public-key"; - source = configHost.ghaf.security.sshKeys.waypipeSshPublicKeyDir; - mountPoint = configHost.ghaf.security.sshKeys.waypipeSshPublicKeyDir; + source = config.ghaf.security.sshKeys.waypipeSshPublicKeyDir; + mountPoint = config.ghaf.security.sshKeys.waypipeSshPublicKeyDir; } { tag = "ro-store"; @@ -124,7 +123,7 @@ x86_64-linux = "q35"; aarch64-linux = "virt"; } - .${configHost.nixpkgs.hostPlatform.system}; + .${config.nixpkgs.hostPlatform.system}; }; }; @@ -211,8 +210,8 @@ in { # This directory needs to be created before any of the microvms start. systemd.services."create-waypipe-ssh-public-key-directory" = let script = pkgs.writeShellScriptBin "create-waypipe-ssh-public-key-directory" '' - mkdir -pv ${configHost.ghaf.security.sshKeys.waypipeSshPublicKeyDir} - chown -v microvm ${configHost.ghaf.security.sshKeys.waypipeSshPublicKeyDir} + mkdir -pv ${config.ghaf.security.sshKeys.waypipeSshPublicKeyDir} + chown -v microvm ${config.ghaf.security.sshKeys.waypipeSshPublicKeyDir} ''; in { enable = true; diff --git a/modules/microvm/virtualization/microvm/modules.nix b/modules/microvm/virtualization/microvm/modules.nix index c55560c16..1774e65cd 100644 --- a/modules/microvm/virtualization/microvm/modules.nix +++ b/modules/microvm/virtualization/microvm/modules.nix @@ -6,20 +6,35 @@ pkgs, ... }: let - inherit (lib) mkOption types optionals optionalAttrs mkEnableOption hasAttr; + inherit (builtins) hasAttr; + inherit (lib) mkOption types optionals optionalAttrs; cfg = config.ghaf.virtualization.microvm; - # Currently only x86 is supported by this module - isX86 = pkgs.stdenv.hostPlatform.isx86; + # Currently only x86 with hw definition supported + inherit (pkgs.stdenv.hostPlatform) isx86; + fullVirtualization = + isx86 + && (hasAttr "hardware" config.ghaf) + && (hasAttr "devices" config.ghaf.hardware); - # Hardware passthrough modules - hardwareModules = optionalAttrs isX86 { + # Hardware devices passthrough modules + deviceModules = optionalAttrs fullVirtualization { inherit - (config.ghaf.hardware.passthrough) + (config.ghaf.hardware.devices) netvmPCIPassthroughModule audiovmPCIPassthroughModule - audiovmKernelParams + guivmPCIPassthroughModule + guivmVirtioInputHostEvdevModule + ; + }; + + # Kernel configurations + kernelConfigs = optionalAttrs fullVirtualization { + inherit + (config.ghaf.kernel) + guivm + audiovm ; }; @@ -28,14 +43,42 @@ config.ghaf.services.firmware.enable = true; }; - # Audio module configuration - audioModule = optionalAttrs cfg.audiovm.audio { - config.ghaf.services.audio.enable = true; + # Qemu configuration modules + qemuModules = { + inherit (config.ghaf.qemu) guivm; }; - # Wifi module configuration - wifiModule = optionalAttrs cfg.netvm.wifi { - config.ghaf.services.wifi.enable = true; + # Service modules + serviceModules = { + # Audio module + audio = optionalAttrs cfg.audiovm.audio { + config.ghaf.services.audio.enable = true; + }; + + # Wifi module + wifi = optionalAttrs cfg.netvm.wifi { + config.ghaf.services.wifi.enable = true; + }; + + # Fprint module + fprint = optionalAttrs cfg.guivm.fprint { + config.ghaf.services.fprint.enable = true; + }; + + # Desktop module + desktop = { + config.ghaf.services.desktop.enable = true; + }; + + # PDF opener + pdfOpener = { + config.ghaf.services.pdfopener.enable = true; + }; + + # Common namespace to share (built-time) between host and VMs + commonNamespace = { + config.ghaf.namespaces = config.ghaf.namespaces; + }; }; # Reference services module @@ -46,12 +89,20 @@ }; }; }; + + # Reference programs module + referenceProgramsModule = { + config.ghaf = optionalAttrs (hasAttr "reference" config.ghaf) { + reference = optionalAttrs (hasAttr "programs" config.ghaf.reference) { + inherit (config.ghaf.reference) programs; + }; + }; + }; in { options.ghaf.virtualization.microvm = { - enable = mkEnableOption "Enable MicroVM module configuration. Only x86 is supported."; netvm.wifi = mkOption { type = types.bool; - default = isX86 && cfg.netvm.enable; + default = isx86 && cfg.netvm.enable; description = '' Enable Wifi module configuration. ''; @@ -63,22 +114,43 @@ in { Enable Audio module configuration. ''; }; + guivm.fprint = mkOption { + type = types.bool; + default = cfg.guivm.enable; + description = '' + Enable Fingerprint module configuration. + ''; + }; }; config = { - ghaf.virtualization.microvm = optionalAttrs isX86 { + # System VM configurations + ghaf.virtualization.microvm = optionalAttrs fullVirtualization { # Netvm modules netvm.extraModules = optionals cfg.netvm.enable [ - hardwareModules.netvmPCIPassthroughModule + deviceModules.netvmPCIPassthroughModule firmwareModule - wifiModule + serviceModules.wifi referenceServiceModule ]; # Audiovm modules audiovm.extraModules = optionals cfg.audiovm.enable [ - hardwareModules.audiovmPCIPassthroughModule - hardwareModules.audiovmKernelParams - audioModule + deviceModules.audiovmPCIPassthroughModule + kernelConfigs.audiovm + serviceModules.audio + ]; + # Guivm modules + guivm.extraModules = optionals cfg.guivm.enable [ + deviceModules.guivmPCIPassthroughModule + deviceModules.guivmVirtioInputHostEvdevModule + kernelConfigs.guivm + firmwareModule + qemuModules.guivm + serviceModules.desktop + serviceModules.fprint + serviceModules.pdfOpener + serviceModules.commonNamespace + referenceProgramsModule ]; }; }; diff --git a/modules/reference/appvms/chromium.nix b/modules/reference/appvms/chromium.nix index 738a0939e..958b9b321 100644 --- a/modules/reference/appvms/chromium.nix +++ b/modules/reference/appvms/chromium.nix @@ -60,7 +60,7 @@ in { microvm.qemu.extraArgs = lib.optionals config.ghaf.hardware.usb.internal.enable config.ghaf.hardware.usb.internal.qemuExtraArgs.webcam; microvm.devices = []; - ghaf.programs.chromium.enable = true; + ghaf.reference.programs.chromium.enable = true; # Set default PDF XDG handler xdg.mime.defaultApplications."application/pdf" = "ghaf-pdf.desktop"; diff --git a/modules/reference/appvms/zathura.nix b/modules/reference/appvms/zathura.nix index 80edc50ed..26eacb95c 100644 --- a/modules/reference/appvms/zathura.nix +++ b/modules/reference/appvms/zathura.nix @@ -15,7 +15,7 @@ { imports = [../programs/zathura.nix]; time.timeZone = config.time.timeZone; - ghaf.programs.zathura.enable = true; + ghaf.reference.programs.zathura.enable = true; } ]; borderColor = "#122263"; diff --git a/modules/reference/programs/chromium.nix b/modules/reference/programs/chromium.nix index 715df1205..2b8e33a81 100644 --- a/modules/reference/programs/chromium.nix +++ b/modules/reference/programs/chromium.nix @@ -5,9 +5,9 @@ lib, ... }: let - cfg = config.ghaf.programs.chromium; + cfg = config.ghaf.reference.programs.chromium; in { - options.ghaf.programs.chromium = { + options.ghaf.reference.programs.chromium = { enable = lib.mkEnableOption "Enable Chromium program settings"; useZathuraVM = lib.mkEnableOption "Open PDFs in Zathura VM"; }; diff --git a/modules/reference/programs/windows-launcher.nix b/modules/reference/programs/windows-launcher.nix index 4f3959d9c..b75df7eb0 100644 --- a/modules/reference/programs/windows-launcher.nix +++ b/modules/reference/programs/windows-launcher.nix @@ -6,13 +6,13 @@ config, ... }: let - cfg = config.ghaf.windows-launcher; + cfg = config.ghaf.reference.programs.windows-launcher; windows-launcher = pkgs.callPackage ../../../packages/windows-launcher {enableSpice = cfg.spice;}; in { #TODO fix all these imports to correct scoping imports = [../../desktop]; - options.ghaf.windows-launcher = { + options.ghaf.reference.programs.windows-launcher = { enable = lib.mkEnableOption "Windows launcher"; spice = lib.mkEnableOption "remote access to the virtual machine using spice"; diff --git a/modules/reference/programs/zathura.nix b/modules/reference/programs/zathura.nix index eb7360181..57ebcaa4a 100644 --- a/modules/reference/programs/zathura.nix +++ b/modules/reference/programs/zathura.nix @@ -5,9 +5,9 @@ lib, ... }: let - cfg = config.ghaf.programs.zathura; + cfg = config.ghaf.reference.programs.zathura; in { - options.ghaf.programs.zathura = { + options.ghaf.reference.programs.zathura = { enable = lib.mkEnableOption "Enable Zathura program settings"; }; config = lib.mkIf cfg.enable { diff --git a/targets/generic-x86_64/flake-module.nix b/targets/generic-x86_64/flake-module.nix index 45d9db3a8..db20a8241 100644 --- a/targets/generic-x86_64/flake-module.nix +++ b/targets/generic-x86_64/flake-module.nix @@ -74,7 +74,7 @@ # Uncomment this line to use Labwc instead of Weston: #graphics.compositor = "labwc"; }; - windows-launcher.enable = true; + reference.programs.windows-launcher.enable = true; }; #TODO: how to handle the majority of laptops that need a little diff --git a/targets/lenovo-x1/everything.nix b/targets/lenovo-x1/everything.nix index ba5bf0548..bd35afbfd 100644 --- a/targets/lenovo-x1/everything.nix +++ b/targets/lenovo-x1/everything.nix @@ -55,9 +55,6 @@ usb.external.enable = true; }; - # TODO: move this to module - services.fprint.enable = true; - reference.appvms = { enable = true; chromium-vm = true; @@ -72,6 +69,13 @@ dendrite = true; }; + reference.programs = { + windows-launcher = { + enable = true; + spice = true; + }; + }; + # Virtualization options virtualization = { microvm-host = { @@ -97,12 +101,7 @@ guivm = { enable = true; - extraModules = - # TODO convert this to an actual module - import ./guivmExtraModules.nix { - inherit lib pkgs self; - configH = config; - }; + extraModules = [self.nixosModules.reference-programs]; }; audiovm = { @@ -126,11 +125,6 @@ profiles = { applications.enable = false; }; - - windows-launcher = { - enable = true; - spice = true; - }; }; }) ] diff --git a/targets/lenovo-x1/guivmExtraModules.nix b/targets/lenovo-x1/guivmExtraModules.nix deleted file mode 100644 index 68ceaea10..000000000 --- a/targets/lenovo-x1/guivmExtraModules.nix +++ /dev/null @@ -1,175 +0,0 @@ -# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors -# SPDX-License-Identifier: Apache-2.0 -# -{ - lib, - pkgs, - self, - configH, - ... -}: let - # TODO: Fix the path to get the sshKeyPath so that - # openPdf can be exported as a normal package from - # packaged/flake-module.nix and hence easily imported - # into all targets - openPdf = pkgs.callPackage ../../packages/openPdf { - inherit (configH.ghaf.security.sshKeys) sshKeyPath; - }; - # TODO generalize this TCP port used by PDF XDG handler - xdgPdfPort = 1200; - - winConfig = configH.ghaf.windows-launcher; - - guivmExtraConfigurations = { - ghaf = { - profiles.graphics.compositor = "labwc"; - graphics = { - launchers = let - hostAddress = "192.168.101.2"; - powerControl = pkgs.callPackage ../../packages/powercontrol {}; - privateSshKeyPath = configH.ghaf.security.sshKeys.sshKeyPath; - in [ - { - # The SPKI fingerprint is calculated like this: - # $ openssl x509 -noout -in mitmproxy-ca-cert.pem -pubkey | openssl asn1parse -noout -inform pem -out public.key - # $ openssl dgst -sha256 -binary public.key | openssl enc -base64 - name = "Chromium"; - path = - if configH.ghaf.virtualization.microvm.idsvm.mitmproxy.enable - then "${pkgs.openssh}/bin/ssh -i ${privateSshKeyPath} -o StrictHostKeyChecking=no chromium-vm run-waypipe chromium --enable-features=UseOzonePlatform --ozone-platform=wayland --user-data-dir=/home/${configH.ghaf.users.accounts.user}/.config/chromium/Default --ignore-certificate-errors-spki-list=Bq49YmAq1CG6FuBzp8nsyRXumW7Dmkp7QQ/F82azxGU=" - else "${pkgs.openssh}/bin/ssh -i ${privateSshKeyPath} -o StrictHostKeyChecking=no chromium-vm run-waypipe chromium --enable-features=UseOzonePlatform --ozone-platform=wayland"; - icon = "${pkgs.icon-pack}/chromium.svg"; - } - - { - name = "GALA"; - path = "${pkgs.openssh}/bin/ssh -i ${privateSshKeyPath} -o StrictHostKeyChecking=no gala-vm run-waypipe gala --enable-features=UseOzonePlatform --ozone-platform=wayland"; - icon = "${pkgs.icon-pack}/distributor-logo-android.svg"; - } - - { - name = "PDF Viewer"; - path = "${pkgs.openssh}/bin/ssh -i ${privateSshKeyPath} -o StrictHostKeyChecking=no zathura-vm run-waypipe zathura"; - icon = "${pkgs.icon-pack}/document-viewer.svg"; - } - - { - name = "Element"; - path = "${pkgs.openssh}/bin/ssh -i ${configH.ghaf.security.sshKeys.sshKeyPath} -o StrictHostKeyChecking=no element-vm run-waypipe element-desktop --enable-features=UseOzonePlatform --ozone-platform=wayland"; - icon = "${pkgs.icon-pack}/element-desktop.svg"; - } - - { - name = "AppFlowy"; - path = "${pkgs.openssh}/bin/ssh -i ${configH.ghaf.security.sshKeys.sshKeyPath} -o StrictHostKeyChecking=no appflowy-vm run-waypipe appflowy"; - icon = "${pkgs.appflowy}/opt/data/flutter_assets/assets/images/flowy_logo.svg"; - } - - { - name = "Windows"; - path = "${pkgs.virt-viewer}/bin/remote-viewer -f spice://${winConfig.spice-host}:${toString winConfig.spice-port}"; - icon = "${pkgs.icon-pack}/distributor-logo-windows.svg"; - } - - { - name = "Network Settings"; - path = "${pkgs.nm-launcher}/bin/nm-launcher"; - icon = "${pkgs.icon-pack}/preferences-system-network.svg"; - } - - { - name = "Shutdown"; - path = "${powerControl.makePowerOffCommand { - inherit hostAddress; - inherit privateSshKeyPath; - }}"; - icon = "${pkgs.icon-pack}/system-shutdown.svg"; - } - - { - name = "Reboot"; - path = "${powerControl.makeRebootCommand { - inherit hostAddress; - inherit privateSshKeyPath; - }}"; - icon = "${pkgs.icon-pack}/system-reboot.svg"; - } - - # Temporarly disabled as it fails to turn off display when suspended - # { - # name = "Suspend"; - # path = "${powerControl.makeSuspendCommand { - # inherit hostAddress; - # inherit privateSshKeyPath; - # }}"; - # icon = "${pkgs.icon-pack}/system-suspend.svg"; - # } - - # Temporarly disabled as it doesn't work at all - # { - # name = "Hibernate"; - # path = "${powerControl.makeHibernateCommand { - # inherit hostAddress; - # inherit privateSshKeyPath; - # }}"; - # icon = "${pkgs.icon-pack}/system-suspend-hibernate.svg"; - # } - ]; - }; - }; - - time.timeZone = configH.time.timeZone; - - # PDF XDG handler service receives a PDF file path from the chromium-vm and executes the openpdf script - systemd.user = { - sockets."pdf" = { - unitConfig = { - Description = "PDF socket"; - }; - socketConfig = { - ListenStream = "${toString xdgPdfPort}"; - Accept = "yes"; - }; - wantedBy = ["sockets.target"]; - }; - - services."pdf@" = { - description = "PDF opener"; - serviceConfig = { - ExecStart = "${openPdf}/bin/openPdf"; - StandardInput = "socket"; - StandardOutput = "journal"; - StandardError = "journal"; - }; - }; - }; - - # Enable all firmware for graphics firmware - hardware = { - enableRedistributableFirmware = true; - enableAllFirmware = true; - }; - - # Early KMS needed for ui to start work inside GuiVM - boot = { - initrd.kernelModules = ["i915"]; - kernelParams = ["earlykms"]; - }; - - # Open TCP port for the PDF XDG socket. - networking.firewall.allowedTCPPorts = [xdgPdfPort]; - - microvm.qemu.extraArgs = - configH.ghaf.hardware.passthrough.guivmQemuExtraArgs - ++ lib.optionals configH.ghaf.services.fprint.enable configH.ghaf.services.fprint.qemuExtraArgs; - }; - - inherit (configH.ghaf.hardware.passthrough) guivmPCIPassthroughModule guivmVirtioInputHostEvdevModule; -in - [ - guivmPCIPassthroughModule - guivmVirtioInputHostEvdevModule - guivmExtraConfigurations - self.nixosModules.reference-programs - ] - ++ lib.optionals configH.ghaf.services.fprint.enable [configH.ghaf.services.fprint.extraConfigurations] diff --git a/targets/nvidia-jetson-orin/flake-module.nix b/targets/nvidia-jetson-orin/flake-module.nix index d6cf5d149..307e90f80 100644 --- a/targets/nvidia-jetson-orin/flake-module.nix +++ b/targets/nvidia-jetson-orin/flake-module.nix @@ -92,7 +92,7 @@ release.enable = variant == "release"; debug.enable = variant == "debug"; }; - windows-launcher.enable = true; + reference.programs.windows-launcher.enable = true; graphics.labwc.renderer = "egl2"; # To enable screen locking set to true graphics.labwc.autolock.enable = false;