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 <[email protected]>
  • Loading branch information
josa41 authored and brianmcgillion committed May 28, 2024
1 parent 9efdb7b commit 58f9b35
Show file tree
Hide file tree
Showing 12 changed files with 339 additions and 34 deletions.
35 changes: 35 additions & 0 deletions modules/hardware/definition.nix
Original file line number Diff line number Diff line change
Expand Up @@ -141,5 +141,40 @@ in {
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/hardware/lenovo-x1/definitions/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ in {
inherit (hwDefinition) disks;
inherit (hwDefinition) network;
inherit (hwDefinition) gpu;
inherit (hwDefinition) audio;

virtioInputHostEvdevs = [
# Lenovo X1 touchpad and keyboard
Expand Down
2 changes: 2 additions & 0 deletions modules/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/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 @@ -12,6 +12,7 @@
./virtualization/microvm/idsvm/mitmproxy
./virtualization/microvm/appvm.nix
./virtualization/microvm/guivm.nix
./virtualization/microvm/audiovm.nix
./networking.nix
./power-control.nix
];
Expand Down
116 changes: 116 additions & 0 deletions modules/microvm/virtualization/microvm/audiovm.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
# 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;
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.5/24";
}
];
})
];
};
cfg = config.ghaf.virtualization.microvm.audiovm;
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
// {
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 @@ -70,6 +70,7 @@
pkgs.waypipe
pkgs.networkmanagerapplet
pkgs.nm-launcher
pkgs.pamixer
]
++ (lib.optional (configHost.ghaf.profiles.debug.enable && configHost.ghaf.virtualization.microvm.idsvm.mitmproxy.enable) pkgs.mitmweb-ui);
};
Expand Down
28 changes: 16 additions & 12 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,26 @@ 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 = {
enable = true;
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
22 changes: 13 additions & 9 deletions targets/lenovo-x1/appvms/element.nix
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,22 @@ in {
extraModules = [
{
# Enable pulseaudio for user ghaf to access mic
security.rtkit.enable = true;
sound.enable = true;
hardware.pulseaudio.enable = true;
users.extraUsers.ghaf.extraGroups = ["audio" "video"];

hardware.pulseaudio = {
enable = true;
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=1 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 = {
Expand Down Expand Up @@ -98,14 +110,6 @@ in {
"qemu-xhci"
"-device"
"usb-host,hostbus=3,hostport=8"
# 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"
# External USB GPS receiver
"-device"
"usb-host,vendorid=0x067b,productid=0x23a3"
Expand Down
Loading

0 comments on commit 58f9b35

Please sign in to comment.