From 9c879b3f55241c4a158aa576c23e6c23785701c0 Mon Sep 17 00:00:00 2001 From: Fedor Korotkov Date: Wed, 6 Nov 2024 12:27:29 -0500 Subject: [PATCH] `tart run --nested` to enable nested virtualization when available (#938) Only works for Linux VMs under Sequoia hosts. Fixes #933 Fixes #701 --- Sources/tart/Commands/Run.swift | 12 ++++++++++++ Sources/tart/Platform/Darwin.swift | 6 +++++- Sources/tart/Platform/Linux.swift | 8 ++++++-- Sources/tart/Platform/Platform.swift | 2 +- Sources/tart/VM.swift | 5 ++++- 5 files changed, 28 insertions(+), 5 deletions(-) diff --git a/Sources/tart/Commands/Run.swift b/Sources/tart/Commands/Run.swift index 48af5f4f..29881785 100644 --- a/Sources/tart/Commands/Run.swift +++ b/Sources/tart/Commands/Run.swift @@ -156,6 +156,9 @@ struct Run: AsyncParsableCommand { """, valueName: "[name:]path[:options]")) var dir: [String] = [] + @Flag(help: ArgumentHelp("Enable nested virtualization if possible")) + var nested: Bool = false + @Option(help: ArgumentHelp(""" Use bridged networking instead of the default shared (NAT) networking \n(e.g. --net-bridged=en0 or --net-bridged=\"Wi-Fi\") """, discussion: """ @@ -221,6 +224,14 @@ struct Run: AsyncParsableCommand { throw ValidationError("--captures-system-keys can only be used with the default VM view") } + if nested { + if #unavailable(macOS 15) { + throw ValidationError("Nested virtualization is supported on hosts starting with macOS 15 (Sequia), and later.") + } else if !VZGenericPlatformConfiguration.isNestedVirtualizationSupported { + throw ValidationError("Nested virtualization is available for Mac with the M3 chip, and later.") + } + } + let localStorage = VMStorageLocal() let vmDir = try localStorage.open(name) if try vmDir.state() == .Suspended { @@ -294,6 +305,7 @@ struct Run: AsyncParsableCommand { directorySharingDevices: directoryShares() + rosettaDirectoryShare(), serialPorts: serialPorts, suspendable: suspendable, + nested: nested, audio: !noAudio, clipboard: !noClipboard, sync: VZDiskImageSynchronizationMode(diskOptions.syncModeRaw) diff --git a/Sources/tart/Platform/Darwin.swift b/Sources/tart/Platform/Darwin.swift index f765864b..71458107 100644 --- a/Sources/tart/Platform/Darwin.swift +++ b/Sources/tart/Platform/Darwin.swift @@ -58,7 +58,11 @@ struct UnsupportedHostOSError: Error, CustomStringConvertible { VZMacOSBootLoader() } - func platform(nvramURL: URL) throws -> VZPlatformConfiguration { + func platform(nvramURL: URL, needsNestedVirtualization: Bool) throws -> VZPlatformConfiguration { + if needsNestedVirtualization { + throw RuntimeError.VMConfigurationError("macOS virtual machines do not support nested virtualization") + } + let result = VZMacPlatformConfiguration() result.machineIdentifier = ecid diff --git a/Sources/tart/Platform/Linux.swift b/Sources/tart/Platform/Linux.swift index fbfe84a5..2d247dcf 100644 --- a/Sources/tart/Platform/Linux.swift +++ b/Sources/tart/Platform/Linux.swift @@ -14,8 +14,12 @@ struct Linux: Platform { return result } - func platform(nvramURL: URL) throws -> VZPlatformConfiguration { - VZGenericPlatformConfiguration() + func platform(nvramURL: URL, needsNestedVirtualization: Bool) throws -> VZPlatformConfiguration { + let config = VZGenericPlatformConfiguration() + if #available(macOS 15, *) { + config.isNestedVirtualizationEnabled = needsNestedVirtualization + } + return config } func graphicsDevice(vmConfig: VMConfig) -> VZGraphicsDeviceConfiguration { diff --git a/Sources/tart/Platform/Platform.swift b/Sources/tart/Platform/Platform.swift index e8627818..c86f2f04 100644 --- a/Sources/tart/Platform/Platform.swift +++ b/Sources/tart/Platform/Platform.swift @@ -3,7 +3,7 @@ import Virtualization protocol Platform: Codable { func os() -> OS func bootLoader(nvramURL: URL) throws -> VZBootLoader - func platform(nvramURL: URL) throws -> VZPlatformConfiguration + func platform(nvramURL: URL, needsNestedVirtualization: Bool) throws -> VZPlatformConfiguration func graphicsDevice(vmConfig: VMConfig) -> VZGraphicsDeviceConfiguration func keyboards() -> [VZKeyboardConfiguration] func pointingDevices() -> [VZPointingDeviceConfiguration] diff --git a/Sources/tart/VM.swift b/Sources/tart/VM.swift index 865a8362..aeff8b6f 100644 --- a/Sources/tart/VM.swift +++ b/Sources/tart/VM.swift @@ -47,6 +47,7 @@ class VM: NSObject, VZVirtualMachineDelegate, ObservableObject { directorySharingDevices: [VZDirectorySharingDeviceConfiguration] = [], serialPorts: [VZSerialPortConfiguration] = [], suspendable: Bool = false, + nested: Bool = false, audio: Bool = true, clipboard: Bool = true, sync: VZDiskImageSynchronizationMode = .full @@ -66,6 +67,7 @@ class VM: NSObject, VZVirtualMachineDelegate, ObservableObject { directorySharingDevices: directorySharingDevices, serialPorts: serialPorts, suspendable: suspendable, + nested: nested, audio: audio, clipboard: clipboard, sync: sync @@ -295,6 +297,7 @@ class VM: NSObject, VZVirtualMachineDelegate, ObservableObject { directorySharingDevices: [VZDirectorySharingDeviceConfiguration], serialPorts: [VZSerialPortConfiguration], suspendable: Bool = false, + nested: Bool = false, audio: Bool = true, clipboard: Bool = true, sync: VZDiskImageSynchronizationMode = .full @@ -309,7 +312,7 @@ class VM: NSObject, VZVirtualMachineDelegate, ObservableObject { configuration.memorySize = vmConfig.memorySize // Platform - configuration.platform = try vmConfig.platform.platform(nvramURL: nvramURL) + configuration.platform = try vmConfig.platform.platform(nvramURL: nvramURL, needsNestedVirtualization: nested) // Display configuration.graphicsDevices = [vmConfig.platform.graphicsDevice(vmConfig: vmConfig)]