Running macOS in a Virtual Machine on Apple Silicon Macs
source link: https://developer.apple.com/documentation/virtualization/running_macos_in_a_virtual_machine_on_apple_silicon_macs
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
Availability
Technology
Overview
This sample code project demonstrates how to install and run macOS virtual machines on Apple Silicon using the Virtualization framework. There are two separate applications included in the Xcode project:
InstallationTool, a command line tool that installs macOS onto a virtual machine from a restore image. You can use the installation tool to download the restore image of the most current macOS release from the network, or, if you already have previously saved a restore image, directly install macOS with your own restore image. The installation tool creates a VM Bundle in your home directory and stores the resulting virtual machine images there.
macOSVirtualMachineSampleApp, a macOS app that launches and controls the macOS virtual guest that loads and runs macOS from the VM Bundle.
You run the installation tool from inside Xcode or the command line to install the macOS image. After it successfully installs the image, you run the macOSVirtualMachineSampleApp to run the macOS guest operating system. The macOSVirtualMachineSampleApp starts the virtual machine and configures a graphical view that you interact with. The virtual Mac continues running until you shut it down from inside the guest OS, or you quit the macOSVirtualMachineSampleApp.
Configure the Sample Code Project
There are four build targets in this project that represent the InstallationTool and the macOSVirtualMachineSampleApp, one set of targets each for Swift and Objective-C versions of the apps. You can use either version, they’re functionally identical.
InstallationTool is a command-line tool that installs a macOS VM Bundle from a restore image. Running this tool without any arguments, it tries to download the latest available restore image from the network and then install macOS from that restore image. In the example below, the InstallationTool reads the values in the downloaded image and creates a macOS VM image on disk:
let virtualMachineConfiguration = VZVirtualMachineConfiguration()
virtualMachineConfiguration.platform = createMacPlatformConfiguration(macOSConfiguration: macOSConfiguration)
virtualMachineConfiguration.cpuCount = MacOSVirtualMachineConfigurationHelper.computeCPUCount()
if virtualMachineConfiguration.cpuCount < macOSConfiguration.minimumSupportedCPUCount {
fatalError("CPUCount is not supported by the macOS configuration.")
}
virtualMachineConfiguration.memorySize = MacOSVirtualMachineConfigurationHelper.computeMemorySize()
if virtualMachineConfiguration.memorySize < macOSConfiguration.minimumSupportedMemorySize {
fatalError("memorySize is not supported by the macOS configuration.")
}
// Create a 64 GB disk image.
createDiskImage()
virtualMachineConfiguration.bootLoader = MacOSVirtualMachineConfigurationHelper.createBootLoader()
virtualMachineConfiguration.graphicsDevices = [MacOSVirtualMachineConfigurationHelper.createGraphicsDeviceConfiguration()]
virtualMachineConfiguration.storageDevices = [MacOSVirtualMachineConfigurationHelper.createBlockDeviceConfiguration()]
virtualMachineConfiguration.networkDevices = [MacOSVirtualMachineConfigurationHelper.createNetworkDeviceConfiguration()]
virtualMachineConfiguration.pointingDevices = [MacOSVirtualMachineConfigurationHelper.createPointingDeviceConfiguration()]
virtualMachineConfiguration.keyboards = [MacOSVirtualMachineConfigurationHelper.createKeyboardConfiguration()]
virtualMachineConfiguration.audioDevices = [MacOSVirtualMachineConfigurationHelper.createAudioDeviceConfiguration()]
try! virtualMachineConfiguration.validate()
virtualMachine = VZVirtualMachine(configuration: virtualMachineConfiguration)
virtualMachineResponder = MacOSVirtualMachineDelegate()
virtualMachine.delegate = virtualMachineResponder
The equivalent Objective-C version is:
VZVirtualMachineConfiguration *configuration = [VZVirtualMachineConfiguration new];
configuration.platform = [self createMacPlatformConfiguration:macOSConfiguration];
assert(configuration.platform);
configuration.CPUCount = [MacOSVirtualMachineConfigurationHelper computeCPUCount];
if (configuration.CPUCount < macOSConfiguration.minimumSupportedCPUCount) {
abortWithErrorMessage(@"CPUCount is not supported by the macOS configuration.");
}
configuration.memorySize = [MacOSVirtualMachineConfigurationHelper computeMemorySize];
if (configuration.memorySize < macOSConfiguration.minimumSupportedMemorySize) {
abortWithErrorMessage(@"memorySize is not supported by the macOS configuration.");
}
// Create a 64 GB disk image.
createDiskImage();
configuration.bootLoader = [MacOSVirtualMachineConfigurationHelper createBootLoader];
configuration.graphicsDevices = @[ [MacOSVirtualMachineConfigurationHelper createGraphicsDeviceConfiguration] ];
configuration.storageDevices = @[ [MacOSVirtualMachineConfigurationHelper createBlockDeviceConfiguration] ];
configuration.networkDevices = @[ [MacOSVirtualMachineConfigurationHelper createNetworkDeviceConfiguration] ];
configuration.pointingDevices = @[ [MacOSVirtualMachineConfigurationHelper createPointingDeviceConfiguration] ];
configuration.keyboards = @[ [MacOSVirtualMachineConfigurationHelper createKeyboardConfiguration] ];
configuration.audioDevices = @[ [MacOSVirtualMachineConfigurationHelper createAudioDeviceConfiguration] ];
assert([configuration validateWithError:nil]);
self->_virtualMachine = [[VZVirtualMachine alloc] initWithConfiguration:configuration];
self->_delegate = [MacOSVirtualMachineDelegate new];
self->_virtualMachine.delegate = self->_delegate;
Running this tool with the path to an ipsw
file, it tries to directly install macOS from the ipsw
file on disk:
DispatchQueue.main.async { [self] in
let installer = VZMacOSInstaller(virtualMachine: virtualMachine, restoringFromImageAt: restoreImageURL)
NSLog("Starting installation.")
installer.install(completionHandler: { (result: Result<Void, Error>) in
if case let .failure(error) = result {
fatalError(error.localizedDescription)
} else {
NSLog("Installation succeeded.")
}
})
// Observe installation progress
installationObserver = installer.progress.observe(\.fractionCompleted, options: [.initial, .new]) { (progress, change) in
NSLog("Installation progress: \(change.newValue! * 100).")
}
}
The equivalent Objective-C version is:
dispatch_async(dispatch_get_main_queue(), ^{
VZMacOSInstaller *installer = [[VZMacOSInstaller alloc] initWithVirtualMachine:self->_virtualMachine restoreImageURL:restoreImageFileURL];
NSLog(@"Starting installation.");
[installer installWithCompletionHandler:^(NSError *error) {
if (error) {
abortWithErrorMessage([NSString stringWithFormat:@"%@", error.localizedDescription]);
} else {
NSLog(@"Installation succeeded.");
}
}];
[installer.progress addObserver:self forKeyPath:@"fractionCompleted" options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew context:nil];
});
InstallationTool creates a VM.bundle
package in the user’s home directory upon a successful installation, it contains:
Disk.img
- The main disk image of the installed OS.AuxiliaryStorage
- The auxiliary storage for macOS.MachineIdentifier
- The data representation of theVZMacMachineIdentifier
object.HardwareModel
- The data representation of theVZMacHardwareModel
object.RestoreImage.ipsw
- The restore image downloaded from the network (this file exists only if the tool runs without arguments).
To reinstall the virtual machine, delete the VM.bundle
package and run InstallationTool again.
macOSVirtualMachineSampleApp is a GUI application that runs the macOS VM installed by the InstallationTool. The virtual machine must be already installed and VM.bundle
must exist before launching this app.
Configuration of the macOSVirtualMachineSampleApp Virtual Machine
macOSVirtualMachineSampleApp uses a VZVirtualMachineConfiguration
object to configure the basic characteristics of the guest, such as the CPU count, memory size, various device configurations, and a VZMacOSBootLoader
object to load operating system from the disk image as shown in the following example:
let virtualMachineConfiguration = VZVirtualMachineConfiguration()
virtualMachineConfiguration.platform = createMacPlaform()
virtualMachineConfiguration.bootLoader = MacOSVirtualMachineConfigurationHelper.createBootLoader()
virtualMachineConfiguration.cpuCount = MacOSVirtualMachineConfigurationHelper.computeCPUCount()
virtualMachineConfiguration.memorySize = MacOSVirtualMachineConfigurationHelper.computeMemorySize()
virtualMachineConfiguration.graphicsDevices = [MacOSVirtualMachineConfigurationHelper.createGraphicsDeviceConfiguration()]
virtualMachineConfiguration.storageDevices = [MacOSVirtualMachineConfigurationHelper.createBlockDeviceConfiguration()]
virtualMachineConfiguration.networkDevices = [MacOSVirtualMachineConfigurationHelper.createNetworkDeviceConfiguration()]
virtualMachineConfiguration.pointingDevices = [MacOSVirtualMachineConfigurationHelper.createPointingDeviceConfiguration()]
virtualMachineConfiguration.keyboards = [MacOSVirtualMachineConfigurationHelper.createKeyboardConfiguration()]
virtualMachineConfiguration.audioDevices = [MacOSVirtualMachineConfigurationHelper.createAudioDeviceConfiguration()]
try! virtualMachineConfiguration.validate()
virtualMachine = VZVirtualMachine(configuration: virtualMachineConfiguration)
The Objective-C equivalent is:
VZVirtualMachineConfiguration *configuration = [VZVirtualMachineConfiguration new];
configuration.platform = [self createMacPlatformConfiguration];
configuration.CPUCount = [MacOSVirtualMachineConfigurationHelper computeCPUCount];
configuration.memorySize = [MacOSVirtualMachineConfigurationHelper computeMemorySize];
configuration.bootLoader = [MacOSVirtualMachineConfigurationHelper createBootLoader];
configuration.graphicsDevices = @[ [MacOSVirtualMachineConfigurationHelper createGraphicsDeviceConfiguration] ];
configuration.storageDevices = @[ [MacOSVirtualMachineConfigurationHelper createBlockDeviceConfiguration] ];
configuration.networkDevices = @[ [MacOSVirtualMachineConfigurationHelper createNetworkDeviceConfiguration] ];
configuration.pointingDevices = @[ [MacOSVirtualMachineConfigurationHelper createPointingDeviceConfiguration] ];
configuration.keyboards = @[ [MacOSVirtualMachineConfigurationHelper createKeyboardConfiguration] ];
configuration.audioDevices = @[ [MacOSVirtualMachineConfigurationHelper createAudioDeviceConfiguration] ];
assert([configuration validateWithError:nil]);
_virtualMachine = [[VZVirtualMachine alloc] initWithConfiguration:configuration];
Inside the general VMconfiguration
method, the app also creates a platform configuration for the virtual machine: VZMacPlatformConfiguration
configures important macOS specific data the macOS guest needs to run, including the specific hardwareModel
that the image supports, as well as a machineIdentifier
that uniquely identifies the current virtual machine instance and differentiates it from any others the user creates:
let macPlatform = VZMacPlatformConfiguration()
let auxiliaryStorage = VZMacAuxiliaryStorage(contentsOf: auxiliaryStorageURL)
macPlatform.auxiliaryStorage = auxiliaryStorage
if !FileManager.default.fileExists(atPath: vmBundlePath) {
fatalError("Missing Virtual Machine Bundle at \(vmBundlePath). Run InstallationTool first to create it.")
}
// Retrieve the hardware model; you should save this value to disk during installation.
guard let hardwareModelData = try? Data(contentsOf: hardwareModelURL) else {
fatalError("Failed to retrieve hardware model data.")
}
guard let hardwareModel = VZMacHardwareModel(dataRepresentation: hardwareModelData) else {
fatalError("Failed to create hardware model.")
}
if !hardwareModel.isSupported {
fatalError("The hardware model is not supported on the current host")
}
macPlatform.hardwareModel = hardwareModel
// Retrieve the machine identifier; you should save this value to disk during installation.
guard let machineIdentifierData = try? Data(contentsOf: machineIdentifierURL) else {
fatalError("Failed to retrieve machine identifier data.")
}
guard let machineIdentifier = VZMacMachineIdentifier(dataRepresentation: machineIdentifierData) else {
fatalError("Failed to create machine identifier.")
}
macPlatform.machineIdentifier = machineIdentifier
The equivalent functionality in Objective-C is:
VZMacPlatformConfiguration *macPlatformConfiguration = [[VZMacPlatformConfiguration alloc] init];
VZMacAuxiliaryStorage *auxiliaryStorage = [[VZMacAuxiliaryStorage alloc] initWithContentsOfURL:getAuxiliaryStorageURL()];
macPlatformConfiguration.auxiliaryStorage = auxiliaryStorage;
if (![[NSFileManager defaultManager] fileExistsAtPath:getVMBundlePath()]) {
abortWithErrorMessage([NSString stringWithFormat:@"Missing Virtual Machine Bundle at %@. Run InstallationTool first to create it.", getVMBundlePath()]);
}
// Retrieve the hardware model; you should save this value to disk during installation.
NSData *hardwareModelData = [[NSData alloc] initWithContentsOfURL:getHardwareModelURL()];
if (!hardwareModelData) {
abortWithErrorMessage(@"Failed to retrieve hardware model data.");
}
VZMacHardwareModel *hardwareModel = [[VZMacHardwareModel alloc] initWithDataRepresentation:hardwareModelData];
if (!hardwareModel) {
abortWithErrorMessage(@"Failed to create hardware model.");
}
if (!hardwareModel.supported) {
abortWithErrorMessage(@"The hardware model is not supported on the current host");
}
macPlatformConfiguration.hardwareModel = hardwareModel;
// Retrieve the machine identifier; you should save this value to disk during installation.
NSData *machineIdentifierData = [[NSData alloc] initWithContentsOfURL:getMachineIdentifierURL()];
if (!machineIdentifierData) {
abortWithErrorMessage(@"Failed to retrieve machine identifier data.");
}
VZMacMachineIdentifier *machineIdentifier = [[VZMacMachineIdentifier alloc] initWithDataRepresentation:machineIdentifierData];
if (!machineIdentifier) {
abortWithErrorMessage(@"Failed to create machine identifier.");
}
macPlatformConfiguration.machineIdentifier = machineIdentifier;
Instantiate and Start the Virtual Machine
After building the configuration data for the virtual machine, macOSVirtualMachineSampleApp uses the VZVirtualMachine
object to start the execution of the macOS guest operating system.
Before calling the virtual machine’s start(completionHandler:)
method, the macOSVirtualMachineSampleApp configures a delegate object to receive messages about the state of the virtual machine. When the macOS guest operating system shuts down, the virtual machine calls the delegate’s guestDidStop(_:)
method. In response, the delegate method prints a message and exits the app. If the macOS guest stops for any reason other than a normal shutdown, the delegate prints an error message and the app exits:
createVirtualMachine()
virtualMachineResponder = MacOSVirtualMachineDelegate()
virtualMachine.delegate = virtualMachineResponder
virtualMachineView.virtualMachine = virtualMachine
virtualMachine.start(completionHandler: { (result) in
switch result {
case let .failure(error):
fatalError("Virtual machine failed to start \(error)")
default:
NSLog("Virtual machine successfully started.")
}
})
The equivalent functionality in Objective-C is:
[self createVirtualMachine];
_delegate = [MacOSVirtualMachineDelegate new];
_virtualMachine.delegate = _delegate;
_virtualMachineView.virtualMachine = _virtualMachine;
dispatch_async(dispatch_get_main_queue(), ^{
[self->_virtualMachine startWithCompletionHandler:^(NSError * _Nullable error) {
if (error) {
abortWithErrorMessage(error.localizedDescription);
}
}];
});
The start(completionHandler:)
method starts the virtual machine asynchronously in the background. The virtual machine loads system image and boots macOS. After macOS finishes booting, the user interacts with a VZVirtualMachineView
window that displays the macOS UI and handles keyboard and mouse input through a VZMacGraphicsDeviceConfiguration
as though the user is interacting directly with the hardware on a stand-alone machine.
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK