From 719108f0f76b55ec3624bbf73950324a12bd83d7 Mon Sep 17 00:00:00 2001 From: osy <50960678+osy@users.noreply.github.com> Date: Tue, 4 Aug 2026 07:21:24 -0700 Subject: [PATCH 1/4] qemu: fix hostmem=8G for iOS --- .../UTMQemuConfiguration+Arguments.swift | 8 - patches/qemu-10.0.12-utm.patch | 201 ++++++++++++++++++ 2 files changed, 201 insertions(+), 8 deletions(-) diff --git a/Configuration/UTMQemuConfiguration+Arguments.swift b/Configuration/UTMQemuConfiguration+Arguments.swift index 36ccfdafd0..1fd7d70b80 100644 --- a/Configuration/UTMQemuConfiguration+Arguments.swift +++ b/Configuration/UTMQemuConfiguration+Arguments.swift @@ -284,21 +284,13 @@ import Virtualization // for getting network interfaces "edid=on" } if isDisplayGLSupported(display) && isVulkanSupported { - #if os(macOS) "hostmem=8G" - #else - "hostmem=256M" - #endif "blob=true" "venus=true" } if isDisplayGLSupported(display) && isNeptuneSupported { if !isVulkanSupported { - #if os(macOS) "hostmem=8G" - #else - "hostmem=256M" - #endif "blob=true" } "neptune=true" diff --git a/patches/qemu-10.0.12-utm.patch b/patches/qemu-10.0.12-utm.patch index 2cc6a8f2a5..70756cdb44 100644 --- a/patches/qemu-10.0.12-utm.patch +++ b/patches/qemu-10.0.12-utm.patch @@ -546,3 +546,204 @@ index a2dddbd37a..f6d7879eff 100644 -- 2.41.0 +From 7311c3651c3a2cbc3d32e6eae262c60339f28d79 Mon Sep 17 00:00:00 2001 +From: osy +Date: Sun, 2 Aug 2026 21:04:16 -0700 +Subject: [PATCH] hw/arm/virt: shrink the high PCIe MMIO window to fit small PA + spaces + +Previously, if the high PCIe MMIO window did not fit in the guest +physical address space, it was silently disabled, leaving no room to +place any 64-bit BAR too large for the 32-bit window. On hosts with a +small IPA space (e.g. a 36-bit IPA), this made devices like virtio-gpu +with a large hostmem BAR unusable. + +Instead, shrink the window to the largest power of two that fits (no +smaller than 1GiB) and warn about it. Only if even the minimum does not +fit is the window disabled, and each high region now warns when it is +disabled for not fitting rather than vanishing silently. + +To make room at all on such hosts, skip the legacy 256GiB floor for the +high IO region when the high regions cannot fit there, placing them +right above RAM instead. !highmem keeps the floor since disabling the +regions is the point of that option. + +Accordingly, highmem-mmio-size now accepts any power of 2 down to 1GiB +instead of rejecting values below the 512GiB default. + +Assisted-by: Claude:claude-fable-5 +--- + docs/system/arm/virt.rst | 7 ++- + hw/arm/virt.c | 95 +++++++++++++++++++++++++++++++++++++--- + 2 files changed, 94 insertions(+), 8 deletions(-) + +diff --git a/docs/system/arm/virt.rst b/docs/system/arm/virt.rst +index 6a719b9586..ad054d4fed 100644 +--- a/docs/system/arm/virt.rst ++++ b/docs/system/arm/virt.rst +@@ -145,8 +145,11 @@ highmem-mmio + The default is ``on``. + + highmem-mmio-size +- Set the high memory region size for PCI MMIO. Must be a power of 2 and +- greater than or equal to the default size (512G). ++ Set the high memory region size for PCI MMIO. Must be a power of 2 and at ++ least 1G. The default is 512G. If the requested window does not fit in the ++ guest physical address space, it is shrunk to the largest power of 2 that ++ does (but no smaller than 1G) and a warning is emitted; if even that does ++ not fit, the window is disabled. + + gic-version + Specify the version of the Generic Interrupt Controller (GIC) to provide. +diff --git a/hw/arm/virt.c b/hw/arm/virt.c +index 22ff33a33f..3314fcc791 100644 +--- a/hw/arm/virt.c ++++ b/hw/arm/virt.c +@@ -197,6 +197,8 @@ static const MemMapEntry base_memmap[] = { + /* Update the docs for highmem-mmio-size when changing this default */ + #define DEFAULT_HIGH_PCIE_MMIO_SIZE_GB 512 + #define DEFAULT_HIGH_PCIE_MMIO_SIZE (DEFAULT_HIGH_PCIE_MMIO_SIZE_GB * GiB) ++/* Smallest window we will auto-shrink to, or accept from the user */ ++#define VIRT_HIGH_PCIE_MMIO_MIN_SIZE (1 * GiB) + + /* + * Highmem IO Regions: This memory map is floating, located after the RAM. +@@ -1812,6 +1814,40 @@ static inline bool *virt_get_high_memmap_enabled(VirtMachineState *vms, + return enabled_array[index - VIRT_LOWMEMMAP_LAST]; + } + ++static inline const char *virt_high_memmap_name(int index) ++{ ++ static const char * const names[] = { ++ "GIC redistributor", ++ "PCIe ECAM", ++ "PCIe MMIO", ++ }; ++ ++ assert(ARRAY_SIZE(extended_memmap) - VIRT_LOWMEMMAP_LAST == ++ ARRAY_SIZE(names)); ++ assert(index - VIRT_LOWMEMMAP_LAST < ARRAY_SIZE(names)); ++ ++ return names[index - VIRT_LOWMEMMAP_LAST]; ++} ++ ++/* ++ * Return the first address above the high memmap if it were laid out ++ * starting at @base, assuming every region fits at full size. Mirrors the ++ * placement rules of virt_set_high_memmap(). ++ */ ++static hwaddr virt_high_memmap_end(VirtMachineState *vms, hwaddr base) ++{ ++ int i; ++ ++ for (i = VIRT_LOWMEMMAP_LAST; i < ARRAY_SIZE(extended_memmap); i++) { ++ if (vms->highmem_compact && !*virt_get_high_memmap_enabled(vms, i)) { ++ continue; ++ } ++ base = ROUND_UP(base, extended_memmap[i].size) + extended_memmap[i].size; ++ } ++ ++ return base; ++} ++ + static void virt_set_high_memmap(VirtMachineState *vms, + hwaddr base, int pa_bits) + { +@@ -1824,6 +1860,33 @@ static void virt_set_high_memmap(VirtMachineState *vms, + region_base = ROUND_UP(base, extended_memmap[i].size); + region_size = extended_memmap[i].size; + ++ /* ++ * The high PCIe MMIO window is the only configurable high region ++ * and by far the largest. If it doesn't fit (e.g. in a 36-bit PA ++ * space), shrink it to the largest power of two that does rather ++ * than dropping it: without it, no 64-bit BAR too large for the ++ * 32-bit window can ever be placed. ++ */ ++ if (i == VIRT_HIGH_PCIE_MMIO && *region_enabled && vms->highmem) { ++ while (region_size > VIRT_HIGH_PCIE_MMIO_MIN_SIZE && ++ region_base + region_size > BIT_ULL(pa_bits)) { ++ region_size /= 2; ++ region_base = ROUND_UP(base, region_size); ++ } ++ if (region_base + region_size > BIT_ULL(pa_bits)) { ++ /* Not even the minimum fits; restore and disable below */ ++ region_size = extended_memmap[i].size; ++ region_base = ROUND_UP(base, region_size); ++ } else if (region_size != extended_memmap[i].size) { ++ g_autofree char *from = size_to_str(extended_memmap[i].size); ++ g_autofree char *to = size_to_str(region_size); ++ ++ warn_report("high PCIe MMIO window shrunk from %s to %s to fit " ++ "in a %d-bit physical address space", from, to, ++ pa_bits); ++ } ++ } ++ + vms->memmap[i].base = region_base; + vms->memmap[i].size = region_size; + +@@ -1836,6 +1899,12 @@ static void virt_set_high_memmap(VirtMachineState *vms, + * For each device that doesn't fit, disable it. + */ + fits = (region_base + region_size) <= BIT_ULL(pa_bits); ++ /* !highmem disables these on purpose, so keep quiet about it */ ++ if (*region_enabled && !fits && vms->highmem) { ++ warn_report("high %s region does not fit in a %d-bit physical " ++ "address space and has been disabled", ++ virt_high_memmap_name(i), pa_bits); ++ } + *region_enabled &= fits; + if (vms->highmem_compact && !*region_enabled) { + continue; +@@ -1851,7 +1920,7 @@ static void virt_set_high_memmap(VirtMachineState *vms, + static void virt_set_memmap(VirtMachineState *vms, int pa_bits) + { + MachineState *ms = MACHINE(vms); +- hwaddr base, device_memory_base, device_memory_size, memtop; ++ hwaddr base, device_memory_base, device_memory_size, memtop, legacy_base; + int i; + + vms->memmap = extended_memmap; +@@ -1900,8 +1969,18 @@ static void virt_set_memmap(VirtMachineState *vms, int pa_bits) + error_report("maxmem/slots too huge"); + exit(EXIT_FAILURE); + } +- if (base < vms->memmap[VIRT_MEM].base + LEGACY_RAMLIMIT_BYTES) { +- base = vms->memmap[VIRT_MEM].base + LEGACY_RAMLIMIT_BYTES; ++ /* ++ * Pin the high IO region at 256GiB so a VM with less than 255GiB of ++ * RAM keeps the legacy memory map. Skip that floor when the high ++ * regions cannot fit there in the PA space, as they would all be ++ * disabled and the guest left with no 64-bit MMIO window. !highmem ++ * keeps the floor: disabling them is the point of that option. ++ */ ++ legacy_base = vms->memmap[VIRT_MEM].base + LEGACY_RAMLIMIT_BYTES; ++ if (base < legacy_base && ++ (!vms->highmem || ++ virt_high_memmap_end(vms, legacy_base) <= BIT_ULL(pa_bits))) { ++ base = legacy_base; + } + + /* We know for sure that at least the memory fits in the PA space */ +@@ -2594,10 +2673,14 @@ static void virt_set_highmem_mmio_size(Object *obj, Visitor *v, + return; + } + +- if (size < DEFAULT_HIGH_PCIE_MMIO_SIZE) { +- char *sz = size_to_str(DEFAULT_HIGH_PCIE_MMIO_SIZE); ++ /* ++ * Windows smaller than the default are allowed: a host with a small ++ * PA space cannot fit the default anywhere. ++ */ ++ if (size < VIRT_HIGH_PCIE_MMIO_MIN_SIZE) { ++ char *sz = size_to_str(VIRT_HIGH_PCIE_MMIO_MIN_SIZE); + error_setg(errp, "highmem-mmio-size cannot be set to a lower value " +- "than the default (%s)", sz); ++ "than %s", sz); + g_free(sz); + return; + } +-- +2.41.0 + From 5f13bb356ac3c19bae01f66d17f4959d1e798012 Mon Sep 17 00:00:00 2001 From: osy <50960678+osy@users.noreply.github.com> Date: Tue, 4 Aug 2026 07:22:46 -0700 Subject: [PATCH 2/4] project: add dxmt to iOS builds --- .../UTMQemuConfiguration+Arguments.swift | 4 -- Platform/iOS/Settings.bundle/Root.plist | 26 ++++++++++ UTM.xcodeproj/project.pbxproj | 8 +-- patches/llvm-project-15.0.7.src.patch | 49 ++++++++++++++++++ patches/sources | 2 +- scripts/build_dependencies.sh | 51 +++++++++++++++---- 6 files changed, 121 insertions(+), 19 deletions(-) create mode 100644 patches/llvm-project-15.0.7.src.patch diff --git a/Configuration/UTMQemuConfiguration+Arguments.swift b/Configuration/UTMQemuConfiguration+Arguments.swift index 1fd7d70b80..8488af2e8b 100644 --- a/Configuration/UTMQemuConfiguration+Arguments.swift +++ b/Configuration/UTMQemuConfiguration+Arguments.swift @@ -349,13 +349,9 @@ import Virtualization // for getting network interfaces } private var isNeptuneSupported: Bool { - #if os(macOS) isGLSupported && (rendererBackend == .qemuRendererBackendAngleMetal || rendererBackend == .qemuRendererBackendDefault) && directXDriver != .qemuDirectXDriverDisabled - #else - false - #endif } private var isSparc: Bool { diff --git a/Platform/iOS/Settings.bundle/Root.plist b/Platform/iOS/Settings.bundle/Root.plist index 4dd7e42818..b7eacab977 100644 --- a/Platform/iOS/Settings.bundle/Root.plist +++ b/Platform/iOS/Settings.bundle/Root.plist @@ -171,6 +171,32 @@ iOS-Remote + + Type + PSMultiValueSpecifier + Title + DirectX Driver + Key + QEMUDirectXDriver + DefaultValue + 0 + Titles + + Default + Disabled + DXMT + + Values + + 0 + 1 + 2 + + ExcludeTargets + + iOS-Remote + + Type PSMultiValueSpecifier diff --git a/UTM.xcodeproj/project.pbxproj b/UTM.xcodeproj/project.pbxproj index e160d35bb8..2221f91b39 100644 --- a/UTM.xcodeproj/project.pbxproj +++ b/UTM.xcodeproj/project.pbxproj @@ -632,6 +632,8 @@ CE2D958E24AD4F990059923A /* UTMApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE2D955524AD4F980059923A /* UTMApp.swift */; }; CE2D958F24AD4FF00059923A /* VMCardView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE2D954324AD4F980059923A /* VMCardView.swift */; }; CE2D959024AD50D50059923A /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 521F3EFB2414F73800130500 /* Localizable.strings */; }; + CE30F93630200C2000024DB2 /* dxmt-native.framework in Embed Libraries */ = {isa = PBXBuildFile; fileRef = CEA3EDE93016810500AAA8E4 /* dxmt-native.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + CE30F93730200C3300024DB2 /* dxmt-native.framework in Embed Libraries */ = {isa = PBXBuildFile; fileRef = CEA3EDE93016810500AAA8E4 /* dxmt-native.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; CE38EC692B5DB3AE008B324B /* UTMRemoteClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE38EC682B5DB3AE008B324B /* UTMRemoteClient.swift */; }; CE4698F924C8FBD9008C1BD6 /* Icons in Resources */ = {isa = PBXBuildFile; fileRef = CE4698F824C8FBD9008C1BD6 /* Icons */; }; CE4698FA24C8FBD9008C1BD6 /* Icons in Resources */ = {isa = PBXBuildFile; fileRef = CE4698F824C8FBD9008C1BD6 /* Icons */; }; @@ -735,9 +737,7 @@ CE9B15482B12A87E003A32DD /* GenerateKey.c in Sources */ = {isa = PBXBuildFile; fileRef = CE9B15462B12A87E003A32DD /* GenerateKey.c */; }; CE9B15492B12A87E003A32DD /* GenerateKey.c in Sources */ = {isa = PBXBuildFile; fileRef = CE9B15462B12A87E003A32DD /* GenerateKey.c */; }; CE9B154A2B12A87E003A32DD /* GenerateKey.c in Sources */ = {isa = PBXBuildFile; fileRef = CE9B15462B12A87E003A32DD /* GenerateKey.c */; }; - CEA3EDEB3016810500AAA8E4 /* dxmt-native.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CEA3EDE93016810500AAA8E4 /* dxmt-native.framework */; }; CEA3EDEC3016810500AAA8E4 /* dxmt-native.framework in Embed Libraries */ = {isa = PBXBuildFile; fileRef = CEA3EDE93016810500AAA8E4 /* dxmt-native.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - CEA3EDED3016810500AAA8E4 /* d3dmetal-native.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CEA3EDEA3016810500AAA8E4 /* d3dmetal-native.framework */; }; CEA3EDEE3016810500AAA8E4 /* d3dmetal-native.framework in Embed Libraries */ = {isa = PBXBuildFile; fileRef = CEA3EDEA3016810500AAA8E4 /* d3dmetal-native.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; CEA3EE05301691C500AAA8E4 /* QEMURenderServer.app in Embed Executables */ = {isa = PBXBuildFile; fileRef = CEA3EDFA30168B9000AAA8E4 /* QEMURenderServer.app */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; CEA45E25263519B5002FA97D /* VMDisplayMetalViewController+Pointer.h in Sources */ = {isa = PBXBuildFile; fileRef = 83FBDD53242FA71900D2C5D7 /* VMDisplayMetalViewController+Pointer.h */; }; @@ -1452,6 +1452,7 @@ dstPath = ""; dstSubfolderSpec = 10; files = ( + CE30F93730200C3300024DB2 /* dxmt-native.framework in Embed Libraries */, CE2D937624AD46670059923A /* gpg-error.0.framework in Embed Libraries */, CE2D937724AD46670059923A /* qemu-mips64el-softmmu.framework in Embed Libraries */, CE2D937824AD46670059923A /* gstcontroller-1.0.0.framework in Embed Libraries */, @@ -1553,6 +1554,7 @@ dstPath = ""; dstSubfolderSpec = 10; files = ( + CE30F93630200C2000024DB2 /* dxmt-native.framework in Embed Libraries */, CEA45F72263519B5002FA97D /* gpg-error.0.framework in Embed Libraries */, CE63B0FD2F0EFB0700C59D13 /* vulkan_kosmickrisp.framework in Embed Libraries */, CEA45F74263519B5002FA97D /* gstcontroller-1.0.0.framework in Embed Libraries */, @@ -2369,7 +2371,6 @@ CEF83F882500949D00557D15 /* gthread-2.0.0.framework in Frameworks */, CE03D08724D90F0700F76B84 /* gobject-2.0.0.framework in Frameworks */, CE9B15362B11A491003A32DD /* SwiftConnect in Frameworks */, - CEA3EDED3016810500AAA8E4 /* d3dmetal-native.framework in Frameworks */, CE0B6F0A24AD677200FE012D /* spice-client-glib-2.0.8.framework in Frameworks */, CE0B6ECF24AD677200FE012D /* gstrtp-1.0.0.framework in Frameworks */, CE0B6ECC24AD677200FE012D /* gstriff-1.0.0.framework in Frameworks */, @@ -2385,7 +2386,6 @@ CE0B6EBB24AD677200FE012D /* libgstgio.a in Frameworks */, 84937EFF28960789003148F4 /* zstd.1.framework in Frameworks */, CE0B6EE224AD677200FE012D /* gstnet-1.0.0.framework in Frameworks */, - CEA3EDEB3016810500AAA8E4 /* dxmt-native.framework in Frameworks */, CE03D08624D90F0700F76B84 /* gmodule-2.0.0.framework in Frameworks */, CED297142CE425CB00F1E3EB /* soup-3.0.0.framework in Frameworks */, CE03D0CA24D9142000F76B84 /* ssl.1.1.framework in Frameworks */, diff --git a/patches/llvm-project-15.0.7.src.patch b/patches/llvm-project-15.0.7.src.patch new file mode 100644 index 0000000000..c321c6ff8f --- /dev/null +++ b/patches/llvm-project-15.0.7.src.patch @@ -0,0 +1,49 @@ +LLVM 15 gates its Mach-O linker behaviour on `CMAKE_SYSTEM_NAME MATCHES "Darwin"`, +which is false when cross-compiling for iOS/visionOS (CMAKE_SYSTEM_NAME is "iOS" / +"visionOS"). The build then falls through to the ELF/COFF paths and passes flags +Apple's ld rejects. Use `APPLE`, which is true for every Apple platform. + +Fixed upstream after 15.x; drop this patch when moving to a newer LLVM. + + 1. add_llvm_symbol_exports() fell through to the Windows branch and handed a + .def file to ld: + ld: unknown file type in '.../LLVMHello.def' + + 2. -Wl,-z,defs is ELF-only and was not excluded for iOS: + ld: unknown options: -z + + 3. add_link_opts() skipped -Wl,-dead_strip, so non-Debug builds silently lost + dead stripping that the macOS build of the same sources already gets. + Not a build failure, just larger output. + +--- a/llvm/cmake/modules/AddLLVM.cmake ++++ b/llvm/cmake/modules/AddLLVM.cmake +@@ -82,7 +82,7 @@ + endfunction() + + function(add_llvm_symbol_exports target_name export_file) +- if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") ++ if(APPLE) + set(native_export_file "${target_name}.exports") + add_custom_command(OUTPUT ${native_export_file} + COMMAND sed -e "s/^/_/" < ${export_file} > ${native_export_file} +@@ -263,7 +263,7 @@ + # linker in a context where the optimizations are not important. + if (NOT uppercase_CMAKE_BUILD_TYPE STREQUAL "DEBUG") + if(NOT LLVM_NO_DEAD_STRIP) +- if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") ++ if(APPLE) + # ld64's implementation of -dead_strip breaks tools that use plugins. + set_property(TARGET ${target_name} APPEND_STRING PROPERTY + LINK_FLAGS " -Wl,-dead_strip") +--- a/llvm/cmake/modules/HandleLLVMOptions.cmake ++++ b/llvm/cmake/modules/HandleLLVMOptions.cmake +@@ -229,7 +229,7 @@ + # Pass -Wl,-z,defs. This makes sure all symbols are defined. Otherwise a DSO + # build might work on ELF but fail on MachO/COFF. + if(NOT (CMAKE_SYSTEM_NAME MATCHES "Darwin|FreeBSD|OpenBSD|DragonFly|AIX|SunOS|OS390" OR +- WIN32 OR CYGWIN) AND ++ APPLE OR WIN32 OR CYGWIN) AND + NOT LLVM_USE_SANITIZER) + set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,-z,defs") + endif() diff --git a/patches/sources b/patches/sources index 6f183033e2..0b77553359 100644 --- a/patches/sources +++ b/patches/sources @@ -53,7 +53,7 @@ MOLTENVK_REPO="https://github.com/utmapp/MoltenVK.git" MOLTENVK_COMMIT="ef1c5461774f5fbd224ddcfd91fd2c0ea23f0384" LLVM15_SRC="https://github.com/llvm/llvm-project/releases/download/llvmorg-15.0.7/llvm-project-15.0.7.src.tar.xz" DXMT_REPO="https://github.com/utmapp/dxmt.git" -DXMT_COMMIT="be80e14c07613cd558036a576b82dc6d78acdfce" +DXMT_COMMIT="822ae637a39512ddbd8e0bbd7744af514670c9d3" D3DMETAL_REPO="https://github.com/utmapp/d3dmetal-native.git" D3DMETAL_COMMIT="edd2e386c25a2039eca0f6ce8f794b3b165841eb" diff --git a/scripts/build_dependencies.sh b/scripts/build_dependencies.sh index 2cfedfbfc2..e39881a068 100755 --- a/scripts/build_dependencies.sh +++ b/scripts/build_dependencies.sh @@ -301,6 +301,26 @@ generate_meson_cross() { else echo "system = '$system'" >> $cross fi + if [ "$system" == "darwin" ]; then + echo "kernel = 'xnu'" >> $cross + case $PLATFORM in + ios_simulator* ) + echo "subsystem = 'ios-simulator'" >> $cross + ;; + ios* ) + echo "subsystem = 'ios'" >> $cross + ;; + visionos_simulator* ) + echo "subsystem = 'visionos-simulator'" >> $cross + ;; + visionos* ) + echo "subsystem = 'visionos'" >> $cross + ;; + macos ) + echo "subsystem = 'macos'" >> $cross + ;; + esac + fi case "$ARCH" in armv7 | armv7s ) echo "cpu_family = 'arm'" >> $cross @@ -625,6 +645,19 @@ meson_darwin_build () { meson_cross_build darwin $@ } +# The generated toolchain file is the single source of truth for CMake builds: +# it sets every compiler, binutil, and flag variable with CACHE FORCE. The +# exported CC/CFLAGS/... are needed by the autotools and meson builds, but for +# CMake they are redundant *and* harmful: a nested host configure that does not +# get the toolchain file (such as LLVM's NATIVE sub-build for llvm-tblgen) +# inherits them from the environment and ends up compiling for the target while +# discovering and linking host libraries. Drop them for every cmake invocation. +cmake_clean_env () { + env -u CC -u CPP -u CXX -u OBJCC -u LD -u AR -u NM -u RANLIB -u STRIP \ + -u CFLAGS -u CPPFLAGS -u CXXFLAGS -u OBJCFLAGS -u LDFLAGS \ + cmake "$@" +} + cmake_build () { SRCDIR="$1" shift 1 @@ -650,7 +683,7 @@ cmake_build () { mkdir -p "$BUILDDIR" echo "${GREEN}Configuring ${NAME}...${NC}" - cmake -S . -B "$BUILDDIR" \ + cmake_clean_env -S . -B "$BUILDDIR" \ -DCMAKE_INSTALL_PREFIX="$PREFIX" \ -DCMAKE_BUILD_TYPE="$BUILD_CONFIGURATION" \ -DCMAKE_TOOLCHAIN_FILE="$CMAKE_TOOLCHAIN" \ @@ -658,10 +691,10 @@ cmake_build () { fi echo "${GREEN}Building ${NAME}...${NC}" - cmake --build "$BUILDDIR" --parallel "$NCPU" + cmake_clean_env --build "$BUILDDIR" --parallel "$NCPU" echo "${GREEN}Installing ${NAME}...${NC}" - cmake --install "$BUILDDIR" + cmake_clean_env --install "$BUILDDIR" cd "$pwd" } @@ -843,8 +876,8 @@ build_vulkan_drivers () { build_d3d_drivers () { LLVM15_NAME=$(basename "${LLVM15_SRC%.tar.*}") cmake_build "$BUILD_DIR/$LLVM15_NAME/llvm" -DLLVM_ENABLE_ZSTD=Off -DLLVM_TARGETS_TO_BUILD="" -DLLVM_BUILD_TOOLS=Off -DLLVM_VERSION_PRINTER_SHOW_HOST_TARGET_INFO=Off - meson_build $DXMT_REPO -Dnative_llvm_path="$PREFIX" - if [ "$ARCH" == "x86_64" ]; then + meson_darwin_build $DXMT_REPO -Dnative_llvm_path="$PREFIX" + if [ "$ARCH" == "x86_64" -a "$PLATFORM" == "macos" ]; then meson_build $D3DMETAL_REPO -Dtests=disabled else # empty placeholder file @@ -1046,7 +1079,7 @@ ios* | visionos* ) PLATFORM_FAMILY_NAME="$PLATFORM_FAMILY_PREFIX" ;; esac - QEMU_PLATFORM_BUILD_FLAGS="--enable-shared-lib --disable-cocoa --disable-coreaudio --disable-slirp-smbd --enable-ucontext --with-coroutine=libucontext $HVF_FLAGS $TCI_BUILD_FLAGS" + QEMU_PLATFORM_BUILD_FLAGS="--enable-shared-lib --disable-cocoa --disable-sdl --disable-coreaudio --disable-slirp-smbd --enable-ucontext --with-coroutine=libucontext $HVF_FLAGS $TCI_BUILD_FLAGS" ;; macos ) if [ -z "$SDKMINVER" ]; then @@ -1055,7 +1088,7 @@ macos ) SDK=macosx CFLAGS_TARGET="-target $ARCH-apple-macos$SDKMINVER" PLATFORM_FAMILY_NAME="macOS" - QEMU_PLATFORM_BUILD_FLAGS="--enable-shared-lib --disable-cocoa --cpu=$CPU" + QEMU_PLATFORM_BUILD_FLAGS="--enable-shared-lib --disable-cocoa --disable-sdl --cpu=$CPU" ;; * ) usage @@ -1176,9 +1209,7 @@ build_qemu_dependencies build $QEMU_DIR --cross-prefix="" $QEMU_PLATFORM_BUILD_FLAGS $QEMU_DEBUG_FLAGS build_spice_client build_vulkan_drivers -if [ "$PLATFORM" == "macos" ]; then - build_d3d_drivers -fi +build_d3d_drivers fixup_all remove_shared_gst_plugins # another hack... echo "${GREEN}All done!${NC}" From f93a7470b6af8cf6f0220312868d4753bcea4ffb Mon Sep 17 00:00:00 2001 From: osy <50960678+osy@users.noreply.github.com> Date: Tue, 4 Aug 2026 08:27:04 -0700 Subject: [PATCH 3/4] project: revert minver bump We can selectively support DXMT on macOS 13, iOS 16 and so we can revert the minver bump. --- .../UTMQemuConfiguration+Arguments.swift | 10 +++-- Platform/macOS/SettingsView.swift | 6 ++- Services/UTMQemuSystem.m | 4 ++ UTM.xcodeproj/project.pbxproj | 8 ++-- scripts/build_dependencies.sh | 38 +++++++++++++++---- 5 files changed, 50 insertions(+), 16 deletions(-) diff --git a/Configuration/UTMQemuConfiguration+Arguments.swift b/Configuration/UTMQemuConfiguration+Arguments.swift index 8488af2e8b..8f7debb1c0 100644 --- a/Configuration/UTMQemuConfiguration+Arguments.swift +++ b/Configuration/UTMQemuConfiguration+Arguments.swift @@ -349,9 +349,13 @@ import Virtualization // for getting network interfaces } private var isNeptuneSupported: Bool { - isGLSupported && - (rendererBackend == .qemuRendererBackendAngleMetal || rendererBackend == .qemuRendererBackendDefault) && - directXDriver != .qemuDirectXDriverDisabled + if #available(macOS 13, iOS 16, *) { + return isGLSupported && + (rendererBackend == .qemuRendererBackendAngleMetal || rendererBackend == .qemuRendererBackendDefault) && + directXDriver != .qemuDirectXDriverDisabled + } else { + return false + } } private var isSparc: Bool { diff --git a/Platform/macOS/SettingsView.swift b/Platform/macOS/SettingsView.swift index cf84742c67..1c20ae6b0c 100644 --- a/Platform/macOS/SettingsView.swift +++ b/Platform/macOS/SettingsView.swift @@ -227,7 +227,11 @@ struct DisplaySettingsView: View { if qemuDirectXDriver == .qemuDirectXDriverD3DMetal { return true // always show option if already selected } else { - return Self.hasD3DMetal + if #available(macOS 14, *) { + return Self.hasD3DMetal + } else { + return false + } } } diff --git a/Services/UTMQemuSystem.m b/Services/UTMQemuSystem.m index 008f8649f1..634ccd49ba 100644 --- a/Services/UTMQemuSystem.m +++ b/Services/UTMQemuSystem.m @@ -87,6 +87,10 @@ - (void)setDirectXDriver:(UTMQEMUDirectXDriver)directXDriver { NSString *backend; NSString *frameworkName; NSURL *library; + if (@available(macOS 13, iOS 16, *)) { + } else { + return; + } switch (directXDriver) { case kQEMUDirectXDriverDefault: case kQEMUDirectXDriverDXMT: diff --git a/UTM.xcodeproj/project.pbxproj b/UTM.xcodeproj/project.pbxproj index 2221f91b39..9b93eff2bb 100644 --- a/UTM.xcodeproj/project.pbxproj +++ b/UTM.xcodeproj/project.pbxproj @@ -5023,13 +5023,13 @@ "$(PROJECT_DIR)/$(QUOTED_SYSROOT_DIR)/include/spice-1", "$(PROJECT_DIR)/$(QUOTED_SYSROOT_DIR)/include/libusb-1.0", ); - IPHONEOS_DEPLOYMENT_TARGET = 16.0; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; LIBRARY_SEARCH_PATHS = ( "$(PROJECT_DIR)/$(QUOTED_SYSROOT_DIR)/lib", "$(PROJECT_DIR)/$(QUOTED_SYSROOT_DIR)/lib/gstreamer-1.0", "$(PROJECT_DIR)/$(QUOTED_SYSROOT_DIR)/lib", ); - MACOSX_DEPLOYMENT_TARGET = 13.0; + MACOSX_DEPLOYMENT_TARGET = 11.3; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = YES; @@ -5106,13 +5106,13 @@ "$(PROJECT_DIR)/$(QUOTED_SYSROOT_DIR)/include/spice-1", "$(PROJECT_DIR)/$(QUOTED_SYSROOT_DIR)/include/libusb-1.0", ); - IPHONEOS_DEPLOYMENT_TARGET = 16.0; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; LIBRARY_SEARCH_PATHS = ( "$(PROJECT_DIR)/$(QUOTED_SYSROOT_DIR)/lib", "$(PROJECT_DIR)/$(QUOTED_SYSROOT_DIR)/lib/gstreamer-1.0", "$(PROJECT_DIR)/$(QUOTED_SYSROOT_DIR)/lib", ); - MACOSX_DEPLOYMENT_TARGET = 13.0; + MACOSX_DEPLOYMENT_TARGET = 11.3; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; QUOTED_SYSROOT_DIR = "\"$(SYSROOT_DIR)\""; diff --git a/scripts/build_dependencies.sh b/scripts/build_dependencies.sh index e39881a068..417721dad2 100755 --- a/scripts/build_dependencies.sh +++ b/scripts/build_dependencies.sh @@ -21,8 +21,8 @@ RED='\033[0;31m' NC='\033[0m' # Knobs -IOS_SDKMINVER="16.0" -MAC_SDKMINVER="13.0" +IOS_SDKMINVER="14.0" +MAC_SDKMINVER="11.0" VISIONOS_SDKMINVER="1.0" # Network operations are retried because concurrent builds on the same CI runner # get rate limited by GitHub and fail to clone/fetch. @@ -612,7 +612,7 @@ meson_cross_build () { SRCDIR="$BUILD_DIR/$NAME" ;; esac - MESON_CROSS="$(realpath "$BUILD_DIR")/meson-$CROSS.cross" + MESON_CROSS="$(realpath "$BUILD_DIR")/meson-$CROSS$MESON_CROSS_SUFFIX.cross" if [ ! -f "$MESON_CROSS" ]; then generate_meson_cross "$MESON_CROSS" "$CROSS" fi @@ -712,9 +712,9 @@ build_angle () { WEBCORE_LIBRARY_DIR="/usr/local/lib" \ NORMAL_UMBRELLA_FRAMEWORKS_DIR="" \ CODE_SIGNING_ALLOWED=NO \ - IPHONEOS_DEPLOYMENT_TARGET="14.0" \ - MACOSX_DEPLOYMENT_TARGET="11.0" \ - XROS_DEPLOYMENT_TARGET="1.0" + IPHONEOS_DEPLOYMENT_TARGET="$IOS_SDKMINVER" \ + MACOSX_DEPLOYMENT_TARGET="$MAC_SDKMINVER" \ + XROS_DEPLOYMENT_TARGET="$VISIONOS_SDKMINVER" rsync -a "ANGLE.xcarchive/Products/usr/local/lib/" "$PREFIX/lib" rsync -a "include/" "$PREFIX/include" cd "$pwd" @@ -876,7 +876,27 @@ build_vulkan_drivers () { build_d3d_drivers () { LLVM15_NAME=$(basename "${LLVM15_SRC%.tar.*}") cmake_build "$BUILD_DIR/$LLVM15_NAME/llvm" -DLLVM_ENABLE_ZSTD=Off -DLLVM_TARGETS_TO_BUILD="" -DLLVM_BUILD_TOOLS=Off -DLLVM_VERSION_PRINTER_SHOW_HOST_TARGET_INFO=Off - meson_darwin_build $DXMT_REPO -Dnative_llvm_path="$PREFIX" + ( + case $PLATFORM in + ios* ) + DXMT_SDKMINVER="16.0" + ;; + macos ) + DXMT_SDKMINVER="13.0" + ;; + * ) + DXMT_SDKMINVER="$SDKMINVER" + ;; + esac + DXMT_CFLAGS_TARGET="${CFLAGS_TARGET/$SDKMINVER/$DXMT_SDKMINVER}" + CC="${CC/$CFLAGS_TARGET/$DXMT_CFLAGS_TARGET}" + CPPFLAGS="${CPPFLAGS/$CFLAGS_TARGET/$DXMT_CFLAGS_TARGET}" + CXXFLAGS="${CXXFLAGS/$CFLAGS_TARGET/$DXMT_CFLAGS_TARGET}" + OBJCFLAGS="${OBJCFLAGS/$CFLAGS_TARGET/$DXMT_CFLAGS_TARGET}" + LDFLAGS="${LDFLAGS/$CFLAGS_TARGET/$DXMT_CFLAGS_TARGET}" + MESON_CROSS_SUFFIX="-dxmt" + meson_darwin_build $DXMT_REPO -Dnative_llvm_path="$PREFIX" + ) if [ "$ARCH" == "x86_64" -a "$PLATFORM" == "macos" ]; then meson_build $D3DMETAL_REPO -Dtests=disabled else @@ -926,9 +946,11 @@ fixup_dylib () { mkdir -p "$FRAMEWORKPATH" mkdir -p "$INFOPATH" cp -a "$FILE" "$NEWFILE" + MINOSVER=$(vtool -show-build-version "$FILE" 2>/dev/null | awk '/minos/ {print $2; exit}') + [ -n "$MINOSVER" ] || MINOSVER="$SDKMINVER" /usr/libexec/PlistBuddy -c "Add :CFBundleExecutable string $LIBNAME" "$INFOPATH/Info.plist" || true /usr/libexec/PlistBuddy -c "Add :CFBundleIdentifier string $BUNDLE_ID" "$INFOPATH/Info.plist" || true - /usr/libexec/PlistBuddy -c "Add :MinimumOSVersion string $SDKMINVER" "$INFOPATH/Info.plist" || true + /usr/libexec/PlistBuddy -c "Add :MinimumOSVersion string $MINOSVER" "$INFOPATH/Info.plist" || true /usr/libexec/PlistBuddy -c "Add :CFBundleVersion string 1" "$INFOPATH/Info.plist" || true /usr/libexec/PlistBuddy -c "Add :CFBundleShortVersionString string 1.0" "$INFOPATH/Info.plist" || true if [ "$PLATFORM" == "macos" ]; then From ea24d5e8633832bb6b19c8b9738d0d54dcb89a7d Mon Sep 17 00:00:00 2001 From: osy <50960678+osy@users.noreply.github.com> Date: Tue, 4 Aug 2026 23:06:52 -0700 Subject: [PATCH 4/4] config(iOS): use physical RAM limit when limit is raised Otherwise the checks won't kick in on a jailbroken device. --- Platform/Shared/VMConfigSystemView.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Platform/Shared/VMConfigSystemView.swift b/Platform/Shared/VMConfigSystemView.swift index 606f97b76d..6f382667d0 100644 --- a/Platform/Shared/VMConfigSystemView.swift +++ b/Platform/Shared/VMConfigSystemView.swift @@ -97,7 +97,7 @@ struct VMConfigSystemView: View { #if os(iOS) || os(visionOS) let availableMemory = UInt64(os_proc_available_memory()) if availableMemory > 0 { - totalDeviceMemory = availableMemory + totalDeviceMemory = min(availableMemory, totalDeviceMemory) } #endif let actualJitSizeMib = jitSizeMib == 0 ? memorySizeMib / 4 : jitSizeMib