[CmdletBinding()] param( [string]$RomPath = (Join-Path $PSScriptRoot 'firmware\s5l8950x-secure-rom.bin'), [Alias('IbootPath')] [string]$DfuImagePath, [string]$MsysRoot = 'C:\msys64', [ValidateRange(1024, 65535)] [int]$UsbBridgePort = 26050, [switch]$NoWindowsUsb, [switch]$SkipBuild, [switch]$Trace, [ValidateRange(0, 65535)] [int]$GdbPort = 0, [switch]$Paused ) Set-StrictMode -Version Latest $ErrorActionPreference = 'Stop' if ([Environment]::OSVersion.Platform -ne [PlatformID]::Win32NT) { throw 'This launcher requires native Windows.' } $qemuSource = $PSScriptRoot $buildDir = Join-Path $qemuSource 'build' $mingwBin = Join-Path $MsysRoot 'mingw64\bin' $msysBin = Join-Path $MsysRoot 'usr\bin' $ninja = Join-Path $mingwBin 'ninja.exe' $qemu = Join-Path $buildDir 'qemu-system-arm.exe' $buildTemp = Join-Path $buildDir 'tmp' $bridge = Join-Path $PSScriptRoot 'windows\QemuA6UsbBridge\build\Release\x64\QemuA6UsbBridge.exe' $bridgeBuild = Join-Path $PSScriptRoot 'windows\QemuA6UsbBridge\build-bridge.ps1' $bridgeStdout = Join-Path $buildDir 'a6-usb-bridge.log' $bridgeStderr = Join-Path $buildDir 'a6-usb-bridge-error.log' if (-not (Test-Path -LiteralPath $RomPath -PathType Leaf)) { throw "SecureROM not found: $RomPath" } $rom = Get-Item -LiteralPath $RomPath if ($rom.Length -ne 0x10000) { throw "SecureROM must be exactly 65536 bytes (actual size: $($rom.Length))." } $dfuImage = $null if ($DfuImagePath) { if (-not (Test-Path -LiteralPath $DfuImagePath -PathType Leaf)) { throw "DFU/iBSS image not found: $DfuImagePath" } $dfuImage = Get-Item -LiteralPath $DfuImagePath if ($dfuImage.Length -eq 0) { throw "The DFU/iBSS image is empty: $($dfuImage.FullName)" } } $windowsUsb = -not $NoWindowsUsb -and $null -eq $dfuImage $env:Path = "$mingwBin;$msysBin;$env:Path" if (-not $SkipBuild) { if (-not (Test-Path -LiteralPath $ninja -PathType Leaf)) { throw "MinGW64 Ninja not found: $ninja" } if (-not (Test-Path -LiteralPath (Join-Path $msysBin 'sort.exe') -PathType Leaf)) { throw "The MSYS2 tool set is incomplete: $msysBin" } if (-not (Test-Path -LiteralPath (Join-Path $buildDir 'build.ninja') -PathType Leaf)) { throw "qemu\build is not configured. Configure QEMU from MSYS2 MinGW64 with --target-list=arm-softmmu." } [IO.Directory]::CreateDirectory($buildTemp) | Out-Null $env:TMP = $buildTemp $env:TEMP = $buildTemp $env:TMPDIR = $buildTemp & $ninja -C $buildDir qemu-system-arm.exe if ($LASTEXITCODE -ne 0) { throw "The QEMU build failed with exit code $LASTEXITCODE." } } if (-not (Test-Path -LiteralPath $qemu -PathType Leaf)) { throw "QEMU binary not found: $qemu" } $machines = & $qemu -machine help 2>&1 if ($LASTEXITCODE -ne 0 -or -not ($machines -match '^iphone5\s')) { throw 'This QEMU binary does not include the iphone5 machine.' } $qemuArgs = @( '-machine', 'iphone5', '-bios', $rom.FullName, '-no-reboot', '-no-shutdown' ) if ($dfuImage) { $qemuArgs += @( '-global', "s5l8950x-usb-otg.dfu-image=$($dfuImage.FullName)" ) } if ($windowsUsb) { $qemuArgs += @( '-chardev', "socket,id=a6usb,host=127.0.0.1,port=$UsbBridgePort,server=on,wait=off", '-global', 's5l8950x-usb-otg.usb-bridge=a6usb' ) } if ($Paused -and $GdbPort -eq 0) { $GdbPort = 1234 } if ($GdbPort -gt 0) { $qemuArgs += @('-gdb', "tcp:127.0.0.1:$GdbPort") } if ($Paused) { $qemuArgs += '-S' } # UART0 and the monitor stay multiplexed on the native console, including # while the CPU is stopped for a debugger (Ctrl+A C toggles the monitor). $qemuArgs += @('-display', 'none', '-serial', 'mon:stdio') if ($Trace) { $tracePath = Join-Path $buildDir 'a6-boot.log' $qemuArgs += @( '-d', 'in_asm,cpu_reset,guest_errors,unimp', '-D', $tracePath ) Write-Host "QEMU trace : $tracePath" } Write-Host "Native Windows QEMU : $qemu" Write-Host "SecureROM : $($rom.FullName)" if ($dfuImage) { Write-Host "DFU/iBSS image : $($dfuImage.FullName)" } if ($windowsUsb) { if (-not (Test-Path -LiteralPath $bridge -PathType Leaf)) { & $bridgeBuild -Configuration Release } Write-Host 'Windows USB : Apple DFU VID_05AC&PID_1227 through UDE' Write-Host "QEMU bridge : 127.0.0.1:$UsbBridgePort" Write-Host "USB bridge log : $bridgeStdout" } if ($dfuImage) { Write-Host 'The USB cable and DFU transfer are simulated internally.' } elseif ($windowsUsb) { Write-Host 'Windows exposes the virtual DFU phone to unmodified host tools.' } else { Write-Host 'Recovery and the USB cable are simulated; the ROM is waiting in DFU mode.' } Write-Host 'A6 UART : this console (Ctrl+A C = monitor)' Write-Host 'Press Ctrl+A, then X to quit.' if ($GdbPort -gt 0) { Write-Host "Debugger : remote GDB at 127.0.0.1:$GdbPort" Write-Host "LLDB : lldb -o 'gdb-remote 127.0.0.1:$GdbPort'" } if ($Paused) { Write-Host 'CPU : paused until the debugger issues continue' } $bridgeProcess = $null $exitCode = 1 try { if ($windowsUsb) { $bridgeProcess = Start-Process -FilePath $bridge ` -ArgumentList @('127.0.0.1', $UsbBridgePort.ToString()) ` -WindowStyle Hidden -PassThru ` -RedirectStandardOutput $bridgeStdout ` -RedirectStandardError $bridgeStderr } & $qemu @qemuArgs $exitCode = $LASTEXITCODE } finally { if ($null -ne $bridgeProcess -and -not $bridgeProcess.HasExited) { Stop-Process -Id $bridgeProcess.Id $bridgeProcess.WaitForExit() } } exit $exitCode