Skip to content

Commit

Permalink
Initial AudioVM implementation with pipewire and pulseaudio
Browse files Browse the repository at this point in the history
Initial version of AudioVM with Pipewire backend and pulseaudio TCP
remote communication layer for the guest VMs. Note that this is not
really secure design (yet) basically all VMs can access the pulseaudio
TCP service.

Signed-off-by: Jon Sahlberg <jon.sahlberg@unikie.com>
  • Loading branch information
josa41 committed May 8, 2024
1 parent 1918f93 commit e359691
Show file tree
Hide file tree
Showing 12 changed files with 334 additions and 33 deletions.
35 changes: 35 additions & 0 deletions modules/common/hardware/definition.nix
Original file line number Diff line number Diff line change
Expand Up @@ -139,5 +139,40 @@
SUBSYSTEM=="input",ATTRS{name}=="AT Translated Set 2 keyboard",GROUP="kvm"
'';
};

audio = {
# With the current implementation, the whole PCI IOMMU group 14:
# 00:1f.x in the example from Lenovo X1 Carbon
# must be defined for passthrough to AudioVM
pciDevices = mkOption {
description = "PCI Devices to passthrough to AudioVM";
type = types.listOf pciDevSubmodule;
default = [];
example = literalExpression ''
[
{
path = "0000:00:1f.0";
vendorId = "8086";
productId = "519d";
}
{
path = "0000:00:1f.3";
vendorId = "8086";
productId = "51ca";
}
{
path = "0000:00:1f.4";
vendorId = "8086";
productId = "51a3";
}
{
path = "0000:00:1f.5";
vendorId = "8086";
productId = "51a4";
}
]
'';
};
};
};
}
1 change: 1 addition & 0 deletions modules/common/hardware/lenovo-x1/definitions/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ in {
inherit (hwDefinition) disks;
inherit (hwDefinition) network;
inherit (hwDefinition) gpu;
inherit (hwDefinition) audio;

# Notes:
# 1. This assembles udev rules for different hw configurations (i.e., different mice/touchpads) by adding
Expand Down
2 changes: 2 additions & 0 deletions modules/common/hardware/lenovo-x1/definitions/x1-gen10.nix
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,6 @@
productId = "46a6";
}
];

audio.pciDevices = [];
}
30 changes: 30 additions & 0 deletions modules/common/hardware/lenovo-x1/definitions/x1-gen11.nix
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,34 @@
productId = "a7a1";
}
];

# With the current implementation, the whole PCI IOMMU group 14:
# 00:1f.x in the example from Lenovo X1 Carbon
# must be defined for passthrough to AudioVM
audio.pciDevices = [
{
# ISA bridge: Intel Corporation Raptor Lake LPC/eSPI Controller (rev 01)
path = "0000:00:1f.0";
vendorId = "8086";
productId = "519d";
}
{
# Audio device: Intel Corporation Raptor Lake-P/U/H cAVS (rev 01)
path = "0000:00:1f.3";
vendorId = "8086";
productId = "51ca";
}
{
# SMBus: Intel Corporation Alder Lake PCH-P SMBus Host Controller (rev 01)
path = "0000:00:1f.4";
vendorId = "8086";
productId = "51a3";
}
{
# Serial bus controller: Intel Corporation Alder Lake-P PCH SPI Controller (rev 01)
path = "0000:00:1f.5";
vendorId = "8086";
productId = "51a4";
}
];
}
1 change: 1 addition & 0 deletions modules/microvm/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
./virtualization/microvm/netvm.nix
./virtualization/microvm/appvm.nix
./virtualization/microvm/guivm.nix
./virtualization/microvm/audiovm.nix
./networking.nix
./power-control.nix
];
Expand Down
120 changes: 120 additions & 0 deletions modules/microvm/virtualization/microvm/audiovm.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors
# SPDX-License-Identifier: Apache-2.0
{
config,
lib,
...
}: let
configHost = config;
vmName = "audio-vm";
macAddress = "02:00:00:03:03:03";
audiovmBaseConfiguration = {
imports = [
(import ./common/vm-networking.nix {inherit vmName macAddress;})
({
lib,
pkgs,
...
}: {
ghaf = {
users.accounts.enable = lib.mkDefault configHost.ghaf.users.accounts.enable;
profiles.debug.enable = lib.mkDefault configHost.ghaf.profiles.debug.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;
};
systemd = {
enable = true;
withName = "audiovm-systemd";
withNss = true;
withResolved = true;
withTimesyncd = true;
withDebug = configHost.ghaf.profiles.debug.enable;
};
};

environment = {
systemPackages = [
pkgs.pulseaudio
pkgs.pamixer
pkgs.pipewire
];
};

system.stateVersion = lib.trivial.release;

nixpkgs.buildPlatform.system = configHost.nixpkgs.buildPlatform.system;
nixpkgs.hostPlatform.system = configHost.nixpkgs.hostPlatform.system;

microvm = {
optimize.enable = true;
vcpu = 1;
mem = 256;
hypervisor = "qemu";
shares = [
{
tag = "ro-store";
source = "/nix/store";
mountPoint = "/nix/.ro-store";
}
];
writableStoreOverlay = lib.mkIf config.ghaf.development.debug.tools.enable "/nix/.rw-store";
qemu = {
machine =
{
# Use the same machine type as the host
x86_64-linux = "q35";
aarch64-linux = "virt";
}
.${configHost.nixpkgs.hostPlatform.system};
};
};

imports = [
../../../common
];

# Fixed IP-address for debugging subnet
systemd.network.networks."10-ethint0".addresses = [
{
addressConfig.Address = "192.168.101.4/24";
}
];
})
];
};
cfg = config.ghaf.virtualization.microvm.audiovm;
# Importing kernel builder function and building guest_audio_hardened_kernel
# Maybe this also needs actual hardenening & remove unused modules etc.
in {
options.ghaf.virtualization.microvm.audiovm = {
enable = lib.mkEnableOption "AudioVM";

extraModules = lib.mkOption {
description = ''
List of additional modules to be imported and evaluated as part of
AudioVM's NixOS configuration.
'';
default = [];
};
};

config = lib.mkIf cfg.enable {
microvm.vms."${vmName}" = {
autostart = true;
config =
audiovmBaseConfiguration
// {
# boot.kernelPackages =
# lib.mkIf config.ghaf.guest.kernel.hardening.audio.enable
# (pkgs.linuxPackagesFor guest_audio_hardened_kernel);

imports =
audiovmBaseConfiguration.imports
++ cfg.extraModules;
};
};
};
}
1 change: 1 addition & 0 deletions modules/microvm/virtualization/microvm/guivm.nix
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@
pkgs.waypipe
pkgs.networkmanagerapplet
pkgs.nm-launcher
pkgs.pamixer
];
};

