mirror of
https://github.com/CHN-beta/nixpkgs.git
synced 2026-01-12 02:40:31 +08:00
Compare commits
11 Commits
pr/root_6_
...
qemu-smm
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d17af89ab7 | ||
|
|
1c71b20f50 | ||
|
|
1f6e0befd8 | ||
|
|
f0bfcb6ddb | ||
|
|
486bf1ea68 | ||
|
|
c28c5ebebe | ||
|
|
88e23db414 | ||
|
|
95adc4f5b6 | ||
|
|
9aaf8c7ae2 | ||
|
|
5c627abaa9 | ||
|
|
28c1494889 |
@@ -10,4 +10,5 @@
|
||||
<xi:include href="images/ocitools.section.xml" />
|
||||
<xi:include href="images/snaptools.section.xml" />
|
||||
<xi:include href="images/portableservice.section.xml" />
|
||||
<xi:include href="images/makediskimage.section.xml" />
|
||||
</chapter>
|
||||
|
||||
181
doc/builders/images/makediskimage.section.md
Normal file
181
doc/builders/images/makediskimage.section.md
Normal file
@@ -0,0 +1,181 @@
|
||||
# `<nixpkgs/nixos/lib/make-disk-image.nix>` {#sec-make-disk-image}
|
||||
|
||||
`<nixpkgs/nixos/lib/make-disk-image.nix>` is a function to create _disk image_ in multiple formats: raw, QCOW2 (QEMU image format), QCOW2-Compressed (compressed version), VDI (Virtual Box native format), VPC (VirtualPC).
|
||||
|
||||
There are two operations mode for this function:
|
||||
|
||||
- either, you want only a Nix store disk image, it can be done using `cptofs` without any virtual machine involved ;
|
||||
- or, you want a full NixOS install on the disk, it will start a virtual machine to perform various tasks such as partitionning, installation and bootloader installation.
|
||||
|
||||
When NixOS tests are running, this function gets called to generate potentially two kinds of disk images:
|
||||
|
||||
- a Nix-store only disk image: useful when the test _do not need_ bootloader
|
||||
- a full-fledged NixOS installation disk image: useful when the test _do need_ to test the bootloader
|
||||
|
||||
## Features
|
||||
|
||||
- whether to produce a Nix-store only image or not, **this can be incompatible with other options**
|
||||
- arbitrary NixOS configuration
|
||||
- multiple partition table layouts: EFI, legacy, legacy + GPT, hybrid, none through `partitionTableType` parameter
|
||||
- automatic or bound disk size: `diskSize` parameter, `additionalSpace` can be set when `diskSize` is `auto` to add a constant of disk space
|
||||
- boot partition size when partition table is `efi` or `hybrid`
|
||||
- arbitrary contents with permissions can be placed in the target filesystem using `contents`, incompatible with Nix-store only image
|
||||
- bootloaders are supported, incompatible with Nix-store only image
|
||||
- EFI variables can be mutated during image production and the result is exposed in `$out`
|
||||
- system management mode (SMM) can enabled during virtual machine operations
|
||||
- OVMF or EFI firmwares and variables templates can be customized
|
||||
- root filesystem `fsType` can be customized to whatever `mkfs.${fsType}` exist during operations
|
||||
- root filesystem label can be customized, defaults to `nix-store` if it's a nix store image, otherwise `nixpkgs/nixos`
|
||||
- a `/etc/nixpkgs/nixos/configuration.nix` can be provided through `configFile`, incompatible with a Nix-store only image
|
||||
- arbitrary code can be executed after the VM has finished its operations with `postVM`
|
||||
- the current nixpkgs can be realized as a channel in the disk image, which will change the hash of the image when the sources are updated
|
||||
- additional store paths can be provided through `additionalPaths`
|
||||
|
||||
Images are **NOT** deterministic, please do not hesitate to try to fix this, source of determinisms are (not exhaustive) :
|
||||
|
||||
- bootloader installation have timestamps ;
|
||||
- SQLite Nix store database contain registration times ;
|
||||
- `/etc/shadow` is in a non-deterministic order
|
||||
|
||||
For more, read the function signature source code for documentation on arguments: <https://github.com/NixOS/nixpkgs/blob/master/nixos/lib/make-disk-image.nix>.
|
||||
|
||||
## Usage
|
||||
|
||||
To produce a Nix-store only image:
|
||||
```nix
|
||||
let
|
||||
pkgs = import <nixpkgs> {};
|
||||
lib = pkgs.lib;
|
||||
make-disk-image = import <nixpkgs/nixos/lib/make-disk-image.nix>;
|
||||
in
|
||||
make-disk-image {
|
||||
inherit pkgs lib;
|
||||
config = {};
|
||||
additionalPaths = [ ];
|
||||
format = "qcow2";
|
||||
onlyNixStore = true;
|
||||
partitionTableType = "none";
|
||||
installBootLoader = false;
|
||||
touchEFIVars = false;
|
||||
diskSize = "auto";
|
||||
additionalSpace = "0M"; # Defaults to 512M.
|
||||
copyChannel = false;
|
||||
}
|
||||
```
|
||||
|
||||
Some arguments can be left out, they are shown explicitly for the sake of the example.
|
||||
|
||||
Building this derivation will provide a QCOW2 disk image containing only the Nix store and its registration information.
|
||||
|
||||
To produce a NixOS installation image disk with UEFI and bootloader installed:
|
||||
```nix
|
||||
let
|
||||
pkgs = import <nixpkgs> {};
|
||||
lib = pkgs.lib;
|
||||
make-disk-image = import <nixpkgs/nixos/lib/make-disk-image.nix>;
|
||||
evalConfig = import <nixpkgs/nixos/lib/eval-config.nix>;
|
||||
in
|
||||
make-disk-image {
|
||||
inherit pkgs lib;
|
||||
config = evalConfig {
|
||||
modules = [
|
||||
{
|
||||
fileSystems."/" = { device = "/dev/vda"; fsType = "ext4"; autoFormat = true; };
|
||||
boot.grub.device = "/dev/vda";
|
||||
}
|
||||
];
|
||||
};
|
||||
format = "qcow2";
|
||||
onlyNixStore = false;
|
||||
partitionTableType = "legacy+gpt";
|
||||
installBootLoader = true;
|
||||
touchEFIVars = true;
|
||||
diskSize = "auto";
|
||||
additionalSpace = "0M"; # Defaults to 512M.
|
||||
copyChannel = false;
|
||||
}
|
||||
```
|
||||
|
||||
## Technical details
|
||||
|
||||
`make-disk-image` has a bit of magic to minimize the amount of work to do in a virtual machine.
|
||||
|
||||
It relies on the [LKL (Linux Kernel Library) project](https://github.com/lkl/linux) which provides Linux kernel as userspace library.
|
||||
|
||||
::: {.note}
|
||||
The Nix-store only image only need to run LKL tools to produce an image and will never spawn a virtual machine, whereas full images will always require a virtual machine, but also use LKL.
|
||||
:::
|
||||
|
||||
### Image preparation phase
|
||||
|
||||
Image preparation phase will produce the initial image layout in a folder:
|
||||
|
||||
- devise a root folder based on `$PWD`
|
||||
- preparing the contents by copying and restoring ACLs in this root folder
|
||||
- load in the Nix store database all additional paths computed by `pkgs.closureInfo` in a temporary Nix store
|
||||
- run `nixos-install` in a temporary folder
|
||||
- transfer from the temporary store the additional paths registered to the installed NixOS
|
||||
- do fancy computations for the size of the disk image based on the apparent size of the root folder
|
||||
- partition the disk image using the corresponding script according to the partition table type
|
||||
- format the partitions if needed
|
||||
- use `cptofs` (LKL tool) to copy the root folder inside the disk image
|
||||
|
||||
At this step, the disk image contains already the Nix store, it only needs to be converted to the desired format to be used.
|
||||
|
||||
### Image conversion phase
|
||||
|
||||
Using `qemu-img`, the disk image is converted from a raw format to the desired format: qcow2(-compressed), vdi, vpc.
|
||||
|
||||
### Partitionning script based on layouts
|
||||
|
||||
#### `none`
|
||||
|
||||
No partition table layout is written.
|
||||
|
||||
#### `legacy`
|
||||
|
||||
This partition table type is composed of a MBR and one primary ext4 partition starting at 1MiB extending to the full disk image.
|
||||
|
||||
It is unsuitable for UEFI.
|
||||
|
||||
#### `legacy+gpt`
|
||||
|
||||
This partition table type uses GPT and:
|
||||
|
||||
- create a "no filesystem" partition from 1MiB to 2MiB ;
|
||||
- set `bios_grub` flag on this "no filesystem" partition, which marks it as a [GRUB BIOS partition](https://www.gnu.org/software/parted/manual/html_node/set.html) ;
|
||||
- create a primary ext4 partition starting at 2MiB and extending to the full disk image ;
|
||||
- perform optimal alignments checks on each partition
|
||||
|
||||
This partition has no ESP partition, it is unsuitable for EFI boot which requires an ESP partition, but it can work with CSM (Compatibility Support Module) which emulates BIOS for UEFI.
|
||||
|
||||
#### `efi`
|
||||
|
||||
This partition table type uses GPT and:
|
||||
|
||||
- creates an FAT32 ESP partition from 8MiB to specified `bootSize` parameter (256MiB by default), set it bootable ;
|
||||
- create a primary ext4 partition starting after the boot one and extending to the full disk image
|
||||
|
||||
#### `hybrid`
|
||||
|
||||
This partition table type uses GPT and:
|
||||
|
||||
- creates a "no filesystem" partition from 0 to 1MiB, set `bios_grub` flag on it ;
|
||||
- creates an FAT32 ESP partition from 8MiB to specified `bootSize` parameter (256MiB by default), set it bootable ;
|
||||
- creates a primary ext4 partition starting after the boot one and extending to the full disk image
|
||||
|
||||
This partition could be booted by a BIOS able to understand GPT layouts and recognizing the MBR at the start.
|
||||
|
||||
### How to run determinism analysis on results?
|
||||
|
||||
Run your derivation with `--check` to rebuild it and verify it is the same.
|
||||
|
||||
Once it fails, you will be left with two folders with one having `.check`.
|
||||
|
||||
`diffoscope` is not able at the moment to diff two QCOW2 filesystems, it is advised to use raw format.
|
||||
|
||||
But even with raw formats, `diffoscope` cannot diff the partition table and partitions recursively.
|
||||
|
||||
For this, you can run `fdisk -l $image` and generate `dd if=$image of=$image-p$i.raw skip=$start count=$sectors` for each `(start, sectors)` listed in the `fdisk` output.
|
||||
|
||||
With this, you will have each partition as a separate file and you can diffoscope pairs of them to use `diffoscope` ability to read filesystems.
|
||||
@@ -47,6 +47,21 @@
|
||||
, # Whether to invoke `switch-to-configuration boot` during image creation
|
||||
installBootLoader ? true
|
||||
|
||||
, # Whether to output have EFIVARS available in $out/efi-vars.fd and use it during disk creation
|
||||
touchEFIVars ? false
|
||||
|
||||
, # Whether to enforce SMM in QEMU for EFI variables manipulation - this can break authenticated variables manipulation such as bootloader installation.
|
||||
systemManagementModeEnforcement ? false
|
||||
|
||||
, # OVMF firmware derivation, defaults to `pkgs.OVMF.fd`
|
||||
OVMF ? pkgs.OVMF.fd
|
||||
|
||||
, # EFI firmware
|
||||
efiFirmware ? OVMF.firmware
|
||||
|
||||
, # EFI variables
|
||||
efiVariables ? OVMF.variables
|
||||
|
||||
, # The root file system type.
|
||||
fsType ? "ext4"
|
||||
|
||||
@@ -70,6 +85,22 @@
|
||||
, # Disk image format, one of qcow2, qcow2-compressed, vdi, vpc, raw.
|
||||
format ? "raw"
|
||||
|
||||
# Whether to fix:
|
||||
# - GPT Disk Unique Identifier (diskGUID)
|
||||
# - GPT Partition Unique Identifier: depends on the layout, root partition UUID can be controlled through `rootGPUID` option
|
||||
# - GPT Partition Type Identifier: fixed according to the layout, e.g. ESP partition, etc. through `parted` invocation.
|
||||
# - Filesystem Unique Identifier when fsType = ext4 for *root partition*.
|
||||
# BIOS/MBR support is "best effort" at the moment.
|
||||
# Boot partitions may not be deterministic.
|
||||
# Also, to fix last time checked of the ext4 partition if fsType = ext4.
|
||||
, deterministic ? true
|
||||
|
||||
# GPT Partition Unique Identifier for root partition.
|
||||
, rootGPUID ? "F222513B-DED1-49FA-B591-20CE86A2FE7F"
|
||||
# When fsType = ext4, this is the root Filesystem Unique Identifier.
|
||||
# TODO: support other filesystems someday.
|
||||
, rootFSUID ? (if fsType == "ext4" then rootGPUID else null)
|
||||
|
||||
, # Whether a nix channel based on the current source tree should be
|
||||
# made available inside the image. Useful for interactive use of nix
|
||||
# utils, but changes the hash of the image when the sources are
|
||||
@@ -80,15 +111,18 @@
|
||||
additionalPaths ? []
|
||||
}:
|
||||
|
||||
assert partitionTableType == "legacy" || partitionTableType == "legacy+gpt" || partitionTableType == "efi" || partitionTableType == "hybrid" || partitionTableType == "none";
|
||||
# We use -E offset=X below, which is only supported by e2fsprogs
|
||||
assert partitionTableType != "none" -> fsType == "ext4";
|
||||
assert (lib.assertOneOf "partitionTableType" partitionTableType [ "legacy" "legacy+gpt" "efi" "hybrid" "none" ]);
|
||||
assert (lib.assertMsg (fsType == "ext4" && deterministic -> rootFSUID != null) "In deterministic mode with a ext4 partition, rootFSUID must be non-null, by default, it is equal to rootGPUID.");
|
||||
# We use -E offset=X below, which is only supported by e2fsprogs
|
||||
assert (lib.assertMsg (partitionTableType != "none" -> fsType == "ext4") "to produce a partition table, we need to use -E offset flag which is support only for fsType = ext4");
|
||||
assert (lib.assertMsg (touchEFIVars -> partitionTableType == "hybrid" || partitionTableType == "efi" || partitionTableType == "legacy+gpt") "EFI variables can be used only with a partition table of type: hybrid, efi or legacy+gpt.");
|
||||
# If only Nix store image, then: contents must be empty, configFile must be unset, and we should no install bootloader.
|
||||
assert (lib.assertMsg (onlyNixStore -> contents == [] && configFile == null && !installBootLoader) "In a only Nix store image, the contents must be empty, no configuration must be provided and no bootloader should be installed.");
|
||||
# Either both or none of {user,group} need to be set
|
||||
assert lib.all
|
||||
assert (lib.assertMsg (lib.all
|
||||
(attrs: ((attrs.user or null) == null)
|
||||
== ((attrs.group or null) == null))
|
||||
contents;
|
||||
assert onlyNixStore -> contents == [] && configFile == null && !installBootLoader;
|
||||
contents) "Contents of the disk image should set none of {user, group} or both at the same time.");
|
||||
|
||||
with lib;
|
||||
|
||||
@@ -127,6 +161,14 @@ let format' = format; in let
|
||||
mkpart primary ext4 2MB -1 \
|
||||
align-check optimal 2 \
|
||||
print
|
||||
${optionalString deterministic ''
|
||||
sgdisk \
|
||||
--disk-guid=97FD5997-D90B-4AA3-8D16-C1723AEA73C \
|
||||
--partition-guid=1:1C06F03B-704E-4657-B9CD-681A087A2FDC \
|
||||
--partition-guid=2:970C694F-AFD0-4B99-B750-CDB7A329AB6F \
|
||||
--partition-guid=3:${rootGPUID} \
|
||||
$diskImage
|
||||
''}
|
||||
'';
|
||||
efi = ''
|
||||
parted --script $diskImage -- \
|
||||
@@ -134,6 +176,13 @@ let format' = format; in let
|
||||
mkpart ESP fat32 8MiB ${bootSize} \
|
||||
set 1 boot on \
|
||||
mkpart primary ext4 ${bootSize} -1
|
||||
${optionalString deterministic ''
|
||||
sgdisk \
|
||||
--disk-guid=97FD5997-D90B-4AA3-8D16-C1723AEA73C \
|
||||
--partition-guid=1:1C06F03B-704E-4657-B9CD-681A087A2FDC \
|
||||
--partition-guid=2:${rootGPUID} \
|
||||
$diskImage
|
||||
''}
|
||||
'';
|
||||
hybrid = ''
|
||||
parted --script $diskImage -- \
|
||||
@@ -143,10 +192,20 @@ let format' = format; in let
|
||||
mkpart no-fs 0 1024KiB \
|
||||
set 2 bios_grub on \
|
||||
mkpart primary ext4 ${bootSize} -1
|
||||
${optionalString deterministic ''
|
||||
sgdisk \
|
||||
--disk-guid=97FD5997-D90B-4AA3-8D16-C1723AEA73C \
|
||||
--partition-guid=1:1C06F03B-704E-4657-B9CD-681A087A2FDC \
|
||||
--partition-guid=2:970C694F-AFD0-4B99-B750-CDB7A329AB6F \
|
||||
--partition-guid=3:${rootGPUID} \
|
||||
$diskImage
|
||||
''}
|
||||
'';
|
||||
none = "";
|
||||
}.${partitionTableType};
|
||||
|
||||
useEFIBoot = touchEFIVars;
|
||||
|
||||
nixpkgs = cleanSource pkgs.path;
|
||||
|
||||
# FIXME: merge with channel.nix / make-channel.nix.
|
||||
@@ -165,6 +224,7 @@ let format' = format; in let
|
||||
[ rsync
|
||||
util-linux
|
||||
parted
|
||||
gptfdisk
|
||||
e2fsprogs
|
||||
lkl
|
||||
config.system.build.nixos-install
|
||||
@@ -368,20 +428,41 @@ let format' = format; in let
|
||||
diskImage=$out/${filename}
|
||||
'';
|
||||
|
||||
createEFIVars = ''
|
||||
efiVars=$out/efi-vars.fd
|
||||
cp ${efiVariables} $efiVars
|
||||
chmod 0644 $efiVars
|
||||
'';
|
||||
|
||||
buildImage = pkgs.vmTools.runInLinuxVM (
|
||||
pkgs.runCommand name {
|
||||
preVM = prepareImage;
|
||||
preVM = prepareImage + lib.optionalString touchEFIVars createEFIVars;
|
||||
buildInputs = with pkgs; [ util-linux e2fsprogs dosfstools ];
|
||||
postVM = moveOrConvertImage + postVM;
|
||||
QEMU_OPTS =
|
||||
concatStringsSep " " (lib.optional useEFIBoot "-drive if=pflash,format=raw,unit=0,readonly=on,file=${efiFirmware}"
|
||||
++ lib.optionals touchEFIVars [
|
||||
"-drive if=pflash,format=raw,unit=1,file=$efiVars"
|
||||
]
|
||||
++ lib.optionals systemManagementModeEnforcement [
|
||||
"-machine type=q35,accel=kvm,smm=on"
|
||||
# Ensure we require EFI firmware to go through SMM to touch secureboot variables (once setup is done)
|
||||
"-global driver=cfi.pflash01,property=secure,value=on"
|
||||
]
|
||||
);
|
||||
memSize = 1024;
|
||||
} ''
|
||||
export PATH=${binPath}:$PATH
|
||||
|
||||
rootDisk=${if partitionTableType != "none" then "/dev/vda${rootPartition}" else "/dev/vda"}
|
||||
|
||||
# Some tools assume these exist
|
||||
ln -s vda /dev/xvda
|
||||
ln -s vda /dev/sda
|
||||
# It is necessary to set root filesystem unique identifier in advance, otherwise
|
||||
# bootloader might get the wrong one and failed to boot.
|
||||
# At the end, we reset again because we want deterministic timestamps.
|
||||
${optionalString (fsType == "ext4" && deterministic) ''
|
||||
tune2fs -T now ${optionalString deterministic "-U ${rootFSUID}"} -c 0 -i 0 $rootDisk
|
||||
''}
|
||||
|
||||
# make systemd-boot find ESP without udev
|
||||
mkdir /dev/block
|
||||
ln -s /dev/vda1 /dev/block/254:1
|
||||
@@ -396,6 +477,8 @@ let format' = format; in let
|
||||
mkdir -p /mnt/boot
|
||||
mkfs.vfat -n ESP /dev/vda1
|
||||
mount /dev/vda1 /mnt/boot
|
||||
|
||||
${optionalString touchEFIVars "mount -t efivarfs efivarfs /sys/firmware/efi/efivars"}
|
||||
''}
|
||||
|
||||
# Install a configuration.nix
|
||||
@@ -405,7 +488,13 @@ let format' = format; in let
|
||||
''}
|
||||
|
||||
${lib.optionalString installBootLoader ''
|
||||
# Set up core system link, GRUB, etc.
|
||||
# In this throwaway resource, we only have /dev/vda, but the actual VM may refer to another disk for bootloader, e.g. /dev/vdb
|
||||
# Use this option to create a symlink from vda to any arbitrary device you want.
|
||||
${optionalString (config.boot.loader.grub.device != "/dev/vda") ''
|
||||
ln -s /dev/vda ${config.boot.loader.grub.device}
|
||||
''}
|
||||
|
||||
# Set up core system link, bootloader (sd-boot, GRUB, uboot, etc.), etc.
|
||||
NIXOS_INSTALL_BOOTLOADER=1 nixos-enter --root $mountPoint -- /nix/var/nix/profiles/system/bin/switch-to-configuration boot
|
||||
|
||||
# The above scripts will generate a random machine-id and we don't want to bake a single ID into all our images
|
||||
@@ -432,8 +521,12 @@ let format' = format; in let
|
||||
# Make sure resize2fs works. Note that resize2fs has stricter criteria for resizing than a normal
|
||||
# mount, so the `-c 0` and `-i 0` don't affect it. Setting it to `now` doesn't produce deterministic
|
||||
# output, of course, but we can fix that when/if we start making images deterministic.
|
||||
# In deterministic mode, this is fixed to 1970-01-01 (UNIX timestamp 0).
|
||||
# This two-step approach is necessary otherwise `tune2fs` will want a fresher filesystem to perform
|
||||
# some changes.
|
||||
${optionalString (fsType == "ext4") ''
|
||||
tune2fs -T now -c 0 -i 0 $rootDisk
|
||||
tune2fs -T now ${optionalString deterministic "-U ${rootFSUID}"} -c 0 -i 0 $rootDisk
|
||||
${optionalString deterministic "tune2fs -f -T 19700101 $rootDisk"}
|
||||
''}
|
||||
''
|
||||
);
|
||||
|
||||
@@ -43,7 +43,7 @@ in {
|
||||
|
||||
sizeMB = mkOption {
|
||||
type = with types; either (enum [ "auto" ]) int;
|
||||
default = 2048;
|
||||
default = 2252;
|
||||
example = 8192;
|
||||
description = lib.mdDoc "The size in MB of the image";
|
||||
};
|
||||
|
||||
@@ -55,6 +55,11 @@ let
|
||||
|
||||
};
|
||||
|
||||
selectPartitionTableLayout = { useEFIBoot, useDefaultFilesystems }:
|
||||
if useDefaultFilesystems then
|
||||
if useEFIBoot then "efi" else "legacy"
|
||||
else "none";
|
||||
|
||||
driveCmdline = idx: { file, driveExtraOpts, deviceExtraOpts, ... }:
|
||||
let
|
||||
drvId = "drive${toString idx}";
|
||||
@@ -98,7 +103,6 @@ let
|
||||
addDeviceNames =
|
||||
imap1 (idx: drive: drive // { device = driveDeviceName idx; });
|
||||
|
||||
|
||||
# Shell script to start the VM.
|
||||
startVM =
|
||||
''
|
||||
@@ -111,8 +115,23 @@ let
|
||||
NIX_DISK_IMAGE=$(readlink -f "''${NIX_DISK_IMAGE:-${config.virtualisation.diskImage}}")
|
||||
|
||||
if ! test -e "$NIX_DISK_IMAGE"; then
|
||||
${qemu}/bin/qemu-img create -f qcow2 "$NIX_DISK_IMAGE" \
|
||||
${toString config.virtualisation.diskSize}M
|
||||
echo "Disk image do not exist, creating the virtualisation disk image..."
|
||||
# If we are using a bootloader and default filesystems layout.
|
||||
# We have to reuse the system image layout as a backing image format (CoW)
|
||||
# So we can write on the top of it.
|
||||
|
||||
# If we are not using the default FS layout, potentially, we are interested into
|
||||
# performing operations in postDeviceCommands or at early boot on the raw device.
|
||||
# We can still boot through QEMU direct kernel boot feature.
|
||||
|
||||
# CoW prevent size to be attributed to an image.
|
||||
# FIXME: raise this issue to upstream.
|
||||
${qemu}/bin/qemu-img create \
|
||||
${concatStringsSep " \\\n" ([ "-f qcow2" ]
|
||||
++ optional (cfg.useBootLoader && cfg.useDefaultFilesystems) "-F qcow2 -b ${systemImage}/nixos.qcow2"
|
||||
++ optional (!(cfg.useBootLoader && cfg.useDefaultFilesystems)) "-o size=${toString config.virtualisation.diskSize}M"
|
||||
++ [ "$NIX_DISK_IMAGE" ])}
|
||||
echo "Virtualisation disk image created."
|
||||
fi
|
||||
|
||||
# Create a directory for storing temporary data of the running VM.
|
||||
@@ -152,17 +171,13 @@ let
|
||||
|
||||
${lib.optionalString cfg.useBootLoader
|
||||
''
|
||||
# Create a writable copy/snapshot of the boot disk.
|
||||
# A writable boot disk can be booted from automatically.
|
||||
${qemu}/bin/qemu-img create -f qcow2 -F qcow2 -b ${bootDisk}/disk.img "$TMPDIR/disk.img"
|
||||
|
||||
NIX_EFI_VARS=$(readlink -f "''${NIX_EFI_VARS:-${cfg.efiVars}}")
|
||||
NIX_EFI_VARS=$(readlink -f "''${NIX_EFI_VARS:-${config.system.name}-efi-vars.fd}")
|
||||
|
||||
${lib.optionalString cfg.useEFIBoot
|
||||
''
|
||||
# VM needs writable EFI vars
|
||||
if ! test -e "$NIX_EFI_VARS"; then
|
||||
cp ${bootDisk}/efi-vars.fd "$NIX_EFI_VARS"
|
||||
cp ${systemImage}/efi-vars.fd "$NIX_EFI_VARS"
|
||||
chmod 0644 "$NIX_EFI_VARS"
|
||||
fi
|
||||
''}
|
||||
@@ -198,96 +213,25 @@ let
|
||||
|
||||
regInfo = pkgs.closureInfo { rootPaths = config.virtualisation.additionalPaths; };
|
||||
|
||||
# System image is akin to a complete NixOS install with
|
||||
# a boot partition and root partition.
|
||||
systemImage = import ../../lib/make-disk-image.nix {
|
||||
inherit pkgs config lib;
|
||||
inherit (cfg.efi) systemManagementModeEnforcement;
|
||||
additionalPaths = [ regInfo ];
|
||||
format = "qcow2";
|
||||
onlyNixStore = false;
|
||||
partitionTableType = selectPartitionTableLayout { inherit (cfg) useDefaultFilesystems useEFIBoot; };
|
||||
installBootLoader = cfg.useBootLoader && cfg.useDefaultFilesystems;
|
||||
touchEFIVars = cfg.useEFIBoot;
|
||||
diskSize = "auto";
|
||||
additionalSpace = "0M";
|
||||
copyChannel = false;
|
||||
OVMF = cfg.efi.OVMF;
|
||||
};
|
||||
|
||||
# Generate a hard disk image containing a /boot partition and GRUB
|
||||
# in the MBR. Used when the `useBootLoader' option is set.
|
||||
# Uses `runInLinuxVM` to create the image in a throwaway VM.
|
||||
# See note [Disk layout with `useBootLoader`].
|
||||
# FIXME: use nixos/lib/make-disk-image.nix.
|
||||
bootDisk =
|
||||
pkgs.vmTools.runInLinuxVM (
|
||||
pkgs.runCommand "nixos-boot-disk"
|
||||
{ preVM =
|
||||
''
|
||||
mkdir $out
|
||||
diskImage=$out/disk.img
|
||||
${qemu}/bin/qemu-img create -f qcow2 $diskImage "60M"
|
||||
${if cfg.useEFIBoot then ''
|
||||
efiVars=$out/efi-vars.fd
|
||||
cp ${cfg.efi.variables} $efiVars
|
||||
chmod 0644 $efiVars
|
||||
'' else ""}
|
||||
'';
|
||||
buildInputs = [ pkgs.util-linux ];
|
||||
QEMU_OPTS = "-nographic -serial stdio -monitor none"
|
||||
+ lib.optionalString cfg.useEFIBoot (
|
||||
" -drive if=pflash,format=raw,unit=0,readonly=on,file=${cfg.efi.firmware}"
|
||||
+ " -drive if=pflash,format=raw,unit=1,file=$efiVars");
|
||||
}
|
||||
''
|
||||
# Create a /boot EFI partition with 60M and arbitrary but fixed GUIDs for reproducibility
|
||||
${pkgs.gptfdisk}/bin/sgdisk \
|
||||
--set-alignment=1 --new=1:34:2047 --change-name=1:BIOSBootPartition --typecode=1:ef02 \
|
||||
--set-alignment=512 --largest-new=2 --change-name=2:EFISystem --typecode=2:ef00 \
|
||||
--attributes=1:set:1 \
|
||||
--attributes=2:set:2 \
|
||||
--disk-guid=97FD5997-D90B-4AA3-8D16-C1723AEA73C1 \
|
||||
--partition-guid=1:1C06F03B-704E-4657-B9CD-681A087A2FDC \
|
||||
--partition-guid=2:970C694F-AFD0-4B99-B750-CDB7A329AB6F \
|
||||
--hybrid 2 \
|
||||
--recompute-chs /dev/vda
|
||||
|
||||
${optionalString (config.boot.loader.grub.device != "/dev/vda")
|
||||
# In this throwaway VM, we only have the /dev/vda disk, but the
|
||||
# actual VM described by `config` (used by `switch-to-configuration`
|
||||
# below) may set `boot.loader.grub.device` to a different device
|
||||
# that's nonexistent in the throwaway VM.
|
||||
# Create a symlink for that device, so that the `grub-install`
|
||||
# by `switch-to-configuration` will hit /dev/vda anyway.
|
||||
''
|
||||
ln -s /dev/vda ${config.boot.loader.grub.device}
|
||||
''
|
||||
}
|
||||
|
||||
${pkgs.dosfstools}/bin/mkfs.fat -F16 /dev/vda2
|
||||
export MTOOLS_SKIP_CHECK=1
|
||||
${pkgs.mtools}/bin/mlabel -i /dev/vda2 ::boot
|
||||
|
||||
# Mount /boot; load necessary modules first.
|
||||
${pkgs.kmod}/bin/insmod ${pkgs.linux}/lib/modules/*/kernel/fs/nls/nls_cp437.ko.xz || true
|
||||
${pkgs.kmod}/bin/insmod ${pkgs.linux}/lib/modules/*/kernel/fs/nls/nls_iso8859-1.ko.xz || true
|
||||
${pkgs.kmod}/bin/insmod ${pkgs.linux}/lib/modules/*/kernel/fs/fat/fat.ko.xz || true
|
||||
${pkgs.kmod}/bin/insmod ${pkgs.linux}/lib/modules/*/kernel/fs/fat/vfat.ko.xz || true
|
||||
${pkgs.kmod}/bin/insmod ${pkgs.linux}/lib/modules/*/kernel/fs/efivarfs/efivarfs.ko.xz || true
|
||||
mkdir /boot
|
||||
mount /dev/vda2 /boot
|
||||
|
||||
${optionalString config.boot.loader.efi.canTouchEfiVariables ''
|
||||
mount -t efivarfs efivarfs /sys/firmware/efi/efivars
|
||||
''}
|
||||
|
||||
# This is needed for GRUB 0.97, which doesn't know about virtio devices.
|
||||
mkdir /boot/grub
|
||||
echo '(hd0) /dev/vda' > /boot/grub/device.map
|
||||
|
||||
# This is needed for systemd-boot to find ESP, and udev is not available here to create this
|
||||
mkdir -p /dev/block
|
||||
ln -s /dev/vda2 /dev/block/254:2
|
||||
|
||||
# Set up system profile (normally done by nixos-rebuild / nix-env --set)
|
||||
mkdir -p /nix/var/nix/profiles
|
||||
ln -s ${config.system.build.toplevel} /nix/var/nix/profiles/system-1-link
|
||||
ln -s /nix/var/nix/profiles/system-1-link /nix/var/nix/profiles/system
|
||||
|
||||
# Install bootloader
|
||||
touch /etc/NIXOS
|
||||
export NIXOS_INSTALL_BOOTLOADER=1
|
||||
${config.system.build.toplevel}/bin/switch-to-configuration boot
|
||||
|
||||
umount /boot
|
||||
'' # */
|
||||
);
|
||||
|
||||
# This image is supposed to have no side-effects on the system image.
|
||||
# It should be used when you do not need the bootloader.
|
||||
storeImage = import ../../lib/make-disk-image.nix {
|
||||
inherit pkgs config lib;
|
||||
additionalPaths = [ regInfo ];
|
||||
@@ -295,17 +239,18 @@ let
|
||||
onlyNixStore = true;
|
||||
partitionTableType = "none";
|
||||
installBootLoader = false;
|
||||
touchEFIVars = false;
|
||||
diskSize = "auto";
|
||||
additionalSpace = "0M";
|
||||
copyChannel = false;
|
||||
};
|
||||
|
||||
in
|
||||
|
||||
{
|
||||
imports = [
|
||||
../profiles/qemu-guest.nix
|
||||
(mkRenamedOptionModule [ "virtualisation" "pathsInNixDB" ] [ "virtualisation" "additionalPaths" ])
|
||||
(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")
|
||||
];
|
||||
|
||||
options = {
|
||||
@@ -360,10 +305,55 @@ in
|
||||
virtualisation.bootDevice =
|
||||
mkOption {
|
||||
type = types.path;
|
||||
default = lookupDriveDeviceName "root" cfg.qemu.drives;
|
||||
defaultText = literalExpression ''lookupDriveDeviceName "root" cfg.qemu.drives'';
|
||||
example = "/dev/vda";
|
||||
description =
|
||||
lib.mdDoc ''
|
||||
The disk to be used for the root filesystem.
|
||||
The disk to be used for the boot filesystem.
|
||||
By default, it is the same disk as the root filesystem.
|
||||
'';
|
||||
};
|
||||
|
||||
virtualisation.bootPartition =
|
||||
mkOption {
|
||||
type = types.nullOr types.path;
|
||||
default = if cfg.useEFIBoot then "${cfg.bootDevice}1" else null;
|
||||
defaultText = literalExpression ''if cfg.useEFIBoot then "''${cfg.bootDevice}1" else null;'';
|
||||
example = "/dev/vda1";
|
||||
description =
|
||||
lib.mdDoc ''
|
||||
The boot partition to be used to mount /boot filesystem.
|
||||
In legacy boots, this should be null.
|
||||
By default, in EFI boot, it is the first partition of the boot device.
|
||||
'';
|
||||
};
|
||||
|
||||
virtualisation.rootDevice =
|
||||
mkOption {
|
||||
type = types.path;
|
||||
default =
|
||||
if (cfg.useBootLoader && cfg.useEFIBoot && cfg.useDefaultFilesystems)
|
||||
then "${cfg.bootDevice}2"
|
||||
else
|
||||
(
|
||||
if (cfg.useBootLoader && !cfg.useEFIBoot && cfg.useDefaultFilesystems)
|
||||
then
|
||||
"${cfg.bootDevice}1"
|
||||
else
|
||||
null
|
||||
);
|
||||
defaultText = ''if (cfg.useBootLoader && !hasPrefix "/dev/mapper/" ''${cfg.bootDevice}) then "''${cfg.bootDevice}2" else cfg.bootDevice;'';
|
||||
example = "/dev/vda2";
|
||||
description =
|
||||
lib.mdDoc ''
|
||||
The disk or partition to be used for the root filesystem.
|
||||
By default :
|
||||
|
||||
- the boot disk in case you're **NOT** using a bootloader (`useBootLoader == false`)
|
||||
- the second partition of the boot disk in case you're using a bootloader (`useBootLoader == true` && boot device is the default one).
|
||||
|
||||
In case you are not using a default boot device, you have to set explicitly your root device.
|
||||
'';
|
||||
};
|
||||
|
||||
@@ -719,10 +709,24 @@ in
|
||||
};
|
||||
|
||||
virtualisation.efi = {
|
||||
OVMF = mkOption {
|
||||
type = types.package;
|
||||
default = (pkgs.OVMF.override {
|
||||
systemManagementModeSupport = cfg.efi.systemManagementModeEnforcement;
|
||||
secureBoot = cfg.useSecureBoot;
|
||||
}).fd;
|
||||
defaultText = ''(pkgs.OVMF.override {
|
||||
systemManagementModeSupport = cfg.efi.systemManagementModeEnforcement;
|
||||
secureBoot = cfg.useSecureBoot;
|
||||
}).fd;'';
|
||||
description =
|
||||
lib.mdDoc "OVMF firmware package, defaults to OVMF configured with secure boot and system management mode if needed.";
|
||||
};
|
||||
|
||||
firmware = mkOption {
|
||||
type = types.path;
|
||||
default = pkgs.OVMF.firmware;
|
||||
defaultText = literalExpression "pkgs.OVMF.firmware";
|
||||
default = cfg.efi.OVMF.firmware;
|
||||
defaultText = literalExpression "cfg.efi.OVMF.firmware";
|
||||
description =
|
||||
lib.mdDoc ''
|
||||
Firmware binary for EFI implementation, defaults to OVMF.
|
||||
@@ -731,13 +735,25 @@ in
|
||||
|
||||
variables = mkOption {
|
||||
type = types.path;
|
||||
default = pkgs.OVMF.variables;
|
||||
defaultText = literalExpression "pkgs.OVMF.variables";
|
||||
default = cfg.efi.OVMF.variables;
|
||||
defaultText = literalExpression "cfg.efi.OVMF.variables";
|
||||
description =
|
||||
lib.mdDoc ''
|
||||
Platform-specific flash binary for EFI variables, implementation-dependent to the EFI firmware.
|
||||
Defaults to OVMF.
|
||||
'';
|
||||
};
|
||||
|
||||
systemManagementModeEnforcement = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description =
|
||||
lib.mdDoc ''
|
||||
Enable system management mode enforcement for QEMU which prevent the OS from arbitrary accessing the UEFI variables memory.
|
||||
It enforces to use the SMM API to perform any changes, useful in SecureBoot contexts.
|
||||
|
||||
WARNING: OVMF implementation seems broken.
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
@@ -756,18 +772,17 @@ in
|
||||
'';
|
||||
};
|
||||
|
||||
virtualisation.efiVars =
|
||||
virtualisation.useSecureBoot =
|
||||
mkOption {
|
||||
type = types.str;
|
||||
default = "./${config.system.name}-efi-vars.fd";
|
||||
defaultText = literalExpression ''"./''${config.system.name}-efi-vars.fd"'';
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description =
|
||||
lib.mdDoc ''
|
||||
Path to nvram image containing UEFI variables. The will be created
|
||||
on startup if it does not exist.
|
||||
Enable Secure Boot support in the EFI firmware.
|
||||
'';
|
||||
};
|
||||
|
||||
|
||||
virtualisation.bios =
|
||||
mkOption {
|
||||
type = types.nullOr types.package;
|
||||
@@ -804,6 +819,20 @@ in
|
||||
]));
|
||||
|
||||
warnings =
|
||||
optional (cfg.efi.systemManagementModeEnforcement)
|
||||
''
|
||||
You have enabled ${opt.efi.systemManagementModeEnforcement} = true.
|
||||
|
||||
This will enable system management mode for QEMU (cfi.pflash01, secure=on)
|
||||
and if you're using the default OVMF image, it will build a SMM-enabled firmware
|
||||
for UEFI.
|
||||
|
||||
This will lock down UEFI authenticated variables to ensure an actually secure
|
||||
SecureBoot for example.
|
||||
|
||||
WARNING: currently, SMM seems to be broken and will cause boot failures and silent hung tasks.
|
||||
''
|
||||
++
|
||||
optional (
|
||||
cfg.writableStore &&
|
||||
cfg.useNixStoreImage &&
|
||||
@@ -823,27 +852,7 @@ in
|
||||
${opt.writableStore} = false;
|
||||
'';
|
||||
|
||||
# Note [Disk layout with `useBootLoader`]
|
||||
#
|
||||
# If `useBootLoader = true`, we configure 2 drives:
|
||||
# `/dev/?da` for the root disk, and `/dev/?db` for the boot disk
|
||||
# which has the `/boot` partition and the boot loader.
|
||||
# Concretely:
|
||||
#
|
||||
# * The second drive's image `disk.img` is created in `bootDisk = ...`
|
||||
# using a throwaway VM. Note that there the disk is always `/dev/vda`,
|
||||
# even though in the final VM it will be at `/dev/*b`.
|
||||
# * The disks are attached in `virtualisation.qemu.drives`.
|
||||
# Their order makes them appear as devices `a`, `b`, etc.
|
||||
# * `fileSystems."/boot"` is adjusted to be on device `b`.
|
||||
|
||||
# If `useBootLoader`, GRUB goes to the second disk, see
|
||||
# note [Disk layout with `useBootLoader`].
|
||||
boot.loader.grub.device = mkVMOverride (
|
||||
if cfg.useBootLoader
|
||||
then driveDeviceName 2 # second disk
|
||||
else cfg.bootDevice
|
||||
);
|
||||
boot.loader.grub.device = mkVMOverride cfg.bootDevice;
|
||||
boot.loader.grub.gfxmodeBios = with cfg.resolution; "${toString x}x${toString y}";
|
||||
|
||||
boot.initrd.kernelModules = optionals (cfg.useNixStoreImage && !cfg.writableStore) [ "erofs" ];
|
||||
@@ -858,10 +867,10 @@ in
|
||||
''
|
||||
# If the disk image appears to be empty, run mke2fs to
|
||||
# initialise.
|
||||
FSTYPE=$(blkid -o value -s TYPE ${cfg.bootDevice} || true)
|
||||
PARTTYPE=$(blkid -o value -s PTTYPE ${cfg.bootDevice} || true)
|
||||
FSTYPE=$(blkid -o value -s TYPE ${cfg.rootDevice} || true)
|
||||
PARTTYPE=$(blkid -o value -s PTTYPE ${cfg.rootDevice} || true)
|
||||
if test -z "$FSTYPE" -a -z "$PARTTYPE"; then
|
||||
mke2fs -t ext4 ${cfg.bootDevice}
|
||||
mke2fs -t ext4 ${cfg.rootDevice}
|
||||
fi
|
||||
'';
|
||||
|
||||
@@ -907,8 +916,6 @@ in
|
||||
optional cfg.writableStore "overlay"
|
||||
++ optional (cfg.qemu.diskInterface == "scsi") "sym53c8xx";
|
||||
|
||||
virtualisation.bootDevice = mkDefault (driveDeviceName 1);
|
||||
|
||||
virtualisation.additionalPaths = [ config.system.build.toplevel ];
|
||||
|
||||
virtualisation.sharedDirectories = {
|
||||
@@ -964,7 +971,13 @@ in
|
||||
])
|
||||
(mkIf cfg.useEFIBoot [
|
||||
"-drive if=pflash,format=raw,unit=0,readonly=on,file=${cfg.efi.firmware}"
|
||||
"-drive if=pflash,format=raw,unit=1,file=$NIX_EFI_VARS"
|
||||
"-drive if=pflash,format=raw,unit=1,readonly=off,file=$NIX_EFI_VARS"
|
||||
])
|
||||
(mkIf cfg.efi.systemManagementModeEnforcement [
|
||||
# SMM requires Q35 machine.
|
||||
"-machine type=q35,accel=kvm,smm=on"
|
||||
# Enforce SMM usage for authenticated variables in UEFI
|
||||
"-global driver=cfi.pflash01,property=secure,value=on"
|
||||
])
|
||||
(mkIf (cfg.bios != null) [
|
||||
"-bios ${cfg.bios}/bios.bin"
|
||||
@@ -980,23 +993,14 @@ in
|
||||
file = ''"$NIX_DISK_IMAGE"'';
|
||||
driveExtraOpts.cache = "writeback";
|
||||
driveExtraOpts.werror = "report";
|
||||
deviceExtraOpts.bootindex = "1";
|
||||
}]
|
||||
(mkIf cfg.useNixStoreImage [{
|
||||
name = "nix-store";
|
||||
file = ''"$TMPDIR"/store.img'';
|
||||
deviceExtraOpts.bootindex = if cfg.useBootLoader then "3" else "2";
|
||||
deviceExtraOpts.bootindex = "2";
|
||||
driveExtraOpts.format = if cfg.writableStore then "qcow2" else "raw";
|
||||
}])
|
||||
(mkIf cfg.useBootLoader [
|
||||
# The order of this list determines the device names, see
|
||||
# note [Disk layout with `useBootLoader`].
|
||||
{
|
||||
name = "boot";
|
||||
file = ''"$TMPDIR"/disk.img'';
|
||||
driveExtraOpts.media = "disk";
|
||||
deviceExtraOpts.bootindex = "1";
|
||||
}
|
||||
])
|
||||
(imap0 (idx: _: {
|
||||
file = "$(pwd)/empty${toString idx}.qcow2";
|
||||
driveExtraOpts.werror = "report";
|
||||
@@ -1027,7 +1031,8 @@ in
|
||||
in
|
||||
mkVMOverride (cfg.fileSystems //
|
||||
optionalAttrs cfg.useDefaultFilesystems {
|
||||
"/".device = cfg.bootDevice;
|
||||
# cfg.useBootLoader -> [ boot partition ; root partition ] layout on *root* disk
|
||||
"/".device = cfg.rootDevice;
|
||||
"/".fsType = "ext4";
|
||||
"/".autoFormat = true;
|
||||
} //
|
||||
@@ -1054,10 +1059,10 @@ in
|
||||
neededForBoot = true;
|
||||
};
|
||||
} //
|
||||
optionalAttrs cfg.useBootLoader {
|
||||
optionalAttrs (cfg.useBootLoader && cfg.bootPartition != null) {
|
||||
# see note [Disk layout with `useBootLoader`]
|
||||
"/boot" = {
|
||||
device = "${lookupDriveDeviceName "boot" cfg.qemu.drives}2"; # 2 for e.g. `vdb2`, as created in `bootDisk`
|
||||
device = cfg.bootPartition; # 1 for e.g. `vda1`, as created in `systemImage`
|
||||
fsType = "vfat";
|
||||
noCheck = true; # fsck fails on a r/o filesystem
|
||||
};
|
||||
|
||||
@@ -63,7 +63,7 @@ in makeTest {
|
||||
# Small root disk for installer
|
||||
512
|
||||
];
|
||||
virtualisation.bootDevice = "/dev/vdb";
|
||||
virtualisation.rootDevice = "/dev/vdb";
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
{ kernelPackages ? null, flavour }: let
|
||||
preparationCode = {
|
||||
raid = ''
|
||||
machine.succeed("vgcreate test_vg /dev/vdc /dev/vdd")
|
||||
machine.succeed("vgcreate test_vg /dev/vdb /dev/vdc")
|
||||
machine.succeed("lvcreate -L 512M --type raid0 test_vg -n test_lv")
|
||||
'';
|
||||
|
||||
thinpool = ''
|
||||
machine.succeed("vgcreate test_vg /dev/vdc")
|
||||
machine.succeed("vgcreate test_vg /dev/vdb")
|
||||
machine.succeed("lvcreate -L 512M -T test_vg/test_thin_pool")
|
||||
machine.succeed("lvcreate -n test_lv -V 16G --thinpool test_thin_pool test_vg")
|
||||
'';
|
||||
|
||||
vdo = ''
|
||||
machine.succeed("vgcreate test_vg /dev/vdc")
|
||||
machine.succeed("vgcreate test_vg /dev/vdb")
|
||||
machine.succeed("lvcreate --type vdo -n test_lv -L 6G -V 12G test_vg/vdo_pool_lv")
|
||||
'';
|
||||
}.${flavour};
|
||||
@@ -79,7 +79,7 @@ in import ../make-test-python.nix ({ pkgs, ... }: {
|
||||
kernelPackages = lib.mkIf (kernelPackages != null) kernelPackages;
|
||||
};
|
||||
|
||||
specialisation.boot-lvm.configuration.virtualisation.bootDevice = "/dev/test_vg/test_lv";
|
||||
specialisation.boot-lvm.configuration.virtualisation.rootDevice = "/dev/test_vg/test_lv";
|
||||
};
|
||||
|
||||
testScript = ''
|
||||
|
||||
@@ -21,14 +21,14 @@ import ./make-test-python.nix ({ lib, pkgs, ... }: {
|
||||
fileSystems = lib.mkVMOverride {
|
||||
"/".fsType = lib.mkForce "btrfs";
|
||||
};
|
||||
virtualisation.bootDevice = "/dev/vdc";
|
||||
virtualisation.rootDevice = "/dev/vdb";
|
||||
};
|
||||
};
|
||||
|
||||
testScript = ''
|
||||
# Create RAID
|
||||
machine.succeed("mkfs.btrfs -d raid0 /dev/vdc /dev/vdd")
|
||||
machine.succeed("mkdir -p /mnt && mount /dev/vdc /mnt && echo hello > /mnt/test && umount /mnt")
|
||||
machine.succeed("mkfs.btrfs -d raid0 /dev/vdb /dev/vdc")
|
||||
machine.succeed("mkdir -p /mnt && mount /dev/vdb /mnt && echo hello > /mnt/test && umount /mnt")
|
||||
|
||||
# Boot from the RAID
|
||||
machine.succeed("bootctl set-default nixos-generation-1-specialisation-boot-btrfs-raid.conf")
|
||||
@@ -38,7 +38,7 @@ import ./make-test-python.nix ({ lib, pkgs, ... }: {
|
||||
|
||||
# Ensure we have successfully booted from the RAID
|
||||
assert "(initrd)" in machine.succeed("systemd-analyze") # booted with systemd in stage 1
|
||||
assert "/dev/vdc on / type btrfs" in machine.succeed("mount")
|
||||
assert "/dev/vdb on / type btrfs" in machine.succeed("mount")
|
||||
assert "hello" in machine.succeed("cat /test")
|
||||
assert "Total devices 2" in machine.succeed("btrfs filesystem show")
|
||||
'';
|
||||
|
||||
@@ -19,19 +19,19 @@ import ./make-test-python.nix ({ lib, pkgs, ... }: {
|
||||
specialisation.boot-luks.configuration = {
|
||||
boot.initrd.luks.devices = lib.mkVMOverride {
|
||||
cryptroot = {
|
||||
device = "/dev/vdc";
|
||||
device = "/dev/vdb";
|
||||
crypttabExtraOpts = [ "fido2-device=auto" ];
|
||||
};
|
||||
};
|
||||
virtualisation.bootDevice = "/dev/mapper/cryptroot";
|
||||
virtualisation.rootDevice = "/dev/mapper/cryptroot";
|
||||
};
|
||||
};
|
||||
|
||||
testScript = ''
|
||||
# Create encrypted volume
|
||||
machine.wait_for_unit("multi-user.target")
|
||||
machine.succeed("echo -n supersecret | cryptsetup luksFormat -q --iter-time=1 /dev/vdc -")
|
||||
machine.succeed("PASSWORD=supersecret SYSTEMD_LOG_LEVEL=debug systemd-cryptenroll --fido2-device=auto /dev/vdc |& systemd-cat")
|
||||
machine.succeed("echo -n supersecret | cryptsetup luksFormat -q --iter-time=1 /dev/vdb -")
|
||||
machine.succeed("PASSWORD=supersecret SYSTEMD_LOG_LEVEL=debug systemd-cryptenroll --fido2-device=auto /dev/vdb |& systemd-cat")
|
||||
|
||||
# Boot from the encrypted disk
|
||||
machine.succeed("bootctl set-default nixos-generation-1-specialisation-boot-luks.conf")
|
||||
|
||||
@@ -27,11 +27,11 @@ in {
|
||||
specialisation.boot-luks.configuration = {
|
||||
boot.initrd.luks.devices = lib.mkVMOverride {
|
||||
cryptroot = {
|
||||
device = "/dev/vdc";
|
||||
device = "/dev/vdb";
|
||||
keyFile = "/etc/cryptroot.key";
|
||||
};
|
||||
};
|
||||
virtualisation.bootDevice = "/dev/mapper/cryptroot";
|
||||
virtualisation.rootDevice = "/dev/mapper/cryptroot";
|
||||
boot.initrd.secrets."/etc/cryptroot.key" = keyfile;
|
||||
};
|
||||
};
|
||||
@@ -39,7 +39,7 @@ in {
|
||||
testScript = ''
|
||||
# Create encrypted volume
|
||||
machine.wait_for_unit("multi-user.target")
|
||||
machine.succeed("cryptsetup luksFormat -q --iter-time=1 -d ${keyfile} /dev/vdc")
|
||||
machine.succeed("cryptsetup luksFormat -q --iter-time=1 -d ${keyfile} /dev/vdb")
|
||||
|
||||
# Boot from the encrypted disk
|
||||
machine.succeed("bootctl set-default nixos-generation-1-specialisation-boot-luks.conf")
|
||||
|
||||
@@ -19,10 +19,10 @@ import ./make-test-python.nix ({ lib, pkgs, ... }: {
|
||||
specialisation.boot-luks.configuration = {
|
||||
boot.initrd.luks.devices = lib.mkVMOverride {
|
||||
# We have two disks and only type one password - key reuse is in place
|
||||
cryptroot.device = "/dev/vdc";
|
||||
cryptroot2.device = "/dev/vdd";
|
||||
cryptroot.device = "/dev/vdb";
|
||||
cryptroot2.device = "/dev/vdc";
|
||||
};
|
||||
virtualisation.bootDevice = "/dev/mapper/cryptroot";
|
||||
virtualisation.rootDevice = "/dev/mapper/cryptroot";
|
||||
# test mounting device unlocked in initrd after switching root
|
||||
virtualisation.fileSystems."/cryptroot2".device = "/dev/mapper/cryptroot2";
|
||||
};
|
||||
@@ -31,9 +31,9 @@ import ./make-test-python.nix ({ lib, pkgs, ... }: {
|
||||
testScript = ''
|
||||
# Create encrypted volume
|
||||
machine.wait_for_unit("multi-user.target")
|
||||
machine.succeed("echo -n supersecret | cryptsetup luksFormat -q --iter-time=1 /dev/vdb -")
|
||||
machine.succeed("echo -n supersecret | cryptsetup luksFormat -q --iter-time=1 /dev/vdc -")
|
||||
machine.succeed("echo -n supersecret | cryptsetup luksFormat -q --iter-time=1 /dev/vdd -")
|
||||
machine.succeed("echo -n supersecret | cryptsetup luksOpen -q /dev/vdd cryptroot2")
|
||||
machine.succeed("echo -n supersecret | cryptsetup luksOpen -q /dev/vdc cryptroot2")
|
||||
machine.succeed("mkfs.ext4 /dev/mapper/cryptroot2")
|
||||
|
||||
# Boot from the encrypted disk
|
||||
@@ -47,7 +47,7 @@ import ./make-test-python.nix ({ lib, pkgs, ... }: {
|
||||
machine.send_console("supersecret\n")
|
||||
machine.wait_for_unit("multi-user.target")
|
||||
|
||||
assert "/dev/mapper/cryptroot on / type ext4" in machine.succeed("mount")
|
||||
assert "/dev/mapper/cryptroot on / type ext4" in machine.succeed("mount"), "/dev/mapper/cryptroot do not appear in mountpoints list"
|
||||
assert "/dev/mapper/cryptroot2 on /cryptroot2 type ext4" in machine.succeed("mount")
|
||||
'';
|
||||
})
|
||||
|
||||
@@ -21,11 +21,11 @@ import ./make-test-python.nix ({ lib, pkgs, ... }: {
|
||||
specialisation.boot-luks.configuration = {
|
||||
boot.initrd.luks.devices = lib.mkVMOverride {
|
||||
cryptroot = {
|
||||
device = "/dev/vdc";
|
||||
device = "/dev/vdb";
|
||||
crypttabExtraOpts = [ "tpm2-device=auto" ];
|
||||
};
|
||||
};
|
||||
virtualisation.bootDevice = "/dev/mapper/cryptroot";
|
||||
virtualisation.rootDevice = "/dev/mapper/cryptroot";
|
||||
};
|
||||
};
|
||||
|
||||
@@ -55,8 +55,8 @@ import ./make-test-python.nix ({ lib, pkgs, ... }: {
|
||||
|
||||
# Create encrypted volume
|
||||
machine.wait_for_unit("multi-user.target")
|
||||
machine.succeed("echo -n supersecret | cryptsetup luksFormat -q --iter-time=1 /dev/vdc -")
|
||||
machine.succeed("PASSWORD=supersecret SYSTEMD_LOG_LEVEL=debug systemd-cryptenroll --tpm2-pcrs= --tpm2-device=auto /dev/vdc |& systemd-cat")
|
||||
machine.succeed("echo -n supersecret | cryptsetup luksFormat -q --iter-time=1 /dev/vdb -")
|
||||
machine.succeed("PASSWORD=supersecret SYSTEMD_LOG_LEVEL=debug systemd-cryptenroll --tpm2-pcrs= --tpm2-device=auto /dev/vdb |& systemd-cat")
|
||||
|
||||
# Boot from the encrypted disk
|
||||
machine.succeed("bootctl set-default nixos-generation-1-specialisation-boot-luks.conf")
|
||||
|
||||
@@ -20,18 +20,18 @@ import ./make-test-python.nix ({ lib, pkgs, ... }: {
|
||||
services.swraid = {
|
||||
enable = true;
|
||||
mdadmConf = ''
|
||||
ARRAY /dev/md0 devices=/dev/vdc,/dev/vdd
|
||||
ARRAY /dev/md0 devices=/dev/vdb,/dev/vdc
|
||||
'';
|
||||
};
|
||||
kernelModules = [ "raid0" ];
|
||||
};
|
||||
|
||||
specialisation.boot-swraid.configuration.virtualisation.bootDevice = "/dev/disk/by-label/testraid";
|
||||
specialisation.boot-swraid.configuration.virtualisation.rootDevice = "/dev/disk/by-label/testraid";
|
||||
};
|
||||
|
||||
testScript = ''
|
||||
# Create RAID
|
||||
machine.succeed("mdadm --create --force /dev/md0 -n 2 --level=raid0 /dev/vdc /dev/vdd")
|
||||
machine.succeed("mdadm --create --force /dev/md0 -n 2 --level=raid0 /dev/vdb /dev/vdc")
|
||||
machine.succeed("mkfs.ext4 -L testraid /dev/md0")
|
||||
machine.succeed("mkdir -p /mnt && mount /dev/md0 /mnt && echo hello > /mnt/test && umount /mnt")
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
, secureBoot ? false
|
||||
, httpSupport ? false
|
||||
, tpmSupport ? false
|
||||
, systemManagementModeSupport ? false
|
||||
}:
|
||||
|
||||
assert csmSupport -> seabios != null;
|
||||
@@ -41,6 +42,7 @@ edk2.mkDerivation projectDscPath (finalAttrs: {
|
||||
|
||||
buildFlags =
|
||||
lib.optionals secureBoot [ "-D SECURE_BOOT_ENABLE=TRUE" ]
|
||||
++ lib.optionals systemManagementModeSupport [ "-D SMM_REQUIRE" ]
|
||||
++ lib.optionals csmSupport [ "-D CSM_ENABLE" "-D FD_SIZE_2MB" ]
|
||||
++ lib.optionals httpSupport [ "-D NETWORK_HTTP_ENABLE=TRUE" "-D NETWORK_HTTP_BOOT_ENABLE=TRUE" ]
|
||||
++ lib.optionals tpmSupport [ "-D TPM_ENABLE" "-D TPM2_ENABLE" "-D TPM2_CONFIG_ENABLE"];
|
||||
|
||||
Reference in New Issue
Block a user