Karya Semi
HomeBlogSearchCategoriesAboutContact
Karya Semi

Less noise. More notes.

HomeBlogAboutContactPrivacy PolicyDisclaimer

© 2026 Karya Semi. All rights reserved.

XGitHubLinkedIn
  1. Home
  2. /Categories
  3. /Technology

Inside the Apple Silicon Hypervisor: Virtualizing Darwin OS Beyond the iOS Simulator

Boot guest iOS on Apple silicon. Use apple virtualization framework ios to run virtual machines. Set up storage, memory, kernel boot args.

Dian Rijal Asyrof/August 31, 2026/4 min read
Illustration for Inside the Apple Silicon Hypervisor: Virtualizing Darwin OS Beyond the iOS Simulator

Virtualize Darwin OS on Apple Silicon requires low-level hypervisor interaction. iOS Simulator translates user-space, lacks true guest kernel. Infrastructure testing, security research, isolated app execution need full kernel virtualization. Apple provides two paths: Hypervisor.framework for raw CPU state management, Virtualization.framework for high-level machine configuration.

Guest environments on Apple Silicon run on ARM64 architecture. Traditional x86 virtualization fails. Guest OS must target host ARM64 instruction set. Native APIs boot guest kernels with minimal overhead.

Virtualization Stack on Apple Silicon

Select abstraction level. Low-level path uses Hypervisor.framework. Interface exposes vCPUs, page tables, registers. Requires custom device emulation, guest memory mapping, device tree parsing. Necessary for custom OS, requires massive boilerplate.

High-level path uses Virtualization.framework. Framework handles device emulation. Provides virtual disks, network interfaces, serial consoles, graphics devices via Virtio protocol.

Boot process is main challenge for iOS-like environments. Apple Silicon boots via custom process using device tree, boot arguments. Virtualization.framework boots macOS guests via VZMacBootLoader. iOS shares Darwin foundation, allows booting custom Darwin kernels or isolated user-space environments.

Entitlements and Project Setup

Configure host security entitlements before writing Swift code. macOS kernel restricts hypervisor access to authorized binaries. Missing entitlements cause initialization failure.

Create Entitlements.entitlements in Xcode project:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>com.apple.security.virtualization</key>
    <true/>
    <key>com.apple.security.hypervisor</key>
    <true/>
</dict>
</plist>

com.apple.security.virtualization entitlement allows VM management. com.apple.security.hypervisor entitlement allows direct CPU virtualization access.

Configuring the Virtual Machine

VZVirtualMachineConfiguration defines guest hardware: CPUs, memory, storage, network.

Initialize VM in Swift:

import Virtualization
 
class VirtualMachineHost {
    private var virtualMachine: VZVirtualMachine?
    
    func createVirtualMachine() throws -> VZVirtualMachine {
        let configuration = VZVirtualMachineConfiguration()
        
        // Allocate hardware resources
        configuration.cpuCount = determineCPUCount()
        configuration.memorySize = determineMemorySize()
        
        // Configure the bootloader
        let bootLoader = VZMacBootLoader()
        configuration.bootLoader = bootLoader
        
        // Configure virtual devices
        try configureStorage(configuration)
        try configureNetwork(configuration)
        configureSerialConsole(configuration)
        configureGraphics(configuration)
        
        // Validate the configuration
        try configuration.validate()
        
        let vm = VZVirtualMachine(configuration: configuration)
        self.virtualMachine = vm
        return vm
    }
    
    private func determineCPUCount() -> Int {
        let hostCount = ProcessInfo.processInfo.activeProcessorCount
        // Leave at least 2 cores for the host OS to prevent UI freezes
        return max(2, hostCount - 2)
    }
    
    private func determineMemorySize() -> UInt64 {
        let hostMemory = ProcessInfo.processInfo.physicalMemory
        // Allocate 4 GB or half of host memory, whichever is lower
        let targetMemory = 4 * 1024 * 1024 * 1024
        return min(UInt64(targetMemory), hostMemory / 2)
    }
}

Helper allocates resources dynamically. Excess allocation starves host OS, causing latency, dropped frames.

Storage and File System Configuration

Guest requires boot disk. Apple Silicon guest expects APFS disk image containing kernel, libraries, user-space binaries. Use VZDiskImageStorageDeviceAttachment to attach raw disk image as block device.

