Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package virtme for openSUSE:Factory checked in at 2024-10-20 10:05:08 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/virtme (Old) and /work/SRC/openSUSE:Factory/.virtme.new.26871 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Package is "virtme" Sun Oct 20 10:05:08 2024 rev:21 rq:1208978 version:1.31 Changes: -------- --- /work/SRC/openSUSE:Factory/virtme/virtme.changes 2024-09-24 17:35:19.374219907 +0200 +++ /work/SRC/openSUSE:Factory/.virtme.new.26871/virtme.changes 2024-10-20 10:05:19.198074429 +0200 @@ -1,0 +2,21 @@ +Sat Oct 19 07:57:42 UTC 2024 - Michael Vetter <mvetter@suse.com> + +- Update to 1.31: + * Fix a packaging issue, after an attempt to modernize the build + system we realized that we were not installing the bash + completion file anymore, so we have temporarily reverted the + change to cut this new release. + See gh/arighi/virtme-ng#181 + * Initial support to run virtme-ng on macOS + +------------------------------------------------------------------- +Tue Oct 15 09:06:54 UTC 2024 - Michael Vetter <mvetter@suse.com> + +- Update to 1.30: + * Initial support for NVIDIA GPUs passthrough + * Possibility to use pre-compiled -rc kernels from Ubuntu mainline builds + * Possibility to use virtiofs natively on arm64 + * Some improvements to run virtme-ng cross-architecture and cross-distro + * Bug fixes + +------------------------------------------------------------------- Old: ---- virtme-ng-1.29.tar.xz New: ---- virtme-ng-1.31.tar.xz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ virtme.spec ++++++ --- /var/tmp/diff_new_pack.B6bwJA/_old 2024-10-20 10:05:20.834136212 +0200 +++ /var/tmp/diff_new_pack.B6bwJA/_new 2024-10-20 10:05:20.838136363 +0200 @@ -22,7 +22,7 @@ %global pythons python311 %endif Name: virtme -Version: 1.29 +Version: 1.31 Release: 0 Summary: Tools for virtualize the running distro or a rootfs License: GPL-2.0-only ++++++ _service ++++++ --- /var/tmp/diff_new_pack.B6bwJA/_old 2024-10-20 10:05:21.130147390 +0200 +++ /var/tmp/diff_new_pack.B6bwJA/_new 2024-10-20 10:05:21.186149505 +0200 @@ -3,7 +3,7 @@ <param name="url">https://github.com/arighi/virtme-ng.git</param> <param name="scm">git</param> <param name="submodules">enable</param> - <param name="revision">v1.29</param> + <param name="revision">v1.31</param> <param name="versionformat">@PARENT_TAG@</param> <param name="versionrewrite-pattern">v(.*)</param> <param name="versionrewrite-replacement">\1</param> ++++++ virtme-ng-1.29.tar.xz -> virtme-ng-1.31.tar.xz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/virtme-ng-1.29/Makefile new/virtme-ng-1.31/Makefile --- old/virtme-ng-1.29/Makefile 2024-09-11 08:38:45.000000000 +0200 +++ new/virtme-ng-1.31/Makefile 2024-10-18 15:30:58.000000000 +0200 @@ -3,10 +3,6 @@ # Get git version information for make install GIT_DESCRIBE := $(shell git describe --always --long --dirty) -.PHONY: init -init: - cd virtme_ng_init && cargo install --path . --root ../virtme/guest - # see README.md '* Install from source' install: install_from_source install_from_source: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/virtme-ng-1.29/README.md new/virtme-ng-1.31/README.md --- old/virtme-ng-1.29/README.md 2024-09-11 08:38:45.000000000 +0200 +++ new/virtme-ng-1.31/README.md 2024-10-18 15:30:58.000000000 +0200 @@ -358,6 +358,26 @@ See also: `.github/workflows/run.yml` as a practical example on how to use virtme-ng inside docker. + - Run virtme-ng with gpu passthrough: +``` + # Confirm host kernel has VFIO and IOMMU support + # Check if NVIDIA module is installed on the host + $ modinfo nvidia + # If the nvidia module is installed, blacklist the nvidia modules + $ sudo bash -c 'echo -e "blacklist nvidia\nblacklist nvidia-drm\nblacklist nvidia-modeset\nblacklist nvidia-peermem\nblacklist nvidia-uvm" > /etc/modprobe.d/blacklist-nvidia.conf' + # Host will need to be rebooted for blacklist to take effect. + # Get GPU device ID + $ lspci -nn | grep NVIDIA + 0000:01:00.0 VGA compatible controller [0300]: NVIDIA Corporation AD104GLM [RTX 3500 Ada Generation Laptop GPU] [10de:27bb] (rev a1) + 0000:01:00.1 Audio device [0403]: NVIDIA Corporation Device [10de:22bc] (rev a1)) + # Configure VFIO for device passthrough + $ sudo bash -c 'options vfio-pci ids=10de:27bb,10de:22bc' > /etc/modprobe.d/vfio.conf + # Load VFIO module + $ sudo modprobe vfio-pci + # Pass PCI address to virtme-ng + $ sudo vng --nvgpu "01:00.0" -r linux +``` + Implementation details ====================== @@ -499,11 +519,13 @@ - [LWN: Faster kernel testing with virtme-ng (November, 2023)](https://lwn.net/Articles/951313/) - [LPC 2023: Speeding up Kernel Testing and Debugging with virtme-ng](https://lpc.events/event/17/contributions/1506/attachments/1143/2441/virtme-...) + - [Kernel Recipes 2024: virtme-ng](https://kernel-recipes.org/en/2024/virtme-ng/) + - [Linux Foundation Mentorship Session: Speeding Up Kernel Development With virtme-ng](https://www.youtube.com/watch?v=ZgMLGM2UazY) Credits ======= -virtme-ng is written by Andrea Righi <andrea.righi@canonical.com> +virtme-ng is written by Andrea Righi <arighi@nvidia.com> virtme-ng is based on virtme, written by Andy Lutomirski <luto@kernel.org> ([web][korg-web] | [git][korg-git]). diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/virtme-ng-1.29/requirements.txt new/virtme-ng-1.31/requirements.txt --- old/virtme-ng-1.29/requirements.txt 2024-09-11 08:38:45.000000000 +0200 +++ new/virtme-ng-1.31/requirements.txt 2024-10-18 15:30:58.000000000 +0200 @@ -1,3 +1,5 @@ argcomplete argparse-manpage requests +setuptools + diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/virtme-ng-1.29/setup.py new/virtme-ng-1.31/setup.py --- old/virtme-ng-1.29/setup.py 2024-09-11 08:38:45.000000000 +0200 +++ new/virtme-ng-1.31/setup.py 2024-10-18 15:30:58.000000000 +0200 @@ -77,7 +77,7 @@ argparse-manpage \ --pyfile ./virtme_ng/run.py --function make_parser \ --prog vng --version v{VERSION} \ - --author "virtme-ng is written by Andrea Righi <andrea.righi@canonical.com>" \ + --author "virtme-ng is written by Andrea Righi <arighi@nvidia.com>" \ --author "Based on virtme by Andy Lutomirski <luto@kernel.org>" \ --project-name virtme-ng --manual-title virtme-ng \ --description "Quickly run kernels inside a virtualized snapshot of your live system" \ @@ -90,10 +90,22 @@ print(f"BUILD_VIRTME_NG_INIT: {build_virtme_ng_init}") # Build virtme-ng-init if build_virtme_ng_init: - check_call(["make", "init"]) + cwd = "virtme_ng_init" + root = "../virtme/guest" + args = ["cargo", "install", "--path", ".", "--root", root] + if platform.system() == "Darwin": + machine = platform.machine() + if machine == "arm64": + machine = "aarch64" + target = f"{machine}-unknown-linux-musl" + args.extend([ + "--target", target, + "--config", f"target.{target}.linker = \"rust-lld\"", + ]) + check_call(args, cwd="virtme_ng_init") check_call( - ["strip", "-s", "../virtme/guest/bin/virtme-ng-init"], - cwd="virtme_ng_init", + ["strip", os.path.join(root, "bin", "virtme-ng-init")], + cwd=cwd, ) # Generate manpage if which('argparse-manpage'): @@ -165,7 +177,7 @@ name="virtme-ng", version=VERSION, author="Andrea Righi", - author_email="andrea.righi@canonical.com", + author_email="arighi@nvidia.com", description="Build and run a kernel inside a virtualized snapshot of your live system", url="https://git.launchpad.net/~arighi/+git/virtme-ng", license="GPLv2", diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/virtme-ng-1.29/virtme/architectures.py new/virtme-ng-1.31/virtme/architectures.py --- old/virtme-ng-1.29/virtme/architectures.py 2024-09-11 08:38:45.000000000 +0200 +++ new/virtme-ng-1.31/virtme/architectures.py 2024-10-18 15:30:58.000000000 +0200 @@ -23,9 +23,10 @@ return False @staticmethod - def qemuargs(is_native, use_kvm) -> List[str]: + def qemuargs(is_native, use_kvm, use_gpu) -> List[str]: _ = is_native _ = use_kvm + _ = use_gpu return [] @staticmethod @@ -51,6 +52,10 @@ return ["-vga", "none", "-display", "none"] @staticmethod + def qemu_nodisplay_nvgpu_args() -> List[str]: + return ["-display", "none"] + + @staticmethod def qemu_display_args() -> List[str]: return ["-device", "virtio-gpu-pci"] @@ -71,6 +76,9 @@ def kimg_path(self) -> str: return "arch/%s/boot/bzImage" % self.linuxname + def img_name(self) -> str: + return "vmlinuz" + @staticmethod def dtb_path() -> Optional[str]: return None @@ -78,8 +86,8 @@ class Arch_unknown(Arch): @staticmethod - def qemuargs(is_native, use_kvm): - return Arch.qemuargs(is_native, use_kvm) + def qemuargs(is_native, use_kvm, use_gpu): + return Arch.qemuargs(is_native, use_kvm, use_gpu) class Arch_x86(Arch): @@ -94,8 +102,8 @@ return True @staticmethod - def qemuargs(is_native, use_kvm): - ret = Arch.qemuargs(is_native, use_kvm) + def qemuargs(is_native, use_kvm, use_gpu): + ret = Arch.qemuargs(is_native, use_kvm, use_gpu) # Add a watchdog. This is useful for testing. ret.extend(["-device", "i6300esb,id=watchdog0"]) @@ -103,7 +111,10 @@ if is_native and use_kvm: # If we're likely to use KVM, request a full-featured CPU. # (NB: if KVM fails, this will cause problems. We should probe.) - ret.extend(["-cpu", "host"]) # We can't migrate regardless. + cpu_str = "host" + if use_gpu: + cpu_str += ",host-phys-bits-limit=0x28" + ret.extend(["-cpu", cpu_str]) else: ret.extend(["-machine", "q35"]) @@ -179,8 +190,8 @@ ] @staticmethod - def qemuargs(is_native, use_kvm): - ret = Arch.qemuargs(is_native, use_kvm) + def qemuargs(is_native, use_kvm, use_gpu): + ret = Arch.qemuargs(is_native, use_kvm, use_gpu) # Use microvm architecture for faster boot ret.extend(["-M", "microvm,accel=kvm,pcie=on,rtc=on"]) @@ -200,8 +211,8 @@ self.defconfig_target = "vexpress_defconfig" @staticmethod - def qemuargs(is_native, use_kvm): - ret = Arch.qemuargs(is_native, use_kvm) + def qemuargs(is_native, use_kvm, use_gpu): + ret = Arch.qemuargs(is_native, use_kvm, use_gpu) # Emulate a vexpress-a15. ret.extend(["-M", "vexpress-a15"]) @@ -242,18 +253,22 @@ class Arch_aarch64(Arch): - def __init__(self): - Arch.__init__(self, "aarch64") + def __init__(self, name): + Arch.__init__(self, name) self.qemuname = "aarch64" self.linuxname = "arm64" self.gccname = "aarch64" @staticmethod - def qemuargs(is_native, use_kvm): - ret = Arch.qemuargs(is_native, use_kvm) + def virtiofs_support() -> bool: + return True + + @staticmethod + def qemuargs(is_native, use_kvm, use_gpu): + ret = Arch.qemuargs(is_native, use_kvm, use_gpu) - if is_native: + if is_native and use_kvm: ret.extend(["-M", "virt,gic-version=host"]) ret.extend(["-cpu", "host"]) else: @@ -292,8 +307,8 @@ self.gccname = "powerpc64le" @staticmethod - def qemuargs(is_native, use_kvm): - ret = Arch.qemuargs(is_native, use_kvm) + def qemuargs(is_native, use_kvm, use_gpu): + ret = Arch.qemuargs(is_native, use_kvm, use_gpu) ret.extend(["-M", "pseries"]) return ret @@ -312,23 +327,24 @@ # Apparently SLOF (QEMU's bundled firmware?) can't boot a zImage. return "vmlinux" + def img_name(self) -> str: + return "vmlinux" + class Arch_riscv64(Arch): def __init__(self): Arch.__init__(self, "riscv64") self.defconfig_target = "defconfig" - self.qemuname = "riscv64" self.linuxname = "riscv" - self.gccname = "riscv64" @staticmethod def virtiofs_support() -> bool: return True @staticmethod - def qemuargs(is_native, use_kvm): - ret = Arch.qemuargs(is_native, use_kvm) + def qemuargs(is_native, use_kvm, use_gpu): + ret = Arch.qemuargs(is_native, use_kvm, use_gpu) ret.extend(["-machine", "virt"]) ret.extend(["-bios", "default"]) @@ -347,13 +363,11 @@ Arch.__init__(self, "sparc64") self.defconfig_target = "sparc64_defconfig" - self.qemuname = "sparc64" self.linuxname = "sparc" - self.gccname = "sparc64" @staticmethod - def qemuargs(is_native, use_kvm): - return Arch.qemuargs(is_native, use_kvm) + def qemuargs(is_native, use_kvm, use_gpu): + return Arch.qemuargs(is_native, use_kvm, use_gpu) def kimg_path(self): return "arch/sparc/boot/image" @@ -368,17 +382,15 @@ def __init__(self): Arch.__init__(self, "s390x") - self.qemuname = "s390x" self.linuxname = "s390" - self.gccname = "s390x" @staticmethod def virtio_dev_type(virtiotype): return "virtio-%s-ccw" % virtiotype @staticmethod - def qemuargs(is_native, use_kvm): - ret = Arch.qemuargs(is_native, use_kvm) + def qemuargs(is_native, use_kvm, use_gpu): + ret = Arch.qemuargs(is_native, use_kvm, use_gpu) # Ask for the latest version of s390-ccw ret.extend(["-M", "s390-ccw-virtio"]) @@ -401,6 +413,9 @@ def qemu_serial_console_args(): return ["-device", "sclpconsole,chardev=console"] + def img_name(self) -> str: + return "image" + ARCHES = { arch.virtmename: arch @@ -409,7 +424,8 @@ Arch_x86("x86_64"), Arch_x86("i386"), Arch_arm(), - Arch_aarch64(), + Arch_aarch64("aarch64"), + Arch_aarch64("arm64"), Arch_ppc("ppc64"), Arch_ppc("ppc64le"), Arch_riscv64(), diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/virtme-ng-1.29/virtme/commands/configkernel.py new/virtme-ng-1.31/virtme/commands/configkernel.py --- old/virtme-ng-1.29/virtme/commands/configkernel.py 2024-09-11 08:38:45.000000000 +0200 +++ new/virtme-ng-1.31/virtme/commands/configkernel.py 2024-10-18 15:30:58.000000000 +0200 @@ -10,12 +10,13 @@ import sys import argparse import os +import platform import shlex import shutil import subprocess import multiprocessing from .. import architectures -from ..util import SilentError, uname +from ..util import SilentError def make_parser(): @@ -27,7 +28,7 @@ "--arch", action="store", metavar="ARCHITECTURE", - default=uname.machine, + default=platform.machine(), help="Target architecture", ) @@ -285,6 +286,7 @@ args = _ARGPARSER.parse_args() arch = architectures.get(args.arch) + is_native = args.arch == platform.machine() custom_conf = [] if args.custom: @@ -325,7 +327,7 @@ if args.cross_compile != "": cross_compile_prefix = args.cross_compile - if shutil.which(f"{cross_compile_prefix}-gcc") and arch.gccname != uname.machine: + if not is_native and shutil.which(f"{cross_compile_prefix}-gcc"): gccname = shlex.quote(f"{cross_compile_prefix}-gcc") archargs.append(f"CROSS_COMPILE={gccname}") diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/virtme-ng-1.29/virtme/commands/run.py new/virtme-ng-1.31/virtme/commands/run.py --- old/virtme-ng-1.29/virtme/commands/run.py 2024-09-11 08:38:45.000000000 +0200 +++ new/virtme-ng-1.31/virtme/commands/run.py 2024-10-18 15:30:58.000000000 +0200 @@ -11,6 +11,7 @@ import argparse import tempfile import os +import platform import errno import fcntl import sys @@ -29,7 +30,7 @@ from .. import qemu_helpers from .. import architectures from .. import resources -from ..util import SilentError, uname, get_username, find_binary_or_raise +from ..util import SilentError, get_username, find_binary_or_raise def make_parser() -> argparse.ArgumentParser: @@ -46,7 +47,7 @@ "--installed-kernel", action="store", nargs="?", - const=uname.release, + const=platform.release(), default=None, metavar="VERSION", help="[Deprecated] use --kimg instead.", @@ -56,7 +57,7 @@ "--kimg", action="store", nargs="?", - const=uname.release, + const=platform.release(), default=None, help="Use specified kernel image or an installed kernel version. " + "If no argument is specified the running kernel will be used.", @@ -74,7 +75,7 @@ "--mods", action="store", choices=["none", "use", "auto"], - default="use", + required=True, help="Setup loadable kernel modules inside a compiled kernel source directory " + "(used in conjunction with --kdir); " + "none: ignore kernel modules, use: asks user to refresh virtme's kernel modules directory, " @@ -201,7 +202,7 @@ "--arch", action="store", metavar="ARCHITECTURE", - default=uname.machine, + default=platform.machine(), help="Guest architecture", ) g.add_argument( @@ -312,6 +313,9 @@ help="Supply a directory that is r/w to the guest but read-only in the host. Use --overlay-rwdir=path.", ) + g.add_argument( + "--nvgpu", action="store", default=None, help="Set guest NVIDIA GPU." + ) return parser @@ -368,7 +372,7 @@ return os.path.abspath(path) -def get_kernel_version(path): +def get_kernel_version(img_name, path): if not os.path.exists(path): arg_fail( "kernel file %s does not exist, try --build to build the kernel" % path @@ -380,7 +384,7 @@ ["file", path], capture_output=True, text=True, check=False ) for item in result.stdout.split(", "): - match = re.search(r"^[vV]ersion (\S+)", item) + match = re.search(r"^[vV]ersion (\S{3,})", item) if match: kernel_version = match.group(1) return kernel_version @@ -393,10 +397,18 @@ result = subprocess.run( ["strings", path], capture_output=True, text=True, check=False ) - match = re.search(r"Linux version (\S+)", result.stdout) + match = re.search(r"Linux version (\S{3,})", result.stdout) if match: kernel_version = match.group(1) return kernel_version + + # The version detection fails s390x using file or strings tools, so check + # if the file itself contains the version number. + if img_name: + match = re.search(fr"{img_name}-(\S{{3,}})", path) + if match: + return match.group(1) + return None @@ -428,16 +440,18 @@ args.kimg = None if args.kimg is not None: + img_name = arch.img_name() + # Try to resolve kimg as a kernel version first, then check if a file # is provided. - kimg = "/usr/lib/modules/%s/vmlinuz" % args.kimg + kimg = "/usr/lib/modules/%s/%s" % (args.kimg, img_name) if not os.path.exists(kimg): - kimg = "/boot/vmlinuz-%s" % args.kimg + kimg = "/boot/%s-%s" % (img_name, args.kimg) if not os.path.exists(kimg): kimg = args.kimg if not os.path.exists(kimg): arg_fail("%s does not exist" % args.kimg) - kver = get_kernel_version(kimg) + kver = get_kernel_version(img_name, kimg) if kver is None: # Unable to detect kernel version, try to boot without # automatically detecting modules. @@ -484,7 +498,7 @@ elif args.kdir is not None: kimg = os.path.join(args.kdir, arch.kimg_path()) # Run get_kernel_version to check at least if the kernel image exist. - kernel.version = get_kernel_version(kimg) + kernel.version = get_kernel_version(None, kimg) kernel.kimg = kimg virtme_mods = os.path.join(args.kdir, ".virtme_mods") mod_file = os.path.join(args.kdir, "modules.order") @@ -573,7 +587,7 @@ def _get_virtiofsd_path(self): # Define the possible virtiofsd paths. # - # NOTE: do not use the C implemention of qemu's virtiofsd, because it + # NOTE: do not use the C implementation of qemu's virtiofsd, because it # doesn't support unprivileged-mode execution and it would be totally # unsafe to export the whole rootfs of the host running as root. # @@ -652,48 +666,59 @@ return True +class VirtioFSConfig: + def __init__(self, path: str, mount_tag: str, guest_tools_path=None, memory=None): + self.path = path + self.mount_tag = mount_tag + self.guest_tools_path = guest_tools_path + self.memory = memory + + def export_virtiofs( arch: architectures.Arch, qemuargs: List[str], - path: str, - mount_tag: str, - guest_tools_path=None, - memory=None, + config: VirtioFSConfig, verbose=False, -) -> None: +) -> bool: if not arch.virtiofs_support(): return False - # Try to start virtiofsd deamon - virtio_fs = VirtioFS(guest_tools_path) - ret = virtio_fs.start(path, verbose) + # Try to start virtiofsd daemon + virtio_fs = VirtioFS(config.guest_tools_path) + ret = virtio_fs.start(config.path, verbose) if not ret: return False # Adjust qemu options to use virtiofsd fsid = "virtfs%d" % len(qemuargs) - vhost_dev_type = arch.vhost_dev_type() + qemuargs.extend(["-chardev", f"socket,id=char{fsid},path={virtio_fs.sock}"]) - qemuargs.extend(["-device", f"{vhost_dev_type},chardev=char{fsid},tag={mount_tag}"]) - if memory is None: - memory = "128M" - elif memory == 0: + qemuargs.extend(["-device", f"{vhost_dev_type},chardev=char{fsid},tag={config.mount_tag}"]) + + memory = config.memory if config.memory is not None else "128M" + if memory == 0: return True + qemuargs.extend(["-object", f"memory-backend-memfd,id=mem,size={memory},share=on"]) qemuargs.extend(["-numa", "node,memdev=mem"]) return True +class VirtFSConfig: + def __init__(self, path: str, mount_tag: str, security_model="none", readonly=True): + self.path = path + self.mount_tag = mount_tag + self.security_model = security_model + self.readonly = readonly + + def export_virtfs( qemu: qemu_helpers.Qemu, arch: architectures.Arch, qemuargs: List[str], - path: str, - mount_tag: str, - security_model="none", - readonly=True, + config: VirtFSConfig, ) -> None: # NB: We can't use -virtfs for this, because it can't handle a mount_tag # that isn't a valid QEMU identifier. @@ -704,9 +729,9 @@ "local,id=%s,path=%s,security_model=%s%s%s" % ( fsid, - qemu.quote_optarg(path), - security_model, - ",readonly=on" if readonly else "", + qemu.quote_optarg(config.path), + config.security_model, + ",readonly=on" if config.readonly else "", ",multidevs=remap" if qemu.has_multidevs else "", ), ] @@ -715,7 +740,7 @@ [ "-device", "%s,fsdev=%s,mount_tag=%s" - % (arch.virtio_dev_type("9p"), fsid, qemu.quote_optarg(mount_tag)), + % (arch.virtio_dev_type("9p"), fsid, qemu.quote_optarg(config.mount_tag)), ] ) @@ -817,7 +842,7 @@ args = _ARGPARSER.parse_args() arch = architectures.get(args.arch) - is_native = args.arch == uname.machine + is_native = args.arch == platform.machine() qemu = qemu_helpers.Qemu(args.qemu_bin, arch.qemuname) qemu.probe() @@ -864,9 +889,12 @@ # Propagate /proc/sys/fs/nr_open from the host to the guest, otherwise we # may see some EPERM errors, because certain applications/settings may # expect to be able to use a higher limit of the max number of open files. - with open('/proc/sys/fs/nr_open', 'r', encoding="utf-8") as file: - nr_open = file.readline().strip() - kernelargs.append(f"nr_open={nr_open}") + try: + with open('/proc/sys/fs/nr_open', 'r', encoding="utf-8") as file: + nr_open = file.readline().strip() + kernelargs.append(f"nr_open={nr_open}") + except FileNotFoundError: + pass # Parse NUMA settings. if args.numa: @@ -926,16 +954,19 @@ virt_arch = architectures.get("microvm") else: virt_arch = arch - use_virtiofs = export_virtiofs( - virt_arch, - qemuargs, - args.root, - "ROOTFS", + virtiofs_config = VirtioFSConfig( + path=args.root, + mount_tag="ROOTFS", guest_tools_path=guest_tools_path, # virtiofsd requires a NUMA not, if --numa is specified simply use # the user-defined NUMA node, otherwise create a NUMA node with all # the memory. memory=0 if args.numa else args.memory, + ) + use_virtiofs = export_virtiofs( + virt_arch, + qemuargs, + virtiofs_config, verbose=args.verbose, ) if can_use_microvm(args) and use_virtiofs: @@ -943,9 +974,12 @@ sys.stderr.write("virtme: use 'microvm' QEMU architecture\n") arch = virt_arch if not use_virtiofs: - export_virtfs( - qemu, arch, qemuargs, args.root, "/dev/root", readonly=(not args.rw) + virtfs_config = VirtFSConfig( + path=args.root, + mount_tag="/dev/root", + readonly=(not args.rw), ) + export_virtfs(qemu, arch, qemuargs, virtfs_config) # Use the faster virtme-ng-init if we are running on a native architecture. if ( @@ -960,7 +994,11 @@ if args.root == "/": initcmds = [f"init={guest_tools_path}/{virtme_init_cmd}"] else: - export_virtfs(qemu, arch, qemuargs, guest_tools_path, "virtme.guesttools") + virtfs_config = VirtFSConfig( + path=guest_tools_path, + mount_tag="virtme.guesttools", + ) + export_virtfs(qemu, arch, qemuargs, virtfs_config) initcmds = [ "init=/bin/sh", "--", @@ -1027,9 +1065,12 @@ idx = mount_index mount_index += 1 tag = "virtme.initmount%d" % idx - export_virtfs( - qemu, arch, qemuargs, hostpath, tag, readonly=(dirtype != "rwdir") + virtfs_config = VirtFSConfig( + path=hostpath, + mount_tag=tag, + readonly=(dirtype != "rwdir"), ) + export_virtfs(qemu, arch, qemuargs, virtfs_config) kernelargs.append("virtme_initmount%d=%s" % (idx, guestpath)) for i, d in enumerate(args.overlay_rwdir): @@ -1037,11 +1078,14 @@ # Turn on KVM if available kvm_ok = can_use_kvm(args) - if is_native and kvm_ok: - qemuargs.extend(["-machine", "accel=kvm:tcg"]) + if is_native: + if kvm_ok: + qemuargs.extend(["-machine", "accel=kvm:tcg"]) + elif platform.system() == "Darwin": + qemuargs.extend(["-machine", "accel=hvf"]) # Add architecture-specific options - qemuargs.extend(arch.qemuargs(is_native, kvm_ok)) + qemuargs.extend(arch.qemuargs(is_native, kvm_ok, args.nvgpu is not None)) # Set up / override baseline devices qemuargs.extend(["-parallel", "none"]) @@ -1077,7 +1121,10 @@ kernelargs.extend(["virtme_console=" + arg for arg in arch.serial_console_args()]) - qemuargs.extend(arch.qemu_nodisplay_args()) + if args.nvgpu is None: + qemuargs.extend(arch.qemu_nodisplay_args()) + else: + qemuargs.extend(arch.qemu_nodisplay_nvgpu_args()) # PS/2 probing is slow; give the kernel a hint to speed it up. kernelargs.extend(["psmouse.proto=exps"]) @@ -1162,7 +1209,10 @@ def do_script(shellcmd: str, ret_path=None, show_boot_console=False) -> None: if args.graphics is None: # Turn off default I/O - qemuargs.extend(arch.qemu_nodisplay_args()) + if args.nvgpu is None: + qemuargs.extend(arch.qemu_nodisplay_args()) + else: + qemuargs.extend(arch.qemu_nodisplay_nvgpu_args()) # Check if we can redirect stdin/stdout/stderr. if not can_access_file("/proc/self/fd/0") or \ @@ -1275,7 +1325,7 @@ show_boot_console=args.show_boot_console, ) - if args.graphics is not None: + if args.graphics is not None and args.nvgpu is None: video_args = arch.qemu_display_args() if video_args: qemuargs.extend(video_args) @@ -1329,6 +1379,9 @@ if args.user: kernelargs.append("virtme_user=%s" % args.user) + if args.nvgpu: + qemuargs.extend(["-device", args.nvgpu]) + # If we are running as root on the host pass this information to the guest # (this can be useful to properly support running virtme-ng instances # inside docker) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/virtme-ng-1.29/virtme/cpiowriter.py new/virtme-ng-1.31/virtme/cpiowriter.py --- old/virtme-ng-1.29/virtme/cpiowriter.py 2024-09-11 08:38:45.000000000 +0200 +++ new/virtme-ng-1.31/virtme/cpiowriter.py 2024-10-18 15:30:58.000000000 +0200 @@ -5,6 +5,30 @@ # as a file called LICENSE with SHA-256 hash: # 8177f97513213526df2cf6184d8ff986c675afb514d4e68a404010521b880643 +class FileMetaData: + def __init__(self, **kwargs): + # Define default values for the metadata + defaults = { + 'ino': None, + 'nlink': None, + 'uid': 0, + 'gid': 0, + 'mtime': 0, + 'devmajor': 0, + 'devminor': 0, + 'rdevmajor': 0, + 'rdevminor': 0 + } + + # Update defaults with any provided keyword arguments + self.meta_data = {**defaults, **kwargs} + + def get(self, key): + return self.meta_data.get(key) + + def set(self, key, value): + self.meta_data[key] = value + class CpioWriter: TYPE_DIR = 0o0040000 @@ -22,23 +46,13 @@ self.__f.write(data) self.__totalsize += len(data) - def write_object( - self, - name, - body, - mode, - ino=None, - nlink=None, - uid=0, - gid=0, - mtime=0, - devmajor=0, - devminor=0, - rdevmajor=0, - rdevminor=0, - ): - if nlink is None: - nlink = 2 if (mode & CpioWriter.TYPE_MASK) == CpioWriter.TYPE_DIR else 1 + def write_object(self, name, body, mode, meta_data=None): + # Set default metadata if not provided + meta_data = meta_data or FileMetaData() + + # Ensure nlink is set correctly based on mode + if meta_data.get('nlink') is None: + meta_data.set('nlink', 2 if (mode & CpioWriter.TYPE_MASK) == CpioWriter.TYPE_DIR else 1) if b"\0" in name: raise ValueError("Filename cannot contain a NUL") @@ -51,25 +65,28 @@ filesize = body.seek(0, 2) body.seek(0) - if ino is None: - ino = self.__next_ino + # Set default ino if not provided + if meta_data.get('ino') is None: + meta_data.set('ino', self.__next_ino) self.__next_ino += 1 + # Prepare fields list using metadata fields = [ - ino, + meta_data.get('ino'), mode, - uid, - gid, - nlink, - mtime, + meta_data.get('uid'), + meta_data.get('gid'), + meta_data.get('nlink'), + meta_data.get('mtime'), filesize, - devmajor, - devminor, - rdevmajor, - rdevminor, + meta_data.get('devmajor'), + meta_data.get('devminor'), + meta_data.get('rdevmajor'), + meta_data.get('rdevminor'), namesize, 0, ] + hdr = ("070701" + "".join("%08X" % f for f in fields)).encode("ascii") self.__write(hdr) @@ -89,14 +106,14 @@ self.__write(((-filesize) % 4) * b"\0") def write_trailer(self): - self.write_object(name=b"TRAILER!!!", body=b"", mode=0, ino=0, nlink=1) + self.write_object(name=b"TRAILER!!!", body=b"", mode=0, meta_data=FileMetaData(ino=0, nlink=1)) self.__write(((-self.__totalsize) % 512) * b"\0") def mkdir(self, name, mode): - self.write_object(name=name, mode=CpioWriter.TYPE_DIR | mode, body=b"") + self.write_object(name=name, body=b"", mode=CpioWriter.TYPE_DIR | mode) def symlink(self, src, dst): - self.write_object(name=dst, mode=CpioWriter.TYPE_SYMLINK | 0o777, body=src) + self.write_object(name=dst, body=src, mode=CpioWriter.TYPE_SYMLINK | 0o777) def write_file(self, name, body, mode): self.write_object(name=name, body=body, mode=CpioWriter.TYPE_REG | mode) @@ -105,8 +122,10 @@ major, minor = dev self.write_object( name=name, - mode=CpioWriter.TYPE_CHRDEV | mode, - rdevmajor=major, - rdevminor=minor, body=b"", - ) + mode=CpioWriter.TYPE_CHRDEV | mode, + meta_data=FileMetaData( + rdevmajor=major, + rdevminor=minor, + ) + ) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/virtme-ng-1.29/virtme/modfinder.py new/virtme-ng-1.31/virtme/modfinder.py --- old/virtme-ng-1.29/virtme/modfinder.py 2024-09-11 08:38:45.000000000 +0200 +++ new/virtme-ng-1.31/virtme/modfinder.py 2024-10-18 15:30:58.000000000 +0200 @@ -15,7 +15,7 @@ import re import subprocess -import os +import platform import itertools from . import util @@ -29,7 +29,7 @@ args += ["-C", "/var/empty"] if root is not None: args += ["-d", root] - if kver is not None and kver != os.uname().release: + if kver is not None and kver != platform.release(): # If booting the loaded kernel, skip -S. This helps certain # buggy modprobe versions that don't support -S. args += ["-S", kver] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/virtme-ng-1.29/virtme/qemu_helpers.py new/virtme-ng-1.31/virtme/qemu_helpers.py --- old/virtme-ng-1.29/virtme/qemu_helpers.py 2024-09-11 08:38:45.000000000 +0200 +++ new/virtme-ng-1.31/virtme/qemu_helpers.py 2024-10-18 15:30:58.000000000 +0200 @@ -6,6 +6,7 @@ # 8177f97513213526df2cf6184d8ff986c675afb514d4e68a404010521b880643 import os +import platform import re import shutil import subprocess @@ -23,7 +24,7 @@ if not qemubin: qemubin = shutil.which("qemu-system-%s" % arch) - if qemubin is None and arch == os.uname().machine: + if qemubin is None and arch == platform.machine(): qemubin = shutil.which("qemu-kvm") if qemubin is None: raise ValueError("cannot find qemu for %s" % arch) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/virtme-ng-1.29/virtme/util.py new/virtme-ng-1.31/virtme/util.py --- old/virtme-ng-1.29/virtme/util.py 2024-09-11 08:38:45.000000000 +0200 +++ new/virtme-ng-1.31/virtme/util.py 2024-10-18 15:30:58.000000000 +0200 @@ -12,8 +12,6 @@ import getpass import itertools -uname = os.uname() - class SilentError(Exception): pass diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/virtme-ng-1.29/virtme_ng/mainline.py new/virtme-ng-1.31/virtme_ng/mainline.py --- old/virtme-ng-1.29/virtme_ng/mainline.py 2024-09-11 08:38:45.000000000 +0200 +++ new/virtme-ng-1.31/virtme_ng/mainline.py 2024-10-18 15:30:58.000000000 +0200 @@ -25,7 +25,7 @@ self.version = version self.arch = arch self.verbose = verbose - self.target = f"{self.kernel_dir}/boot/vmlinuz*generic*" + self.target = f"{self.kernel_dir}/boot/vmlinuz*generic" if not glob(self.target): self._fetch_kernel() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/virtme-ng-1.29/virtme_ng/run.py new/virtme-ng-1.31/virtme_ng/run.py --- old/virtme-ng-1.29/virtme_ng/run.py 2024-09-11 08:38:45.000000000 +0200 +++ new/virtme-ng-1.31/virtme_ng/run.py 2024-10-18 15:30:58.000000000 +0200 @@ -6,6 +6,7 @@ import argparse import re import os +import platform import sys import socket import shutil @@ -29,7 +30,7 @@ # pylint: disable=unused-argument pass -from virtme.util import SilentError, uname, get_username +from virtme.util import SilentError, get_username from virtme_ng.utils import CONF_FILE, spinner_decorator from virtme_ng.mainline import KernelDownloader from virtme_ng.version import VERSION @@ -114,7 +115,7 @@ "-r", action="store", nargs="?", - const=uname.release, + const=platform.release(), default=None, help="Run a specified kernel; " "--run can accept one of the following arguments: 1) nothing (in this " @@ -465,6 +466,13 @@ nargs="*", help="Additional Makefile variables", ) + + parser.add_argument( + "--nvgpu", + action="store", + metavar="[GPU PCI Address]", + help="Add a passthrough NVIDIA GPU", + ) return parser @@ -535,11 +543,17 @@ # Use Ubuntu's cloud images to create a rootfs, these images are fairly # small and they provide a nice environment to test kernels. if release is None: - release = ( - check_output("lsb_release -s -c", shell=True) - .decode(sys.stdout.encoding) - .rstrip() - ) + try: + release = ( + check_output("lsb_release -s -c", shell=True) + .decode(sys.stdout.encoding) + .rstrip() + ) + if release == "n/a": + raise ValueError("unknown release") + except (CalledProcessError, ValueError): + print("Unknown release, try specifying an Ubuntu release with --root-release") + sys.exit(1) url = ( "https://cloud-images.ubuntu.com/" + f"{release}/current/{release}-server-cloudimg-{arch}-root.tar.xz" @@ -551,6 +565,20 @@ os.chdir(prevdir) +def get_host_arch(): + """Translate host architecture to the corresponding virtme-ng arch name.""" + arch = platform.machine() + arch_map = { + 'x86_64': 'amd64', + 'aarch64': 'arm64', + 'armv7l': 'armhf', + 'ppc64le': 'ppc64el', + 'riscv64': 'riscv64', + 's390x': 's390x', + } + return arch_map.get(arch, None) + + class KernelSource: """Main class that implement actions to perform on a kernel source directory.""" @@ -745,7 +773,7 @@ arg_fail(f"unsupported architecture: {arch}") target = ARCH_MAPPING[arch]["kernel_target"] cross_compile = ARCH_MAPPING[arch]["cross_compile"] - if args.cross_compile != "": + if args.cross_compile: cross_compile = args.cross_compile cross_arch = ARCH_MAPPING[arch]["linux_name"] @@ -816,7 +844,7 @@ def _get_virtme_root(self, args): if args.root is not None: - create_root(args.root, args.arch or "amd64", args.root_release) + create_root(args.root, args.arch or get_host_arch(), args.root_release) self.virtme_param["root"] = f"--root {args.root}" else: self.virtme_param["root"] = "" @@ -840,12 +868,12 @@ def _get_virtme_rodir(self, args): self.virtme_param["rodir"] = "" for item in args.rodir: - self.virtme_param["rodir"] += "--rodir " + item + self.virtme_param["rodir"] += f"--rodir {item} " def _get_virtme_rwdir(self, args): self.virtme_param["rwdir"] = "" for item in args.rwdir: - self.virtme_param["rwdir"] += "--rwdir " + item + self.virtme_param["rwdir"] += f"--rwdir {item} " def _get_virtme_overlay_rwdir(self, args): # Set default overlays if rootfs is mounted in read-only mode. @@ -865,9 +893,9 @@ # If an upstream version is specified (using an upstream tag) fetch # and run the corresponding kernel from the Ubuntu mainline # repository. - if re.match(r'^v\d+(\.\d+)*$', args.run): + if re.match(r'^v\d+(\.\d+)*(-rc\d+)?$', args.run): if args.arch is None: - arch = 'amd64' + arch = get_host_arch() else: arch = args.arch try: @@ -886,7 +914,7 @@ self.virtme_param["kdir"] = "--kdir ./" def _get_virtme_mods(self, args): - if args.skip_modules: + if args.skip_modules or platform.system() != "Linux": self.virtme_param["mods"] = "--mods none" else: self.virtme_param["mods"] = "--mods auto" @@ -1041,6 +1069,12 @@ else: self.virtme_param["qemu_opts"] = "" + def _get_virtme_nvgpu(self, args): + if args.nvgpu is not None: + self.virtme_param["nvgpu"] = f"--nvgpu 'vfio-pci,host={args.nvgpu}'" + else: + self.virtme_param["nvgpu"] = "" + def run(self, args): """Execute a kernel inside virtme-ng.""" self._get_virtme_name(args) @@ -1076,6 +1110,7 @@ self._get_virtme_busybox(args) self._get_virtme_qemu(args) self._get_virtme_qemu_opts(args) + self._get_virtme_nvgpu(args) # Start VM using virtme-run cmd = ( @@ -1113,6 +1148,7 @@ + f'{self.virtme_param["busybox"]} ' + f'{self.virtme_param["qemu"]} ' + f'{self.virtme_param["qemu_opts"]} ' + + f'{self.virtme_param["nvgpu"]} ' ) check_call(cmd, shell=True) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/virtme-ng-1.29/virtme_ng/version.py new/virtme-ng-1.31/virtme_ng/version.py --- old/virtme-ng-1.29/virtme_ng/version.py 2024-09-11 08:38:45.000000000 +0200 +++ new/virtme-ng-1.31/virtme_ng/version.py 2024-10-18 15:30:58.000000000 +0200 @@ -7,7 +7,7 @@ from subprocess import check_output, DEVNULL, CalledProcessError import pkg_resources -PKG_VERSION = "1.29" +PKG_VERSION = "1.31" def get_package_version(): @@ -34,7 +34,7 @@ # Otherwise fallback to the static version defined in PKG_VERSION. version = ( check_output( - "cd %s && [ -e ../.git ] && git describe --always --long --dirty" % os.path.dirname(__file__), + "cd %s && [ -e ../.git ] && git describe --long --dirty" % os.path.dirname(__file__), shell=True, stderr=DEVNULL, )