Skip to content

Commit

Permalink
tart run --nested to enable nested virtualization when available (#938
Browse files Browse the repository at this point in the history
)

Only works for Linux VMs under Sequoia hosts.

Fixes #933
Fixes #701
  • Loading branch information
fkorotkov authored Nov 6, 2024
1 parent f7b3876 commit 9c879b3
Show file tree
Hide file tree
Showing 5 changed files with 28 additions and 5 deletions.
12 changes: 12 additions & 0 deletions Sources/tart/Commands/Run.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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: """
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -294,6 +305,7 @@ struct Run: AsyncParsableCommand {
directorySharingDevices: directoryShares() + rosettaDirectoryShare(),
serialPorts: serialPorts,
suspendable: suspendable,
nested: nested,
audio: !noAudio,
clipboard: !noClipboard,
sync: VZDiskImageSynchronizationMode(diskOptions.syncModeRaw)
Expand Down
6 changes: 5 additions & 1 deletion Sources/tart/Platform/Darwin.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
8 changes: 6 additions & 2 deletions Sources/tart/Platform/Linux.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
2 changes: 1 addition & 1 deletion Sources/tart/Platform/Platform.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down
5 changes: 4 additions & 1 deletion Sources/tart/VM.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -66,6 +67,7 @@ class VM: NSObject, VZVirtualMachineDelegate, ObservableObject {
directorySharingDevices: directorySharingDevices,
serialPorts: serialPorts,
suspendable: suspendable,
nested: nested,
audio: audio,
clipboard: clipboard,
sync: sync
Expand Down Expand Up @@ -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
Expand All @@ -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)]
Expand Down

0 comments on commit 9c879b3

Please sign in to comment.