private func configureStorage(_ configuration: VZVirtualMachineConfiguration) throws {
    let diskURL = URL(fileURLWithPath: "/var/vm/guest_disk.raw")
    
    // Create a raw disk image if it does not exist
    if !FileManager.default.fileExists(atPath: diskURL.path) {
        let diskSize: Int64 = 30 * 1024 * 1024 * 1024 // 30 GB
        FileManager.default.createFile(atPath: diskURL.path, contents: nil, attributes: nil)
        let fileHandle = try FileHandle(forWritingTo: diskURL)
        try fileHandle.truncate(atOffset: UInt64(diskSize))
        try fileHandle.close()
    }
    
    // Attach the disk using Virtio-Block
    let storageAttachment = try VZDiskImageStorageDeviceAttachment(
        url: diskURL,
        readOnly: false
    )
    
    let blockDevice = VZVirtioBlockDeviceConfiguration(attachment: storageAttachment)
    configuration.storageDevices = [blockDevice]
}

Code creates sparse disk image by truncating file. Avoids immediate 30 GB allocation. Host allocates blocks only on guest write.

Network Configuration

Guest needs network for automated testing. Configure interfaces via NAT or Bridged. NAT mode requires no host privileges, works on any interface.

private func configureNetwork(_ configuration: VZVirtualMachineConfiguration) throws {
    let networkAttachment = VZNATNetworkDeviceAttachment()
    let networkConfiguration = VZVirtioNetworkDeviceConfiguration()
    networkConfiguration.attachment = networkAttachment
    
    // Generate a stable MAC address for the guest
    if let macAddress = VZMACAddress(string: "02:56:aa:bb:cc:dd") {
        networkConfiguration.macAddress = macAddress
    }
    
    configuration.networkDevices = [networkConfiguration]
}

Use static MAC address. Random MAC address causes guest to treat every boot as new network, triggering DHCP lease exhaustion, resetting network configs.

Serial Console Redirection

Early boot stages of custom Darwin kernels lack graphical output. Configure serial console to capture kernel panics, system logs.

private func configureSerialConsole(_ configuration: VZVirtualMachineConfiguration) {
    let serialConnection = VZVirtioConsoleDeviceSerialPortConfiguration()
    
    // Redirect serial output to standard output
    let fileHandleForWriting = FileHandle.standardOutput
    let fileHandleForReading = FileHandle.standardInput
    
    let attachment = VZFileHandleSerialPortAttachment(
        fileHandleForReading: fileHandleForReading,
        fileHandleForWriting: fileHandleForWriting
    )
    
    serialConnection.attachment = attachment
    configuration.serialPorts = [serialConnection]
}

Setup connects guest virtual serial port to host standard input, output. Guest serial writes output to host terminal.

Graphics and Input Devices

Graphical interface requires virtual display, input devices. Virtualization framework uses Virtio-GPU for display.

private func configureGraphics(_ configuration: VZVirtualMachineConfiguration) {
    let graphicsConfiguration = VZMacGraphicsDeviceConfiguration()
    
    // Define the virtual display dimensions and pixel density
    let displayConfiguration = VZMacGraphicsDisplayConfiguration(
        widthInPixels: 1920,
        heightInPixels: 1200,
        pixelsPerInch: 220
    )
    
    graphicsConfiguration.displays = [displayConfiguration]
    configuration.graphicsDevices = [graphicsConfiguration]
    
    // Add input devices
    let pointingDevice = VZUSBScreenCoordinatePointingDeviceConfiguration()
    let keyboardDevice = VZUSBKeyboardConfiguration()
    
    configuration.pointingDevices = [pointingDevice]
    configuration.keyboards = [keyboardDevice]
}

Pointing device uses screen coordinates. Pointer movements translate to absolute coordinates in guest. Required for UI testing.

Managing the Virtual Machine Lifecycle

Instantiate VM after validation. VZVirtualMachine manages state transitions. VM start is asynchronous, requires completion handler, delegate monitoring.

class VMManager: NSObject, VZVirtualMachineDelegate {
    private var vm: VZVirtualMachine?
    
    func boot(configuration: VZVirtualMachineConfiguration) {
        let vm = VZVirtualMachine(configuration: configuration)
        vm.delegate = self
        self.vm = vm
        
        vm.start { result in
            switch result {
            case .success:
                print("Virtual machine booted successfully.")
            case .failure(let error):
                print("Failed to boot virtual machine: \(error.localizedDescription)")
            }
        }
    }
    
    // MARK: VZVirtualMachineDelegate
    
    func guestDidStop(_ virtualMachine: VZVirtualMachine) {
        print("Guest operating system shut down.")
    }
    
    func virtualMachine(_ virtualMachine: VZVirtualMachine, didStopWithError error: Error) {
        print("Virtual machine stopped due to error: \(error.localizedDescription)")
    }
}

