2020-10-22 14:57:33 +02:00
|
|
|
{ config, lib, pkgs, ... }:
|
|
|
|
|
|
|
|
with lib;
|
2020-11-01 20:21:27 +01:00
|
|
|
with import ../tests/lib/qemu-flags.nix { inherit pkgs; };
|
2020-10-22 14:57:33 +02:00
|
|
|
|
|
|
|
let
|
|
|
|
|
2020-11-01 20:21:27 +01:00
|
|
|
qemu = config.system.build.qemu;
|
2020-10-22 14:57:33 +02:00
|
|
|
|
|
|
|
cfg = config.virtualisation;
|
|
|
|
|
|
|
|
consoles = lib.concatMapStringsSep " " (c: "console=${c}") cfg.qemu.consoles;
|
|
|
|
|
2020-11-01 20:21:27 +01:00
|
|
|
efiPrefix = if (pkgs.stdenv.isi686 || pkgs.stdenv.isx86_64) then
|
2021-02-06 21:11:39 +01:00
|
|
|
"${pkgs.buildPackages.buildPackages.OVMF.fd}/FV/OVMF"
|
2020-11-01 20:21:27 +01:00
|
|
|
else if pkgs.stdenv.isAarch64 then
|
2021-02-06 21:11:39 +01:00
|
|
|
"${pkgs.buildPackages.buildPackages.OVMF.fd}/FV/AAVMF"
|
2020-11-01 20:21:27 +01:00
|
|
|
else
|
|
|
|
throw "No EFI firmware available for platform";
|
2020-10-22 14:57:33 +02:00
|
|
|
efiFirmware = "${efiPrefix}_CODE.fd";
|
|
|
|
efiVarsDefault = "${efiPrefix}_VARS.fd";
|
|
|
|
|
|
|
|
# Shell script to start the VM.
|
2020-11-01 20:21:27 +01:00
|
|
|
startVM = ''
|
|
|
|
#! ${pkgs.buildPackages.runtimeShell}
|
2020-12-30 21:07:30 +01:00
|
|
|
'' + lib.optionalString (config.virtualisation.diskImage != null) ''
|
2020-11-01 20:21:27 +01:00
|
|
|
NIX_DISK_IMAGE=$(readlink -f ''${NIX_DISK_IMAGE:-${config.virtualisation.diskImage}})
|
2020-10-22 14:57:33 +02:00
|
|
|
|
2020-12-30 21:07:30 +01:00
|
|
|
if ! test -w "$NIX_DISK_IMAGE"; then
|
|
|
|
${qemu}/bin/qemu-img create -f qcow2 -b $NIX_DISK_IMAGE $TMPDIR/disk.img || exit 1
|
|
|
|
NIX_DISK_IMAGE=$TMPDIR/disk.img
|
|
|
|
fi
|
|
|
|
|
2020-11-01 20:21:27 +01:00
|
|
|
if ! test -e "$NIX_DISK_IMAGE"; then
|
|
|
|
${qemu}/bin/qemu-img create -f qcow2 "$NIX_DISK_IMAGE" \
|
|
|
|
${toString config.virtualisation.diskSize}M || exit 1
|
|
|
|
fi
|
2020-10-22 14:57:33 +02:00
|
|
|
|
2020-12-30 21:07:30 +01:00
|
|
|
'' + ''
|
2020-11-01 20:21:27 +01:00
|
|
|
# Create a directory for storing temporary data of the running VM.
|
|
|
|
if [ -z "$TMPDIR" -o -z "$USE_TMPDIR" ]; then
|
|
|
|
TMPDIR=$(mktemp -d nix-vm.XXXXXXXXXX --tmpdir)
|
|
|
|
fi
|
2020-10-22 14:57:33 +02:00
|
|
|
|
2020-11-01 20:21:27 +01:00
|
|
|
# Start QEMU.
|
2020-12-30 21:07:30 +01:00
|
|
|
set -v
|
2020-11-01 20:21:27 +01:00
|
|
|
exec ${qemuBinary qemu} \
|
|
|
|
-name ${config.system.name} \
|
|
|
|
-m ${toString config.virtualisation.memorySize} \
|
|
|
|
-smp ${toString config.virtualisation.cores} \
|
|
|
|
${toString config.virtualisation.qemu.options} \
|
2021-03-20 19:46:52 +01:00
|
|
|
${
|
|
|
|
if config.hardware.genode.usb.storage.enable then
|
|
|
|
"-drive id=usbdisk,if=none,file=$NIX_DISK_IMAGE -device usb-storage,drive=usbdisk"
|
|
|
|
else
|
|
|
|
"$NIX_DISK_IMAGE"
|
|
|
|
} \
|
2020-11-01 20:21:27 +01:00
|
|
|
$QEMU_OPTS \
|
|
|
|
"$@"
|
|
|
|
'';
|
|
|
|
|
|
|
|
in {
|
2020-10-22 14:57:33 +02:00
|
|
|
options = {
|
|
|
|
|
2020-11-01 20:21:27 +01:00
|
|
|
virtualisation.memorySize = mkOption {
|
|
|
|
default = 384;
|
|
|
|
description = ''
|
|
|
|
Memory size (M) of virtual machine.
|
|
|
|
'';
|
|
|
|
};
|
2020-10-22 14:57:33 +02:00
|
|
|
|
2020-11-01 20:21:27 +01:00
|
|
|
virtualisation.diskSize = mkOption {
|
|
|
|
default = 512;
|
|
|
|
description = ''
|
|
|
|
Disk size (M) of virtual machine.
|
|
|
|
'';
|
|
|
|
};
|
2020-10-22 14:57:33 +02:00
|
|
|
|
2020-11-01 20:21:27 +01:00
|
|
|
virtualisation.diskImage = mkOption {
|
2020-12-30 21:07:30 +01:00
|
|
|
type = with types; nullOr path;
|
|
|
|
default = null;
|
2020-11-01 20:21:27 +01:00
|
|
|
description = ''
|
|
|
|
Path to the disk image containing the root filesystem.
|
|
|
|
The image will be created on startup if it does not
|
|
|
|
exist.
|
|
|
|
'';
|
|
|
|
};
|
2020-10-22 14:57:33 +02:00
|
|
|
|
2020-11-01 20:21:27 +01:00
|
|
|
virtualisation.emptyDiskImages = mkOption {
|
|
|
|
default = [ ];
|
|
|
|
type = types.listOf types.int;
|
|
|
|
description = ''
|
|
|
|
Additional disk images to provide to the VM. The value is
|
|
|
|
a list of size in megabytes of each disk. These disks are
|
|
|
|
writeable by the VM.
|
|
|
|
'';
|
|
|
|
};
|
2020-10-22 14:57:33 +02:00
|
|
|
|
2020-11-01 20:21:27 +01:00
|
|
|
virtualisation.graphics = mkOption {
|
|
|
|
default = true;
|
|
|
|
description = ''
|
|
|
|
Whether to run QEMU with a graphics window, or in nographic mode.
|
|
|
|
Serial console will be enabled on both settings, but this will
|
|
|
|
change the preferred console.
|
|
|
|
'';
|
|
|
|
};
|
2020-10-22 14:57:33 +02:00
|
|
|
|
2020-11-01 20:21:27 +01:00
|
|
|
virtualisation.cores = mkOption {
|
|
|
|
default = 1;
|
|
|
|
type = types.int;
|
|
|
|
description = ''
|
|
|
|
Specify the number of cores the guest is permitted to use.
|
|
|
|
The number can be higher than the available cores on the
|
|
|
|
host system.
|
|
|
|
'';
|
|
|
|
};
|
2020-10-22 14:57:33 +02:00
|
|
|
|
2020-11-01 20:21:27 +01:00
|
|
|
virtualisation.pathsInNixDB = mkOption {
|
|
|
|
default = [ ];
|
|
|
|
description = ''
|
|
|
|
The list of paths whose closure is registered in the Nix
|
|
|
|
database in the VM. All other paths in the host Nix store
|
|
|
|
appear in the guest Nix store as well, but are considered
|
|
|
|
garbage (because they are not registered in the Nix
|
|
|
|
database in the guest).
|
|
|
|
'';
|
|
|
|
};
|
2020-10-22 14:57:33 +02:00
|
|
|
|
2020-11-01 20:21:27 +01:00
|
|
|
virtualisation.vlans = mkOption {
|
2021-04-12 20:45:47 +02:00
|
|
|
default = [ ];
|
2020-11-01 20:21:27 +01:00
|
|
|
example = [ 1 2 ];
|
|
|
|
description = ''
|
|
|
|
Virtual networks to which the VM is connected. Each
|
|
|
|
number <replaceable>N</replaceable> in this list causes
|
|
|
|
the VM to have a virtual Ethernet interface attached to a
|
|
|
|
separate virtual network on which it will be assigned IP
|
|
|
|
address
|
|
|
|
<literal>192.168.<replaceable>N</replaceable>.<replaceable>M</replaceable></literal>,
|
|
|
|
where <replaceable>M</replaceable> is the index of this VM
|
|
|
|
in the list of VMs.
|
|
|
|
'';
|
|
|
|
};
|
2020-10-22 14:57:33 +02:00
|
|
|
|
2020-11-01 20:21:27 +01:00
|
|
|
virtualisation.writableStore = mkOption {
|
|
|
|
default = true; # FIXME
|
|
|
|
description = ''
|
|
|
|
If enabled, the Nix store in the VM is made writable by
|
|
|
|
layering an overlay filesystem on top of the host's Nix
|
|
|
|
store.
|
|
|
|
'';
|
|
|
|
};
|
2020-10-22 14:57:33 +02:00
|
|
|
|
2020-11-01 20:21:27 +01:00
|
|
|
virtualisation.writableStoreUseTmpfs = mkOption {
|
|
|
|
default = true;
|
|
|
|
description = ''
|
|
|
|
Use a tmpfs for the writable store instead of writing to the VM's
|
|
|
|
own filesystem.
|
|
|
|
'';
|
|
|
|
};
|
2020-10-22 14:57:33 +02:00
|
|
|
|
2020-11-01 20:21:27 +01:00
|
|
|
networking.primaryIPAddress = mkOption {
|
|
|
|
default = "";
|
|
|
|
internal = true;
|
|
|
|
description = "Primary IP address used in /etc/hosts.";
|
|
|
|
};
|
2020-10-22 14:57:33 +02:00
|
|
|
|
|
|
|
virtualisation.qemu = {
|
2020-11-01 20:21:27 +01:00
|
|
|
options = mkOption {
|
|
|
|
type = types.listOf types.unspecified;
|
|
|
|
default = [ ];
|
|
|
|
example = [ "-vga std" ];
|
|
|
|
description = "Options passed to QEMU.";
|
|
|
|
};
|
2020-10-22 14:57:33 +02:00
|
|
|
|
|
|
|
consoles = mkOption {
|
|
|
|
type = types.listOf types.str;
|
2020-11-01 20:21:27 +01:00
|
|
|
default = let consoles = [ "${qemuSerialDevice},115200n8" "tty0" ];
|
2020-10-22 14:57:33 +02:00
|
|
|
in if cfg.graphics then consoles else reverseList consoles;
|
|
|
|
example = [ "console=tty1" ];
|
|
|
|
description = ''
|
|
|
|
The output console devices to pass to the kernel command line via the
|
|
|
|
<literal>console</literal> parameter, the primary console is the last
|
|
|
|
item of this list.
|
|
|
|
|
|
|
|
By default it enables both serial console and
|
|
|
|
<literal>tty0</literal>. The preferred console (last one) is based on
|
|
|
|
the value of <option>virtualisation.graphics</option>.
|
|
|
|
'';
|
|
|
|
};
|
|
|
|
|
2021-04-12 20:45:47 +02:00
|
|
|
nics = mkOption {
|
|
|
|
description = "QEMU network devices.";
|
|
|
|
default = { };
|
|
|
|
type = with lib.types;
|
|
|
|
attrsOf (submodule {
|
|
|
|
options = {
|
|
|
|
netdev = mkOption {
|
|
|
|
type = submodule {
|
|
|
|
options = {
|
|
|
|
kind = mkOption {
|
|
|
|
type = str;
|
|
|
|
default = "user";
|
|
|
|
};
|
|
|
|
settings = mkOption {
|
|
|
|
type = attrsOf str;
|
|
|
|
default = { };
|
|
|
|
};
|
|
|
|
};
|
|
|
|
};
|
|
|
|
};
|
|
|
|
device = mkOption {
|
|
|
|
type = submodule {
|
|
|
|
options = {
|
|
|
|
kind = mkOption {
|
|
|
|
type = str;
|
|
|
|
default = "virtio-net-pci";
|
|
|
|
};
|
|
|
|
settings = mkOption {
|
|
|
|
type = attrsOf str;
|
|
|
|
default = { };
|
|
|
|
};
|
|
|
|
};
|
|
|
|
};
|
|
|
|
};
|
|
|
|
};
|
|
|
|
});
|
2020-10-22 14:57:33 +02:00
|
|
|
};
|
|
|
|
|
2020-11-01 20:21:27 +01:00
|
|
|
diskInterface = mkOption {
|
2020-12-30 21:07:30 +01:00
|
|
|
default = "ahci";
|
|
|
|
example = "usb";
|
|
|
|
type = types.enum [ "ahci" "usb" "virtio" ];
|
2020-11-01 20:21:27 +01:00
|
|
|
description = "The interface used for the virtual hard disks.";
|
2020-10-22 14:57:33 +02:00
|
|
|
};
|
|
|
|
|
2020-12-30 21:07:30 +01:00
|
|
|
kernel = mkOption {
|
|
|
|
type = types.path;
|
|
|
|
description = "Guest kernel.";
|
|
|
|
};
|
|
|
|
|
|
|
|
initrd = mkOption {
|
|
|
|
type = types.path;
|
|
|
|
description = "Guest initrd.";
|
2020-10-22 14:57:33 +02:00
|
|
|
};
|
2020-12-30 21:07:30 +01:00
|
|
|
|
|
|
|
cmdline = mkOption {
|
|
|
|
type = types.str;
|
|
|
|
description = "Command line options to pass to guest.";
|
|
|
|
};
|
|
|
|
|
2020-11-01 20:21:27 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
virtualisation.useBootLoader = mkOption {
|
|
|
|
default = false;
|
|
|
|
description = ''
|
|
|
|
If enabled, the virtual machine will be booted using the
|
|
|
|
regular boot loader (i.e., GRUB 1 or 2). This allows
|
|
|
|
testing of the boot loader. If
|
|
|
|
disabled (the default), the VM directly boots the NixOS
|
|
|
|
kernel and initial ramdisk, bypassing the boot loader
|
|
|
|
altogether.
|
|
|
|
'';
|
|
|
|
};
|
|
|
|
|
|
|
|
virtualisation.useEFIBoot = mkOption {
|
|
|
|
default = false;
|
|
|
|
description = ''
|
|
|
|
If enabled, the virtual machine will provide a EFI boot
|
|
|
|
manager.
|
|
|
|
useEFIBoot is ignored if useBootLoader == false.
|
|
|
|
'';
|
|
|
|
};
|
|
|
|
|
|
|
|
virtualisation.efiVars = mkOption {
|
|
|
|
default = "./${config.system.name}-efi-vars.fd";
|
|
|
|
description = ''
|
|
|
|
Path to nvram image containing UEFI variables. The will be created
|
|
|
|
on startup if it does not exist.
|
|
|
|
'';
|
|
|
|
};
|
|
|
|
|
|
|
|
virtualisation.bios = mkOption {
|
|
|
|
default = null;
|
|
|
|
type = types.nullOr types.package;
|
|
|
|
description = ''
|
|
|
|
An alternate BIOS (such as <package>qboot</package>) with which to start the VM.
|
|
|
|
Should contain a file named <literal>bios.bin</literal>.
|
|
|
|
If <literal>null</literal>, QEMU's builtin SeaBIOS will be used.
|
|
|
|
'';
|
|
|
|
};
|
2020-10-22 14:57:33 +02:00
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
config = {
|
|
|
|
|
|
|
|
# FIXME: Consolidate this one day.
|
|
|
|
virtualisation.qemu.options = mkMerge [
|
|
|
|
(mkIf cfg.useEFIBoot [
|
|
|
|
"-drive if=pflash,format=raw,unit=0,readonly,file=${efiFirmware}"
|
|
|
|
"-drive if=pflash,format=raw,unit=1,file=$NIX_EFI_VARS"
|
|
|
|
])
|
2020-11-01 20:21:27 +01:00
|
|
|
(mkIf (cfg.bios != null) [ "-bios ${cfg.bios}/bios.bin" ])
|
|
|
|
(mkIf (!cfg.graphics) [ "-nographic" ])
|
2021-04-12 20:45:47 +02:00
|
|
|
(let
|
|
|
|
toFlags = bind:
|
|
|
|
{ kind, settings }:
|
|
|
|
lib.strings.concatStringsSep "," ([ "${kind},${bind}" ]
|
|
|
|
++ (lib.attrsets.mapAttrsToList (k: v: "${k}=${v}") settings));
|
|
|
|
in lib.attrsets.mapAttrsToList (id: nic: [
|
|
|
|
"-netdev ${toFlags "id=${id}" nic.netdev}"
|
|
|
|
"-device ${toFlags "netdev=${id}" nic.device}"
|
|
|
|
]) cfg.qemu.nics)
|
2020-10-22 14:57:33 +02:00
|
|
|
];
|
|
|
|
|
2020-11-01 20:21:27 +01:00
|
|
|
system.build.vm = pkgs.runCommand "nixos-vm" { preferLocalBuild = true; } ''
|
|
|
|
mkdir -p $out/bin
|
|
|
|
ln -s ${
|
|
|
|
pkgs.writeScript "run-nixos-vm" startVM
|
|
|
|
} $out/bin/run-${config.system.name}-vm
|
|
|
|
'';
|
2020-10-22 14:57:33 +02:00
|
|
|
|
|
|
|
};
|
|
|
|
}
|