···44# `config'. By default, the Nix store is shared read-only with the
55# host, which makes (re)building VMs very efficient.
6677-{ config, lib, pkgs, options, ... }:
77+{
88+ config,
99+ lib,
1010+ pkgs,
1111+ options,
1212+ ...
1313+}:
814915with lib;
1016···22282329 consoles = lib.concatMapStringsSep " " (c: "console=${c}") cfg.qemu.consoles;
24302525- driveOpts = { ... }: {
3131+ driveOpts =
3232+ { ... }:
3333+ {
26342727- options = {
3535+ options = {
28362929- file = mkOption {
3030- type = types.str;
3131- description = "The file image used for this drive.";
3232- };
3737+ file = mkOption {
3838+ type = types.str;
3939+ description = "The file image used for this drive.";
4040+ };
33413434- driveExtraOpts = mkOption {
3535- type = types.attrsOf types.str;
3636- default = {};
3737- description = "Extra options passed to drive flag.";
3838- };
4242+ driveExtraOpts = mkOption {
4343+ type = types.attrsOf types.str;
4444+ default = { };
4545+ description = "Extra options passed to drive flag.";
4646+ };
39474040- deviceExtraOpts = mkOption {
4141- type = types.attrsOf types.str;
4242- default = {};
4343- description = "Extra options passed to device flag.";
4444- };
4848+ deviceExtraOpts = mkOption {
4949+ type = types.attrsOf types.str;
5050+ default = { };
5151+ description = "Extra options passed to device flag.";
5252+ };
5353+5454+ name = mkOption {
5555+ type = types.nullOr types.str;
5656+ default = null;
5757+ description = "A name for the drive. Must be unique in the drives list. Not passed to qemu.";
5858+ };
45594646- name = mkOption {
4747- type = types.nullOr types.str;
4848- default = null;
4949- description = "A name for the drive. Must be unique in the drives list. Not passed to qemu.";
5060 };
51615262 };
53635454- };
6464+ selectPartitionTableLayout =
6565+ { useEFIBoot, useDefaultFilesystems }:
6666+ if useDefaultFilesystems then if useEFIBoot then "efi" else "legacy" else "none";
55675656- selectPartitionTableLayout = { useEFIBoot, useDefaultFilesystems }:
5757- if useDefaultFilesystems then
5858- if useEFIBoot then "efi" else "legacy"
5959- else "none";
6060-6161- driveCmdline = idx: { file, driveExtraOpts, deviceExtraOpts, ... }:
6868+ driveCmdline =
6969+ idx:
7070+ {
7171+ file,
7272+ driveExtraOpts,
7373+ deviceExtraOpts,
7474+ ...
7575+ }:
6276 let
6377 drvId = "drive${toString idx}";
6464- mkKeyValue = generators.mkKeyValueDefault {} "=";
7878+ mkKeyValue = generators.mkKeyValueDefault { } "=";
6579 mkOpts = opts: concatStringsSep "," (mapAttrsToList mkKeyValue opts);
6666- driveOpts = mkOpts (driveExtraOpts // {
6767- index = idx;
6868- id = drvId;
6969- "if" = "none";
7070- inherit file;
7171- });
7272- deviceOpts = mkOpts (deviceExtraOpts // {
7373- drive = drvId;
7474- });
8080+ driveOpts = mkOpts (
8181+ driveExtraOpts
8282+ // {
8383+ index = idx;
8484+ id = drvId;
8585+ "if" = "none";
8686+ inherit file;
8787+ }
8888+ );
8989+ deviceOpts = mkOpts (
9090+ deviceExtraOpts
9191+ // {
9292+ drive = drvId;
9393+ }
9494+ );
7595 device =
7696 if cfg.qemu.diskInterface == "scsi" then
7797 "-device lsi53c895a -device scsi-hd,${deviceOpts}"
7898 else
7999 "-device virtio-blk-pci,${deviceOpts}";
80100 in
8181- "-drive ${driveOpts} ${device}";
101101+ "-drive ${driveOpts} ${device}";
8210283103 drivesCmdLine = drives: concatStringsSep "\\\n " (imap1 driveCmdline drives);
8410485105 # Shell script to start the VM.
8686- startVM =
8787- ''
8888- #! ${hostPkgs.runtimeShell}
106106+ startVM = ''
107107+ #! ${hostPkgs.runtimeShell}
891089090- export PATH=${makeBinPath [ hostPkgs.coreutils ]}''${PATH:+:}$PATH
9191-9292- set -e
109109+ export PATH=${makeBinPath [ hostPkgs.coreutils ]}''${PATH:+:}$PATH
931109494- # Create an empty ext4 filesystem image. A filesystem image does not
9595- # contain a partition table but just a filesystem.
9696- createEmptyFilesystemImage() {
9797- local name=$1
9898- local size=$2
9999- local temp=$(mktemp)
100100- ${qemu}/bin/qemu-img create -f raw "$temp" "$size"
101101- ${hostPkgs.e2fsprogs}/bin/mkfs.ext4 -L ${rootFilesystemLabel} "$temp"
102102- ${qemu}/bin/qemu-img convert -f raw -O qcow2 "$temp" "$name"
103103- rm "$temp"
104104- }
111111+ set -e
105112106106- NIX_DISK_IMAGE=$(readlink -f "''${NIX_DISK_IMAGE:-${toString config.virtualisation.diskImage}}") || test -z "$NIX_DISK_IMAGE"
113113+ # Create an empty ext4 filesystem image. A filesystem image does not
114114+ # contain a partition table but just a filesystem.
115115+ createEmptyFilesystemImage() {
116116+ local name=$1
117117+ local size=$2
118118+ local temp=$(mktemp)
119119+ ${qemu}/bin/qemu-img create -f raw "$temp" "$size"
120120+ ${hostPkgs.e2fsprogs}/bin/mkfs.ext4 -L ${rootFilesystemLabel} "$temp"
121121+ ${qemu}/bin/qemu-img convert -f raw -O qcow2 "$temp" "$name"
122122+ rm "$temp"
123123+ }
107124108108- if test -n "$NIX_DISK_IMAGE" && ! test -e "$NIX_DISK_IMAGE"; then
109109- echo "Disk image do not exist, creating the virtualisation disk image..."
125125+ NIX_DISK_IMAGE=$(readlink -f "''${NIX_DISK_IMAGE:-${toString config.virtualisation.diskImage}}") || test -z "$NIX_DISK_IMAGE"
110126111111- ${if (cfg.useBootLoader && cfg.useDefaultFilesystems) then ''
112112- # Create a writable qcow2 image using the systemImage as a backing
113113- # image.
127127+ if test -n "$NIX_DISK_IMAGE" && ! test -e "$NIX_DISK_IMAGE"; then
128128+ echo "Disk image do not exist, creating the virtualisation disk image..."
114129115115- # CoW prevent size to be attributed to an image.
116116- # FIXME: raise this issue to upstream.
117117- ${qemu}/bin/qemu-img create \
118118- -f qcow2 \
119119- -b ${systemImage}/nixos.qcow2 \
120120- -F qcow2 \
121121- "$NIX_DISK_IMAGE"
122122- '' else if cfg.useDefaultFilesystems then ''
123123- createEmptyFilesystemImage "$NIX_DISK_IMAGE" "${toString cfg.diskSize}M"
124124- '' else ''
125125- # Create an empty disk image without a filesystem.
126126- ${qemu}/bin/qemu-img create -f qcow2 "$NIX_DISK_IMAGE" "${toString cfg.diskSize}M"
127127- ''
128128- }
129129- echo "Virtualisation disk image created."
130130- fi
130130+ ${
131131+ if (cfg.useBootLoader && cfg.useDefaultFilesystems) then
132132+ ''
133133+ # Create a writable qcow2 image using the systemImage as a backing
134134+ # image.
131135132132- # Create a directory for storing temporary data of the running VM.
133133- if [ -z "$TMPDIR" ] || [ -z "$USE_TMPDIR" ]; then
134134- TMPDIR=$(mktemp -d nix-vm.XXXXXXXXXX --tmpdir)
135135- fi
136136+ # CoW prevent size to be attributed to an image.
137137+ # FIXME: raise this issue to upstream.
138138+ ${qemu}/bin/qemu-img create \
139139+ -f qcow2 \
140140+ -b ${systemImage}/nixos.qcow2 \
141141+ -F qcow2 \
142142+ "$NIX_DISK_IMAGE"
143143+ ''
144144+ else if cfg.useDefaultFilesystems then
145145+ ''
146146+ createEmptyFilesystemImage "$NIX_DISK_IMAGE" "${toString cfg.diskSize}M"
147147+ ''
148148+ else
149149+ ''
150150+ # Create an empty disk image without a filesystem.
151151+ ${qemu}/bin/qemu-img create -f qcow2 "$NIX_DISK_IMAGE" "${toString cfg.diskSize}M"
152152+ ''
153153+ }
154154+ echo "Virtualisation disk image created."
155155+ fi
136156137137- ${lib.optionalString (cfg.useNixStoreImage) ''
138138- echo "Creating Nix store image..."
157157+ # Create a directory for storing temporary data of the running VM.
158158+ if [ -z "$TMPDIR" ] || [ -z "$USE_TMPDIR" ]; then
159159+ TMPDIR=$(mktemp -d nix-vm.XXXXXXXXXX --tmpdir)
160160+ fi
139161140140- ${hostPkgs.gnutar}/bin/tar --create \
141141- --absolute-names \
142142- --verbatim-files-from \
143143- --transform 'flags=rSh;s|/nix/store/||' \
144144- --files-from ${hostPkgs.closureInfo { rootPaths = [ config.system.build.toplevel regInfo ]; }}/store-paths \
145145- | ${hostPkgs.erofs-utils}/bin/mkfs.erofs \
146146- --quiet \
147147- --force-uid=0 \
148148- --force-gid=0 \
149149- -L ${nixStoreFilesystemLabel} \
150150- -U eb176051-bd15-49b7-9e6b-462e0b467019 \
151151- -T 0 \
152152- --tar=f \
153153- "$TMPDIR"/store.img
162162+ ${lib.optionalString (cfg.useNixStoreImage) ''
163163+ echo "Creating Nix store image..."
154164155155- echo "Created Nix store image."
156156- ''
157157- }
165165+ ${hostPkgs.gnutar}/bin/tar --create \
166166+ --absolute-names \
167167+ --verbatim-files-from \
168168+ --transform 'flags=rSh;s|/nix/store/||' \
169169+ --files-from ${
170170+ hostPkgs.closureInfo {
171171+ rootPaths = [
172172+ config.system.build.toplevel
173173+ regInfo
174174+ ];
175175+ }
176176+ }/store-paths \
177177+ | ${hostPkgs.erofs-utils}/bin/mkfs.erofs \
178178+ --quiet \
179179+ --force-uid=0 \
180180+ --force-gid=0 \
181181+ -L ${nixStoreFilesystemLabel} \
182182+ -U eb176051-bd15-49b7-9e6b-462e0b467019 \
183183+ -T 0 \
184184+ --tar=f \
185185+ "$TMPDIR"/store.img
158186159159- # Create a directory for exchanging data with the VM.
160160- mkdir -p "$TMPDIR/xchg"
187187+ echo "Created Nix store image."
188188+ ''}
161189162162- ${lib.optionalString cfg.useHostCerts
163163- ''
164164- mkdir -p "$TMPDIR/certs"
165165- if [ -e "$NIX_SSL_CERT_FILE" ]; then
166166- cp -L "$NIX_SSL_CERT_FILE" "$TMPDIR"/certs/ca-certificates.crt
167167- else
168168- echo \$NIX_SSL_CERT_FILE should point to a valid file if virtualisation.useHostCerts is enabled.
169169- fi
170170- ''}
190190+ # Create a directory for exchanging data with the VM.
191191+ mkdir -p "$TMPDIR/xchg"
171192172172- ${lib.optionalString cfg.useEFIBoot
173173- ''
174174- # Expose EFI variables, it's useful even when we are not using a bootloader (!).
175175- # We might be interested in having EFI variable storage present even if we aren't booting via UEFI, hence
176176- # no guard against `useBootLoader`. Examples:
177177- # - testing PXE boot or other EFI applications
178178- # - directbooting LinuxBoot, which `kexec()s` into a UEFI environment that can boot e.g. Windows
179179- NIX_EFI_VARS=$(readlink -f "''${NIX_EFI_VARS:-${config.system.name}-efi-vars.fd}")
180180- # VM needs writable EFI vars
181181- if ! test -e "$NIX_EFI_VARS"; then
182182- ${if cfg.efi.keepVariables then
183183- # We still need the EFI var from the make-disk-image derivation
184184- # because our "switch-to-configuration" process might
185185- # write into it and we want to keep this data.
186186- ''cp ${systemImage}/efi-vars.fd "$NIX_EFI_VARS"''
187187- else
188188- ''cp ${cfg.efi.variables} "$NIX_EFI_VARS"''
189189- }
190190- chmod 0644 "$NIX_EFI_VARS"
191191- fi
192192- ''}
193193+ ${lib.optionalString cfg.useHostCerts ''
194194+ mkdir -p "$TMPDIR/certs"
195195+ if [ -e "$NIX_SSL_CERT_FILE" ]; then
196196+ cp -L "$NIX_SSL_CERT_FILE" "$TMPDIR"/certs/ca-certificates.crt
197197+ else
198198+ echo \$NIX_SSL_CERT_FILE should point to a valid file if virtualisation.useHostCerts is enabled.
199199+ fi
200200+ ''}
193201194194- ${lib.optionalString cfg.tpm.enable ''
195195- NIX_SWTPM_DIR=$(readlink -f "''${NIX_SWTPM_DIR:-${config.system.name}-swtpm}")
196196- mkdir -p "$NIX_SWTPM_DIR"
197197- ${lib.getExe cfg.tpm.package} \
198198- socket \
199199- --tpmstate dir="$NIX_SWTPM_DIR" \
200200- --ctrl type=unixio,path="$NIX_SWTPM_DIR"/socket,terminate \
201201- --pid file="$NIX_SWTPM_DIR"/pid --daemon \
202202- --tpm2 \
203203- --log file="$NIX_SWTPM_DIR"/stdout,level=6
202202+ ${lib.optionalString cfg.useEFIBoot ''
203203+ # Expose EFI variables, it's useful even when we are not using a bootloader (!).
204204+ # We might be interested in having EFI variable storage present even if we aren't booting via UEFI, hence
205205+ # no guard against `useBootLoader`. Examples:
206206+ # - testing PXE boot or other EFI applications
207207+ # - directbooting LinuxBoot, which `kexec()s` into a UEFI environment that can boot e.g. Windows
208208+ NIX_EFI_VARS=$(readlink -f "''${NIX_EFI_VARS:-${config.system.name}-efi-vars.fd}")
209209+ # VM needs writable EFI vars
210210+ if ! test -e "$NIX_EFI_VARS"; then
211211+ ${
212212+ if cfg.efi.keepVariables then
213213+ # We still need the EFI var from the make-disk-image derivation
214214+ # because our "switch-to-configuration" process might
215215+ # write into it and we want to keep this data.
216216+ ''cp ${systemImage}/efi-vars.fd "$NIX_EFI_VARS"''
217217+ else
218218+ ''cp ${cfg.efi.variables} "$NIX_EFI_VARS"''
219219+ }
220220+ chmod 0644 "$NIX_EFI_VARS"
221221+ fi
222222+ ''}
204223205205- # Enable `fdflags` builtin in Bash
206206- # We will need it to perform surgical modification of the file descriptor
207207- # passed in the coprocess to remove `FD_CLOEXEC`, i.e. close the file descriptor
208208- # on exec.
209209- # If let alone, it will trigger the coprocess to read EOF when QEMU is `exec`
210210- # at the end of this script. To work around that, we will just clear
211211- # the `FD_CLOEXEC` bits as a first step.
212212- enable -f ${hostPkgs.bash}/lib/bash/fdflags fdflags
213213- # leave a dangling subprocess because the swtpm ctrl socket has
214214- # "terminate" when the last connection disconnects, it stops swtpm.
215215- # When qemu stops, or if the main shell process ends, the coproc will
216216- # get signaled by virtue of the pipe between main and coproc ending.
217217- # Which in turns triggers a socat connect-disconnect to swtpm which
218218- # will stop it.
219219- coproc waitingswtpm {
220220- read || :
221221- echo "" | ${lib.getExe hostPkgs.socat} STDIO UNIX-CONNECT:"$NIX_SWTPM_DIR"/socket
222222- }
223223- # Clear `FD_CLOEXEC` on the coprocess' file descriptor stdin.
224224- fdflags -s-cloexec ''${waitingswtpm[1]}
225225- ''}
224224+ ${lib.optionalString cfg.tpm.enable ''
225225+ NIX_SWTPM_DIR=$(readlink -f "''${NIX_SWTPM_DIR:-${config.system.name}-swtpm}")
226226+ mkdir -p "$NIX_SWTPM_DIR"
227227+ ${lib.getExe cfg.tpm.package} \
228228+ socket \
229229+ --tpmstate dir="$NIX_SWTPM_DIR" \
230230+ --ctrl type=unixio,path="$NIX_SWTPM_DIR"/socket,terminate \
231231+ --pid file="$NIX_SWTPM_DIR"/pid --daemon \
232232+ --tpm2 \
233233+ --log file="$NIX_SWTPM_DIR"/stdout,level=6
226234227227- cd "$TMPDIR"
235235+ # Enable `fdflags` builtin in Bash
236236+ # We will need it to perform surgical modification of the file descriptor
237237+ # passed in the coprocess to remove `FD_CLOEXEC`, i.e. close the file descriptor
238238+ # on exec.
239239+ # If let alone, it will trigger the coprocess to read EOF when QEMU is `exec`
240240+ # at the end of this script. To work around that, we will just clear
241241+ # the `FD_CLOEXEC` bits as a first step.
242242+ enable -f ${hostPkgs.bash}/lib/bash/fdflags fdflags
243243+ # leave a dangling subprocess because the swtpm ctrl socket has
244244+ # "terminate" when the last connection disconnects, it stops swtpm.
245245+ # When qemu stops, or if the main shell process ends, the coproc will
246246+ # get signaled by virtue of the pipe between main and coproc ending.
247247+ # Which in turns triggers a socat connect-disconnect to swtpm which
248248+ # will stop it.
249249+ coproc waitingswtpm {
250250+ read || :
251251+ echo "" | ${lib.getExe hostPkgs.socat} STDIO UNIX-CONNECT:"$NIX_SWTPM_DIR"/socket
252252+ }
253253+ # Clear `FD_CLOEXEC` on the coprocess' file descriptor stdin.
254254+ fdflags -s-cloexec ''${waitingswtpm[1]}
255255+ ''}
228256229229- ${lib.optionalString (cfg.emptyDiskImages != []) "idx=0"}
230230- ${flip concatMapStrings cfg.emptyDiskImages (size: ''
231231- if ! test -e "empty$idx.qcow2"; then
232232- ${qemu}/bin/qemu-img create -f qcow2 "empty$idx.qcow2" "${toString size}M"
233233- fi
234234- idx=$((idx + 1))
235235- '')}
257257+ cd "$TMPDIR"
236258237237- # Start QEMU.
238238- exec ${qemu-common.qemuBinary qemu} \
239239- -name ${config.system.name} \
240240- -m ${toString config.virtualisation.memorySize} \
241241- -smp ${toString config.virtualisation.cores} \
242242- -device virtio-rng-pci \
243243- ${concatStringsSep " " config.virtualisation.qemu.networkingOptions} \
244244- ${concatStringsSep " \\\n "
245245- (mapAttrsToList
246246- (tag: share: "-virtfs local,path=${share.source},security_model=${share.securityModel},mount_tag=${tag}")
247247- config.virtualisation.sharedDirectories)} \
248248- ${drivesCmdLine config.virtualisation.qemu.drives} \
249249- ${concatStringsSep " \\\n " config.virtualisation.qemu.options} \
250250- $QEMU_OPTS \
251251- "$@"
252252- '';
259259+ ${lib.optionalString (cfg.emptyDiskImages != [ ]) "idx=0"}
260260+ ${flip concatMapStrings cfg.emptyDiskImages (size: ''
261261+ if ! test -e "empty$idx.qcow2"; then
262262+ ${qemu}/bin/qemu-img create -f qcow2 "empty$idx.qcow2" "${toString size}M"
263263+ fi
264264+ idx=$((idx + 1))
265265+ '')}
253266267267+ # Start QEMU.
268268+ exec ${qemu-common.qemuBinary qemu} \
269269+ -name ${config.system.name} \
270270+ -m ${toString config.virtualisation.memorySize} \
271271+ -smp ${toString config.virtualisation.cores} \
272272+ -device virtio-rng-pci \
273273+ ${concatStringsSep " " config.virtualisation.qemu.networkingOptions} \
274274+ ${
275275+ concatStringsSep " \\\n " (
276276+ mapAttrsToList (
277277+ tag: share:
278278+ "-virtfs local,path=${share.source},security_model=${share.securityModel},mount_tag=${tag}"
279279+ ) config.virtualisation.sharedDirectories
280280+ )
281281+ } \
282282+ ${drivesCmdLine config.virtualisation.qemu.drives} \
283283+ ${concatStringsSep " \\\n " config.virtualisation.qemu.options} \
284284+ $QEMU_OPTS \
285285+ "$@"
286286+ '';
254287255288 regInfo = hostPkgs.closureInfo { rootPaths = config.virtualisation.additionalPaths; };
256289···292325 OVMF = cfg.efi.OVMF;
293326 };
294327328328+ virtualisationOptions = import ./virtualisation-options.nix;
329329+295330in
296331297332{
298333 imports = [
334334+ ./virtualisation-options.nix
299335 ../profiles/qemu-guest.nix
300300- (mkRenamedOptionModule [ "virtualisation" "pathsInNixDB" ] [ "virtualisation" "additionalPaths" ])
301301- (mkRemovedOptionModule [ "virtualisation" "bootDevice" ] "This option was renamed to `virtualisation.rootDevice`, as it was incorrectly named and misleading. Take the time to review what you want to do and look at the new options like `virtualisation.{bootLoaderDevice, bootPartition}`, open an issue in case of issues.")
302302- (mkRemovedOptionModule [ "virtualisation" "efiVars" ] "This option was removed, it is possible to provide a template UEFI variable with `virtualisation.efi.variables` ; if this option is important to you, open an issue")
303303- (mkRemovedOptionModule [ "virtualisation" "persistBootDevice" ] "Boot device is always persisted if you use a bootloader through the root disk image ; if this does not work for your usecase, please examine carefully what `virtualisation.{bootDevice, rootDevice, bootPartition}` options offer you and open an issue explaining your need.`")
336336+ virtualisationOptions.diskSize
337337+ (mkRenamedOptionModule
338338+ [
339339+ "virtualisation"
340340+ "pathsInNixDB"
341341+ ]
342342+ [
343343+ "virtualisation"
344344+ "additionalPaths"
345345+ ]
346346+ )
347347+ (mkRemovedOptionModule
348348+ [
349349+ "virtualisation"
350350+ "bootDevice"
351351+ ]
352352+ "This option was renamed to `virtualisation.rootDevice`, as it was incorrectly named and misleading. Take the time to review what you want to do and look at the new options like `virtualisation.{bootLoaderDevice, bootPartition}`, open an issue in case of issues."
353353+ )
354354+ (mkRemovedOptionModule
355355+ [
356356+ "virtualisation"
357357+ "efiVars"
358358+ ]
359359+ "This option was removed, it is possible to provide a template UEFI variable with `virtualisation.efi.variables` ; if this option is important to you, open an issue"
360360+ )
361361+ (mkRemovedOptionModule
362362+ [
363363+ "virtualisation"
364364+ "persistBootDevice"
365365+ ]
366366+ "Boot device is always persisted if you use a bootloader through the root disk image ; if this does not work for your usecase, please examine carefully what `virtualisation.{bootDevice, rootDevice, bootPartition}` options offer you and open an issue explaining your need.`"
367367+ )
304368 ];
305369306370 options = {
307371308372 virtualisation.fileSystems = options.fileSystems;
309373310310- virtualisation.memorySize =
311311- mkOption {
312312- type = types.ints.positive;
313313- default = 1024;
314314- description = ''
315315- The memory size in megabytes of the virtual machine.
316316- '';
317317- };
374374+ virtualisation.memorySize = mkOption {
375375+ type = types.ints.positive;
376376+ default = 1024;
377377+ description = ''
378378+ The memory size in megabytes of the virtual machine.
379379+ '';
380380+ };
318381319319- virtualisation.msize =
320320- mkOption {
321321- type = types.ints.positive;
322322- default = 16384;
323323- description = ''
324324- The msize (maximum packet size) option passed to 9p file systems, in
325325- bytes. Increasing this should increase performance significantly,
326326- at the cost of higher RAM usage.
327327- '';
328328- };
382382+ virtualisation.msize = mkOption {
383383+ type = types.ints.positive;
384384+ default = 16384;
385385+ description = ''
386386+ The msize (maximum packet size) option passed to 9p file systems, in
387387+ bytes. Increasing this should increase performance significantly,
388388+ at the cost of higher RAM usage.
389389+ '';
390390+ };
329391330330- virtualisation.diskSize =
331331- mkOption {
332332- type = types.ints.positive;
333333- default = 1024;
334334- description = ''
335335- The disk size in megabytes of the virtual machine.
336336- '';
337337- };
392392+ virtualisation.diskImage = mkOption {
393393+ type = types.nullOr types.str;
394394+ default = "./${config.system.name}.qcow2";
395395+ defaultText = literalExpression ''"./''${config.system.name}.qcow2"'';
396396+ description = ''
397397+ Path to the disk image containing the root filesystem.
398398+ The image will be created on startup if it does not
399399+ exist.
338400339339- virtualisation.diskImage =
340340- mkOption {
341341- type = types.nullOr types.str;
342342- default = "./${config.system.name}.qcow2";
343343- defaultText = literalExpression ''"./''${config.system.name}.qcow2"'';
344344- description = ''
345345- Path to the disk image containing the root filesystem.
346346- The image will be created on startup if it does not
347347- exist.
401401+ If null, a tmpfs will be used as the root filesystem and
402402+ the VM's state will not be persistent.
403403+ '';
404404+ };
348405349349- If null, a tmpfs will be used as the root filesystem and
350350- the VM's state will not be persistent.
351351- '';
352352- };
353353-354354- virtualisation.bootLoaderDevice =
355355- mkOption {
356356- type = types.path;
357357- default = "/dev/disk/by-id/virtio-${rootDriveSerialAttr}";
358358- defaultText = literalExpression ''/dev/disk/by-id/virtio-${rootDriveSerialAttr}'';
359359- example = "/dev/disk/by-id/virtio-boot-loader-device";
360360- description = ''
361361- The path (inside th VM) to the device to boot from when legacy booting.
362362- '';
363363- };
406406+ virtualisation.bootLoaderDevice = mkOption {
407407+ type = types.path;
408408+ default = "/dev/disk/by-id/virtio-${rootDriveSerialAttr}";
409409+ defaultText = literalExpression ''/dev/disk/by-id/virtio-${rootDriveSerialAttr}'';
410410+ example = "/dev/disk/by-id/virtio-boot-loader-device";
411411+ description = ''
412412+ The path (inside th VM) to the device to boot from when legacy booting.
413413+ '';
414414+ };
364415365365- virtualisation.bootPartition =
366366- mkOption {
367367- type = types.nullOr types.path;
368368- default = if cfg.useEFIBoot then "/dev/disk/by-label/${espFilesystemLabel}" else null;
369369- defaultText = literalExpression ''if cfg.useEFIBoot then "/dev/disk/by-label/${espFilesystemLabel}" else null'';
370370- example = "/dev/disk/by-label/esp";
371371- description = ''
372372- The path (inside the VM) to the device containing the EFI System Partition (ESP).
416416+ virtualisation.bootPartition = mkOption {
417417+ type = types.nullOr types.path;
418418+ default = if cfg.useEFIBoot then "/dev/disk/by-label/${espFilesystemLabel}" else null;
419419+ defaultText = literalExpression ''if cfg.useEFIBoot then "/dev/disk/by-label/${espFilesystemLabel}" else null'';
420420+ example = "/dev/disk/by-label/esp";
421421+ description = ''
422422+ The path (inside the VM) to the device containing the EFI System Partition (ESP).
373423374374- If you are *not* booting from a UEFI firmware, this value is, by
375375- default, `null`. The ESP is mounted to `boot.loader.efi.efiSysMountpoint`.
376376- '';
377377- };
424424+ If you are *not* booting from a UEFI firmware, this value is, by
425425+ default, `null`. The ESP is mounted to `boot.loader.efi.efiSysMountpoint`.
426426+ '';
427427+ };
378428379379- virtualisation.rootDevice =
380380- mkOption {
381381- type = types.nullOr types.path;
382382- default = "/dev/disk/by-label/${rootFilesystemLabel}";
383383- defaultText = literalExpression ''/dev/disk/by-label/${rootFilesystemLabel}'';
384384- example = "/dev/disk/by-label/nixos";
385385- description = ''
386386- The path (inside the VM) to the device containing the root filesystem.
387387- '';
388388- };
429429+ virtualisation.rootDevice = mkOption {
430430+ type = types.nullOr types.path;
431431+ default = "/dev/disk/by-label/${rootFilesystemLabel}";
432432+ defaultText = literalExpression ''/dev/disk/by-label/${rootFilesystemLabel}'';
433433+ example = "/dev/disk/by-label/nixos";
434434+ description = ''
435435+ The path (inside the VM) to the device containing the root filesystem.
436436+ '';
437437+ };
389438390390- virtualisation.emptyDiskImages =
391391- mkOption {
392392- type = types.listOf types.ints.positive;
393393- default = [];
394394- description = ''
395395- Additional disk images to provide to the VM. The value is
396396- a list of size in megabytes of each disk. These disks are
397397- writeable by the VM.
398398- '';
399399- };
439439+ virtualisation.emptyDiskImages = mkOption {
440440+ type = types.listOf types.ints.positive;
441441+ default = [ ];
442442+ description = ''
443443+ Additional disk images to provide to the VM. The value is
444444+ a list of size in megabytes of each disk. These disks are
445445+ writeable by the VM.
446446+ '';
447447+ };
400448401401- virtualisation.graphics =
402402- mkOption {
403403- type = types.bool;
404404- default = true;
405405- description = ''
406406- Whether to run QEMU with a graphics window, or in nographic mode.
407407- Serial console will be enabled on both settings, but this will
408408- change the preferred console.
409409- '';
410410- };
449449+ virtualisation.graphics = mkOption {
450450+ type = types.bool;
451451+ default = true;
452452+ description = ''
453453+ Whether to run QEMU with a graphics window, or in nographic mode.
454454+ Serial console will be enabled on both settings, but this will
455455+ change the preferred console.
456456+ '';
457457+ };
411458412412- virtualisation.resolution =
413413- mkOption {
414414- type = options.services.xserver.resolutions.type.nestedTypes.elemType;
415415- default = { x = 1024; y = 768; };
416416- description = ''
417417- The resolution of the virtual machine display.
418418- '';
459459+ virtualisation.resolution = mkOption {
460460+ type = options.services.xserver.resolutions.type.nestedTypes.elemType;
461461+ default = {
462462+ x = 1024;
463463+ y = 768;
419464 };
465465+ description = ''
466466+ The resolution of the virtual machine display.
467467+ '';
468468+ };
420469421421- virtualisation.cores =
422422- mkOption {
423423- type = types.ints.positive;
424424- default = 1;
425425- description = ''
426426- Specify the number of cores the guest is permitted to use.
427427- The number can be higher than the available cores on the
428428- host system.
429429- '';
430430- };
470470+ virtualisation.cores = mkOption {
471471+ type = types.ints.positive;
472472+ default = 1;
473473+ description = ''
474474+ Specify the number of cores the guest is permitted to use.
475475+ The number can be higher than the available cores on the
476476+ host system.
477477+ '';
478478+ };
431479432432- virtualisation.sharedDirectories =
433433- mkOption {
434434- type = types.attrsOf
435435- (types.submodule {
436436- options.source = mkOption {
437437- type = types.str;
438438- description = "The path of the directory to share, can be a shell variable";
439439- };
440440- options.target = mkOption {
441441- type = types.path;
442442- description = "The mount point of the directory inside the virtual machine";
443443- };
444444- options.securityModel = mkOption {
445445- type = types.enum [ "passthrough" "mapped-xattr" "mapped-file" "none" ];
446446- default = "mapped-xattr";
447447- description = ''
448448- The security model to use for this share:
480480+ virtualisation.sharedDirectories = mkOption {
481481+ type = types.attrsOf (
482482+ types.submodule {
483483+ options.source = mkOption {
484484+ type = types.str;
485485+ description = "The path of the directory to share, can be a shell variable";
486486+ };
487487+ options.target = mkOption {
488488+ type = types.path;
489489+ description = "The mount point of the directory inside the virtual machine";
490490+ };
491491+ options.securityModel = mkOption {
492492+ type = types.enum [
493493+ "passthrough"
494494+ "mapped-xattr"
495495+ "mapped-file"
496496+ "none"
497497+ ];
498498+ default = "mapped-xattr";
499499+ description = ''
500500+ The security model to use for this share:
449501450450- - `passthrough`: files are stored using the same credentials as they are created on the guest (this requires QEMU to run as root)
451451- - `mapped-xattr`: some of the file attributes like uid, gid, mode bits and link target are stored as file attributes
452452- - `mapped-file`: the attributes are stored in the hidden .virtfs_metadata directory. Directories exported by this security model cannot interact with other unix tools
453453- - `none`: same as "passthrough" except the sever won't report failures if it fails to set file attributes like ownership
454454- '';
455455- };
456456- });
457457- default = { };
458458- example = {
459459- my-share = { source = "/path/to/be/shared"; target = "/mnt/shared"; };
502502+ - `passthrough`: files are stored using the same credentials as they are created on the guest (this requires QEMU to run as root)
503503+ - `mapped-xattr`: some of the file attributes like uid, gid, mode bits and link target are stored as file attributes
504504+ - `mapped-file`: the attributes are stored in the hidden .virtfs_metadata directory. Directories exported by this security model cannot interact with other unix tools
505505+ - `none`: same as "passthrough" except the sever won't report failures if it fails to set file attributes like ownership
506506+ '';
507507+ };
508508+ }
509509+ );
510510+ default = { };
511511+ example = {
512512+ my-share = {
513513+ source = "/path/to/be/shared";
514514+ target = "/mnt/shared";
460515 };
461461- description = ''
462462- An attributes set of directories that will be shared with the
463463- virtual machine using VirtFS (9P filesystem over VirtIO).
464464- The attribute name will be used as the 9P mount tag.
465465- '';
466516 };
517517+ description = ''
518518+ An attributes set of directories that will be shared with the
519519+ virtual machine using VirtFS (9P filesystem over VirtIO).
520520+ The attribute name will be used as the 9P mount tag.
521521+ '';
522522+ };
467523468468- virtualisation.additionalPaths =
469469- mkOption {
470470- type = types.listOf types.path;
471471- default = [];
472472- description = ''
473473- A list of paths whose closure should be made available to
474474- the VM.
524524+ virtualisation.additionalPaths = mkOption {
525525+ type = types.listOf types.path;
526526+ default = [ ];
527527+ description = ''
528528+ A list of paths whose closure should be made available to
529529+ the VM.
475530476476- When 9p is used, the closure is registered in the Nix
477477- database in the VM. All other paths in the host Nix store
478478- appear in the guest Nix store as well, but are considered
479479- garbage (because they are not registered in the Nix
480480- database of the guest).
531531+ When 9p is used, the closure is registered in the Nix
532532+ database in the VM. All other paths in the host Nix store
533533+ appear in the guest Nix store as well, but are considered
534534+ garbage (because they are not registered in the Nix
535535+ database of the guest).
481536482482- When {option}`virtualisation.useNixStoreImage` is
483483- set, the closure is copied to the Nix store image.
484484- '';
485485- };
537537+ When {option}`virtualisation.useNixStoreImage` is
538538+ set, the closure is copied to the Nix store image.
539539+ '';
540540+ };
486541487542 virtualisation.forwardPorts = mkOption {
488488- type = types.listOf
489489- (types.submodule {
543543+ type = types.listOf (
544544+ types.submodule {
490545 options.from = mkOption {
491491- type = types.enum [ "host" "guest" ];
546546+ type = types.enum [
547547+ "host"
548548+ "guest"
549549+ ];
492550 default = "host";
493551 description = ''
494494- Controls the direction in which the ports are mapped:
552552+ Controls the direction in which the ports are mapped:
495553496496- - `"host"` means traffic from the host ports
497497- is forwarded to the given guest port.
498498- - `"guest"` means traffic from the guest ports
499499- is forwarded to the given host port.
500500- '';
554554+ - `"host"` means traffic from the host ports
555555+ is forwarded to the given guest port.
556556+ - `"guest"` means traffic from the guest ports
557557+ is forwarded to the given host port.
558558+ '';
501559 };
502560 options.proto = mkOption {
503503- type = types.enum [ "tcp" "udp" ];
561561+ type = types.enum [
562562+ "tcp"
563563+ "udp"
564564+ ];
504565 default = "tcp";
505566 description = "The protocol to forward.";
506567 };
···522583 type = types.port;
523584 description = "The guest port to be mapped.";
524585 };
525525- });
526526- default = [];
527527- example = lib.literalExpression
528528- ''
586586+ }
587587+ );
588588+ default = [ ];
589589+ example = lib.literalExpression ''
529590 [ # forward local port 2222 -> 22, to ssh into the VM
530591 { from = "host"; host.port = 2222; guest.port = 22; }
531592···535596 host.address = "127.0.0.1"; host.port = 80;
536597 }
537598 ]
538538- '';
599599+ '';
539600 description = ''
540540- When using the SLiRP user networking (default), this option allows to
541541- forward ports to/from the host/guest.
601601+ When using the SLiRP user networking (default), this option allows to
602602+ forward ports to/from the host/guest.
542603543543- ::: {.warning}
544544- If the NixOS firewall on the virtual machine is enabled, you also
545545- have to open the guest ports to enable the traffic between host and
546546- guest.
547547- :::
604604+ ::: {.warning}
605605+ If the NixOS firewall on the virtual machine is enabled, you also
606606+ have to open the guest ports to enable the traffic between host and
607607+ guest.
608608+ :::
548609549549- ::: {.note}
550550- Currently QEMU supports only IPv4 forwarding.
551551- :::
552552- '';
610610+ ::: {.note}
611611+ Currently QEMU supports only IPv4 forwarding.
612612+ :::
613613+ '';
553614 };
554615555555- virtualisation.restrictNetwork =
556556- mkOption {
557557- type = types.bool;
558558- default = false;
559559- example = true;
560560- description = ''
561561- If this option is enabled, the guest will be isolated, i.e. it will
562562- not be able to contact the host and no guest IP packets will be
563563- routed over the host to the outside. This option does not affect
564564- any explicitly set forwarding rules.
565565- '';
566566- };
616616+ virtualisation.restrictNetwork = mkOption {
617617+ type = types.bool;
618618+ default = false;
619619+ example = true;
620620+ description = ''
621621+ If this option is enabled, the guest will be isolated, i.e. it will
622622+ not be able to contact the host and no guest IP packets will be
623623+ routed over the host to the outside. This option does not affect
624624+ any explicitly set forwarding rules.
625625+ '';
626626+ };
567627568568- virtualisation.vlans =
569569- mkOption {
570570- type = types.listOf types.ints.unsigned;
571571- default = if config.virtualisation.interfaces == {} then [ 1 ] else [ ];
572572- defaultText = lib.literalExpression ''if config.virtualisation.interfaces == {} then [ 1 ] else [ ]'';
573573- example = [ 1 2 ];
574574- description = ''
575575- Virtual networks to which the VM is connected. Each
576576- number «N» in this list causes
577577- the VM to have a virtual Ethernet interface attached to a
578578- separate virtual network on which it will be assigned IP
579579- address
580580- `192.168.«N».«M»`,
581581- where «M» is the index of this VM
582582- in the list of VMs.
583583- '';
584584- };
628628+ virtualisation.vlans = mkOption {
629629+ type = types.listOf types.ints.unsigned;
630630+ default = if config.virtualisation.interfaces == { } then [ 1 ] else [ ];
631631+ defaultText = lib.literalExpression ''if config.virtualisation.interfaces == {} then [ 1 ] else [ ]'';
632632+ example = [
633633+ 1
634634+ 2
635635+ ];
636636+ description = ''
637637+ Virtual networks to which the VM is connected. Each
638638+ number «N» in this list causes
639639+ the VM to have a virtual Ethernet interface attached to a
640640+ separate virtual network on which it will be assigned IP
641641+ address
642642+ `192.168.«N».«M»`,
643643+ where «M» is the index of this VM
644644+ in the list of VMs.
645645+ '';
646646+ };
585647586648 virtualisation.interfaces = mkOption {
587587- default = {};
649649+ default = { };
588650 example = {
589651 enp1s0.vlan = 1;
590652 };
591653 description = ''
592654 Network interfaces to add to the VM.
593655 '';
594594- type = with types; attrsOf (submodule {
595595- options = {
596596- vlan = mkOption {
597597- type = types.ints.unsigned;
598598- description = ''
599599- VLAN to which the network interface is connected.
600600- '';
601601- };
656656+ type =
657657+ with types;
658658+ attrsOf (submodule {
659659+ options = {
660660+ vlan = mkOption {
661661+ type = types.ints.unsigned;
662662+ description = ''
663663+ VLAN to which the network interface is connected.
664664+ '';
665665+ };
602666603603- assignIP = mkOption {
604604- type = types.bool;
605605- default = false;
606606- description = ''
607607- Automatically assign an IP address to the network interface using the same scheme as
608608- virtualisation.vlans.
609609- '';
667667+ assignIP = mkOption {
668668+ type = types.bool;
669669+ default = false;
670670+ description = ''
671671+ Automatically assign an IP address to the network interface using the same scheme as
672672+ virtualisation.vlans.
673673+ '';
674674+ };
610675 };
611611- };
612612- });
676676+ });
613677 };
614678615615- virtualisation.writableStore =
616616- mkOption {
617617- type = types.bool;
618618- default = cfg.mountHostNixStore;
619619- defaultText = literalExpression "cfg.mountHostNixStore";
620620- description = ''
621621- If enabled, the Nix store in the VM is made writable by
622622- layering an overlay filesystem on top of the host's Nix
623623- store.
679679+ virtualisation.writableStore = mkOption {
680680+ type = types.bool;
681681+ default = cfg.mountHostNixStore;
682682+ defaultText = literalExpression "cfg.mountHostNixStore";
683683+ description = ''
684684+ If enabled, the Nix store in the VM is made writable by
685685+ layering an overlay filesystem on top of the host's Nix
686686+ store.
624687625625- By default, this is enabled if you mount a host Nix store.
626626- '';
627627- };
688688+ By default, this is enabled if you mount a host Nix store.
689689+ '';
690690+ };
628691629629- virtualisation.writableStoreUseTmpfs =
630630- mkOption {
631631- type = types.bool;
632632- default = true;
633633- description = ''
634634- Use a tmpfs for the writable store instead of writing to the VM's
635635- own filesystem.
636636- '';
637637- };
692692+ virtualisation.writableStoreUseTmpfs = mkOption {
693693+ type = types.bool;
694694+ default = true;
695695+ description = ''
696696+ Use a tmpfs for the writable store instead of writing to the VM's
697697+ own filesystem.
698698+ '';
699699+ };
638700639639- networking.primaryIPAddress =
640640- mkOption {
641641- type = types.str;
642642- default = "";
643643- internal = true;
644644- description = "Primary IP address used in /etc/hosts.";
645645- };
701701+ networking.primaryIPAddress = mkOption {
702702+ type = types.str;
703703+ default = "";
704704+ internal = true;
705705+ description = "Primary IP address used in /etc/hosts.";
706706+ };
646707647647- networking.primaryIPv6Address =
648648- mkOption {
649649- type = types.str;
650650- default = "";
651651- internal = true;
652652- description = "Primary IPv6 address used in /etc/hosts.";
653653- };
708708+ networking.primaryIPv6Address = mkOption {
709709+ type = types.str;
710710+ default = "";
711711+ internal = true;
712712+ description = "Primary IPv6 address used in /etc/hosts.";
713713+ };
654714655715 virtualisation.host.pkgs = mkOption {
656716 type = options.nixpkgs.pkgs.type;
···666726 };
667727668728 virtualisation.qemu = {
669669- package =
670670- mkOption {
671671- type = types.package;
672672- default = if hostPkgs.stdenv.hostPlatform.qemuArch == pkgs.stdenv.hostPlatform.qemuArch then hostPkgs.qemu_kvm else hostPkgs.qemu;
673673- defaultText = literalExpression "if hostPkgs.stdenv.hostPlatform.qemuArch == pkgs.stdenv.hostPlatform.qemuArch then config.virtualisation.host.pkgs.qemu_kvm else config.virtualisation.host.pkgs.qemu";
674674- example = literalExpression "pkgs.qemu_test";
675675- description = "QEMU package to use.";
676676- };
729729+ package = mkOption {
730730+ type = types.package;
731731+ default =
732732+ if hostPkgs.stdenv.hostPlatform.qemuArch == pkgs.stdenv.hostPlatform.qemuArch then
733733+ hostPkgs.qemu_kvm
734734+ else
735735+ hostPkgs.qemu;
736736+ defaultText = literalExpression "if hostPkgs.stdenv.hostPlatform.qemuArch == pkgs.stdenv.hostPlatform.qemuArch then config.virtualisation.host.pkgs.qemu_kvm else config.virtualisation.host.pkgs.qemu";
737737+ example = literalExpression "pkgs.qemu_test";
738738+ description = "QEMU package to use.";
739739+ };
677740678678- options =
679679- mkOption {
680680- type = types.listOf types.str;
681681- default = [];
682682- example = [ "-vga std" ];
683683- description = ''
684684- Options passed to QEMU.
685685- See [QEMU User Documentation](https://www.qemu.org/docs/master/system/qemu-manpage) for a complete list.
686686- '';
687687- };
741741+ options = mkOption {
742742+ type = types.listOf types.str;
743743+ default = [ ];
744744+ example = [ "-vga std" ];
745745+ description = ''
746746+ Options passed to QEMU.
747747+ See [QEMU User Documentation](https://www.qemu.org/docs/master/system/qemu-manpage) for a complete list.
748748+ '';
749749+ };
688750689751 consoles = mkOption {
690752 type = types.listOf types.str;
691691- default = let
692692- consoles = [ "${qemu-common.qemuSerialDevice},115200n8" "tty0" ];
693693- in if cfg.graphics then consoles else reverseList consoles;
753753+ default =
754754+ let
755755+ consoles = [
756756+ "${qemu-common.qemuSerialDevice},115200n8"
757757+ "tty0"
758758+ ];
759759+ in
760760+ if cfg.graphics then consoles else reverseList consoles;
694761 example = [ "console=tty1" ];
695762 description = ''
696763 The output console devices to pass to the kernel command line via the
···703770 '';
704771 };
705772706706- networkingOptions =
707707- mkOption {
708708- type = types.listOf types.str;
709709- default = [ ];
710710- example = [
711711- "-net nic,netdev=user.0,model=virtio"
712712- "-netdev user,id=user.0,\${QEMU_NET_OPTS:+,$QEMU_NET_OPTS}"
713713- ];
714714- description = ''
715715- Networking-related command-line options that should be passed to qemu.
716716- The default is to use userspace networking (SLiRP).
717717- See the [QEMU Wiki on Networking](https://wiki.qemu.org/Documentation/Networking) for details.
773773+ networkingOptions = mkOption {
774774+ type = types.listOf types.str;
775775+ default = [ ];
776776+ example = [
777777+ "-net nic,netdev=user.0,model=virtio"
778778+ "-netdev user,id=user.0,\${QEMU_NET_OPTS:+,$QEMU_NET_OPTS}"
779779+ ];
780780+ description = ''
781781+ Networking-related command-line options that should be passed to qemu.
782782+ The default is to use userspace networking (SLiRP).
783783+ See the [QEMU Wiki on Networking](https://wiki.qemu.org/Documentation/Networking) for details.
718784719719- If you override this option, be advised to keep
720720- `''${QEMU_NET_OPTS:+,$QEMU_NET_OPTS}` (as seen in the example)
721721- to keep the default runtime behaviour.
722722- '';
723723- };
724724-725725- drives =
726726- mkOption {
727727- type = types.listOf (types.submodule driveOpts);
728728- description = "Drives passed to qemu.";
729729- };
730730-731731- diskInterface =
732732- mkOption {
733733- type = types.enum [ "virtio" "scsi" "ide" ];
734734- default = "virtio";
735735- example = "scsi";
736736- description = "The interface used for the virtual hard disks.";
737737- };
785785+ If you override this option, be advised to keep
786786+ `''${QEMU_NET_OPTS:+,$QEMU_NET_OPTS}` (as seen in the example)
787787+ to keep the default runtime behaviour.
788788+ '';
789789+ };
738790739739- guestAgent.enable =
740740- mkOption {
741741- type = types.bool;
742742- default = true;
743743- description = ''
744744- Enable the Qemu guest agent.
745745- '';
746746- };
791791+ drives = mkOption {
792792+ type = types.listOf (types.submodule driveOpts);
793793+ description = "Drives passed to qemu.";
794794+ };
747795748748- virtioKeyboard =
749749- mkOption {
750750- type = types.bool;
751751- default = true;
752752- description = ''
753753- Enable the virtio-keyboard device.
754754- '';
755755- };
756756- };
796796+ diskInterface = mkOption {
797797+ type = types.enum [
798798+ "virtio"
799799+ "scsi"
800800+ "ide"
801801+ ];
802802+ default = "virtio";
803803+ example = "scsi";
804804+ description = "The interface used for the virtual hard disks.";
805805+ };
757806758758- virtualisation.useNixStoreImage =
759759- mkOption {
807807+ guestAgent.enable = mkOption {
760808 type = types.bool;
761761- default = false;
809809+ default = true;
762810 description = ''
763763- Build and use a disk image for the Nix store, instead of
764764- accessing the host's one through 9p.
765765-766766- For applications which do a lot of reads from the store,
767767- this can drastically improve performance, but at the cost of
768768- disk space and image build time.
769769-770770- The Nix store image is built just-in-time right before the VM is
771771- started. Because it does not produce another derivation, the image is
772772- not cached between invocations and never lands in the store or binary
773773- cache.
774774-775775- If you want a full disk image with a partition table and a root
776776- filesystem instead of only a store image, enable
777777- {option}`virtualisation.useBootLoader` instead.
811811+ Enable the Qemu guest agent.
778812 '';
779813 };
780814781781- virtualisation.mountHostNixStore =
782782- mkOption {
815815+ virtioKeyboard = mkOption {
783816 type = types.bool;
784784- default = !cfg.useNixStoreImage && !cfg.useBootLoader;
785785- defaultText = literalExpression "!cfg.useNixStoreImage && !cfg.useBootLoader";
817817+ default = true;
786818 description = ''
787787- Mount the host Nix store as a 9p mount.
819819+ Enable the virtio-keyboard device.
788820 '';
789821 };
822822+ };
790823791791- virtualisation.directBoot = {
792792- enable =
793793- mkOption {
794794- type = types.bool;
795795- default = !cfg.useBootLoader;
796796- defaultText = "!cfg.useBootLoader";
797797- description = ''
798798- If enabled, the virtual machine will boot directly into the kernel instead of through a bootloader.
799799- Read more about this feature in the [QEMU documentation on Direct Linux Boot](https://qemu-project.gitlab.io/qemu/system/linuxboot.html)
824824+ virtualisation.useNixStoreImage = mkOption {
825825+ type = types.bool;
826826+ default = false;
827827+ description = ''
828828+ Build and use a disk image for the Nix store, instead of
829829+ accessing the host's one through 9p.
800830801801- This is enabled by default.
802802- If you want to test netboot, consider disabling this option.
803803- Enable a bootloader with {option}`virtualisation.useBootLoader` if you need.
831831+ For applications which do a lot of reads from the store,
832832+ this can drastically improve performance, but at the cost of
833833+ disk space and image build time.
804834805805- Relevant parameters such as those set in `boot.initrd` and `boot.kernelParams` are also passed to QEMU.
806806- Additional parameters can be supplied on invocation through the environment variable `$QEMU_KERNEL_PARAMS`.
807807- They are added to the `-append` option, see [QEMU User Documentation](https://www.qemu.org/docs/master/system/qemu-manpage) for details
808808- For example, to let QEMU use the parent terminal as the serial console, set `QEMU_KERNEL_PARAMS="console=ttyS0"`.
835835+ The Nix store image is built just-in-time right before the VM is
836836+ started. Because it does not produce another derivation, the image is
837837+ not cached between invocations and never lands in the store or binary
838838+ cache.
809839810810- This will not (re-)boot correctly into a system that has switched to a different configuration on disk.
811811- '';
812812- };
813813- initrd =
814814- mkOption {
815815- type = types.str;
816816- default = "${config.system.build.initialRamdisk}/${config.system.boot.loader.initrdFile}";
817817- defaultText = "\${config.system.build.initialRamdisk}/\${config.system.boot.loader.initrdFile}";
818818- description = ''
819819- In direct boot situations, you may want to influence the initrd to load
820820- to use your own customized payload.
840840+ If you want a full disk image with a partition table and a root
841841+ filesystem instead of only a store image, enable
842842+ {option}`virtualisation.useBootLoader` instead.
843843+ '';
844844+ };
821845822822- This is useful if you want to test the netboot image without
823823- testing the firmware or the loading part.
824824- '';
825825- };
846846+ virtualisation.mountHostNixStore = mkOption {
847847+ type = types.bool;
848848+ default = !cfg.useNixStoreImage && !cfg.useBootLoader;
849849+ defaultText = literalExpression "!cfg.useNixStoreImage && !cfg.useBootLoader";
850850+ description = ''
851851+ Mount the host Nix store as a 9p mount.
852852+ '';
826853 };
827854828828- virtualisation.useBootLoader =
829829- mkOption {
855855+ virtualisation.directBoot = {
856856+ enable = mkOption {
830857 type = types.bool;
831831- default = false;
858858+ default = !cfg.useBootLoader;
859859+ defaultText = "!cfg.useBootLoader";
832860 description = ''
833833- Use a boot loader to boot the system.
834834- This allows, among other things, testing the boot loader.
861861+ If enabled, the virtual machine will boot directly into the kernel instead of through a bootloader.
862862+ Read more about this feature in the [QEMU documentation on Direct Linux Boot](https://qemu-project.gitlab.io/qemu/system/linuxboot.html)
835863836836- If disabled, the kernel and initrd are directly booted,
837837- forgoing any bootloader.
864864+ This is enabled by default.
865865+ If you want to test netboot, consider disabling this option.
866866+ Enable a bootloader with {option}`virtualisation.useBootLoader` if you need.
838867839839- Check the documentation on {option}`virtualisation.directBoot.enable` for details.
840840- '';
868868+ Relevant parameters such as those set in `boot.initrd` and `boot.kernelParams` are also passed to QEMU.
869869+ Additional parameters can be supplied on invocation through the environment variable `$QEMU_KERNEL_PARAMS`.
870870+ They are added to the `-append` option, see [QEMU User Documentation](https://www.qemu.org/docs/master/system/qemu-manpage) for details
871871+ For example, to let QEMU use the parent terminal as the serial console, set `QEMU_KERNEL_PARAMS="console=ttyS0"`.
872872+873873+ This will not (re-)boot correctly into a system that has switched to a different configuration on disk.
874874+ '';
875875+ };
876876+ initrd = mkOption {
877877+ type = types.str;
878878+ default = "${config.system.build.initialRamdisk}/${config.system.boot.loader.initrdFile}";
879879+ defaultText = "\${config.system.build.initialRamdisk}/\${config.system.boot.loader.initrdFile}";
880880+ description = ''
881881+ In direct boot situations, you may want to influence the initrd to load
882882+ to use your own customized payload.
883883+884884+ This is useful if you want to test the netboot image without
885885+ testing the firmware or the loading part.
886886+ '';
841887 };
888888+ };
842889843843- virtualisation.useEFIBoot =
844844- mkOption {
845845- type = types.bool;
846846- default = false;
847847- description = ''
848848- If enabled, the virtual machine will provide a EFI boot
849849- manager.
850850- useEFIBoot is ignored if useBootLoader == false.
851851- '';
852852- };
890890+ virtualisation.useBootLoader = mkOption {
891891+ type = types.bool;
892892+ default = false;
893893+ description = ''
894894+ Use a boot loader to boot the system.
895895+ This allows, among other things, testing the boot loader.
896896+897897+ If disabled, the kernel and initrd are directly booted,
898898+ forgoing any bootloader.
899899+900900+ Check the documentation on {option}`virtualisation.directBoot.enable` for details.
901901+ '';
902902+ };
903903+904904+ virtualisation.useEFIBoot = mkOption {
905905+ type = types.bool;
906906+ default = false;
907907+ description = ''
908908+ If enabled, the virtual machine will provide a EFI boot
909909+ manager.
910910+ useEFIBoot is ignored if useBootLoader == false.
911911+ '';
912912+ };
853913854914 virtualisation.efi = {
855915 OVMF = mkOption {
856916 type = types.package;
857857- default = (pkgs.OVMF.override {
858858- secureBoot = cfg.useSecureBoot;
859859- }).fd;
860860- defaultText = ''(pkgs.OVMF.override {
861861- secureBoot = cfg.useSecureBoot;
862862- }).fd'';
917917+ default =
918918+ (pkgs.OVMF.override {
919919+ secureBoot = cfg.useSecureBoot;
920920+ }).fd;
921921+ defaultText = ''
922922+ (pkgs.OVMF.override {
923923+ secureBoot = cfg.useSecureBoot;
924924+ }).fd'';
863925 description = "OVMF firmware package, defaults to OVMF configured with secure boot if needed.";
864926 };
865927···868930 default = cfg.efi.OVMF.firmware;
869931 defaultText = literalExpression "cfg.efi.OVMF.firmware";
870932 description = ''
871871- Firmware binary for EFI implementation, defaults to OVMF.
872872- '';
933933+ Firmware binary for EFI implementation, defaults to OVMF.
934934+ '';
873935 };
874936875937 variables = mkOption {
···877939 default = cfg.efi.OVMF.variables;
878940 defaultText = literalExpression "cfg.efi.OVMF.variables";
879941 description = ''
880880- Platform-specific flash binary for EFI variables, implementation-dependent to the EFI firmware.
881881- Defaults to OVMF.
882882- '';
942942+ Platform-specific flash binary for EFI variables, implementation-dependent to the EFI firmware.
943943+ Defaults to OVMF.
944944+ '';
883945 };
884946885947 keepVariables = mkOption {
···897959898960 deviceModel = mkOption {
899961 type = types.str;
900900- default = ({
901901- "i686-linux" = "tpm-tis";
902902- "x86_64-linux" = "tpm-tis";
903903- "ppc64-linux" = "tpm-spapr";
904904- "armv7-linux" = "tpm-tis-device";
905905- "aarch64-linux" = "tpm-tis-device";
906906- }.${pkgs.stdenv.hostPlatform.system} or (throw "Unsupported system for TPM2 emulation in QEMU"));
962962+ default = (
963963+ {
964964+ "i686-linux" = "tpm-tis";
965965+ "x86_64-linux" = "tpm-tis";
966966+ "ppc64-linux" = "tpm-spapr";
967967+ "armv7-linux" = "tpm-tis-device";
968968+ "aarch64-linux" = "tpm-tis-device";
969969+ }
970970+ .${pkgs.stdenv.hostPlatform.system} or (throw "Unsupported system for TPM2 emulation in QEMU")
971971+ );
907972 defaultText = ''
908973 Based on the guest platform Linux system:
909974···916981 };
917982 };
918983919919- virtualisation.useDefaultFilesystems =
920920- mkOption {
921921- type = types.bool;
922922- default = true;
923923- description = ''
924924- If enabled, the boot disk of the virtual machine will be
925925- formatted and mounted with the default filesystems for
926926- testing. Swap devices and LUKS will be disabled.
984984+ virtualisation.useDefaultFilesystems = mkOption {
985985+ type = types.bool;
986986+ default = true;
987987+ description = ''
988988+ If enabled, the boot disk of the virtual machine will be
989989+ formatted and mounted with the default filesystems for
990990+ testing. Swap devices and LUKS will be disabled.
927991928928- If disabled, a root filesystem has to be specified and
929929- formatted (for example in the initial ramdisk).
930930- '';
931931- };
992992+ If disabled, a root filesystem has to be specified and
993993+ formatted (for example in the initial ramdisk).
994994+ '';
995995+ };
932996933933- virtualisation.useSecureBoot =
934934- mkOption {
935935- type = types.bool;
936936- default = false;
937937- description = ''
938938- Enable Secure Boot support in the EFI firmware.
939939- '';
940940- };
997997+ virtualisation.useSecureBoot = mkOption {
998998+ type = types.bool;
999999+ default = false;
10001000+ description = ''
10011001+ Enable Secure Boot support in the EFI firmware.
10021002+ '';
10031003+ };
9411004942942- virtualisation.bios =
943943- mkOption {
944944- type = types.nullOr types.package;
945945- default = null;
946946- description = ''
947947- An alternate BIOS (such as `qboot`) with which to start the VM.
948948- Should contain a file named `bios.bin`.
949949- If `null`, QEMU's builtin SeaBIOS will be used.
950950- '';
951951- };
10051005+ virtualisation.bios = mkOption {
10061006+ type = types.nullOr types.package;
10071007+ default = null;
10081008+ description = ''
10091009+ An alternate BIOS (such as `qboot`) with which to start the VM.
10101010+ Should contain a file named `bios.bin`.
10111011+ If `null`, QEMU's builtin SeaBIOS will be used.
10121012+ '';
10131013+ };
9521014953953- virtualisation.useHostCerts =
954954- mkOption {
955955- type = types.bool;
956956- default = false;
957957- description = ''
958958- If enabled, when `NIX_SSL_CERT_FILE` is set on the host,
959959- pass the CA certificates from the host to the VM.
960960- '';
961961- };
10151015+ virtualisation.useHostCerts = mkOption {
10161016+ type = types.bool;
10171017+ default = false;
10181018+ description = ''
10191019+ If enabled, when `NIX_SSL_CERT_FILE` is set on the host,
10201020+ pass the CA certificates from the host to the VM.
10211021+ '';
10221022+ };
96210239631024 };
96410259651026 config = {
96610279671028 assertions =
968968- lib.concatLists (lib.flip lib.imap cfg.forwardPorts (i: rule:
969969- [
970970- { assertion = rule.from == "guest" -> rule.proto == "tcp";
971971- message =
972972- ''
10291029+ lib.concatLists (
10301030+ lib.flip lib.imap cfg.forwardPorts (
10311031+ i: rule: [
10321032+ {
10331033+ assertion = rule.from == "guest" -> rule.proto == "tcp";
10341034+ message = ''
9731035 Invalid virtualisation.forwardPorts.<entry ${toString i}>.proto:
9741036 Guest forwarding supports only TCP connections.
9751037 '';
976976- }
977977- { assertion = rule.from == "guest" -> lib.hasPrefix "10.0.2." rule.guest.address;
978978- message =
979979- ''
10381038+ }
10391039+ {
10401040+ assertion = rule.from == "guest" -> lib.hasPrefix "10.0.2." rule.guest.address;
10411041+ message = ''
9801042 Invalid virtualisation.forwardPorts.<entry ${toString i}>.guest.address:
9811043 The address must be in the default VLAN (10.0.2.0/24).
9821044 '';
983983- }
984984- ])) ++ [
985985- { assertion = pkgs.stdenv.hostPlatform.is32bit -> cfg.memorySize < 2047;
986986- message = ''
987987- virtualisation.memorySize is above 2047, but qemu is only able to allocate 2047MB RAM on 32bit max.
988988- '';
989989- }
990990- { assertion = cfg.directBoot.enable || cfg.directBoot.initrd == options.virtualisation.directBoot.initrd.default;
991991- message =
992992- ''
993993- You changed the default of `virtualisation.directBoot.initrd` but you are not
994994- using QEMU direct boot. This initrd will not be used in your current
995995- boot configuration.
10451045+ }
10461046+ ]
10471047+ )
10481048+ )
10491049+ ++ [
10501050+ {
10511051+ assertion = pkgs.stdenv.hostPlatform.is32bit -> cfg.memorySize < 2047;
10521052+ message = ''
10531053+ virtualisation.memorySize is above 2047, but qemu is only able to allocate 2047MB RAM on 32bit max.
10541054+ '';
10551055+ }
10561056+ {
10571057+ assertion =
10581058+ cfg.directBoot.enable || cfg.directBoot.initrd == options.virtualisation.directBoot.initrd.default;
10591059+ message = ''
10601060+ You changed the default of `virtualisation.directBoot.initrd` but you are not
10611061+ using QEMU direct boot. This initrd will not be used in your current
10621062+ boot configuration.
9961063997997- Either do not mutate `virtualisation.directBoot.initrd` or enable direct boot.
10641064+ Either do not mutate `virtualisation.directBoot.initrd` or enable direct boot.
9981065999999- If you have a more advanced usecase, please open an issue or a pull request.
10001000- '';
10011001- }
10021002- ];
10661066+ If you have a more advanced usecase, please open an issue or a pull request.
10671067+ '';
10681068+ }
10691069+ ];
1003107010041004- warnings =
10051005- optional (cfg.directBoot.enable && cfg.useBootLoader)
10061006- ''
10071007- You enabled direct boot and a bootloader, QEMU will not boot your bootloader, rendering
10081008- `useBootLoader` useless. You might want to disable one of those options.
10091009- '';
10711071+ warnings = optional (cfg.directBoot.enable && cfg.useBootLoader) ''
10721072+ You enabled direct boot and a bootloader, QEMU will not boot your bootloader, rendering
10731073+ `useBootLoader` useless. You might want to disable one of those options.
10741074+ '';
1010107510111076 # In UEFI boot, we use a EFI-only partition table layout, thus GRUB will fail when trying to install
10121077 # legacy and UEFI. In order to avoid this, we have to put "nodev" to force UEFI-only installs.
···10241089 # allow `system.build.toplevel' to be included. (If we had a direct
10251090 # reference to ${regInfo} here, then we would get a cyclic
10261091 # dependency.)
10271027- boot.postBootCommands = lib.mkIf config.nix.enable
10281028- ''
10291029- if [[ "$(cat /proc/cmdline)" =~ regInfo=([^ ]*) ]]; then
10301030- ${config.nix.package.out}/bin/nix-store --load-db < ''${BASH_REMATCH[1]}
10311031- fi
10321032- '';
10921092+ boot.postBootCommands = lib.mkIf config.nix.enable ''
10931093+ if [[ "$(cat /proc/cmdline)" =~ regInfo=([^ ]*) ]]; then
10941094+ ${config.nix.package.out}/bin/nix-store --load-db < ''${BASH_REMATCH[1]}
10951095+ fi
10961096+ '';
1033109710341098 boot.initrd.availableKernelModules =
10351099 optional (cfg.qemu.diskInterface == "scsi") "sym53c8xx"
···1066113010671131 virtualisation.qemu.networkingOptions =
10681132 let
10691069- forwardingOptions = flip concatMapStrings cfg.forwardPorts
10701070- ({ proto, from, host, guest }:
10711071- if from == "host"
10721072- then "hostfwd=${proto}:${host.address}:${toString host.port}-" +
10731073- "${guest.address}:${toString guest.port},"
10741074- else "'guestfwd=${proto}:${guest.address}:${toString guest.port}-" +
10751075- "cmd:${pkgs.netcat}/bin/nc ${host.address} ${toString host.port}',"
10761076- );
11331133+ forwardingOptions = flip concatMapStrings cfg.forwardPorts (
11341134+ {
11351135+ proto,
11361136+ from,
11371137+ host,
11381138+ guest,
11391139+ }:
11401140+ if from == "host" then
11411141+ "hostfwd=${proto}:${host.address}:${toString host.port}-"
11421142+ + "${guest.address}:${toString guest.port},"
11431143+ else
11441144+ "'guestfwd=${proto}:${guest.address}:${toString guest.port}-"
11451145+ + "cmd:${pkgs.netcat}/bin/nc ${host.address} ${toString host.port}',"
11461146+ );
10771147 restrictNetworkOption = lib.optionalString cfg.restrictNetwork "restrict=on,";
10781148 in
10791149 [
···10861156 "-device virtio-keyboard"
10871157 ])
10881158 (mkIf pkgs.stdenv.hostPlatform.isx86 [
10891089- "-usb" "-device usb-tablet,bus=usb-bus.0"
11591159+ "-usb"
11601160+ "-device usb-tablet,bus=usb-bus.0"
10901161 ])
10911162 (mkIf pkgs.stdenv.hostPlatform.isAarch [
10921092- "-device virtio-gpu-pci" "-device usb-ehci,id=usb0" "-device usb-kbd" "-device usb-tablet"
11631163+ "-device virtio-gpu-pci"
11641164+ "-device usb-ehci,id=usb0"
11651165+ "-device usb-kbd"
11661166+ "-device usb-tablet"
10931167 ])
10941094- (let
10951095- alphaNumericChars = lowerChars ++ upperChars ++ (map toString (range 0 9));
10961096- # Replace all non-alphanumeric characters with underscores
10971097- sanitizeShellIdent = s: concatMapStrings (c: if builtins.elem c alphaNumericChars then c else "_") (stringToCharacters s);
10981098- in mkIf cfg.directBoot.enable [
10991099- "-kernel \${NIXPKGS_QEMU_KERNEL_${sanitizeShellIdent config.system.name}:-${config.system.build.toplevel}/kernel}"
11001100- "-initrd ${cfg.directBoot.initrd}"
11011101- ''-append "$(cat ${config.system.build.toplevel}/kernel-params) init=${config.system.build.toplevel}/init regInfo=${regInfo}/registration ${consoles} $QEMU_KERNEL_PARAMS"''
11021102- ])
11681168+ (
11691169+ let
11701170+ alphaNumericChars = lowerChars ++ upperChars ++ (map toString (range 0 9));
11711171+ # Replace all non-alphanumeric characters with underscores
11721172+ sanitizeShellIdent =
11731173+ s:
11741174+ concatMapStrings (c: if builtins.elem c alphaNumericChars then c else "_") (stringToCharacters s);
11751175+ in
11761176+ mkIf cfg.directBoot.enable [
11771177+ "-kernel \${NIXPKGS_QEMU_KERNEL_${sanitizeShellIdent config.system.name}:-${config.system.build.toplevel}/kernel}"
11781178+ "-initrd ${cfg.directBoot.initrd}"
11791179+ ''-append "$(cat ${config.system.build.toplevel}/kernel-params) init=${config.system.build.toplevel}/init regInfo=${regInfo}/registration ${consoles} $QEMU_KERNEL_PARAMS"''
11801180+ ]
11811181+ )
11031182 (mkIf cfg.useEFIBoot [
11041183 "-drive if=pflash,format=raw,unit=0,readonly=on,file=${cfg.efi.firmware}"
11051184 "-drive if=pflash,format=raw,unit=1,readonly=off,file=$NIX_EFI_VARS"
···11161195 "-device ${cfg.tpm.deviceModel},tpmdev=tpm_dev_0"
11171196 ])
11181197 (mkIf (pkgs.stdenv.hostPlatform.isx86 && cfg.efi.OVMF.systemManagementModeRequired) [
11191119- "-machine" "q35,smm=on"
11201120- "-global" "driver=cfi.pflash01,property=secure,value=on"
11981198+ "-machine"
11991199+ "q35,smm=on"
12001200+ "-global"
12011201+ "driver=cfi.pflash01,property=secure,value=on"
11211202 ])
11221203 ];
1123120411241205 virtualisation.qemu.drives = mkMerge [
11251125- (mkIf (cfg.diskImage != null) [{
11261126- name = "root";
11271127- file = ''"$NIX_DISK_IMAGE"'';
11281128- driveExtraOpts.cache = "writeback";
11291129- driveExtraOpts.werror = "report";
11301130- deviceExtraOpts.bootindex = "1";
11311131- deviceExtraOpts.serial = rootDriveSerialAttr;
11321132- }])
11331133- (mkIf cfg.useNixStoreImage [{
11341134- name = "nix-store";
11351135- file = ''"$TMPDIR"/store.img'';
11361136- deviceExtraOpts.bootindex = "2";
11371137- driveExtraOpts.format = "raw";
11381138- }])
12061206+ (mkIf (cfg.diskImage != null) [
12071207+ {
12081208+ name = "root";
12091209+ file = ''"$NIX_DISK_IMAGE"'';
12101210+ driveExtraOpts.cache = "writeback";
12111211+ driveExtraOpts.werror = "report";
12121212+ deviceExtraOpts.bootindex = "1";
12131213+ deviceExtraOpts.serial = rootDriveSerialAttr;
12141214+ }
12151215+ ])
12161216+ (mkIf cfg.useNixStoreImage [
12171217+ {
12181218+ name = "nix-store";
12191219+ file = ''"$TMPDIR"/store.img'';
12201220+ deviceExtraOpts.bootindex = "2";
12211221+ driveExtraOpts.format = "raw";
12221222+ }
12231223+ ])
11391224 (imap0 (idx: _: {
11401225 file = "$(pwd)/empty${toString idx}.qcow2";
11411226 driveExtraOpts.werror = "report";
···11491234 # override by setting `virtualisation.fileSystems = lib.mkForce { };`.
11501235 fileSystems = lib.mkIf (cfg.fileSystems != { }) (mkVMOverride cfg.fileSystems);
1151123611521152- virtualisation.fileSystems = let
11531153- mkSharedDir = tag: share:
11541154- {
12371237+ virtualisation.diskSizeAutoSupported = false;
12381238+12391239+ virtualisation.fileSystems =
12401240+ let
12411241+ mkSharedDir = tag: share: {
11551242 name = share.target;
11561243 value.device = tag;
11571244 value.fsType = "9p";
11581245 value.neededForBoot = true;
11591159- value.options =
11601160- [ "trans=virtio" "version=9p2000.L" "msize=${toString cfg.msize}" ]
11611161- ++ lib.optional (tag == "nix-store") "cache=loose";
12461246+ value.options = [
12471247+ "trans=virtio"
12481248+ "version=9p2000.L"
12491249+ "msize=${toString cfg.msize}"
12501250+ ] ++ lib.optional (tag == "nix-store") "cache=loose";
11621251 };
11631163- in lib.mkMerge [
11641164- (lib.mapAttrs' mkSharedDir cfg.sharedDirectories)
11651165- {
11661166- "/" = lib.mkIf cfg.useDefaultFilesystems (if cfg.diskImage == null then {
11671167- device = "tmpfs";
11681168- fsType = "tmpfs";
11691169- } else {
11701170- device = cfg.rootDevice;
11711171- fsType = "ext4";
11721172- });
11731173- "/tmp" = lib.mkIf config.boot.tmp.useTmpfs {
11741174- device = "tmpfs";
11751175- fsType = "tmpfs";
11761176- neededForBoot = true;
11771177- # Sync with systemd's tmp.mount;
11781178- options = [ "mode=1777" "strictatime" "nosuid" "nodev" "size=${toString config.boot.tmp.tmpfsSize}" ];
11791179- };
11801180- "/nix/store" = lib.mkIf (cfg.useNixStoreImage || cfg.mountHostNixStore) (if cfg.writableStore then {
11811181- overlay = {
11821182- lowerdir = [ "/nix/.ro-store" ];
11831183- upperdir = "/nix/.rw-store/upper";
11841184- workdir = "/nix/.rw-store/work";
12521252+ in
12531253+ lib.mkMerge [
12541254+ (lib.mapAttrs' mkSharedDir cfg.sharedDirectories)
12551255+ {
12561256+ "/" = lib.mkIf cfg.useDefaultFilesystems (
12571257+ if cfg.diskImage == null then
12581258+ {
12591259+ device = "tmpfs";
12601260+ fsType = "tmpfs";
12611261+ }
12621262+ else
12631263+ {
12641264+ device = cfg.rootDevice;
12651265+ fsType = "ext4";
12661266+ }
12671267+ );
12681268+ "/tmp" = lib.mkIf config.boot.tmp.useTmpfs {
12691269+ device = "tmpfs";
12701270+ fsType = "tmpfs";
12711271+ neededForBoot = true;
12721272+ # Sync with systemd's tmp.mount;
12731273+ options = [
12741274+ "mode=1777"
12751275+ "strictatime"
12761276+ "nosuid"
12771277+ "nodev"
12781278+ "size=${toString config.boot.tmp.tmpfsSize}"
12791279+ ];
12801280+ };
12811281+ "/nix/store" = lib.mkIf (cfg.useNixStoreImage || cfg.mountHostNixStore) (
12821282+ if cfg.writableStore then
12831283+ {
12841284+ overlay = {
12851285+ lowerdir = [ "/nix/.ro-store" ];
12861286+ upperdir = "/nix/.rw-store/upper";
12871287+ workdir = "/nix/.rw-store/work";
12881288+ };
12891289+ }
12901290+ else
12911291+ {
12921292+ device = "/nix/.ro-store";
12931293+ options = [ "bind" ];
12941294+ }
12951295+ );
12961296+ "/nix/.ro-store" = lib.mkIf cfg.useNixStoreImage {
12971297+ device = "/dev/disk/by-label/${nixStoreFilesystemLabel}";
12981298+ fsType = "erofs";
12991299+ neededForBoot = true;
13001300+ options = [ "ro" ];
13011301+ };
13021302+ "/nix/.rw-store" = lib.mkIf (cfg.writableStore && cfg.writableStoreUseTmpfs) {
13031303+ fsType = "tmpfs";
13041304+ options = [ "mode=0755" ];
13051305+ neededForBoot = true;
11851306 };
11861186- } else {
11871187- device = "/nix/.ro-store";
11881188- options = [ "bind" ];
11891189- });
11901190- "/nix/.ro-store" = lib.mkIf cfg.useNixStoreImage {
11911191- device = "/dev/disk/by-label/${nixStoreFilesystemLabel}";
11921192- fsType = "erofs";
11931193- neededForBoot = true;
11941194- options = [ "ro" ];
11951195- };
11961196- "/nix/.rw-store" = lib.mkIf (cfg.writableStore && cfg.writableStoreUseTmpfs) {
11971197- fsType = "tmpfs";
11981198- options = [ "mode=0755" ];
11991199- neededForBoot = true;
12001200- };
12011201- "${config.boot.loader.efi.efiSysMountPoint}" = lib.mkIf (cfg.useBootLoader && cfg.bootPartition != null) {
12021202- device = cfg.bootPartition;
12031203- fsType = "vfat";
12041204- };
12051205- }
12061206- ];
13071307+ "${config.boot.loader.efi.efiSysMountPoint}" =
13081308+ lib.mkIf (cfg.useBootLoader && cfg.bootPartition != null)
13091309+ {
13101310+ device = cfg.bootPartition;
13111311+ fsType = "vfat";
13121312+ };
13131313+ }
13141314+ ];
1207131512081316 swapDevices = (if cfg.useDefaultFilesystems then mkVMOverride else mkDefault) [ ];
12091209- boot.initrd.luks.devices = (if cfg.useDefaultFilesystems then mkVMOverride else mkDefault) {};
13171317+ boot.initrd.luks.devices = (if cfg.useDefaultFilesystems then mkVMOverride else mkDefault) { };
1210131812111319 # Don't run ntpd in the guest. It should get the correct time from KVM.
12121320 services.timesyncd.enable = false;
1213132112141322 services.qemuGuest.enable = cfg.qemu.guestAgent.enable;
1215132312161216- system.build.vm = hostPkgs.runCommand "nixos-vm" {
12171217- preferLocalBuild = true;
12181218- meta.mainProgram = "run-${config.system.name}-vm";
12191219- }
12201220- ''
12211221- mkdir -p $out/bin
12221222- ln -s ${config.system.build.toplevel} $out/system
12231223- ln -s ${hostPkgs.writeScript "run-nixos-vm" startVM} $out/bin/run-${config.system.name}-vm
12241224- '';
13241324+ system.build.vm =
13251325+ hostPkgs.runCommand "nixos-vm"
13261326+ {
13271327+ preferLocalBuild = true;
13281328+ meta.mainProgram = "run-${config.system.name}-vm";
13291329+ }
13301330+ ''
13311331+ mkdir -p $out/bin
13321332+ ln -s ${config.system.build.toplevel} $out/system
13331333+ ln -s ${hostPkgs.writeScript "run-nixos-vm" startVM} $out/bin/run-${config.system.name}-vm
13341334+ '';
1225133512261336 # When building a regular system configuration, override whatever
12271337 # video driver the host uses.
12281338 services.xserver.videoDrivers = mkVMOverride [ "modesetting" ];
12291339 services.xserver.defaultDepth = mkVMOverride 0;
12301340 services.xserver.resolutions = mkVMOverride [ cfg.resolution ];
12311231- services.xserver.monitorSection =
12321232- ''
12331233- # Set a higher refresh rate so that resolutions > 800x600 work.
12341234- HorizSync 30-140
12351235- VertRefresh 50-160
12361236- '';
13411341+ services.xserver.monitorSection = ''
13421342+ # Set a higher refresh rate so that resolutions > 800x600 work.
13431343+ HorizSync 30-140
13441344+ VertRefresh 50-160
13451345+ '';
1237134612381347 # Wireless won't work in the VM.
12391348 networking.wireless.enable = mkVMOverride false;
···1244135312451354 networking.usePredictableInterfaceNames = false;
1246135512471247- system.requiredKernelConfig = with config.lib.kernelConfig;
12481248- [ (isEnabled "VIRTIO_BLK")
13561356+ system.requiredKernelConfig =
13571357+ with config.lib.kernelConfig;
13581358+ [
13591359+ (isEnabled "VIRTIO_BLK")
12491360 (isEnabled "VIRTIO_PCI")
12501361 (isEnabled "VIRTIO_NET")
12511362 (isEnabled "EXT4_FS")
···12571368 (isYes "NET_CORE")
12581369 (isYes "INET")
12591370 (isYes "NETWORK_FILESYSTEMS")
12601260- ] ++ optionals (!cfg.graphics) [
13711371+ ]
13721372+ ++ optionals (!cfg.graphics) [
12611373 (isYes "SERIAL_8250_CONSOLE")
12621374 (isYes "SERIAL_8250")
12631263- ] ++ optionals (cfg.writableStore) [
13751375+ ]
13761376+ ++ optionals (cfg.writableStore) [
12641377 (isEnabled "OVERLAY_FS")
12651378 ];
12661379
···11+# This modules declares shared options for virtual machines,
22+# containers and anything else in `virtualisation`.
33+#
44+# This is useful to declare e.g. defaults for
55+# `virtualisation.diskSize` once, while building multiple
66+# different image formats of a NixOS configuration.
77+#
88+# Additional options can be migrated over time from
99+# `modules/virtualisation/qemu-vm.nix` and others.
1010+# Please keep defaults and descriptions here generic
1111+# and independent of i.e. hypervisor-specific notes
1212+# and defaults where.
1313+# Those can be added in the consuming modules where needed.
1414+# needed.
1515+let
1616+ _file = ./virtualisation-options.nix;
1717+ key = _file;
1818+in
1919+{
2020+ diskSize =
2121+ { lib, config, ... }:
2222+ let
2323+ t = lib.types;
2424+ in
2525+ {
2626+ inherit _file key;
2727+2828+ options = {
2929+ virtualisation.diskSizeAutoSupported = lib.mkOption {
3030+ type = t.bool;
3131+ default = true;
3232+ description = ''
3333+ Whether the current image builder or vm runner supports `virtualisation.diskSize = "auto".`
3434+ '';
3535+ internal = true;
3636+ };
3737+3838+ virtualisation.diskSize = lib.mkOption {
3939+ type = t.either (t.enum [ "auto" ]) t.ints.positive;
4040+ default = "auto";
4141+ description = ''
4242+ The disk size in megabytes of the virtual machine.
4343+ '';
4444+ };
4545+ };
4646+4747+ config =
4848+ let
4949+ inherit (config.virtualisation) diskSize diskSizeAutoSupported;
5050+ in
5151+ {
5252+ assertions = [
5353+ {
5454+ assertion = diskSize != "auto" || diskSizeAutoSupported;
5555+ message = "Setting virtualisation.diskSize to `auto` is not supported by the current image build or vm runner; use an explicit size.";
5656+ }
5757+ ];
5858+ };
5959+ };
6060+}
···41414242stdenv.mkDerivation rec {
4343 pname = "mlt";
4444- version = "7.24.0";
4444+ version = "7.26.0";
45454646 src = fetchFromGitHub {
4747 owner = "mltframework";
4848 repo = "mlt";
4949 rev = "v${version}";
5050- hash = "sha256-rs02V6+9jMF0S78rCCXcDn3gzghqnOtWEHMo/491JxA=";
5050+ hash = "sha256-MC7D7bgguDFZi8Dyip1wAa2zxxkpLupl05xFiDc8Byw=";
5151 # The submodule contains glaxnimate code, since MLT uses internally some functions defined in glaxnimate.
5252 # Since glaxnimate is not available as a library upstream, we cannot remove for now this dependency on
5353 # submodules until upstream exports glaxnimate as a library: https://gitlab.com/mattbas/glaxnimate/-/issues/545
···6060 patchShebangs misc/*
6161 '';
62626363+ # some tools like 'zoekt' want an unambiguous binary name, so give it to them
6464+ postInstall = ''
6565+ ln -s $out/bin/ctags $out/bin/universal-ctags
6666+ '';
6767+6368 doCheck = true;
64696570 checkFlags = [
···6060 # bootstrap.tar.xz has 444 permissions, which means that simple deletes fail
6161 # and steam will not be able to start
6262 fixBootstrap = ''
6363- if [ -r $HOME/.local/share/Steam/bootstrap.tar.xz ]; then
6464- chmod +w $HOME/.local/share/Steam/bootstrap.tar.xz
6363+ if [ -r $HOME/.steam/steam/bootstrap.tar.xz ]; then
6464+ chmod +w $HOME/.steam/steam/bootstrap.tar.xz
6565 fi
6666 '';
6767