Kernel panic stops VM, triggers didStopWithError delegate. Monitor callback to log failures, spawn clean instances.

Handling the iOS Boot Process and IPSW Files

Booting iOS directly requires bypassing security architecture. Physical iOS uses Secure Enclave Processor (SEP) to verify boot chain, decrypt files. Virtual guest lacks SEP access.

Two approaches run iOS apps in virtual infrastructure:

Approach 1: macOS Guest Wrapper

Boot minimal macOS guest instead of raw iOS IPSW. macOS guests run iOS apps natively on Apple Silicon. Avoids boot security bypass. Install, run iOS apps in guest macOS via command-line:

# Install an iOS application package (IPA) inside the guest
xcrun simctl install booted /path/to/app.ipa
 
# Launch the application
xcrun simctl launch booted com.example.iosapp

Method uses official APIs, runs iOS tests in isolated guest.

Approach 2: Custom Darwin Kernels

Security research requires custom Darwin kernel. Extract kernelcache from iOS IPSW, package with custom device tree.

Extract IPSW:

unzip -p iOS_Restore.ipsw AssetData/boot/kernelcache.release.iphone13 > kernelcache

Use custom bootloader to pass kernel image, boot arguments. Default VZMacBootLoader expects macOS container. Configure virtual disks to match Darwin kernel layout.

let customBootLoader = VZLinuxBootLoader()
customBootLoader.kernelURL = URL(fileURLWithPath: "/path/to/extracted/kernelcache")
customBootLoader.commandLine = "console=uart0 acpi=off root=/dev/vda1"

Linux bootloader workarounds boot raw Mach-O kernels. Bypasses macOS boot checks, passes custom device trees, boot arguments directly.

Performance Tuning for CI/CD Pipelines

Optimize boot time, resource consumption for CI/CD.

Minimize disk I/O: store base images on APFS, use copy-on-write clones. Duplicates 30 GB image instantly without physical copy:

# Create an instant clone of the base image
cp -c /var/vm/base_image.raw /var/vm/test_instance_1.raw

-c flag shares physical blocks between base, clone. Writes occur only on modification, reducing disk wear, accelerating provisioning.

Disable graphics for command-line access. Removing graphics config saves megabytes, increases VM concurrency per host.

DR

Dian Rijal Asyrof

Writes about useful AI tools, programming practice, and the craft of building reliable software.

Previous articlePorting Gemma 4 in Pure JAX Across TPU and GPU Architectures
VirtualizationIosBootingCIApple Silicon
On this page↓
  1. Virtualization Stack on Apple Silicon
  2. Entitlements and Project Setup
  3. Configuring the Virtual Machine
  4. Storage and File System Configuration
  5. Network Configuration
  6. Serial Console Redirection
  7. Graphics and Input Devices
  8. Managing the Virtual Machine Lifecycle
  9. Handling the iOS Boot Process and IPSW Files
  10. Approach 1: macOS Guest Wrapper
  11. Approach 2: Custom Darwin Kernels
  12. Performance Tuning for CI/CD Pipelines

On this page

  1. Virtualization Stack on Apple Silicon
  2. Entitlements and Project Setup
  3. Configuring the Virtual Machine
  4. Storage and File System Configuration
  5. Network Configuration
  6. Serial Console Redirection
  7. Graphics and Input Devices
  8. Managing the Virtual Machine Lifecycle
  9. Handling the iOS Boot Process and IPSW Files
  10. Approach 1: macOS Guest Wrapper
  11. Approach 2: Custom Darwin Kernels
  12. Performance Tuning for CI/CD Pipelines

See also

Illustration for Automating Headless iOS Virtual Machines for Security Research with vphone-cli
Technology/Aug 31, 2026

Automating Headless iOS Virtual Machines for Security Research with vphone-cli

Configure virtual iphone virtualization framework on macOS. Boot iOS environments with vphone-cli tool for fast local testing and debugging.

7 min read
VirtualizationIphones
Illustration for Boot Virtual iOS Instances Using Apple Virtualization Framework CLI
Software Engineering/Aug 31, 2026

Boot Virtual iOS Instances Using Apple Virtualization Framework CLI

Boot virtual iphone virtualization framework instances on Apple silicon. Control iOS environments via CLI. Speed up development.

8 min read
IosCli
Illustration for GitHub Actions Parallel Steps: What CI Teams Should Check First
Software Engineering/Jun 29, 2026

GitHub Actions Parallel Steps: What CI Teams Should Check First

GitHub Actions parallel steps can cut CI waiting time, but only if teams clean up shared state, logs, caches, and test ownership first.

4 min read
GitHub ActionsCI