-
-
Save xpn/09a54dcef9c7302d22de938947a50193 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Foundation | |
import Virtualization | |
import Darwin.C | |
class VirtualMachine :NSObject, VZVirtualMachineDelegate { | |
private var virtualMachine: VZVirtualMachine! = nil | |
private var memSize :UInt64 | |
private var cpuCount :Int | |
private let kernelURL :URL | |
private var ramdiskURL :URL? = nil | |
private var bootableImageURL :URL? = nil | |
init(cpuCount :Int, memSize : UInt64, kernelPath :String, ramdiskPath:String?, blockdiskPath :String?) { | |
self.cpuCount = cpuCount | |
self.memSize = memSize | |
self.kernelURL = URL.init(fileURLWithPath: kernelPath) | |
if ramdiskPath != nil { | |
self.ramdiskURL = URL.init(fileURLWithPath: ramdiskPath!) | |
} | |
if blockdiskPath != nil { | |
self.bootableImageURL = URL.init(fileURLWithPath: blockdiskPath!) | |
} | |
} | |
func resizeMemory(newMemSize : UInt64) { | |
if let memDevice = self.virtualMachine!.memoryBalloonDevices.first as? VZVirtioTraditionalMemoryBalloonDevice { | |
memDevice.targetVirtualMachineMemorySize = newMemSize | |
} | |
} | |
func stopvm() -> Bool { | |
if self.virtualMachine!.canRequestStop { | |
do { | |
try self.virtualMachine!.requestStop() | |
return true | |
} catch { | |
return false | |
} | |
} | |
return false | |
} | |
func startvm() -> Bool { | |
// Set up the Linux bootloader (default console to hypervisor) | |
let bootloader = VZLinuxBootLoader(kernelURL: kernelURL) | |
bootloader.initialRamdiskURL = ramdiskURL | |
bootloader.commandLine = "console=hvc0" | |
// We'll use a serial VirtIO device for comms | |
// Just attach our STDIN/STDOUT for this example | |
let serialPort = VZVirtioConsoleDeviceSerialPortConfiguration() | |
serialPort.attachment = VZFileHandleSerialPortAttachment( | |
fileHandleForReading: FileHandle.standardOutput, | |
fileHandleForWriting: FileHandle.standardInput | |
) | |
// Allows us to dynamically control the size of memory allocated to the guest | |
let balloonConfig = VZVirtioTraditionalMemoryBalloonDeviceConfiguration() | |
// Allocate persistant storage | |
var blockAttachment: VZDiskImageStorageDeviceAttachment? = nil | |
var blockDevice :VZVirtioBlockDeviceConfiguration? = nil | |
if self.bootableImageURL != nil { | |
do { | |
blockAttachment = try VZDiskImageStorageDeviceAttachment( | |
url: bootableImageURL!, | |
readOnly: true | |
) | |
blockDevice = VZVirtioBlockDeviceConfiguration(attachment: blockAttachment!) | |
} catch { | |
NSLog("[!] Failed to load bootableImage: \(error)") | |
return false | |
} | |
} | |
// Only option is to NAT at the minute because... Apple :( | |
// https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_vm_networking?language=objc | |
let networkDevice = VZVirtioNetworkDeviceConfiguration() | |
networkDevice.attachment = VZNATNetworkDeviceAttachment() | |
networkDevice.macAddress = VZMACAddress.randomLocallyAdministered() | |
// Configuration of devices for this VM | |
let config = VZVirtualMachineConfiguration() | |
config.bootLoader = bootloader | |
config.memoryBalloonDevices = [balloonConfig] | |
config.serialPorts = [serialPort] | |
config.networkDevices = [networkDevice] | |
if blockDevice != nil { | |
config.storageDevices = [blockDevice!] | |
} | |
// Set resources allocated | |
config.cpuCount = self.cpuCount | |
config.memorySize = self.memSize | |
do { | |
// Make sure our config checks out | |
try config.validate() | |
// Create the VM | |
self.virtualMachine = VZVirtualMachine(configuration: config) | |
self.virtualMachine.delegate = self | |
// Start the VM | |
self.virtualMachine.start { result in | |
switch result { | |
case .success: | |
break | |
case .failure(_): | |
NSLog("[!] Failure whilst starting VM: \(result)") | |
break | |
} | |
} | |
return true | |
} catch { | |
NSLog("[!] Unexpected error: \(error).") | |
return false | |
} | |
} | |
// Delegate function - Called when VM stops | |
func guestDidStop(_ virtualMachine: VZVirtualMachine) { | |
print("[*] VM Stopped") | |
} | |
func virtualMachine(_ virtualMachine: VZVirtualMachine, | |
didStopWithError error: Error) { | |
print("[!] Error occurred and VM Stopped") | |
} | |
} | |
print("@_xpn_ - Apple Virtualization framework POC") | |
var v = VirtualMachine(cpuCount: 2, memSize: 2 * 1024 * 1024 * 1024, kernelPath: "/tmp/kernel", ramdiskPath: "/tmp/ramdisk", blockdiskPath: nil) | |
if v.startvm() == false { | |
print("[x] Error starting VM") | |
exit(1) | |
} | |
var runloop = RunLoop.current | |
var d = Date.init(timeIntervalSinceNow: 1.0) | |
while(runloop.run(mode: RunLoop.Mode.default, before: d)) { | |
// pass | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment