From 011dac1ce82ebeff7d774eaad18dc04c7496b42c Mon Sep 17 00:00:00 2001 From: Joel Granados Date: Tue, 27 May 2025 13:47:15 +0200 Subject: [PATCH 1/5] kern: Move image check to lib/qemu/kern; where it belongs Signed-off-by: Joel Granados --- cmd/run | 10 ---------- lib/qemu/kern | 17 +++++++++++++++++ 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/cmd/run b/cmd/run index 97241fd..f702c45 100644 --- a/cmd/run +++ b/cmd/run @@ -102,16 +102,6 @@ _run() { _load_vm - if [[ ! -f "${VMROOT}/$GUEST_BOOT" && ! -v do_print ]]; then - if [[ ! -f "${VMROOT}/$GUEST_BOOT_BASE" ]]; then - _fatal 1 "base image '${VMROOT}/${GUEST_BOOT_BASE}' does not exist" - fi - - _log "creating boot image ('$GUEST_BOOT') from base ('$GUEST_BOOT_BASE')" - _log_named "qemu-img create" "$QEMU_IMG" create -f qcow2 -b "$(basename "$GUEST_BOOT_BASE")" \ - -F qcow2 "${VMIMG}/$(basename "$GUEST_BOOT")" "$GUEST_BOOT_SIZE" - fi - if [[ -v do_gdb && -v do_background ]]; then _fatal 1 "--gdb cannot be used with --background" fi diff --git a/lib/qemu/kern b/lib/qemu/kern index 503aab7..ad629ff 100644 --- a/lib/qemu/kern +++ b/lib/qemu/kern @@ -11,6 +11,23 @@ _qemu_kern_noimg_add() { _qemu_kern_img_add() { local cmdline_extra="$1" + + for var in GUEST_BOOT GUEST_BOOT_BASE; do + if [[ ! -v ${var} ]]; then + _fatal 1 "${FUNCNAME[0]} Missing var ${var}. Add it to ${VMCONFIG}" + fi + done + + if [[ ! -f "${VMROOT}/$GUEST_BOOT" ]]; then + if [[ ! -f "${VMROOT}/$GUEST_BOOT_BASE" ]]; then + _fatal 1 "base image '${VMROOT}/${GUEST_BOOT_BASE}' does not exist" + fi + + _log "creating boot image ('$GUEST_BOOT') from base ('$GUEST_BOOT_BASE')" + _log_named "qemu-img create" "$QEMU_IMG" create -f qcow2 -b "$(basename "$GUEST_BOOT_BASE")" \ + -F qcow2 "${VMIMG}/$(basename "$GUEST_BOOT")" "$GUEST_BOOT_SIZE" + fi + if [[ ! -v GUEST_KERNEL_BOOTDEV ]]; then GUEST_KERNEL_BOOTDEV="/dev/vda1" fi From 4922cef8e0a507362a6a11e22115d2d24c41acf8 Mon Sep 17 00:00:00 2001 From: Joel Granados Date: Tue, 3 Jun 2025 21:17:11 +0200 Subject: [PATCH 2/5] run: Remove NoCloud datasource; cloud-init-seed is used Signed-off-by: Joel Granados --- cmd/run | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/cmd/run b/cmd/run index f702c45..81b087a 100644 --- a/cmd/run +++ b/cmd/run @@ -145,13 +145,7 @@ _run() { QEMU_PARAMS+=("-pidfile" "${VMROOT}/run/${VMNAME}/pidfile") if [[ -v kernel_dir ]]; then - local cmdline_extra="" - if [[ -v cloud_init_seed ]]; then - cmdline_extra="ci.datasource=NoCloud" - fi - - qemu_kern_add_custom --kernel-dir "${kernel_dir}" \ - --cmdline-extra "${cmdline_extra}" + qemu_kern_add_custom --kernel-dir "${kernel_dir}" fi if [[ -v do_background ]]; then From 4ffca9fc55c66e2ae963916bd2c487344db8d71c Mon Sep 17 00:00:00 2001 From: Joel Granados Date: Tue, 20 May 2025 18:22:53 +0200 Subject: [PATCH 3/5] nixvm: Add imageless nix VMs * Modify README to include info about image-less VMs * contrib/nix : New directory containing init exec source and nix default base system config. * x86_64-q35-noimgnix-base: New base configuration for nix imageless VMs * lib/qemu/nix: Functions needed to execute image-less VMs Signed-off-by: Joel Granados --- README.md | 121 +++++++++++++-------- common/rc | 8 ++ contrib/nix/Makefile | 21 ++++ contrib/nix/vmctl_init.c | 107 ++++++++++++++++++ contrib/nix/x86_64-noimgnix-base.nix | 76 +++++++++++++ examples/vm/x86_64-q35-noimgnix-base.conf | 59 ++++++++++ lib/qemu/kern | 7 +- lib/qemu/nix | 127 ++++++++++++++++++++++ 8 files changed, 476 insertions(+), 50 deletions(-) create mode 100644 contrib/nix/Makefile create mode 100644 contrib/nix/vmctl_init.c create mode 100644 contrib/nix/x86_64-noimgnix-base.nix create mode 100644 examples/vm/x86_64-q35-noimgnix-base.conf create mode 100644 lib/qemu/nix diff --git a/README.md b/README.md index ca58575..1b556a2 100644 --- a/README.md +++ b/README.md @@ -37,27 +37,20 @@ running. $ vmctl -c CONFIG ``` -4. Prepare a boot image. The base configruation `*-base.conf` will look for a - base image in `img/base.qcow2`. You can use [archbase][archbase] to build a - lean Arch Linux base image or grab a QCOW2-based [Ubuntu cloud - image][ubuntu-cloud-image] if that's your vice. +4. Pre-run prep: + `vmctl` supports two methods: + * Imageless: Requires nix infrastructure and is based on creating the + system through the `nix build` command. Goto [Prep nix](#Prep-nix) before + running. + * Image-Backed: This is the "usual" way of running QEMU where the OS is in + images (qcow) on your filesystem. Goto [Prep boot img](#Prep-boot-img) + before running. - In the case of a standard "cloud image", you probably want to resize it - since it is usually shrunk to be as small as possible by default. - - $ qemu-img resize img/base.qcow2 8G - - **Note** The example `nvme.conf` will define `GUEST_BOOT="img/nvme.qcow2"`. - You do not need to provide that image - if it is not there `$GUEST_BOOT` - will be a differential image backed by `img/base.qcow2`. So, if you ever - need to reset to the "base" state, just remove the `img/nvme.qcow2` image. 5. Start from an example and edit it as you see fit. $ edit $HOME/vms/nvme.conf -[archbase]: https://github.com/OpenMPDK/archbase -[ubuntu-cloud-image]: https://cloud-images.ubuntu.com ## Virtual Machine Configurations @@ -74,35 +67,6 @@ the `CONFIG` config file in interactive mode such that the VM serial output is sent to standard out. The QEMU monitor is multiplexed to standard out, so you can access it by issuing `Ctrl-a c`. -### cloud-init - -If your chosen base image is meant to be configured through [cloud-init][cloud-init], -you can use the included cloud-config helper script to generate a basic -cloud-init seed image: - - $ ./contrib/generate-cloud-config-seed.sh ~/.ssh/id_rsa.pub - -If the image is running freebsd, use the script with `-freebsd` suffix: - - $ ./contrib/generate-cloud-config-seed-freebsd.sh ~/.ssh/id_rsa.pub - -This will generate a simple cloud-init seed image that will set up the image -with a default `vmuser` account that can be logged into using the given public -key. Place the output image (`seed.img`) in `img/` and pass the `--cloud-init` -(short: `'-c'`) option to `vmctl run` to initialize the image on first boot: - - $ vmctl -c CONFIG run -c - -cloud-init will automatically power off the virtual machine when it has been -configured. - -NOTE: For the cloud-config helper script to work `cloud-utils` is required. - -[cloud-init]: https://cloudinit.readthedocs.io/en/latest/ - - -### SSH, Serial console and QEMU monitor - By default, `vmctl` will launch the guest such that the serial console and the QEMU monitor is multiplexed to standard out. This means that you will see the serial console output directly on the screen. @@ -117,6 +81,7 @@ the console and monitor using $ vmctl -c CONFIG console $ vmctl -c CONFIG monitor + ### Tracing The `--trace` (short: `-t`) option can be used to enable tracing inside QEMU. @@ -130,6 +95,7 @@ to IRQs, use `vmctl` inserts an implicit `*`-suffix such that all traces with the given prefix is traced. + ### Custom kernel Finally, the `--kernel-dir` (short: `-k`) can be used to point to a custom @@ -142,6 +108,73 @@ that configures the image to support this. In non-cloud-init settings, see `contrib/systemd` for a systemd service that should be usable on most distributions. +## Prep nix + +Imageless mode depends on nix commands and flake infrastrcture. If you are on +nixos, add the following text to your configuration file (usually located at +/etc/nixos/configuration.nix): + + ``` + nix = { + package = pkgs.nix; + extraOptions = '' + experimental-features = nix-command flakes + ''; + }; + ``` + +On the other hand, if you have installed nix on another distribution (like Debian), +add the following text to your configruation file (usually /etc/nix/nix.conf): + + experimental-features = nix-command flakes + + +## Prep boot img + +The base configruation `*-base.conf` will look for a base image in +`img/base.qcow2`. You can use [archbase][archbase] to build a lean Arch Linux +base image or grab a QCOW2-based [Ubuntu cloud image][ubuntu-cloud-image] if +that's your vice. + +In the case of a standard "cloud image", you probably want to resize it since +it is usually shrunk to be as small as possible by default. + + $ qemu-img resize img/base.qcow2 8G + +**Note** The example `nvme.conf` will define `GUEST_BOOT="img/nvme.qcow2"`. +You do not need to provide that image - if it is not there `$GUEST_BOOT` +will be a differential image backed by `img/base.qcow2`. So, if you ever +need to reset to the "base" state, just remove the `img/nvme.qcow2` image. + +[archbase]: https://github.com/OpenMPDK/archbase +[ubuntu-cloud-image]: https://cloud-images.ubuntu.com + +### cloud-init + +If your chosen base image is meant to be configured through [cloud-init][cloud-init], +you can use the included cloud-config helper script to generate a basic +cloud-init seed image: + + $ ./contrib/generate-cloud-config-seed.sh ~/.ssh/id_rsa.pub + +If the image is running freebsd, use the script with `-freebsd` suffix: + + $ ./contrib/generate-cloud-config-seed-freebsd.sh ~/.ssh/id_rsa.pub + +This will generate a simple cloud-init seed image that will set up the image +with a default `vmuser` account that can be logged into using the given public +key. Place the output image (`seed.img`) in `img/` and pass the `--cloud-init` +(short: `'-c'`) option to `vmctl run` to initialize the image on first boot: + + $ vmctl -c CONFIG run -c + +cloud-init will automatically power off the virtual machine when it has been +configured. + +**Note**: For the cloud-config helper script to work `cloud-utils` is required. + +[cloud-init]: https://cloudinit.readthedocs.io/en/latest/ + ## License diff --git a/common/rc b/common/rc index f5391df..3222a5a 100644 --- a/common/rc +++ b/common/rc @@ -129,6 +129,14 @@ _require_program() { fi } +_require_path() { + local rpath="$1"; shift + + if [[ ! -d "${rpath}" ]]; then + _fatal 1 "Path not found (${rpath}). $*" + fi +} + _is_running() { if [[ -f "${VMRUN}/pidfile" ]]; then return 0 diff --git a/contrib/nix/Makefile b/contrib/nix/Makefile new file mode 100644 index 0000000..b71cf1f --- /dev/null +++ b/contrib/nix/Makefile @@ -0,0 +1,21 @@ +CC := $(shell command -v musl-clang 2>/dev/null || echo gcc) +CFLAGS := -Os -static -ffunction-sections -fdata-sections +LDFLAGS := -Wl,--gc-sections +BUILDDIR ?= . +SRC := vmctl_init.c +OUT := $(BUILDDIR)/init +IMG := $(BUILDDIR)/init.img + +.PHONY: clean + +$(OUT): $(SRC) + @echo "Compiler: $(CC)" + $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< + +$(IMG): $(OUT) + find . -type f -name init -printf '%P\n' | cpio -ov -H newc > $(IMG) + +all: $(IMG) + +clean: + rm -f $(OUT) $(IMG) diff --git a/contrib/nix/vmctl_init.c b/contrib/nix/vmctl_init.c new file mode 100644 index 0000000..bdaffef --- /dev/null +++ b/contrib/nix/vmctl_init.c @@ -0,0 +1,107 @@ +#include +#include +#include +#include +#include + +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) + +#define VMCTL_INIT_VERBOSITY 0 +#if VMCTL_INIT_VERBOSITY +#include +#include + +char const * const indent_str = " "; +static void info(unsigned indent, char const *fmt, ...) { + while(0 != indent--) + fprintf(stdout, "%s", indent_str); + + va_list args; + va_start(args, fmt); + vfprintf(stdout, fmt, args); + va_end(args); + + fprintf(stdout, "\n"); +} +static void prnt_args(int argc, char *argv[]) { + info(1, "Arguments :\n"); + info(2, "argc = %d\n", argc); + info(2, "argv:\n"); + for (int i = 0; i < argc; i++) + info(3, "argv[%d] = %s\n", i, argv[i]); +} + +#else +static inline void info(unsigned indent, char const *fmt, ...) {} +static inline void prnt_args(int argc, char *argv[]) {} +#endif + +struct ArgOpt{ + const char *arg_str; + char **arg_val; +}; + +void parse_args(int const argc, char *argv[], struct ArgOpt *opts, int const opts_count) { + for (int i = 1; i < argc; i++) { + for (int j = 0; j < opts_count; j++) { + if (strcmp(argv[i], opts[j].arg_str) == 0 && i + 1 < argc) { + *opts[j].arg_val = argv[i + 1]; + i++; + break; + } + } + } +} + +int ensure_dir(const char *path) { + if (mkdir(path, 0755) == -1 && errno != EEXIST) { + info(1, "Error: creating the path (%s)", path); + return -1; + } + return 0; +} + +int mount_nix_store() { + int ret; + // This must match the tag used in virtiofsd + const char *source = "nixstore"; + const char *target = "/nix/store"; + + if (ensure_dir("/nix") || ensure_dir("/nix/store")) + return 1; + + if (mount(source, target, "virtiofs", 0, "") == -1) { + info(1, "Error: mounting source (%s) at target (%s)", source, target); + return 1; + } + + info(1, "Mounted %s at %s\n", source, target); + + return 0; +} + +int main(int argc, char *argv[]) { + int ret; + char *init_path = NULL; + struct ArgOpt opts[] = { + {"--init-path", &init_path}, + }; + + info(1, "VMCTL image-less INIT started ... \n"); + prnt_args(argc, argv); + + parse_args(argc, argv, opts, ARRAY_SIZE(opts)); + if (init_path == NULL) { + info(1, "Error: missing --init-path arg\n"); + return -1; + } + + if(mount_nix_store()) + return 1; + + info(1, "Attempting to exec: %s\n", init_path); + execv(init_path, argv); + info(1, "Error: execv failed"); + + return -1; +} diff --git a/contrib/nix/x86_64-noimgnix-base.nix b/contrib/nix/x86_64-noimgnix-base.nix new file mode 100644 index 0000000..5a41e91 --- /dev/null +++ b/contrib/nix/x86_64-noimgnix-base.nix @@ -0,0 +1,76 @@ +# SPDX-License-Identifier: GPL-3.0-or-later +# Copied from https://github.com/metaspace/run-kernel +# Original from Andreas Hindborg + +{ + description = "A system expression for vmctl"; + inputs = { nixpkgs.url = "nixpkgs/nixos-24.11"; }; + + outputs = { nixpkgs, ... }: { + nixosConfigurations.vm = nixpkgs.lib.nixosSystem { + system = "x86_64-linux"; + modules = [ + + ({ pkgs, lib, modulesPath, ... }: { + imports = [ (modulesPath + "/profiles/minimal.nix") ]; + options = { }; + config = { + # Silence nix warning + system.stateVersion = "24.11"; + + # Silence missing root warning + fileSystems."/" = lib.mkImageMediaOverride { + fsType = "tmpfs"; + options = [ "mode=0755" ]; + }; + + # Disable early boot remount + systemd.services.systemd-remount-fs.enable = lib.mkForce false; + + # Disable Grub + boot.loader.grub.enable = false; + + # Uncomment if kernel and initrd are unneeded and take too long + #boot.kernel.enable = false; + #boot.initrd.enable = false; + + networking.hostName = ""; + + # Ensure DNS resolv works + networking.networkmanager.enable = true; + networking.resolvconf.enable = true; + + # The system is static. + users.mutableUsers = false; + + # Empty password for root + users.users.root.initialHashedPassword = ""; + + # Log in root automatically + services.getty.autologinUser = "root"; + + # Disable the oom killer + systemd.oomd.enable = false; + + # Disable firewall + networking.firewall.enable = false; + + # The system cannot be rebuilt + nix.enable = false; + + # No logical volume management + services.lvm.enable = false; + + # Enable ssh and allow root login without password + services.sshd.enable = true; + services.openssh.settings.PermitRootLogin = "yes"; + services.openssh.settings.PermitEmptyPasswords = "yes"; + security.pam.services.sshd.allowNullPassword = true; + + environment.systemPackages = [ pkgs.coreutils pkgs.python3 pkgs.wget ]; + }; + }) + ]; + }; + }; +} diff --git a/examples/vm/x86_64-q35-noimgnix-base.conf b/examples/vm/x86_64-q35-noimgnix-base.conf new file mode 100644 index 0000000..4a43375 --- /dev/null +++ b/examples/vm/x86_64-q35-noimgnix-base.conf @@ -0,0 +1,59 @@ +#!/usr/bin/env bash +# Q35 x86_64 nix imageless configuration +# +# Requires the `nix` command and the `/nix/store` directory +# +# Configuration variables +# +# GUEST_DISPLAY Set to '1' to enable graphical output +# GUEST_CPU CPU model (QEMU -cpu paramater, default: 'host') +# GUEST_SMP SMP configuration (QEMU -smp parameter, default: '4') +# GUEST_MEMORY Guest RAM size (QEMU -m parameter, default: '8G') +# GUEST_NET_USER_HOSTFWDS List of ports to forward when using user-level networking +# (format: "[tcp|udp]:[hostaddr]:hostport-[guestaddr]:guestport", +# default: "tcp::2222-:22") +# NIX_INIT_REBUILD Force re-build nix init image. rebuild only when == "yes" + +if [[ -f "common.conf" ]]; then + source "common.conf" +fi + +: "${QEMU_SYSTEM_BINARY:="${QEMU_SYSTEM_X86_64}"}" +: "${NIX_STORE_PATH:="/nix/store"}" +: "${NIX_INIT_REBUILD:="no"}" +: "${GUEST_DISPLAY:="0"}" +: "${GUEST_CPU:="host"}" +: "${GUEST_SMP:="4"}" +: "${GUEST_MEMORY:="8G"}" +: "${GUEST_GDB_PORT:=""}" +: "${GUEST_NET_USER_HOSTFWDS:="tcp::${GUEST_SSH_PORT}-:22"}" +: "${GUEST_KERNEL_IMAGE:="arch/x86_64/boot/bzImage"}" +: "${GUEST_KERNEL_APPEND_EXTRA:="audit=0 earlyprintk=serial"}" +: "${GUEST_KERNEL_CONSOLE:="ttyS0,115200"}" + +_setup_x86_64_q35_noimgnix_base() { + + QEMU_PARAMS+=("-nodefaults") + + if [[ $GUEST_DISPLAY -eq 0 ]]; then + QEMU_PARAMS+=("-display" "none") + else + QEMU_PARAMS+=("-vga" "std") + fi + + QEMU_PARAMS+=("-machine" "q35,accel=kvm,kernel-irqchip=split") + QEMU_PARAMS+=("-cpu" "$GUEST_CPU") + QEMU_PARAMS+=("-smp" "$GUEST_SMP") + QEMU_PARAMS+=("-m" "$GUEST_MEMORY") + + if [[ -n $GUEST_GDB_PORT ]]; then + QEMU_PARAMS+=("-gdb" "tcp::$GUEST_GDB_PORT") + fi + + # simple user-level networking + QEMU_PARAMS+=("-netdev" "user,id=net0,hostfwd=${GUEST_NET_USER_HOSTFWDS}") + QEMU_PARAMS+=("-device" "virtio-net-pci,netdev=net0") + + # hw rng + QEMU_PARAMS+=("-device" "virtio-rng-pci") +} diff --git a/lib/qemu/kern b/lib/qemu/kern index ad629ff..333f012 100644 --- a/lib/qemu/kern +++ b/lib/qemu/kern @@ -4,11 +4,6 @@ # # Written by Joel Granados - -_qemu_kern_noimg_add() { - _fatal 1 "${FUNCNAME[0]} is not supported yet" -} - _qemu_kern_img_add() { local cmdline_extra="$1" @@ -84,7 +79,7 @@ qemu_kern_add_custom() { done if [[ -v NIX_STORE_PATH ]]; then - _qemu_kern_noimg_add + qemu_nix_add_noimgnix else _qemu_kern_img_add "${cmdline_extra}" fi diff --git a/lib/qemu/nix b/lib/qemu/nix new file mode 100644 index 0000000..b93658f --- /dev/null +++ b/lib/qemu/nix @@ -0,0 +1,127 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: GPL-3.0-or-later +# Copyright (c) 2025 Samsung Electronics Co., Ltd. All Rights Reserved. +# +# Written by Joel Granados + +_qemu_nix_create_nix_sys () { + local flake_path="$1" + local dst_path="$2" + local flake_attr_path="nixosConfigurations.vm.config.system.build.toplevel" + local flake_dst_dir="${VMSTATE}" + local flake_dst_path="${flake_dst_dir}/flake.nix" + + if ! cp -f "${flake_path}" "${flake_dst_path}"; then + _fatal 1 "${FUNCNAME[0]}: Error copying \ + src:(${flake_path}) dest:(${flake_dst_path})" + fi + + local nix_build_cmd="nix build \ + 'path:${flake_dst_dir}#${flake_attr_path}' \ + -o \"${dst_path}\" \ + --no-write-lock-file" + if ! eval "${nix_build_cmd}"; then + _fatal 1 "${FUNCNAME[0]}: Error running '${nix_build_cmd}'" + fi +} + +_qemu_nix_create_nix_init () { + local state_dir="$1" + local init_path="${state_dir}/init" + if [[ -f ${init_path} && -v NIX_INIT_REBUILD && ${NIX_INIT_REBUILD} != "yes" ]]; then + return + fi + + _require_program make + _require_program "$(command -v musl-clang 2> /dev/null || echo gcc)" + + local make_path="${state_dir}/Makefile" + if [[ ! -f ${make_path} ]]; then + if ! ln -s "${BASEDIR}/contrib/nix/Makefile" "${make_path}"; then + _fatal 1 "${FUNCNAME[0]}: Error creating sym link for ${make_path}" + fi + fi + + local c_init_path="${state_dir}/vmctl_init.c" + if [[ ! -f ${c_init_path} ]]; then + if ! ln -s "${BASEDIR}/contrib/nix/vmctl_init.c" "${c_init_path}"; then + _fatal 1 "${FUNCNAME[0]}: Error creating sym link for ${c_init_path}" + fi + fi + + if [[ ${NIX_INIT_REBUILD} == "yes" ]]; then + if ! make -C "${state_dir}" clean; then + _fatal 1 "${FUNCNAME[0]}: Error cleaning ${c_init_path}" + fi + fi + if ! make -C "${state_dir}" all; then + _fatal 1 "${FUNCNAME[0]}: Error compiling ${c_init_path}" + fi +} + +qemu_nix_add_noimgnix () { + _require_program nix + _require_path "${NIX_STORE_PATH}" + + local long="flake-path:,cmd-line-extra:" + + if ! tmp=$(getopt -o "" --long "$long" -n "${FUNCNAME[0]}" -- "$@"); then + exit 1 + fi + + eval set -- "$tmp" + unset tmp + + local cmd_line_extra="" + while true; do + case "$1" in + '--flake-path' ) + local flake_path="$2"; shift 2 + ;; + '--cmd-line-extra' ) + cmd_line_extra="$2"; shift 2 + ;; + '--' ) + shift; break + ;; + * ) + _fatal 1 "unknown argument '$1'" + ;; + esac + done + + if [[ ! -v VMSTATE ]]; then + _fatal 1 "${FUNCNAME[0]}: VMSTATE global var is not set" + fi + + if [[ ! -v flake_path ]]; then + local flake_path="${BASEDIR}/contrib/nix/x86_64-noimgnix-base.nix" + fi + + if [[ ! -f ${flake_path} ]]; then + _fatal 1 "${FUNCNAME[0]}: File not found (${flake_path})" + fi + + local dst_path="${VMSTATE}/nixsys" + if [[ -v NIX_SYS_BUILD_FORCE || ! -h ${dst_path} ]]; then + _qemu_nix_create_nix_sys "${flake_path}" "${dst_path}" + fi + + _qemu_nix_create_nix_init "${VMSTATE}" + + local nix_init_path="" + nix_init_path="$(readlink "${dst_path}")/init" + local cmd_line="root=/dev/ram0 console=${GUEST_KERNEL_CONSOLE}" + if [[ -v GUEST_KERNEL_APPEND_EXTRA ]]; then + cmd_line="${cmd_line} ${GUEST_KERNEL_APPEND_EXTRA}" + fi + cmd_line="${cmd_line} ${cmd_line_extra} -- --init-path ${nix_init_path}" + + QEMU_PARAMS+=("-kernel" "${kernel_dir}/${GUEST_KERNEL_IMAGE}") + QEMU_PARAMS+=("-append" "${cmd_line}") + QEMU_PARAMS+=("-initrd" "${VMSTATE}/init.img") + + qemu_share_add --shared-dir "${NIX_STORE_PATH}" \ + --tag "nixstore" \ + --share-type "viofs" +} From 66c7ce14098b104b6c98c5cfe4a2f8c6ea75d7b7 Mon Sep 17 00:00:00 2001 From: Joel Granados Date: Thu, 5 Jun 2025 13:03:38 +0200 Subject: [PATCH 4/5] nix: Control the Flake source path with NIX_FLAKE_PATH variable Signed-off-by: Joel Granados --- examples/vm/x86_64-q35-noimgnix-base.conf | 5 ++++ lib/qemu/nix | 30 +++++++++-------------- 2 files changed, 17 insertions(+), 18 deletions(-) diff --git a/examples/vm/x86_64-q35-noimgnix-base.conf b/examples/vm/x86_64-q35-noimgnix-base.conf index 4a43375..966014f 100644 --- a/examples/vm/x86_64-q35-noimgnix-base.conf +++ b/examples/vm/x86_64-q35-noimgnix-base.conf @@ -13,6 +13,9 @@ # (format: "[tcp|udp]:[hostaddr]:hostport-[guestaddr]:guestport", # default: "tcp::2222-:22") # NIX_INIT_REBUILD Force re-build nix init image. rebuild only when == "yes" +# NIX_STORE_PATH Set it if your store is != than /nix/store +# NIX_FLAKE_PATH Set it if you want a != sys than the default +# NIX_SYS_BUILD_FORCE Set it to "yes" when you want to force rebuild if [[ -f "common.conf" ]]; then source "common.conf" @@ -21,6 +24,8 @@ fi : "${QEMU_SYSTEM_BINARY:="${QEMU_SYSTEM_X86_64}"}" : "${NIX_STORE_PATH:="/nix/store"}" : "${NIX_INIT_REBUILD:="no"}" +: "${NIX_FLAKE_PATH:="${BASEDIR}/contrib/nix/x86_64-noimgnix-base.nix"}" +: "${NIX_SYS_BUILD_FORCE:="no"}" : "${GUEST_DISPLAY:="0"}" : "${GUEST_CPU:="host"}" : "${GUEST_SMP:="4"}" diff --git a/lib/qemu/nix b/lib/qemu/nix index b93658f..c52c74f 100644 --- a/lib/qemu/nix +++ b/lib/qemu/nix @@ -5,15 +5,19 @@ # Written by Joel Granados _qemu_nix_create_nix_sys () { - local flake_path="$1" - local dst_path="$2" + local dst_path="$1" local flake_attr_path="nixosConfigurations.vm.config.system.build.toplevel" local flake_dst_dir="${VMSTATE}" local flake_dst_path="${flake_dst_dir}/flake.nix" - if ! cp -f "${flake_path}" "${flake_dst_path}"; then + : "${NIX_FLAKE_PATH:="${BASEDIR}/contrib/nix/x86_64-noimgnix-base.nix"}" + if [[ ! -f ${NIX_FLAKE_PATH} ]]; then + _fatal 1 "${FUNCNAME[0]}: File not found (${NIX_FLAKE_PATH})" + fi + + if ! cp -f "${NIX_FLAKE_PATH}" "${flake_dst_path}"; then _fatal 1 "${FUNCNAME[0]}: Error copying \ - src:(${flake_path}) dest:(${flake_dst_path})" + src:(${NIX_FLAKE_PATH}) dest:(${flake_dst_path})" fi local nix_build_cmd="nix build \ @@ -63,7 +67,7 @@ qemu_nix_add_noimgnix () { _require_program nix _require_path "${NIX_STORE_PATH}" - local long="flake-path:,cmd-line-extra:" + local long="cmd-line-extra:" if ! tmp=$(getopt -o "" --long "$long" -n "${FUNCNAME[0]}" -- "$@"); then exit 1 @@ -75,9 +79,6 @@ qemu_nix_add_noimgnix () { local cmd_line_extra="" while true; do case "$1" in - '--flake-path' ) - local flake_path="$2"; shift 2 - ;; '--cmd-line-extra' ) cmd_line_extra="$2"; shift 2 ;; @@ -94,17 +95,10 @@ qemu_nix_add_noimgnix () { _fatal 1 "${FUNCNAME[0]}: VMSTATE global var is not set" fi - if [[ ! -v flake_path ]]; then - local flake_path="${BASEDIR}/contrib/nix/x86_64-noimgnix-base.nix" - fi - - if [[ ! -f ${flake_path} ]]; then - _fatal 1 "${FUNCNAME[0]}: File not found (${flake_path})" - fi - local dst_path="${VMSTATE}/nixsys" - if [[ -v NIX_SYS_BUILD_FORCE || ! -h ${dst_path} ]]; then - _qemu_nix_create_nix_sys "${flake_path}" "${dst_path}" + : "${NIX_SYS_BUILD_FORCE:="no"}" # defaults to "no" if not set + if [[ ${NIX_SYS_BUILD_FORCE} == "yes" || ! -h ${dst_path} ]]; then + _qemu_nix_create_nix_sys "${dst_path}" fi _qemu_nix_create_nix_init "${VMSTATE}" From 426d098a606dbd86f05ad2f37391208962de4625 Mon Sep 17 00:00:00 2001 From: Joel Granados Date: Fri, 6 Jun 2025 15:07:00 +0200 Subject: [PATCH 5/5] examples: Add other configs to the nvme starter example Signed-off-by: Joel Granados --- examples/vm/nvme.conf | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/examples/vm/nvme.conf b/examples/vm/nvme.conf index 495d4f5..c51db08 100644 --- a/examples/vm/nvme.conf +++ b/examples/vm/nvme.conf @@ -3,11 +3,21 @@ QEMU_SYSTEM_X86_64=$(/usr/bin/which qemu-system-x86_64) source "x86_64-q35-base.conf" +#source "x86_64-q35-noimgnix-base.conf" +#source "aarch64-virt-base.conf" _setup_nvme() { # setup basevm + + # For x86_64-q35-base.conf _setup_x86_64_q35_base + # Uncomment for x86_64-q35-noimgnix-base.conf + #_setup_x86_64_q35_noimgnix_base + + # Uncomment for aarch64-virt-base.conf + #_setup_aarch64_virt_base + # pcie root port qemu_pcie_add_root_port "pcie_root_port0" \ --chassis 1 --slot 0