Expand Down
24 changes: 13 additions & 11 deletions targets/lenovo-x1/appvms/chromium.nix
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ in {
'';
in [
pkgs.chromium
pkgs.pamixer
pkgs.pulseaudio
pkgs.xdg-utils
xdgPdfItem
xdgOpenPdf
Expand All @@ -32,22 +32,24 @@ in {
cores = 4;
extraModules = [
{
# Enable pulseaudio for user ghaf
# Enable pulseaudio for Chromium VM
security.rtkit.enable = true;
sound.enable = true;
hardware.pulseaudio.enable = true;
users.extraUsers.ghaf.extraGroups = ["audio"];
users.extraUsers.ghaf.extraGroups = ["audio" "video"];

hardware.pulseaudio.extraConfig = ''
load-module module-tunnel-sink sink_name=chromium-speaker server=audio-vm.ghaf:4713 format=s16le channels=2 rate=48000
load-module module-tunnel-source source_name=chromium-mic server=audio-vm.ghaf:4713 format=s16le channels=1 rate=48000
# Set sink and source default max volume to about 90% (0-65536)
set-sink-volume chromium-speaker 60000
set-source-volume chromium-mic 60000
'';

time.timeZone = "Asia/Dubai";

microvm.qemu.extraArgs = [
# Connect sound device to hosts pulseaudio socket
"-audiodev"
"pa,id=pa1,server=unix:/run/pulse/native"
# Add HDA sound device to guest
"-device"
"intel-hda"
"-device"
"hda-duplex,audiodev=pa1"
# Lenovo X1 integrated usb webcam
"-device"
"qemu-xhci"
Expand Down
23 changes: 15 additions & 8 deletions targets/lenovo-x1/appvms/element.nix
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,21 @@
extraArgs = ["-n"]; # Do not wait for a client to connect before polling
};

# Enable pulseaudio for Element VM
security.rtkit.enable = true;
sound.enable = true;
hardware.pulseaudio.enable = true;
users.extraUsers.ghaf.extraGroups = ["audio" "video"];

hardware.pulseaudio.extraConfig = ''
load-module module-tunnel-sink sink_name=element-speaker server=audio-vm.ghaf:4713 format=s16le channels=2 rate=48000
load-module module-tunnel-source source_name=element-mic server=audio-vm.ghaf:4713 format=s16le channels=2 rate=48000
# Set sink and source default max volume to about 90% (0-65536)
set-sink-volume element-speaker 60000
set-source-volume element-mic 60000
'';

systemd.services.element-gps = {
description = "Element-gps is a GPS location provider for Element websocket interface.";
enable = true;
Expand All @@ -42,14 +57,6 @@
# External USB GPS receiver
"-device"
"usb-host,vendorid=0x067b,productid=0x23a3"
# Connect sound device to hosts pulseaudio socket
"-audiodev"
"pa,id=pa1,server=unix:/run/pulse/native"
# Add HDA sound device to guest
"-device"
"intel-hda"
"-device"
"hda-duplex,audiodev=pa1"
];
}
];
Expand Down
Loading

0 comments on commit e359691

Please sign in to comment.