├── .gitattributes ├── .github └── workflows │ └── main.yml ├── .gitignore ├── BuildScripts ├── build-toolspkg.py ├── build-universal.tool └── package.tool ├── Changelog.md ├── Docs ├── HyperV-devices.md ├── HyperV-ref-docs.md ├── HyperV-versions.md └── modules.md ├── LICENSE.txt ├── MacHyperVFramebuffer ├── HyperVGraphicsFramebuffer.cpp ├── HyperVGraphicsFramebuffer.hpp ├── HyperVGraphicsFramebufferPrivate.cpp └── Info.plist ├── MacHyperVSupport.xcodeproj └── project.pbxproj ├── MacHyperVSupport ├── ACPIFixup │ ├── HyperVACPIFixup.cpp │ ├── HyperVACPIFixup.hpp │ ├── HyperVACPIPlatformExpertShim.cpp │ ├── HyperVACPIPlatformExpertShim.hpp │ └── acpi.hpp ├── CPU │ ├── HyperVCPU.cpp │ └── HyperVCPU.hpp ├── Controller │ ├── HyperV.hpp │ ├── HyperVController.cpp │ ├── HyperVController.hpp │ ├── HyperVControllerHypercalls.cpp │ └── HyperVControllerInterrupts.cpp ├── Graphics │ ├── HyperVGraphics.cpp │ ├── HyperVGraphics.hpp │ ├── HyperVGraphicsPlatformFunctions.cpp │ ├── HyperVGraphicsPlatformFunctions.hpp │ ├── HyperVGraphicsPrivate.cpp │ └── HyperVGraphicsRegs.hpp ├── GraphicsBridge │ ├── HyperVGraphicsBridge.cpp │ └── HyperVGraphicsBridge.hpp ├── Info.plist ├── IntegrationComponents │ ├── FileCopy │ │ ├── HyperVFileCopy.cpp │ │ ├── HyperVFileCopy.hpp │ │ ├── HyperVFileCopyRegs.hpp │ │ ├── HyperVFileCopyRegsUser.h │ │ ├── HyperVFileCopyUserClient.cpp │ │ ├── HyperVFileCopyUserClient.h │ │ ├── HyperVFileCopyUserClientInternal.hpp │ │ └── HyperVFileCopyUserClientPrivate.cpp │ ├── Heartbeat │ │ ├── HyperVHeartbeat.cpp │ │ ├── HyperVHeartbeat.hpp │ │ └── HyperVHeartbeatRegs.hpp │ ├── HyperVIC.hpp │ ├── HyperVICService.cpp │ ├── HyperVICService.hpp │ ├── HyperVICUserClient.cpp │ ├── HyperVICUserClient.hpp │ ├── Shutdown │ │ ├── HyperVShutdown.cpp │ │ ├── HyperVShutdown.hpp │ │ ├── HyperVShutdownRegs.hpp │ │ ├── HyperVShutdownUserClient.cpp │ │ ├── HyperVShutdownUserClient.h │ │ ├── HyperVShutdownUserClientInternal.hpp │ │ └── HyperVShutdownUserClientPrivate.cpp │ └── TimeSync │ │ ├── HyperVTimeSync.cpp │ │ ├── HyperVTimeSync.hpp │ │ ├── HyperVTimeSyncRegs.hpp │ │ ├── HyperVTimeSyncUserClient.cpp │ │ ├── HyperVTimeSyncUserClient.h │ │ └── HyperVTimeSyncUserClientInternal.hpp ├── InterruptController │ ├── HyperVInterruptController.cpp │ └── HyperVInterruptController.hpp ├── Keyboard │ ├── HyperVADBMaps.hpp │ ├── HyperVKeyboard.cpp │ ├── HyperVKeyboard.hpp │ ├── HyperVKeyboardBase.cpp │ ├── HyperVKeyboardBase.hpp │ ├── HyperVKeyboardRegs.hpp │ ├── HyperVPS2Keyboard.cpp │ ├── HyperVPS2Keyboard.hpp │ └── HyperVPS2KeyboardRegs.hpp ├── Mouse │ ├── HyperVMouse.cpp │ ├── HyperVMouse.hpp │ ├── HyperVMousePrivate.cpp │ └── HyperVMouseRegs.hpp ├── Network │ ├── HyperVNetwork.cpp │ ├── HyperVNetwork.hpp │ ├── HyperVNetworkPrivate.cpp │ ├── HyperVNetworkRNDIS.cpp │ └── HyperVNetworkRegs.hpp ├── PCIBridge │ ├── DevicePathPropertyDatabase.hpp │ ├── HyperVPCIBridge.cpp │ ├── HyperVPCIBridge.hpp │ ├── HyperVPCIBridgeDevProps.cpp │ ├── HyperVPCIBridgePrivate.cpp │ └── HyperVPCIBridgeRegs.hpp ├── PCIRoot │ ├── HyperVPCIRoot.cpp │ └── HyperVPCIRoot.hpp ├── PlatformProvider │ ├── HyperVPlatformProvider.cpp │ └── HyperVPlatformProvider.hpp ├── Storage │ ├── HyperVStorage.cpp │ ├── HyperVStorage.hpp │ ├── HyperVStoragePrivate.cpp │ └── HyperVStorageRegs.hpp ├── VMBus │ ├── HyperVVMBus.cpp │ ├── HyperVVMBus.hpp │ ├── HyperVVMBusChannel.cpp │ ├── HyperVVMBusInterrupts.cpp │ ├── HyperVVMBusPrivate.cpp │ └── VMBus.hpp ├── VMBusDevice │ ├── HyperVVMBusDevice.cpp │ ├── HyperVVMBusDevice.hpp │ └── HyperVVMBusDevicePrivate.cpp └── kern_start.cpp ├── README.md └── Tools ├── Daemons ├── hvdebug.h ├── hvfilecopyd │ ├── fish.goldfish64.hvfilecopyd.plist │ ├── hvfilecopyd.c │ └── postinstall ├── hviokit.c ├── hviokit.h ├── hvshutdownd │ ├── fish.goldfish64.hvshutdownd.plist │ ├── hvshutdownd.c │ └── postinstall └── hvtimesyncd │ ├── fish.goldfish64.hvtimesyncd.plist │ ├── hvtimesyncd.c │ └── postinstall └── package ├── Localizable_EN.strings └── scripts.js /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | pull_request: 6 | workflow_dispatch: 7 | release: 8 | types: [published] 9 | 10 | env: 11 | PROJECT_TYPE: KEXT 12 | ACID32: 1 13 | 14 | jobs: 15 | build: 16 | name: Build 17 | runs-on: macos-latest 18 | env: 19 | JOB_TYPE: BUILD 20 | steps: 21 | - uses: actions/checkout@v4 22 | - uses: actions/checkout@v4 23 | with: 24 | repository: acidanthera/MacKernelSDK 25 | path: MacKernelSDK 26 | - name: CI Bootstrap 27 | run: | 28 | src=$(/usr/bin/curl -Lfs https://raw.githubusercontent.com/acidanthera/ocbuild/master/ci-bootstrap.sh) && eval "$src" || exit 1 29 | - name: macOS 10.13 SDK Bootstrap 30 | run: | 31 | src=$(/usr/bin/curl -Lfs https://raw.githubusercontent.com/acidanthera/ocbuild/master/macosx10.13-sdk-bootstrap.sh) && eval "$src" || exit 1 32 | - name: Lilu Bootstrap 33 | run: | 34 | src=$(/usr/bin/curl -Lfs https://raw.githubusercontent.com/acidanthera/Lilu/master/Lilu/Scripts/bootstrap.sh) && eval "$src" || exit 1 35 | 36 | - run: xcodebuild -jobs 1 -target "Package Universal" -configuration Debug -arch ACID32 -arch x86_64 37 | - run: xcodebuild -jobs 1 -target "Package Universal" -configuration Release -arch ACID32 -arch x86_64 38 | 39 | - name: Upload to Artifacts 40 | uses: actions/upload-artifact@v4 41 | with: 42 | name: Artifacts 43 | path: build/*/*.zip 44 | - name: Upload to Release 45 | if: github.event_name == 'release' 46 | uses: svenstaro/upload-release-action@e74ff71f7d8a4c4745b560a485cc5fdb9b5b999d 47 | with: 48 | repo_token: ${{ secrets.GITHUB_TOKEN }} 49 | file: build/*/*.zip 50 | tag: ${{ github.ref }} 51 | file_glob: true 52 | 53 | analyze-clang: 54 | name: Analyze Clang 55 | runs-on: macos-latest 56 | env: 57 | JOB_TYPE: ANALYZE 58 | steps: 59 | - uses: actions/checkout@v4 60 | - uses: actions/checkout@v4 61 | with: 62 | repository: acidanthera/MacKernelSDK 63 | path: MacKernelSDK 64 | - name: CI Bootstrap 65 | run: | 66 | src=$(/usr/bin/curl -Lfs https://raw.githubusercontent.com/acidanthera/ocbuild/master/ci-bootstrap.sh) && eval "$src" || exit 1 67 | - name: macOS 10.13 SDK Bootstrap 68 | run: | 69 | src=$(/usr/bin/curl -Lfs https://raw.githubusercontent.com/acidanthera/ocbuild/master/macosx10.13-sdk-bootstrap.sh) && eval "$src" || exit 1 70 | - name: Lilu Bootstrap 71 | run: | 72 | src=$(/usr/bin/curl -Lfs https://raw.githubusercontent.com/acidanthera/Lilu/master/Lilu/Scripts/bootstrap.sh) && eval "$src" || exit 1 73 | 74 | - run: xcodebuild analyze -quiet -target "Package Universal" -configuration Debug -arch ACID32 -arch x86_64 CLANG_ANALYZER_OUTPUT=plist-html CLANG_ANALYZER_OUTPUT_DIR="$(pwd)/clang-analyze" && [ "$(find clang-analyze -name "*.html")" = "" ] 75 | - run: xcodebuild clean -quiet -scheme "Package Universal" 76 | - run: xcodebuild analyze -quiet -target "Package Universal" -configuration Release -arch ACID32 -arch x86_64 CLANG_ANALYZER_OUTPUT=plist-html CLANG_ANALYZER_OUTPUT_DIR="$(pwd)/clang-analyze" && [ "$(find clang-analyze -name "*.html")" = "" ] 77 | 78 | analyze-coverity: 79 | name: Analyze Coverity 80 | runs-on: macos-latest 81 | env: 82 | JOB_TYPE: COVERITY 83 | if: github.repository_owner == 'acidanthera' && github.event_name != 'pull_request' 84 | steps: 85 | - uses: actions/checkout@v4 86 | - uses: actions/checkout@v4 87 | with: 88 | repository: acidanthera/MacKernelSDK 89 | path: MacKernelSDK 90 | - name: CI Bootstrap 91 | run: | 92 | src=$(/usr/bin/curl -Lfs https://raw.githubusercontent.com/acidanthera/ocbuild/master/ci-bootstrap.sh) && eval "$src" || exit 1 93 | - name: macOS 10.13 SDK Bootstrap 94 | run: | 95 | src=$(/usr/bin/curl -Lfs https://raw.githubusercontent.com/acidanthera/ocbuild/master/macosx10.13-sdk-bootstrap.sh) && eval "$src" || exit 1 96 | - name: Lilu Bootstrap 97 | run: | 98 | src=$(/usr/bin/curl -Lfs https://raw.githubusercontent.com/acidanthera/Lilu/master/Lilu/Scripts/bootstrap.sh) && eval "$src" || exit 1 99 | 100 | - name: Run Coverity 101 | run: | 102 | src=$(/usr/bin/curl -Lfs https://raw.githubusercontent.com/acidanthera/ocbuild/master/coverity/covstrap.sh) && eval "$src" || exit 1 103 | env: 104 | COVERITY_SCAN_TOKEN: ${{ secrets.COVERITY_SCAN_TOKEN }} 105 | COVERITY_SCAN_EMAIL: ${{ secrets.COVERITY_SCAN_EMAIL }} 106 | COVERITY_BUILD_COMMAND: xcodebuild -configuration Release 107 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | DerivedData 3 | xcuserdata 4 | project.xcworkspace 5 | build 6 | xcshareddata 7 | Lilu.kext 8 | /MacKernelSDK 9 | /clang32 10 | -------------------------------------------------------------------------------- /BuildScripts/build-universal.tool: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # build-universal.tool 4 | # Combines x86_64 and i386 binaries into one universal binary. 5 | # 6 | # Usage: build-universal.tool "x86_64 slice" "i386 slice" "output" 7 | # 8 | 9 | if [ "${TARGET_BUILD_DIR}" = "" ]; then 10 | echo "This must not be run outside of Xcode" 11 | exit 1 12 | fi 13 | 14 | cd "${TARGET_BUILD_DIR}" 15 | 16 | # Xcode 10.2 build system seems to have a race condition between 17 | # dependency scheduling, and for some reason does not complete 18 | # the compilation of dependencies even though it should have been. 19 | # Adding a delay here "fixes" it. TODO: bugreport. 20 | if [ ! -f libaistat.dylib ]; then 21 | sleep 5 22 | fi 23 | 24 | lipo -create -arch x86_64 "$1" -arch i386 "$2" -output "$3" 25 | -------------------------------------------------------------------------------- /BuildScripts/package.tool: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ "${TARGET_BUILD_DIR}" = "" ]; then 4 | echo "This must not be run outside of Xcode" 5 | exit 1 6 | fi 7 | 8 | cd "${TARGET_BUILD_DIR}" 9 | archive="MacHyperVSupport-${MODULE_VERSION}-$(echo $CONFIGURATION | tr /a-z/ /A-Z/).zip" 10 | 11 | # clean / build 12 | if [ "$1" != "analyze" ]; then 13 | rm -rf package "${archive}" || exit 1 14 | fi 15 | 16 | if [ "$1" != "" ]; then 17 | echo "Got action $1, skipping!" 18 | exit 0 19 | fi 20 | 21 | mkdir -p package/Tools || exit 1 22 | mkdir -p package/Kexts || exit 1 23 | 24 | cd package || exit 1 25 | 26 | # Xcode 10.2 build system seems to have a race condition between 27 | # dependency scheduling, and for some reason does not complete 28 | # the compilation of dependencies even though it should have been. 29 | # Adding a delay here "fixes" it. TODO: bugreport. 30 | if [ ! -f ../libaistat.dylib ]; then 31 | sleep 5 32 | fi 33 | 34 | cp "${PROJECT_DIR}/Tools/Daemons/hvfilecopyd/fish.goldfish64.hvfilecopyd.plist" Tools/fish.goldfish64.hvfilecopyd.plist || exit 1 35 | if [[ -f "../hvfilecopyd-universal" ]] && [[ "../hvfilecopyd-universal" -nt "../hvfilecopyd" ]]; then 36 | cp ../hvfilecopyd-universal Tools/hvfilecopyd || exit 1 37 | else 38 | cp ../hvfilecopyd Tools/hvfilecopyd || exit 1 39 | fi 40 | if [[ -f "../hvfilecopyd-tiger" ]]; then 41 | cp ../hvfilecopyd-tiger Tools/ || exit 1 42 | fi 43 | 44 | cp "${PROJECT_DIR}/Tools/Daemons/hvfilecopyd/fish.goldfish64.hvfilecopyd.plist" Tools/fish.goldfish64.hvshutdownd.plist || exit 1 45 | if [[ -f "../hvshutdownd-universal" ]] && [[ "../hvshutdownd-universal" -nt "../hvshutdownd" ]]; then 46 | cp ../hvshutdownd-universal Tools/hvshutdownd || exit 1 47 | else 48 | cp ../hvshutdownd Tools/hvshutdownd || exit 1 49 | fi 50 | if [[ -f "../hvshutdownd-tiger" ]]; then 51 | cp ../hvshutdownd-tiger Tools/ || exit 1 52 | fi 53 | 54 | cp "${PROJECT_DIR}/Tools/Daemons/hvfilecopyd/fish.goldfish64.hvfilecopyd.plist" Tools/fish.goldfish64.hvtimesyncd.plist || exit 1 55 | if [[ -f "../hvtimesyncd-universal" ]] && [[ "../hvtimesyncd-universal" -nt "../hvtimesyncd" ]]; then 56 | cp ../hvtimesyncd-universal Tools/hvtimesyncd || exit 1 57 | else 58 | cp ../hvtimesyncd Tools/hvtimesyncd || exit 1 59 | fi 60 | if [[ -f "../hvtimesyncd-tiger" ]]; then 61 | cp ../hvtimesyncd-tiger Tools/ || exit 1 62 | fi 63 | 64 | for kext in ../*.kext; do 65 | echo "$kext" 66 | cp -a "$kext" Kexts/ || exit 1 67 | done 68 | 69 | # Workaround Xcode 10 bug 70 | if [ "$CONFIGURATION" = "" ]; then 71 | if [ "$(basename "$TARGET_BUILD_DIR")" = "Debug" ]; then 72 | CONFIGURATION="Debug" 73 | elif [ "$(basename "$TARGET_BUILD_DIR")" = "Sanitize" ]; then 74 | CONFIGURATION="Sanitize" 75 | else 76 | CONFIGURATION="Release" 77 | fi 78 | fi 79 | 80 | if [ "$CONFIGURATION" = "Release" ]; then 81 | mkdir -p dSYM || exit 1 82 | for dsym in ../*.dSYM; do 83 | if [ "$dsym" = "../*.dSYM" ]; then 84 | continue 85 | fi 86 | echo "$dsym" 87 | cp -a "$dsym" dSYM/ || exit 1 88 | done 89 | fi 90 | 91 | zip -qry -FS ../"${archive}" * || exit 1 92 | -------------------------------------------------------------------------------- /Changelog.md: -------------------------------------------------------------------------------- 1 | MacHyperVSupport Changelog 2 | ============================ 3 | #### v0.9.8 4 | - Added constants for macOS 15 support 5 | - Fixed completion flag not being added for storage commands 6 | - Added 32-bit builds of all userspace components 7 | - Added support for the synthetic IDE controller on Gen1 VMs 8 | - Added PS/2 keyboard driver to support Gen1 VMs on Server 2008 R2 and older 9 | - Added framebuffer driver for basic graphics support 10 | - Fixed DVD drives not appearing on Gen2 SCSI controller 11 | - Prevent sleep/doze from occuring 12 | - Rework MMIO handling into HyperVPCIRoot 13 | - Added ACPI fixup module to resolve ACPI issues on macOS 10.6 and older without SSDT 14 | - Removed requirement to force legacy mode on macOS 10.4 and 10.5, use the `ClearTaskSwitchBit` OpenCore Booter quirk instead 15 | - Added installer package for userspace components 16 | - Fixed storage hotplug not working on the first addition/removal of a disk 17 | 18 | #### v0.9.7 19 | - Fixed disks on a passed-in PCI device not being usable 20 | - Improved PCI interrupt handling to support both MSI and MSI-X 21 | - Added support for device property injection on PCI devices 22 | 23 | #### v0.9.6 24 | - Fixed extended registers not being correctly read/written 25 | 26 | #### v0.9.5 27 | - Fixed no packets being received on certain older versions of Hyper-V 28 | - Added support for promiscuous mode 29 | 30 | #### v0.9.4 31 | - Added constants for macOS 14 support 32 | 33 | #### v0.9.3 34 | - Created daemons for each userspace function replacing hvutil 35 | - Added support for host to guest file copy (Guest Services integration service) 36 | - Fixed very high storage latency when heaving network I/O is occurring 37 | 38 | #### v0.9.2 39 | - Fixed crash when control key is pressed under macOS 10.4 40 | - Fixed DMA allocations under macOS 10.4 and 10.5 41 | - Refactored and cleaned up VMBus core logic and integration services 42 | - Fixed crash caused by a buffer overrun in network packet sending 43 | - Fixed intermittent hangs in storage and network drivers 44 | - Added support for storage disk addition and removal while VM is running 45 | - Added support for guest restart via Restart-VM 46 | - Renamed hvshutdown daemon to hvutil to support all userspace-side functions 47 | - Added guest time synchronization support 48 | - Added support for "Type clipboard text" function 49 | 50 | #### v0.9.1 51 | - Added initial PCI passthrough support 52 | - Fixed crash related to IOPCIBridge on 12.0 and newer 53 | - Added support for macOS 10.4 and 10.5 54 | - Added hvshutdown daemon to support shutdowns from Hyper-V 55 | - Standardized boot arguments 56 | 57 | #### v0.9 58 | - Added constants for macOS 13 support 59 | 60 | #### v0.8 61 | - Latest Windows support 62 | 63 | #### v0.7 64 | - Added networking support 65 | 66 | #### v0.6 67 | - Added constants for macOS 12 support 68 | - Enable loading on 32-bit macOS 10.6 and 10.7 69 | 70 | #### v0.5 71 | - Initial developer preview release 72 | -------------------------------------------------------------------------------- /Docs/HyperV-devices.md: -------------------------------------------------------------------------------- 1 | # Hyper-V devices 2 | This page describes the different services and devices exposed to a virtual machine over the VMBus. 3 | 4 | ### Automatic virtual machine activation (AVMA) 5 | [Provides](https://learn.microsoft.com/en-us/windows-server/get-started/automatic-vm-activation) an activation method for Windows Server guests on Windows Server Datacenter hosts. 6 | * Availability: Windows Server 2012 R2 and later (Datacenter edition only) 7 | * GUID: `3375baf4-9e15-4b30-b765-67acb10d607b` 8 | * Support status: No 9 | 10 | ### Backup (volume snapshot) 11 | [Provides](https://learn.microsoft.com/en-us/virtualization/hyper-v-on-windows/reference/integration-services#hyper-v-volume-shadow-copy-requestor) Volume Shadow Copy support to guests. 12 | * Availability: Windows Server 2008 SP2 and later 13 | * GUID: `35fa2e29-ea23-4236-96ae-3a6ebacba440` 14 | * Support status: Planned 15 | 16 | ### Data Exchange 17 | [Provides](https://learn.microsoft.com/en-us/virtualization/hyper-v-on-windows/reference/integration-services#hyper-v-data-exchange-service-kvp) a mechanism to exchange basic metadata between the guest and the host via a key/value pair system. 18 | * Availability: Windows Server 2008 and later 19 | * GUID: `a9a0f4e7-5a45-4d96-b827-8a841e8c03e6` 20 | * Support status: Planned 21 | 22 | ### Dynamic memory 23 | [Provides](https://learn.microsoft.com/en-us/previous-versions/windows/it-pro/windows-server-2012-R2-and-2012/hh831766(v=ws.11)) overprovisioning of memory in guests. 24 | * Availability: Windows Server 2008 R2 SP1 and later 25 | * GUID: `525074dc-8985-46e2-8057-a307dc18a502` 26 | * Support status: No 27 | 28 | ### Guest services 29 | [Provides](https://learn.microsoft.com/en-us/virtualization/hyper-v-on-windows/reference/integration-services#hyper-v-guest-service-interface) an interface to allow for files to be copied to/from a guest and the host. 30 | * Availability: Windows Server 2008 and later 31 | * GUID: `34d14be3-dee4-41c8-9ae7-6b174977c192` 32 | * Support status: Handled by HyperVFileCopy 33 | 34 | ### Heartbeat 35 | [Provides](https://learn.microsoft.com/en-us/virtualization/hyper-v-on-windows/reference/integration-services#hyper-v-heartbeat-service) an interface to allow the host to ensure the guest has booted and operating correctly. 36 | * Availability: Windows Server 2008 and later 37 | * GUID: `57164f39-9115-4e78-ab55-382f3bd5422d` 38 | * Support status: Handled by HyperVHeartbeat 39 | 40 | ### Operating system shutdown 41 | [Provides](https://learn.microsoft.com/en-us/virtualization/hyper-v-on-windows/reference/integration-services#hyper-v-guest-shutdown-service) an interface to allow the host to gracefully shutdown or restart the guest. 42 | * Availability: Windows Server 2008 and later 43 | * GUID: `0e0b6031-5213-4934-818b-38d90ced39db` 44 | * Support status: Handled by HyperVShutdown 45 | 46 | ### PCI passthrough (Discrete Device Assignment) 47 | [Provides](https://learn.microsoft.com/en-us/windows-server/virtualization/hyper-v/deploy/deploying-graphics-devices-using-dda) PCI passthrough support to a guest. 48 | * Availability: Windows Server 2016 and later 49 | * GUID: `44c4f61d-4444-4400-9d52-802e27ede19f` 50 | * Support status: Handled by HyperVPCIBridge 51 | 52 | ### Remote Desktop control channel 53 | * GUID: `f8e65716-3cb3-4a06-9a60-1889c5cccab5` 54 | * Support status: No 55 | 56 | ### Remote Desktop virtualization 57 | * GUID: `276aacf4-ac15-426c-98dd-7521ad3f01fe` 58 | * Support status: No 59 | 60 | ### Synthetic Fibre Channel adapter 61 | [Provides](https://learn.microsoft.com/en-us/previous-versions/windows/it-pro/windows-server-2012-R2-and-2012/hh831413(v=ws.11)) a virtual interface to connect to Fibre Channel storage devices from a guest. 62 | * Availability: Windows Server 2012 and later 63 | * GUID: `2f9bcc4a-0069-4af3-b76b-6fd0be528cda` 64 | * Support status: No 65 | 66 | ### Synthetic graphics framebuffer 67 | Provides virtual graphics framebuffer support. 68 | * Availability: Windows Server 2008 and later 69 | * GUID: `da0a7802-e377-4aac-8e77-0558eb1073f8` 70 | * Support status: Handled by HyperVGraphics (requires HyperVFramebuffer for full functionality) 71 | 72 | ### Synthetic IDE controller 73 | [Provides](https://learn.microsoft.com/en-us/windows-server/administration/performance-tuning/role/hyper-v-server/storage-io-performance#ide) enhanced performance over the emulated IDE controller in a Gen1 guest. Virtual hard disks will appear on both the emulated controller and the synthetic controller, CD devices will appear only on the emulated controller. 74 | * Availability: Windows Server 2008 and later (Gen1 only) 75 | * GUID: `32412632-86cb-44a2-9b5c-50d1417354f5` 76 | * Support status: Handled by HyperVStorage 77 | 78 | ### Synthetic keyboard 79 | Provides virtual keyboard support. 80 | * Availability: Windows Server 2012 and later 81 | * GUID: `f912ad6d-2b17-48ea-bd65-f927a61c7684` 82 | * Support status: Handled by HyperVKeyboard 83 | 84 | ### Synthetic mouse 85 | Provides virtual mouse support. 86 | * Availability: Windows Server 2008 and later 87 | * GUID: `cfa8b69e-5b4a-4cc0-b98b-8ba1a1f3f95a` 88 | * Support status: Handled by HyperVMouse 89 | 90 | ### Synthetic network controller 91 | Provides virtual network support. 92 | * Availability: Windows Server 2008 and later 93 | * GUID: `f8615163-df3e-46c5-913f-f2d2f965ed0e` 94 | * Support status: Handled by HyperVNetwork 95 | 96 | ### Synthetic RDMA 97 | * Availability: ? 98 | * GUID: `8c2eaf3d-32a7-4b09-ab99-bd1f1c86b501` 99 | * Support status: No 100 | 101 | ### Synthetic SCSI controller 102 | Provides virtual SCSI storage support. Booting from the SCSI controller requires a Gen2 VM. 103 | * Availability: Windows Server 2008 and later 104 | * GUID: `ba6163d9-04a1-4d29-b605-72e2ffb1dc7f` 105 | * Support status: Handled by HyperVStorage 106 | 107 | ### Time synchronization 108 | [Provides](https://learn.microsoft.com/en-us/virtualization/hyper-v-on-windows/reference/integration-services#hyper-v-time-synchronization-service) an interface for time synchronization between the guest and the host. 109 | * Availability: Windows Server 2008 and later 110 | * GUID: `9527e630-d0ae-497b-adce-e80ab0175caf` 111 | * Support status: Handled by HyperVTimeSync 112 | -------------------------------------------------------------------------------- /Docs/HyperV-ref-docs.md: -------------------------------------------------------------------------------- 1 | # Hyper-V reference documents and links 2 | List of various reference documents containing details and internals regarding Hyper-V and integration services. 3 | 4 | - [Bringing Hyper-V to "Windows 8"](https://web.archive.org/web/20111123161149/http://blogs.msdn.com/b/b8/archive/2011/09/07/bringing-hyper-v-to-windows-8.aspx) 5 | - [Enabling Linux Support on Windows Server 2012 R2 Hyper-V](https://web.archive.org/web/20141109005505/http://blogs.technet.com/b/virtualization/archive/2013/07/24/enabling-linux-support-on-windows-server-2012-r2-hyper-v.aspx) 6 | - [Generation 2 Virtual Machine Overview](https://learn.microsoft.com/en-us/previous-versions/windows/it-pro/windows-server-2012-R2-and-2012/dn282285(v=ws.11)) 7 | - [Hypervisor Top Level Functional Specification](https://learn.microsoft.com/en-us/virtualization/hyper-v-on-windows/tlfs/tlfs) 8 | https://hvinternals.blogspot.com/2015/10/hyper-v-internals.html 9 | - [Hyper-V Internals](https://github.com/gerhart01/Hyper-V-Internals) 10 | - [Hyper-V Internals and LIS](https://re.alisa.sh/notes/Hyper-V-LIS.html) 11 | - [Linux Integration Services for Microsoft Hyper-V](https://github.com/LIS) 12 | - [Manage Hyper-V hypervisor scheduler types](https://learn.microsoft.com/en-us/windows-server/virtualization/hyper-v/manage/manage-hyper-v-scheduler-types) 13 | - [Memory Ballooning in Hyper-V](https://performancebydesign.blogspot.com/2017/12/memory-ballooning-in-hyper-v.html) 14 | - [VMBus (Hyper-V) devices in QEMU/KVM](https://www.linux-kvm.org/images/4/41/03x04A-Roman_Kagan-VMBus_Hyper-V_devices_in_QEMU_KVM.pdf) 15 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2021-2025, Goldfish64 2 | 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 6 | 7 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 8 | 9 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 10 | 11 | 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 12 | 13 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 14 | -------------------------------------------------------------------------------- /MacHyperVFramebuffer/HyperVGraphicsFramebuffer.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // HyperVGraphicsFramebuffer.hpp 3 | // Hyper-V synthetic graphics framebuffer driver 4 | // 5 | // Copyright © 2025 Goldfish64. All rights reserved. 6 | // 7 | 8 | #ifndef MacHyperVFramebuffer_hpp 9 | #define MacHyperVFramebuffer_hpp 10 | 11 | #include 12 | 13 | #include "HyperV.hpp" 14 | #include "HyperVGraphicsPlatformFunctions.hpp" 15 | #include "HyperVGraphicsRegs.hpp" 16 | 17 | typedef struct { 18 | UInt32 width; 19 | UInt32 height; 20 | } HyperVGraphicsMode; 21 | 22 | class HyperVGraphicsFramebuffer : public IOFramebuffer { 23 | OSDeclareDefaultStructors(HyperVGraphicsFramebuffer); 24 | HVDeclareLogFunctions("gfxfb"); 25 | typedef IOFramebuffer super; 26 | 27 | private: 28 | IOService *_hvGfxProvider = nullptr; 29 | IOPhysicalAddress _gfxBase = 0; 30 | UInt32 _gfxLength = 0; 31 | HyperVGraphicsMode *_gfxModes = nullptr; 32 | IOItemCount _gfxModesCount = 0; 33 | VMBusVersion _gfxVersion = { }; 34 | 35 | UInt8 *_cursorData = nullptr; 36 | size_t _cursorDataSize = kHyperVGraphicsCursorMaxSize; 37 | bool _hasCursorHotspot = false; 38 | 39 | IODisplayModeID _currentDisplayMode = 4; 40 | 41 | // 42 | // Internal functions. 43 | // 44 | inline UInt32 getScreenDepth() { return (_gfxVersion.value == kHyperVGraphicsVersionV3_0) ? kHyperVGraphicsBitDepth2008 : kHyperVGraphicsBitDepth; } 45 | IOReturn initGraphicsService(); 46 | IOReturn buildGraphicsModes(); 47 | IOReturn buildFallbackMode(); 48 | 49 | public: 50 | // 51 | // IOService overrides. 52 | // 53 | bool start(IOService *provider) APPLE_KEXT_OVERRIDE; 54 | void stop(IOService *provider) APPLE_KEXT_OVERRIDE; 55 | 56 | // 57 | // IOFramebuffer overrides. 58 | // 59 | IOReturn enableController() APPLE_KEXT_OVERRIDE; 60 | bool isConsoleDevice() APPLE_KEXT_OVERRIDE; 61 | IODeviceMemory* getApertureRange(IOPixelAperture aperture) APPLE_KEXT_OVERRIDE; 62 | const char* getPixelFormats() APPLE_KEXT_OVERRIDE; 63 | IOItemCount getDisplayModeCount() APPLE_KEXT_OVERRIDE; 64 | IOReturn getDisplayModes(IODisplayModeID *allDisplayModes) APPLE_KEXT_OVERRIDE; 65 | IOReturn getInformationForDisplayMode(IODisplayModeID displayMode, IODisplayModeInformation *info) APPLE_KEXT_OVERRIDE; 66 | UInt64 getPixelFormatsForDisplayMode(IODisplayModeID displayMode, IOIndex depth) APPLE_KEXT_OVERRIDE; 67 | IOReturn getPixelInformation(IODisplayModeID displayMode, IOIndex depth, 68 | IOPixelAperture aperture, IOPixelInformation *pixelInfo) APPLE_KEXT_OVERRIDE; 69 | IOReturn getCurrentDisplayMode(IODisplayModeID *displayMode, IOIndex *depth) APPLE_KEXT_OVERRIDE; 70 | IOReturn setDisplayMode(IODisplayModeID displayMode, IOIndex depth) APPLE_KEXT_OVERRIDE; 71 | IOReturn getAttribute(IOSelect attribute, uintptr_t *value) APPLE_KEXT_OVERRIDE; 72 | IOReturn setCursorImage(void *cursorImage) APPLE_KEXT_OVERRIDE; 73 | IOReturn setCursorState(SInt32 x, SInt32 y, bool visible) APPLE_KEXT_OVERRIDE; 74 | void flushCursor() APPLE_KEXT_OVERRIDE; 75 | }; 76 | 77 | #endif 78 | -------------------------------------------------------------------------------- /MacHyperVFramebuffer/HyperVGraphicsFramebufferPrivate.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // HyperVGraphicsFramebufferPrivate.cpp 3 | // Hyper-V synthetic graphics framebuffer driver 4 | // 5 | // Copyright © 2025 Goldfish64. All rights reserved. 6 | // 7 | 8 | #include "HyperVGraphicsFramebuffer.hpp" 9 | 10 | #define kHyperVSupportedResolutionsKey "SupportedResolutions" 11 | #define kHyperVHeightKey "Height" 12 | #define kHyperVWidthKey "Width" 13 | 14 | IOReturn HyperVGraphicsFramebuffer::initGraphicsService() { 15 | IOReturn status; 16 | 17 | if (_hvGfxProvider == nullptr) { 18 | return kIOReturnUnsupported; 19 | } 20 | 21 | // 22 | // Initialize graphics service and get version and graphics memory location. 23 | // 24 | status = _hvGfxProvider->callPlatformFunction(kHyperVGraphicsPlatformFunctionInit, true, &_gfxVersion, &_gfxBase, &_gfxLength, nullptr); 25 | if (status != kIOReturnSuccess) { 26 | return status; 27 | } 28 | 29 | HVDBGLOG("Graphics version %u.%u", _gfxVersion.major, _gfxVersion.minor); 30 | HVDBGLOG("Graphics memory located at %p length 0x%X", _gfxBase, _gfxLength); 31 | HVDBGLOG("Graphics bit depth: %u-bit", (_gfxVersion.value == kHyperVGraphicsVersionV3_0) ? kHyperVGraphicsBitDepth2008 : kHyperVGraphicsBitDepth); 32 | return kIOReturnSuccess; 33 | } 34 | 35 | IOReturn HyperVGraphicsFramebuffer::buildGraphicsModes() { 36 | OSArray *resArray = OSDynamicCast(OSArray, getProperty(kHyperVSupportedResolutionsKey)); 37 | if (resArray == nullptr) { 38 | return buildFallbackMode(); 39 | } 40 | 41 | // 42 | // Populate modes. 43 | // 44 | _gfxModesCount = resArray->getCount(); 45 | _gfxModes = static_cast(IOMalloc(sizeof (*_gfxModes) * _gfxModesCount)); 46 | if (_gfxModes == nullptr) { 47 | HVSYSLOG("Failed to allocate graphics modes"); 48 | return kIOReturnNoResources; 49 | } 50 | 51 | for (UInt32 i = 0; i < _gfxModesCount; i++) { 52 | // 53 | // Get height/width for each mode. 54 | // 55 | OSDictionary *modeDict = OSDynamicCast(OSDictionary, resArray->getObject(i)); 56 | if (modeDict == nullptr) { 57 | HVSYSLOG("Graphics mode %u is not a dictionary", i); 58 | IOFree(_gfxModes, sizeof (*_gfxModes) * _gfxModesCount); 59 | return buildFallbackMode(); 60 | } 61 | 62 | OSNumber *width = OSDynamicCast(OSNumber, modeDict->getObject(kHyperVWidthKey)); 63 | OSNumber *height = OSDynamicCast(OSNumber, modeDict->getObject(kHyperVHeightKey)); 64 | if ((width == nullptr) || (height == nullptr)) { 65 | HVSYSLOG("Graphics mode %u is missing keys", i); 66 | IOFree(_gfxModes, sizeof (*_gfxModes) * _gfxModesCount); 67 | return buildFallbackMode(); 68 | } 69 | 70 | _gfxModes[i].width = width->unsigned32BitValue(); 71 | _gfxModes[i].height = height->unsigned32BitValue(); 72 | 73 | // 74 | // Validate sizes are within range. 75 | // 76 | if (_gfxVersion.value == kHyperVGraphicsVersionV3_0) { 77 | if ((_gfxModes[i].width > kHyperVGraphicsMaxWidth2008) || (_gfxModes[i].height > kHyperVGraphicsMaxHeight2008)) { 78 | HVSYSLOG("Invalid screen resolution %ux%u at %u", _gfxModes[i].width, _gfxModes[i].height, i); 79 | IOFree(_gfxModes, sizeof (*_gfxModes) * _gfxModesCount); 80 | return buildFallbackMode(); 81 | } 82 | } 83 | if ((_gfxModes[i].width < kHyperVGraphicsMinWidth) || (_gfxModes[i].height < kHyperVGraphicsMinHeight) 84 | || ((_gfxModes[i].width * _gfxModes[i].height * (getScreenDepth() / kHyperVGraphicsBitsPerByte)) > _gfxLength)) { 85 | HVSYSLOG("Invalid screen resolution %ux%u at %u", _gfxModes[i].width, _gfxModes[i].height, i); 86 | IOFree(_gfxModes, sizeof (*_gfxModes) * _gfxModesCount); 87 | return buildFallbackMode(); 88 | } 89 | HVDBGLOG("Added graphics mode %ux%u at %u", _gfxModes[i].width, _gfxModes[i].height, i); 90 | } 91 | 92 | return kIOReturnSuccess; 93 | } 94 | 95 | IOReturn HyperVGraphicsFramebuffer::buildFallbackMode() { 96 | HVSYSLOG("Graphics modes could not be loaded, using fallback"); 97 | 98 | // 99 | // Use default 1024x768 mode if the modes could not be fetched. 100 | // 101 | _gfxModesCount = 1; 102 | _gfxModes = static_cast(IOMalloc(sizeof (*_gfxModes) * _gfxModesCount)); 103 | if (_gfxModes == nullptr) { 104 | HVSYSLOG("Failed to allocate graphics modes"); 105 | return kIOReturnNoResources; 106 | } 107 | _gfxModes[0] = { 1024, 768 }; 108 | return kIOReturnSuccess; 109 | } 110 | -------------------------------------------------------------------------------- /MacHyperVFramebuffer/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | $(MARKETING_VERSION) 19 | CFBundleVersion 20 | $(CURRENT_PROJECT_VERSION) 21 | IOKitPersonalities 22 | 23 | HyperVGraphicsFramebuffer 24 | 25 | CFBundleIdentifier 26 | $(PRODUCT_BUNDLE_IDENTIFIER) 27 | IOClass 28 | HyperVGraphicsFramebuffer 29 | IOMatchCategory 30 | IOFramebuffer 31 | IOPCIMatch 32 | 0x53531414 33 | IOProbeScore 34 | 50000 35 | IOProviderClass 36 | IOPCIDevice 37 | SupportedResolutions 38 | 39 | 40 | Height 41 | 480 42 | Width 43 | 640 44 | 45 | 46 | Height 47 | 600 48 | Width 49 | 800 50 | 51 | 52 | Height 53 | 768 54 | Width 55 | 1024 56 | 57 | 58 | Height 59 | 864 60 | Width 61 | 1152 62 | 63 | 64 | Height 65 | 720 66 | Width 67 | 1280 68 | 69 | 70 | Height 71 | 1024 72 | Width 73 | 1280 74 | 75 | 76 | 77 | 78 | NSHumanReadableCopyright 79 | Copyright © 2021-2025 Goldfish64. All rights reserved. 80 | OSBundleCompatibleVersion 81 | $(CURRENT_PROJECT_VERSION) 82 | OSBundleLibraries 83 | 84 | com.apple.iokit.IOGraphicsFamily 85 | 1.0.0b1 86 | com.apple.iokit.IOPCIFamily 87 | 1.0.0b1 88 | com.apple.kpi.bsd 89 | 8.0.0 90 | com.apple.kpi.iokit 91 | 8.0.0 92 | com.apple.kpi.libkern 93 | 8.0.0 94 | com.apple.kpi.mach 95 | 8.0.0 96 | com.apple.kpi.unsupported 97 | 8.0.0 98 | 99 | OSBundleRequired 100 | Safe Boot 101 | 102 | 103 | -------------------------------------------------------------------------------- /MacHyperVSupport/ACPIFixup/HyperVACPIFixup.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // HyperVACPIFixup.cpp 3 | // Hyper-V ACPI fixup module 4 | // 5 | // Copyright © 2025 Goldfish64. All rights reserved. 6 | // 7 | 8 | #include "HyperVACPIFixup.hpp" 9 | 10 | HyperVACPIFixup *HyperVACPIFixup::_instance; 11 | 12 | void HyperVACPIFixup::init() { 13 | HVCheckDebugArgs(); 14 | HVDBGLOG("Initializing ACPI fixup"); 15 | 16 | // 17 | // Patch AcpiWalkNamespace to call our wrapper function instead. 18 | // 19 | _acpiWalkNamespaceAddr = (mach_vm_address_t)AcpiWalkNamespace; 20 | 21 | // Save start of function. 22 | lilu_os_memcpy(_acpiWalkNamespaceOrig, (void *) _acpiWalkNamespaceAddr, sizeof (_acpiWalkNamespaceOrig)); 23 | 24 | // Patch to call wrapper. 25 | #if defined(__i386__) 26 | UInt64 patched[2] {0x25FF | ((_acpiWalkNamespaceAddr + 8) << 16), (UInt32)wrapAcpiWalkNamespace}; 27 | #elif defined(__x86_64__) 28 | UInt64 patched[2] {0x0225FF, (uintptr_t)wrapAcpiWalkNamespace}; 29 | #else 30 | #error Unsupported arch 31 | #endif 32 | if (MachInfo::setKernelWriting(true, KernelPatcher::kernelWriteLock) == KERN_SUCCESS) { 33 | lilu_os_memcpy((void *) _acpiWalkNamespaceAddr, patched, sizeof (patched)); 34 | MachInfo::setKernelWriting(false, KernelPatcher::kernelWriteLock); 35 | } 36 | 37 | HVDBGLOG("Patched AcpiWalkNamespace"); 38 | } 39 | 40 | ACPI_STATUS HyperVACPIFixup::wrapAcpiWalkNamespace(ACPI_OBJECT_TYPE Type, ACPI_HANDLE StartObject, UInt32 MaxDepth, 41 | ACPI_WALK_CALLBACK UserFunction, void *Context, void **ReturnValue) { 42 | ACPI_STATUS status; 43 | 44 | // 45 | // Replace the callback function passed to AcpiWalkNamespace only if 46 | // we are walking ACPI Device objects. 47 | // 48 | _instance->HVDBGLOG("Walking namespace for object type 0x%X", Type); 49 | if (Type == ACPI_TYPE_DEVICE) { 50 | _instance->_origAcpiWalkCallback = UserFunction; 51 | _instance->HVDBGLOG("Walking namespace for ACPI Device objects, original callback is %p", 52 | _instance->_origAcpiWalkCallback); 53 | UserFunction = acpiWalkCallback; 54 | } 55 | 56 | // 57 | // Restore and call original AcpiWalkNamespace function. 58 | // 59 | if (MachInfo::setKernelWriting(true, KernelPatcher::kernelWriteLock) == KERN_SUCCESS) { 60 | lilu_os_memcpy((void *) _instance->_acpiWalkNamespaceAddr, _instance->_acpiWalkNamespaceOrig, sizeof (_instance->_acpiWalkNamespaceOrig)); 61 | MachInfo::setKernelWriting(false, KernelPatcher::kernelWriteLock); 62 | } 63 | status = FunctionCast(wrapAcpiWalkNamespace, _instance->_acpiWalkNamespaceAddr)(Type, StartObject, MaxDepth, 64 | UserFunction, Context, ReturnValue); 65 | 66 | // 67 | // If we did not walk devices, patch again for another pass. 68 | // 69 | if (Type != ACPI_TYPE_DEVICE) { 70 | // Patch to call wrapper. 71 | #if defined(__i386__) 72 | UInt64 patched[2] {0x25FF | ((_instance->_acpiWalkNamespaceAddr + 8) << 16), (UInt32)wrapAcpiWalkNamespace}; 73 | #elif defined(__x86_64__) 74 | UInt64 patched[2] {0x0225FF, (uintptr_t)wrapAcpiWalkNamespace}; 75 | #else 76 | #error Unsupported arch 77 | #endif 78 | if (MachInfo::setKernelWriting(true, KernelPatcher::kernelWriteLock) == KERN_SUCCESS) { 79 | lilu_os_memcpy((void *) _instance->_acpiWalkNamespaceAddr, patched, sizeof (patched)); 80 | MachInfo::setKernelWriting(false, KernelPatcher::kernelWriteLock); 81 | } 82 | } else { 83 | _instance->HVDBGLOG("Removing ACPI fixup"); 84 | delete _instance; 85 | } 86 | 87 | return status; 88 | } 89 | 90 | ACPI_STATUS HyperVACPIFixup::acpiWalkCallback(ACPI_HANDLE ObjHandle, UInt32 NestingLevel, void *Context, void **ReturnValue) { 91 | ACPI_STATUS status; 92 | ACPI_NAMESPACE_NODE *node; 93 | ACPI_DEVICE_ID hid; 94 | bool skipDevice = false; 95 | 96 | // 97 | // Get node from handle. 98 | // 99 | _instance->HVDBGLOG("Callback on ACPI handle %p, nest level %u", ObjHandle, NestingLevel); 100 | node = static_cast(ObjHandle); 101 | 102 | // 103 | // Check _HID of node. 104 | // The large number of ACPI0007 devices in Windows Server 2019 and newer cause significant 105 | // issues in the ACPI implementations in macOS 10.6 and older. 106 | // 107 | status = AcpiUtExecute_HID(node, &hid); 108 | if (status == AE_OK) { 109 | if (strncmp(hid.Value, "ACPI0007", sizeof (hid.Value)) == 0) { 110 | _instance->HVDBGLOG("Skipping unsupported ACPI0007 device: %.4s", node->Name.Ascii); 111 | skipDevice = true; 112 | } 113 | } 114 | 115 | return skipDevice ? AE_OK : _instance->_origAcpiWalkCallback(ObjHandle, NestingLevel, Context, ReturnValue); 116 | } 117 | -------------------------------------------------------------------------------- /MacHyperVSupport/ACPIFixup/HyperVACPIFixup.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // HyperVACPIFixup.hpp 3 | // Hyper-V ACPI fixup module 4 | // 5 | // Copyright © 2025 Goldfish64. All rights reserved. 6 | // 7 | 8 | #ifndef HyperVACPIFixup_hpp 9 | #define HyperVACPIFixup_hpp 10 | 11 | #include 12 | 13 | #include "acpi.hpp" 14 | #include "HyperV.hpp" 15 | 16 | class HyperVACPIFixup { 17 | HVDeclareLogFunctionsNonIOKit("acpifix", "HyperVACPIFixup"); 18 | 19 | private: 20 | // 21 | // Global instance. 22 | // 23 | static HyperVACPIFixup *_instance; 24 | 25 | // 26 | // AcpiWalkNamespace wrapper 27 | // 28 | mach_vm_address_t _acpiWalkNamespaceAddr = 0; 29 | UInt64 _acpiWalkNamespaceOrig[2] {}; 30 | static ACPI_STATUS wrapAcpiWalkNamespace(ACPI_OBJECT_TYPE Type, ACPI_HANDLE StartObject, UInt32 MaxDepth, 31 | ACPI_WALK_CALLBACK UserFunction, void *Context, void **ReturnValue); 32 | ACPI_WALK_CALLBACK _origAcpiWalkCallback = nullptr; 33 | static ACPI_STATUS acpiWalkCallback(ACPI_HANDLE ObjHandle, UInt32 NestingLevel, void *Context, void **ReturnValue); 34 | 35 | // 36 | // Initialization function. 37 | // 38 | void init(); 39 | 40 | public: 41 | // 42 | // Instance creator. 43 | // 44 | static HyperVACPIFixup *createInstance() { 45 | if (_instance == nullptr) { 46 | _instance = new HyperVACPIFixup; 47 | if (_instance != nullptr) { 48 | _instance->init(); 49 | } 50 | } 51 | 52 | return _instance; 53 | } 54 | }; 55 | 56 | #endif 57 | -------------------------------------------------------------------------------- /MacHyperVSupport/ACPIFixup/HyperVACPIPlatformExpertShim.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // HyperVACPIPlatformExpertShim.cpp 3 | // Hyper-V ACPI platform expert shim 4 | // 5 | // Copyright © 2025 Goldfish64. All rights reserved. 6 | // 7 | 8 | #include "HyperVACPIPlatformExpertShim.hpp" 9 | #include "HyperVACPIFixup.hpp" 10 | 11 | OSDefineMetaClassAndStructors(HyperVACPIPlatformExpertShim, super); 12 | 13 | IOService* HyperVACPIPlatformExpertShim::probe(IOService *provider, SInt32 *score) { 14 | HVCheckDebugArgs(); 15 | 16 | if (getKernelVersion() <= KernelVersion::SnowLeopard) { 17 | HVDBGLOG("Creating HyperVACPIFixup instance"); 18 | HyperVACPIFixup::createInstance(); 19 | } else { 20 | HVDBGLOG("Skipping ACPI fixup on XNU %u", getKernelVersion()); 21 | } 22 | 23 | // 24 | // Don't actually bind to anything. 25 | // 26 | return nullptr; 27 | } 28 | -------------------------------------------------------------------------------- /MacHyperVSupport/ACPIFixup/HyperVACPIPlatformExpertShim.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // HyperVACPIPlatformExpertShim.hpp 3 | // Hyper-V ACPI platform expert shim 4 | // 5 | // Copyright © 2025 Goldfish64. All rights reserved. 6 | // 7 | 8 | #ifndef HyperVACPIPlatformExpertShim_hpp 9 | #define HyperVACPIPlatformExpertShim_hpp 10 | 11 | #include 12 | #include "HyperV.hpp" 13 | 14 | class HyperVACPIPlatformExpertShim : public IOService { 15 | OSDeclareDefaultStructors(HyperVACPIPlatformExpertShim); 16 | HVDeclareLogFunctions("acpishim"); 17 | typedef IOService super; 18 | 19 | public: 20 | // 21 | // IOService overrides. 22 | // 23 | IOService* probe(IOService *provider,SInt32 *score) APPLE_KEXT_OVERRIDE; 24 | }; 25 | 26 | #endif 27 | -------------------------------------------------------------------------------- /MacHyperVSupport/ACPIFixup/acpi.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // acpi.hpp 3 | // ACPI definitions from acpica 4 | // 5 | // Copyright © 1999-2008, Intel Corp. 6 | // 7 | 8 | #ifndef acpi_hpp 9 | #define acpi_hpp 10 | 11 | #include 12 | 13 | /* 14 | * Miscellaneous types 15 | */ 16 | typedef UInt32 ACPI_STATUS; /* All ACPI Exceptions */ 17 | typedef UInt32 ACPI_NAME; /* 4-byte ACPI name */ 18 | typedef char * ACPI_STRING; /* Null terminated ASCII string */ 19 | typedef void * ACPI_HANDLE; /* Actually a ptr to a NS Node */ 20 | 21 | #define ACPI_SUCCESS(a) (!(a)) 22 | #define ACPI_FAILURE(a) (a) 23 | 24 | #define AE_OK (ACPI_STATUS) 0x0000 25 | 26 | /* Length of _HID, _UID, _CID, and UUID values */ 27 | #define ACPI_DEVICE_ID_LENGTH 0x09 28 | #define ACPI_MAX_CID_LENGTH 48 29 | #define ACPI_UUID_LENGTH 16 30 | 31 | /* 32 | * Types associated with ACPI names and objects. The first group of 33 | * values (up to ACPI_TYPE_EXTERNAL_MAX) correspond to the definition 34 | * of the ACPI ObjectType() operator (See the ACPI Spec). Therefore, 35 | * only add to the first group if the spec changes. 36 | * 37 | * NOTE: Types must be kept in sync with the global AcpiNsProperties 38 | * and AcpiNsTypeNames arrays. 39 | */ 40 | typedef UInt32 ACPI_OBJECT_TYPE; 41 | 42 | #define ACPI_TYPE_ANY 0x00 43 | #define ACPI_TYPE_INTEGER 0x01 /* Byte/Word/Dword/Zero/One/Ones */ 44 | #define ACPI_TYPE_STRING 0x02 45 | #define ACPI_TYPE_BUFFER 0x03 46 | #define ACPI_TYPE_PACKAGE 0x04 /* ByteConst, multiple DataTerm/Constant/SuperName */ 47 | #define ACPI_TYPE_FIELD_UNIT 0x05 48 | #define ACPI_TYPE_DEVICE 0x06 /* Name, multiple Node */ 49 | #define ACPI_TYPE_EVENT 0x07 50 | #define ACPI_TYPE_METHOD 0x08 /* Name, ByteConst, multiple Code */ 51 | #define ACPI_TYPE_MUTEX 0x09 52 | #define ACPI_TYPE_REGION 0x0A 53 | #define ACPI_TYPE_POWER 0x0B /* Name,ByteConst,WordConst,multi Node */ 54 | #define ACPI_TYPE_PROCESSOR 0x0C /* Name,ByteConst,DWordConst,ByteConst,multi NmO */ 55 | #define ACPI_TYPE_THERMAL 0x0D /* Name, multiple Node */ 56 | #define ACPI_TYPE_BUFFER_FIELD 0x0E 57 | #define ACPI_TYPE_DDB_HANDLE 0x0F 58 | #define ACPI_TYPE_DEBUG_OBJECT 0x10 59 | 60 | #define ACPI_TYPE_EXTERNAL_MAX 0x10 61 | 62 | /* Owner IDs are used to track namespace nodes for selective deletion */ 63 | typedef UInt8 ACPI_OWNER_ID; 64 | #define ACPI_OWNER_ID_MAX 0xFF 65 | 66 | typedef union acpi_name_union { 67 | UInt32 Integer; 68 | char Ascii[4]; 69 | } ACPI_NAME_UNION; 70 | 71 | /* 72 | * The Namespace Node describes a named object that appears in the AML. 73 | * DescriptorType is used to differentiate between internal descriptors. 74 | * 75 | * The node is optimized for both 32-bit and 64-bit platforms: 76 | * 20 bytes for the 32-bit case, 32 bytes for the 64-bit case. 77 | * 78 | * Note: The DescriptorType and Type fields must appear in the identical 79 | * position in both the ACPI_NAMESPACE_NODE and ACPI_OPERAND_OBJECT 80 | * structures. 81 | */ 82 | typedef struct acpi_namespace_node { 83 | union acpi_operand_object *Object; /* Interpreter object */ 84 | UInt8 DescriptorType; /* Differentiate object descriptor types */ 85 | UInt8 Type; /* ACPI Type associated with this name */ 86 | UInt8 Flags; /* Miscellaneous flags */ 87 | ACPI_OWNER_ID OwnerId; /* Node creator */ 88 | ACPI_NAME_UNION Name; /* ACPI Name, always 4 chars per ACPI spec */ 89 | struct acpi_namespace_node *Child; /* First child */ 90 | struct acpi_namespace_node *Peer; /* Peer. Parent if ANOBJ_END_OF_PEER_LIST set */ 91 | } ACPI_NAMESPACE_NODE; 92 | 93 | typedef ACPI_STATUS (*ACPI_WALK_CALLBACK) (ACPI_HANDLE ObjHandle, UInt32 NestingLevel, void *Context, void **ReturnValue); 94 | 95 | /* Common string version of device HIDs and UIDs */ 96 | typedef struct acpi_device_id { 97 | char Value[ACPI_DEVICE_ID_LENGTH]; 98 | } ACPI_DEVICE_ID; 99 | 100 | // 101 | // Exported acpica functions from AppleACPIPlatform. 102 | // 103 | extern "C" ACPI_STATUS AcpiWalkNamespace(ACPI_OBJECT_TYPE Type, ACPI_HANDLE StartObject, UInt32 MaxDepth, 104 | ACPI_WALK_CALLBACK UserFunction, void *Context, void **ReturnValue); 105 | extern "C" ACPI_STATUS AcpiUtExecute_HID(ACPI_NAMESPACE_NODE *DeviceNode, ACPI_DEVICE_ID *Hid); 106 | 107 | #endif 108 | -------------------------------------------------------------------------------- /MacHyperVSupport/CPU/HyperVCPU.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // HyperVCPU.cpp 3 | // Hyper-V CPU disabler driver 4 | // 5 | // Copyright © 2022 Goldfish64. All rights reserved. 6 | // 7 | 8 | #include "HyperVCPU.hpp" 9 | 10 | OSDefineMetaClassAndStructors(HyperVCPU, super); 11 | 12 | #define kMaxCPUsTiger 16 13 | 14 | IOService* HyperVCPU::probe(IOService *provider, SInt32 *score) { 15 | #if __MAC_OS_X_VERSION_MIN_REQUIRED < __MAC_10_5 16 | // 17 | // Do not probe on versions newer than 10.4. 18 | // macOS 10.4 Tiger is the only version observed to have issues with this, as 19 | // Hyper-V can have large amounts of CPU objects in ACPI. 20 | // 21 | // Having more than the max number of CPU objects present will 22 | // cause stalls/panics during boot. 23 | // 24 | if (getKernelVersion() >= KernelVersion::Leopard) { 25 | return nullptr; 26 | } 27 | HVCheckDebugArgs(); 28 | 29 | OSNumber *cpuIndexNumber = OSDynamicCast(OSNumber, provider->getProperty("cpu index")); 30 | if (cpuIndexNumber == nullptr) { 31 | return nullptr; 32 | } 33 | 34 | UInt32 cpuIndex = cpuIndexNumber->unsigned32BitValue(); 35 | HVDBGLOG("Probing CPU %u", cpuIndex); 36 | 37 | // 38 | // Allow CPUs within the limit to load AppleACPICPU. 39 | // 40 | if (cpuIndex < kMaxCPUsTiger) { 41 | return nullptr; 42 | } 43 | 44 | HVDBGLOG("Blocking CPU %u", cpuIndex); 45 | return super::probe(provider, score); 46 | #else 47 | return nullptr; 48 | #endif 49 | } 50 | -------------------------------------------------------------------------------- /MacHyperVSupport/CPU/HyperVCPU.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // HyperVCPU.hpp 3 | // Hyper-V CPU disabler driver 4 | // 5 | // Copyright © 2022 Goldfish64. All rights reserved. 6 | // 7 | 8 | #ifndef HyperVCPU_hpp 9 | #define HyperVCPU_hpp 10 | 11 | #include 12 | #include "HyperV.hpp" 13 | 14 | class HyperVCPU : public IOService { 15 | OSDeclareDefaultStructors(HyperVCPU); 16 | HVDeclareLogFunctions("cpu"); 17 | typedef IOService super; 18 | 19 | public: 20 | // 21 | // IOService overrides. 22 | // 23 | IOService* probe(IOService *provider,SInt32 *score) APPLE_KEXT_OVERRIDE; 24 | }; 25 | 26 | #endif 27 | -------------------------------------------------------------------------------- /MacHyperVSupport/Controller/HyperVController.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // HyperVController.hpp 3 | // Hyper-V core controller driver 4 | // 5 | // Copyright © 2022 Goldfish64. All rights reserved. 6 | // 7 | 8 | #ifndef HyperVController_hpp 9 | #define HyperVController_hpp 10 | 11 | #include 12 | #include 13 | 14 | #include "HyperV.hpp" 15 | 16 | extern "C" { 17 | #include 18 | #include 19 | #include 20 | } 21 | 22 | typedef struct { 23 | UInt32 *interruptVector; 24 | bool *supportsHvVpIndex; 25 | 26 | UInt64 interruptCounter; 27 | UInt64 virtualCPUIndex; 28 | 29 | HyperVDMABuffer messageDma; 30 | HyperVDMABuffer eventFlagsDma; 31 | 32 | HyperVMessage *messages; 33 | volatile HyperVEventFlags *eventFlags; //TODO: testing 34 | 35 | HyperVDMABuffer postMessageDma; 36 | } HyperVCPUData; 37 | 38 | class HyperVInterruptController; 39 | class HyperVVMBus; 40 | class HyperVUserClient; 41 | 42 | class HyperVController : public IOService { 43 | OSDeclareDefaultStructors(HyperVController); 44 | HVDeclareLogFunctions("ctrl"); 45 | typedef IOService super; 46 | 47 | private: 48 | // 49 | // Hyper-V reported features. 50 | // 51 | UInt32 _hvMaxLeaf = 0; 52 | UInt32 _hvFeatures = 0; 53 | UInt32 _hvPmFeatures = 0; 54 | UInt32 _hvFeatures3 = 0; 55 | UInt16 _hvMajorVersion = 0; 56 | UInt32 _hvRecommends = 0; 57 | 58 | #if __MAC_OS_X_VERSION_MIN_REQUIRED >= __MAC_10_6 59 | pmCallBacks_t _pmCallbacks = { }; 60 | IOSimpleLock *_preemptionLock = nullptr; 61 | #endif 62 | 63 | // 64 | // Hypercall page. 65 | // 66 | void *hypercallPage = nullptr; 67 | IOMemoryDescriptor *hypercallDesc = nullptr; 68 | 69 | // 70 | // Interrupt and event data. 71 | // 72 | UInt32 _cpuDataCount = 0; 73 | HyperVCPUData *_cpuData = nullptr; 74 | UInt32 _interruptVector = 0; 75 | bool _supportsHvVpIndex = false; 76 | bool _useLegacyEventFlags = false; 77 | HyperVEventFlags *_vmbusRxEventFlags = nullptr; 78 | 79 | HyperVInterruptController *_hvInterruptController = nullptr; 80 | HyperVVMBus *_hvVMBus = nullptr; 81 | HyperVUserClient *_userClientInstance = nullptr; 82 | 83 | // 84 | // Misc functions. 85 | // 86 | bool identifyHyperV(); 87 | bool initVMBus(); 88 | 89 | // 90 | // Hypercalls/interrupts. 91 | // 92 | bool initHypercalls(); 93 | void destroyHypercalls(); 94 | void freeHypercallPage(); 95 | bool allocateInterruptBuffers(); 96 | bool initInterrupts(); 97 | void destroySynIC(); 98 | void handleInterrupt(OSObject *target, void *refCon, IOService *nub, int source); 99 | 100 | public: 101 | // 102 | // IOService overrides. 103 | // 104 | bool start(IOService *provider) APPLE_KEXT_OVERRIDE; 105 | 106 | // 107 | // Misc functions. 108 | // 109 | bool allocateDmaBuffer(HyperVDMABuffer *dmaBuf, size_t size); 110 | void freeDmaBuffer(HyperVDMABuffer *dmaBuf); 111 | bool addInterruptProperties(OSDictionary *dict, UInt32 interruptVector); 112 | 113 | // 114 | // Hypercalls/interrupts. 115 | // 116 | HypercallStatus hypercallPostMessage(UInt32 connectionId, HyperVMessageType messageType, void *data, UInt32 size); 117 | HypercallStatus hypercallSignalEvent(UInt32 connectionId); 118 | bool enableInterrupts(HyperVEventFlags *legacyEventFlags = nullptr); 119 | void disableInterrupts(); 120 | void sendSynICEOM(UInt32 cpu); 121 | 122 | // 123 | // Time reference counter. 124 | // 125 | inline bool isTimeRefCounterSupported() { return (_hvFeatures & kHyperVCpuidMsrTimeRefCnt); } 126 | inline UInt64 readTimeRefCounter() { return isTimeRefCounterSupported() ? rdmsr64(kHyperVMsrTimeRefCount) : 0; } 127 | 128 | // 129 | // Messages. 130 | // 131 | inline HyperVMessage* getPendingMessage(UInt32 cpuIndex, UInt32 messageIndex) { 132 | return &_cpuData[cpuIndex].messages[messageIndex]; 133 | } 134 | inline void clearPendingMessage(UInt32 cpuIndex, UInt32 messageIndex) { 135 | _cpuData[cpuIndex].messages[messageIndex].type = kHyperVMessageTypeNone; 136 | } 137 | }; 138 | 139 | #endif 140 | -------------------------------------------------------------------------------- /MacHyperVSupport/Controller/HyperVControllerHypercalls.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // HyperVControllerHypercalls.cpp 3 | // Hyper-V hypercalls support 4 | // 5 | // Copyright © 2021-2022 Goldfish64. All rights reserved. 6 | // 7 | 8 | #include "HyperVController.hpp" 9 | 10 | #include 11 | 12 | extern vm_map_t kernel_map; 13 | 14 | bool HyperVController::initHypercalls() { 15 | kern_return_t kernStatus; 16 | UInt64 hvHypercall; 17 | UInt64 hypercallPhysAddr; 18 | 19 | // 20 | // Allocate hypercall memory. 21 | // This must be a page that can be executed. 22 | // 23 | kernStatus = vm_allocate(kernel_map, reinterpret_cast(&hypercallPage), PAGE_SIZE, VM_FLAGS_ANYWHERE); 24 | if (kernStatus != KERN_SUCCESS) { 25 | HVSYSLOG("Failed to call vm_allocate for hypercall page with status 0x%X", kernStatus); 26 | return false; 27 | } 28 | kernStatus = vm_protect(kernel_map, reinterpret_cast(hypercallPage), PAGE_SIZE, false, VM_PROT_ALL); 29 | if (kernStatus != KERN_SUCCESS) { 30 | HVSYSLOG("Failed to call vm_protect for hypercall page with status 0x%X", kernStatus); 31 | freeHypercallPage(); 32 | return false; 33 | } 34 | 35 | hypercallDesc = IOMemoryDescriptor::withAddress(hypercallPage, PAGE_SIZE, kIODirectionInOut); 36 | if (hypercallDesc == nullptr) { 37 | HVSYSLOG("Failed to map hypercall page"); 38 | freeHypercallPage(); 39 | return false; 40 | } 41 | hypercallDesc->prepare(); 42 | hypercallPhysAddr = hypercallDesc->getPhysicalAddress(); 43 | 44 | // 45 | // Setup hypercall page with Hyper-V. 46 | // 47 | hvHypercall = rdmsr64(kHyperVMsrHypercall); 48 | HVDBGLOG("Hypercall MSR current value: 0x%llX", hvHypercall); 49 | HVDBGLOG("Allocated hypercall page to phys 0x%llX (virt %p)", hypercallPhysAddr, hypercallPage); 50 | 51 | hvHypercall = ((hypercallPhysAddr << PAGE_SHIFT) >> kHyperVMsrHypercallPageShift) 52 | | (hvHypercall & kHyperVMsrHypercallRsvdMask) | kHyperVMsrHypercallEnable; 53 | wrmsr64(kHyperVMsrHypercall, hvHypercall); 54 | 55 | hvHypercall = rdmsr64(kHyperVMsrHypercall); 56 | HVDBGLOG("Hypercall MSR new value: 0x%llX", hvHypercall); 57 | 58 | // 59 | // Verify hypercalls are enabled. 60 | // 61 | if ((hvHypercall & kHyperVMsrHypercallEnable) == 0) { 62 | HVSYSLOG("Failed to enable hypercalls"); 63 | freeHypercallPage(); 64 | return false; 65 | } 66 | 67 | HVDBGLOG("Hypercalls are now enabled"); 68 | return true; 69 | } 70 | 71 | void HyperVController::destroyHypercalls() { 72 | UInt64 hvHypercall; 73 | 74 | // 75 | // Disable hypercalls. 76 | // 77 | hvHypercall = rdmsr64(kHyperVMsrHypercall); 78 | wrmsr64(kHyperVMsrHypercall, hvHypercall & kHyperVMsrHypercallRsvdMask); 79 | freeHypercallPage(); 80 | 81 | HVDBGLOG("Hypercalls are now disabled"); 82 | } 83 | 84 | void HyperVController::freeHypercallPage() { 85 | if (hypercallDesc != nullptr) { 86 | hypercallDesc->complete(); 87 | hypercallDesc->release(); 88 | hypercallDesc = nullptr; 89 | } 90 | 91 | if (hypercallPage != nullptr) { 92 | vm_deallocate(kernel_map, reinterpret_cast(hypercallPage), PAGE_SIZE); 93 | hypercallPage = nullptr; 94 | } 95 | } 96 | 97 | HypercallStatus HyperVController::hypercallPostMessage(UInt32 connectionId, HyperVMessageType messageType, void *data, UInt32 size) { 98 | UInt64 status; 99 | 100 | if (size > kHyperVMessageDataSize) { 101 | HVSYSLOG("Attempted to send message that is too big of %u bytes", size); 102 | return kHypercallStatusInvalidParameter; 103 | } 104 | 105 | // 106 | // Get per-CPU hypercall post message page. 107 | // 108 | HyperVDMABuffer *postPageBuffer = &_cpuData[cpu_number()].postMessageDma; 109 | 110 | HypercallPostMessage *postMessage = (HypercallPostMessage*) postPageBuffer->buffer; 111 | postMessage->connectionId = connectionId; 112 | postMessage->reserved = 0; 113 | postMessage->messageType = messageType; 114 | postMessage->size = size; 115 | memcpy(&postMessage->data[0], data, size); 116 | 117 | // 118 | // Perform HvPostMessage hypercall. 119 | // 120 | // During hypercall, the calling processor will be suspended until the hypercall returns. 121 | // Linux disables preemption during this time, but unsure if that is needed due to the above. 122 | // 123 | #if defined(__i386__) 124 | asm volatile ("call *%5" : "=A" (status) : "d" (0), "a" (kHypercallTypePostMessage), "b" (0), "c" ((uint32_t) postPageBuffer->physAddr), "m" (hypercallPage)); 125 | #elif defined(__x86_64__) 126 | asm volatile ("call *%3" : "=a" (status) : "c" (kHypercallTypePostMessage), "d" (postPageBuffer->physAddr), "m" (hypercallPage)); 127 | #else 128 | #error Unsupported arch 129 | #endif 130 | return (HypercallStatus)(status & kHypercallStatusMask); 131 | } 132 | 133 | HypercallStatus HyperVController::hypercallSignalEvent(UInt32 connectionId) { 134 | UInt64 status; 135 | 136 | // 137 | // Perform a fast version of HvSignalEvent hypercall. 138 | // 139 | #if defined(__i386__) 140 | asm volatile ("call *%5" : "=A" (status) : "d" (0), "a" (kHypercallTypeSignalEvent), "b" (0), "c" (connectionId), "m" (hypercallPage)); 141 | #elif defined(__x86_64__) 142 | asm volatile ("call *%3" : "=a" (status) : "c" (kHypercallTypeSignalEvent), "d" (connectionId), "m" (hypercallPage)); 143 | #else 144 | #error Unsupported arch 145 | #endif 146 | return (HypercallStatus)(status & kHypercallStatusMask); 147 | } 148 | -------------------------------------------------------------------------------- /MacHyperVSupport/Graphics/HyperVGraphics.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // HyperVGraphics.cpp 3 | // Hyper-V synthetic graphics driver 4 | // 5 | // Copyright © 2025 Goldfish64. All rights reserved. 6 | // 7 | 8 | #include "HyperVGraphics.hpp" 9 | 10 | OSDefineMetaClassAndStructors(HyperVGraphics, super); 11 | 12 | bool HyperVGraphics::start(IOService *provider) { 13 | bool result = false; 14 | IOReturn status; 15 | 16 | // 17 | // Get parent VMBus device object. 18 | // 19 | _hvDevice = OSDynamicCast(HyperVVMBusDevice, provider); 20 | if (_hvDevice == nullptr) { 21 | HVSYSLOG("Provider is not HyperVVMBusDevice"); 22 | return false; 23 | } 24 | _hvDevice->retain(); 25 | 26 | HVCheckDebugArgs(); 27 | HVDBGLOG("Initializing Hyper-V Synthetic Graphics"); 28 | 29 | if (HVCheckOffArg()) { 30 | HVSYSLOG("Disabling Hyper-V Synthetic Graphics due to boot arg"); 31 | OSSafeReleaseNULL(_hvDevice); 32 | return false; 33 | } 34 | 35 | if (!super::start(provider)) { 36 | HVSYSLOG("super::start() returned false"); 37 | OSSafeReleaseNULL(_hvDevice); 38 | return false; 39 | } 40 | 41 | do { 42 | // 43 | // Initialize work loop and command gate. 44 | // 45 | _workLoop = IOWorkLoop::workLoop(); 46 | if (_workLoop == nullptr) { 47 | HVSYSLOG("Failed to create work loop"); 48 | break; 49 | } 50 | _cmdGate = IOCommandGate::commandGate(this); 51 | if (_cmdGate == nullptr) { 52 | HVSYSLOG("Failed to create command gate"); 53 | break; 54 | } 55 | status = _workLoop->addEventSource(_cmdGate); 56 | if (status != kIOReturnSuccess) { 57 | HVSYSLOG("Failed to add command gate event source with status 0x%X", status); 58 | break; 59 | } 60 | 61 | // 62 | // Initialize refresh timer. 63 | // 64 | _timerEventSource = IOTimerEventSource::timerEventSource(this, 65 | OSMemberFunctionCast(IOTimerEventSource::Action, this, &HyperVGraphics::handleRefreshTimer)); 66 | if (_timerEventSource == nullptr) { 67 | HVSYSLOG("Failed to create screen refresh timer event source"); 68 | break; 69 | } 70 | status = getWorkLoop()->addEventSource(_timerEventSource); 71 | if (status != kIOReturnSuccess) { 72 | HVSYSLOG("Failed to add screen refresh timer event source with status 0x%X", status); 73 | break; 74 | } 75 | 76 | // 77 | // Install packet handler. 78 | // 79 | status = _hvDevice->installPacketActions(this, OSMemberFunctionCast(HyperVVMBusDevice::PacketReadyAction, this, &HyperVGraphics::handlePacket), 80 | nullptr, kHyperVGraphicsMaxPacketSize); 81 | if (status != kIOReturnSuccess) { 82 | HVSYSLOG("Failed to install packet handler with status 0x%X", status); 83 | break; 84 | } 85 | 86 | // 87 | // Open VMBus channel and connect to graphics system. 88 | // 89 | status = _hvDevice->openVMBusChannel(kHyperVGraphicsRingBufferSize, kHyperVGraphicsRingBufferSize); 90 | if (status != kIOReturnSuccess) { 91 | HVSYSLOG("Failed to open VMBus channel with status 0x%X", status); 92 | break; 93 | } 94 | 95 | registerService(); 96 | HVDBGLOG("Initialized Hyper-V Synthetic Graphics"); 97 | result = true; 98 | } while (false); 99 | 100 | if (!result) { 101 | stop(provider); 102 | } 103 | return result; 104 | } 105 | 106 | void HyperVGraphics::stop(IOService *provider) { 107 | HVDBGLOG("Hyper-V Synthetic Graphics is stopping"); 108 | 109 | if (_timerEventSource != nullptr) { 110 | _timerEventSource->disable(); 111 | getWorkLoop()->removeEventSource(_timerEventSource); 112 | OSSafeReleaseNULL(_timerEventSource); 113 | } 114 | if (_cmdGate != nullptr) { 115 | _workLoop->removeEventSource(_cmdGate); 116 | OSSafeReleaseNULL(_cmdGate); 117 | } 118 | OSSafeReleaseNULL(_workLoop); 119 | 120 | if (_gfxMsgCursorShape != nullptr) { 121 | IOFree(_gfxMsgCursorShape, _gfxMsgCursorShapeSize); 122 | _gfxMsgCursorShape = nullptr; 123 | } 124 | 125 | if (_hvDevice != nullptr) { 126 | _hvDevice->closeVMBusChannel(); 127 | _hvDevice->uninstallPacketActions(); 128 | OSSafeReleaseNULL(_hvDevice); 129 | } 130 | 131 | super::stop(provider); 132 | } 133 | 134 | IOReturn HyperVGraphics::callPlatformFunction(const OSSymbol *functionName, bool waitForFunction, 135 | void *param1, void *param2, void *param3, void *param4) { 136 | if (functionName->isEqualTo(kHyperVGraphicsPlatformFunctionInit)) { 137 | return platformInitGraphics(static_cast(param1), static_cast(param2), static_cast(param3)); 138 | } else if (functionName->isEqualTo(kHyperVGraphicsPlatformFunctionSetResolution)) { 139 | return platformSetScreenResolution(static_cast(param1), static_cast(param2)); 140 | } else if (functionName->isEqualTo(kHyperVGraphicsPlatformFunctionSetCursorShape)) { 141 | return setCursorShape(static_cast(param1)); 142 | } else if (functionName->isEqualTo(kHyperVGraphicsPlatformFunctionSetCursorPosition)) { 143 | return platformSetCursorPosition(static_cast(param1), static_cast(param2), static_cast(param3)); 144 | } else { 145 | HVDBGLOG("Called unknown platform function '%s'", functionName->getCStringNoCopy()); 146 | } 147 | 148 | return super::callPlatformFunction(functionName, waitForFunction, param1, param2, param3, param4); 149 | } 150 | -------------------------------------------------------------------------------- /MacHyperVSupport/Graphics/HyperVGraphics.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // HyperVGraphics.hpp 3 | // Hyper-V synthetic graphics driver 4 | // 5 | // Copyright © 2025 Goldfish64. All rights reserved. 6 | // 7 | 8 | #ifndef HyperVGraphics_hpp 9 | #define HyperVGraphics_hpp 10 | 11 | #include 12 | #include 13 | 14 | #include "HyperVVMBusDevice.hpp" 15 | #include "HyperVGraphicsPlatformFunctions.hpp" 16 | #include "HyperVGraphicsRegs.hpp" 17 | 18 | class HyperVGraphics : public IOService { 19 | OSDeclareDefaultStructors(HyperVGraphics); 20 | HVDeclareLogFunctionsVMBusChild("gfx"); 21 | typedef IOService super; 22 | 23 | private: 24 | HyperVVMBusDevice *_hvDevice = nullptr; 25 | IOWorkLoop *_workLoop = nullptr; 26 | IOCommandGate *_cmdGate = nullptr; 27 | IOTimerEventSource *_timerEventSource = nullptr; 28 | 29 | 30 | VMBusVersion _gfxVersion = { }; 31 | UInt32 _bitDepth = 32; // TODO: Not all version support 32-bit 32 | IOPhysicalAddress _gfxBase = 0; 33 | UInt32 _gfxLength = 0; 34 | UInt32 _screenWidth = 0; 35 | UInt32 _screenHeight = 0; 36 | bool _fbReady = false; 37 | 38 | HyperVGraphicsMessage *_gfxMsgCursorShape = nullptr; 39 | size_t _gfxMsgCursorShapeSize = 0; 40 | 41 | void handleRefreshTimer(IOTimerEventSource *sender); 42 | void handlePacket(VMBusPacketHeader *pktHeader, UInt32 pktHeaderLength, UInt8 *pktData, UInt32 pktDataLength); 43 | 44 | // 45 | // Internal functions. 46 | // 47 | inline UInt32 getScreenDepth() { return (_gfxVersion.value == kHyperVGraphicsVersionV3_0) ? kHyperVGraphicsBitDepth2008 : kHyperVGraphicsBitDepth; } 48 | IOReturn sendGraphicsMessage(HyperVGraphicsMessage *gfxMessage, HyperVGraphicsMessageType responseType = kHyperVGraphicsMessageTypeError, 49 | HyperVGraphicsMessage *gfxMessageResponse = nullptr); 50 | IOReturn negotiateVersion(VMBusVersion version); 51 | IOReturn allocateGraphicsMemory(IOPhysicalAddress *outBase, UInt32 *outLength); 52 | IOReturn refreshFramebufferImage(); 53 | IOReturn setGraphicsMemory(IOPhysicalAddress base, UInt32 length); 54 | IOReturn setScreenResolution(UInt32 width, UInt32 height, bool waitForAck = true); 55 | IOReturn setScreenResolutionGated(UInt32 *width, UInt32 *height, bool *waitForAck); 56 | IOReturn setCursorShape(HyperVGraphicsPlatformFunctionSetCursorShapeParams *params, bool refreshCursor = false); 57 | IOReturn setCursorShapeGated(HyperVGraphicsPlatformFunctionSetCursorShapeParams *params, bool *refreshCursor); 58 | IOReturn setCursorPosition(SInt32 x, SInt32 y, bool isVisible, bool refreshCursor = false); 59 | IOReturn setCursorPositionGated(SInt32 *x, SInt32 *y, bool *isVisible, bool *refreshCursor); 60 | 61 | // 62 | // Platform functions. 63 | // 64 | IOReturn platformInitGraphics(VMBusVersion *outVersion, IOPhysicalAddress *outMemBase, UInt32 *outMemLength); 65 | IOReturn platformSetScreenResolution(UInt32 *inWidth, UInt32 *inHeight); 66 | IOReturn platformSetCursorPosition(SInt32 *x, SInt32 *y, bool *isVisible); 67 | 68 | public: 69 | // 70 | // IOService overrides. 71 | // 72 | bool start(IOService *provider) APPLE_KEXT_OVERRIDE; 73 | void stop(IOService *provider) APPLE_KEXT_OVERRIDE; 74 | IOReturn callPlatformFunction(const OSSymbol *functionName, bool waitForFunction, 75 | void *param1, void *param2, void *param3, void *param4) APPLE_KEXT_OVERRIDE; 76 | }; 77 | 78 | #endif 79 | -------------------------------------------------------------------------------- /MacHyperVSupport/Graphics/HyperVGraphicsPlatformFunctions.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // HyperVGraphicsPlatformFunctions.cpp 3 | // Hyper-V synthetic graphics driver platform functions 4 | // 5 | // Copyright © 2025 Goldfish64. All rights reserved. 6 | // 7 | 8 | #include "HyperVGraphics.hpp" 9 | 10 | IOReturn HyperVGraphics::platformInitGraphics(VMBusVersion *outVersion, IOPhysicalAddress *outMemBase, UInt32 *outMemLength) { 11 | IOReturn status; 12 | 13 | if ((outVersion == nullptr) || (outMemBase == nullptr) || (outMemLength == nullptr)) { 14 | return kIOReturnBadArgument; 15 | } 16 | 17 | if (_fbReady) { 18 | *outVersion = _gfxVersion; 19 | *outMemBase = _gfxBase; 20 | *outMemLength = _gfxLength; 21 | HVDBGLOG("Graphics system already initialized"); 22 | return kIOReturnSuccess; 23 | } 24 | 25 | // 26 | // Negotiate graphics system version. 27 | // 28 | VMBusVersion graphicsVersion; 29 | switch (_hvDevice->getVMBusVersion()) { 30 | case kVMBusVersionWIN7: 31 | case kVMBusVersionWS2008: 32 | graphicsVersion.value = kHyperVGraphicsVersionV3_0; 33 | break; 34 | 35 | case kVMBusVersionWIN8: 36 | case kVMBusVersionWIN8_1: 37 | case kVMBusVersionWIN10: 38 | case kVMBusVersionWIN10_V4_1: // TODO: Check if this is correct. 39 | graphicsVersion.value = kHyperVGraphicsVersionV3_2; 40 | break; 41 | 42 | default: 43 | graphicsVersion.value = kHyperVGraphicsVersionV3_5; 44 | break; 45 | } 46 | 47 | status = negotiateVersion(graphicsVersion); 48 | if (status != kIOReturnSuccess) { 49 | HVSYSLOG("Could not negotiate graphics version"); 50 | return kIOReturnUnsupported; 51 | } 52 | _gfxVersion = graphicsVersion; 53 | 54 | // 55 | // Allocate graphics memory. 56 | // 57 | status = allocateGraphicsMemory(&_gfxBase, &_gfxLength); 58 | if (status != kIOReturnSuccess) { 59 | HVSYSLOG("Failed to allocate graphics memory with status 0x%X", status); 60 | return status; 61 | } 62 | 63 | // 64 | // Send memory location to Hyper-V. 65 | // 66 | status = setGraphicsMemory(_gfxBase, _gfxLength); 67 | if (status != kIOReturnSuccess) { 68 | HVSYSLOG("Failed to send graphics memory location with status 0x%X", status); 69 | return status; 70 | } 71 | 72 | *outVersion = _gfxVersion; 73 | *outMemBase = _gfxBase; 74 | *outMemLength = _gfxLength; 75 | 76 | // 77 | // Start refresh timer sending screen updates. 78 | // 79 | _timerEventSource->enable(); 80 | _timerEventSource->setTimeoutMS(kHyperVGraphicsImageUpdateRefreshRateMS); 81 | _fbReady = true; 82 | 83 | HVDBGLOG("Graphics system initialized"); 84 | return kIOReturnSuccess; 85 | } 86 | 87 | IOReturn HyperVGraphics::platformSetScreenResolution(UInt32 *inWidth, UInt32 *inHeight) { 88 | if ((inWidth == nullptr) || (inHeight == nullptr)) { 89 | return kIOReturnBadArgument; 90 | } 91 | return setScreenResolution(*inWidth, *inHeight); 92 | } 93 | 94 | IOReturn HyperVGraphics::platformSetCursorPosition(SInt32 *x, SInt32 *y, bool *isVisible) { 95 | if ((x == nullptr) || (y == nullptr) || (isVisible == nullptr)) { 96 | return kIOReturnBadArgument; 97 | } 98 | return setCursorPosition(*x, *y, *isVisible); 99 | } 100 | -------------------------------------------------------------------------------- /MacHyperVSupport/Graphics/HyperVGraphicsPlatformFunctions.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // HyperVGraphicsPlatformFunctions.hpp 3 | // Hyper-V synthetic graphics driver platform function definitions 4 | // 5 | // Copyright © 2025 Goldfish64. All rights reserved. 6 | // 7 | 8 | #ifndef HyperVGraphicsPlatformFunctions_hpp 9 | #define HyperVGraphicsPlatformFunctions_hpp 10 | 11 | // 12 | // Platform function names. 13 | // 14 | #define kHyperVGraphicsPlatformFunctionInit "HyperVGraphicsPlatformFunctionInit" 15 | #define kHyperVGraphicsPlatformFunctionSetResolution "HyperVGraphicsPlatformFunctionSetResolution" 16 | #define kHyperVGraphicsPlatformFunctionSetCursorShape "HyperVGraphicsPlatformFunctionSetCursorShape" 17 | #define kHyperVGraphicsPlatformFunctionSetCursorPosition "HyperVGraphicsPlatformFunctionSetCursorPosition" 18 | 19 | // 20 | // HyperVGraphicsPlatformFunctionSetCursorShape parameters 21 | // 22 | typedef struct { 23 | UInt8 *cursorData; 24 | UInt32 width; 25 | UInt32 height; 26 | UInt32 hotX; 27 | UInt32 hotY; 28 | } HyperVGraphicsPlatformFunctionSetCursorShapeParams; 29 | 30 | #endif 31 | -------------------------------------------------------------------------------- /MacHyperVSupport/GraphicsBridge/HyperVGraphicsBridge.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // HyperVGraphicsBridge.hpp 3 | // Hyper-V synthetic graphics bridge 4 | // 5 | // Copyright © 2021-2025 Goldfish64. All rights reserved. 6 | // 7 | 8 | #ifndef HyperVGraphicsBridge_hpp 9 | #define HyperVGraphicsBridge_hpp 10 | 11 | #include 12 | 13 | #include "HyperVVMBusDevice.hpp" 14 | 15 | class HyperVGraphicsBridge : public HV_PCIBRIDGE_CLASS { 16 | OSDeclareDefaultStructors(HyperVGraphicsBridge); 17 | HVDeclareLogFunctions("gfxb"); 18 | typedef HV_PCIBRIDGE_CLASS super; 19 | 20 | private: 21 | UInt8 _pciBusNumber = 0; 22 | 23 | // 24 | // Fake PCI structures. 25 | // 26 | IOSimpleLock *_pciLock = nullptr; 27 | UInt8 _fakePCIDeviceSpace[256]; 28 | UInt32 _fbInitialBase = 0; 29 | UInt32 _fbInitialLength = 0; 30 | 31 | public: 32 | // 33 | // IOService overrides. 34 | // 35 | IOService *probe(IOService *provider, SInt32 *score) APPLE_KEXT_OVERRIDE; 36 | bool start(IOService *provider) APPLE_KEXT_OVERRIDE; 37 | void stop(IOService *provider) APPLE_KEXT_OVERRIDE; 38 | 39 | // 40 | // IOPCIBridge overrides. 41 | // 42 | bool configure(IOService *provider) APPLE_KEXT_OVERRIDE; 43 | IODeviceMemory *ioDeviceMemory() APPLE_KEXT_OVERRIDE { return nullptr; } 44 | UInt32 configRead32(IOPCIAddressSpace space, UInt8 offset) APPLE_KEXT_OVERRIDE; 45 | void configWrite32(IOPCIAddressSpace space, UInt8 offset, UInt32 data) APPLE_KEXT_OVERRIDE; 46 | UInt16 configRead16(IOPCIAddressSpace space, UInt8 offset) APPLE_KEXT_OVERRIDE; 47 | void configWrite16(IOPCIAddressSpace space, UInt8 offset, UInt16 data) APPLE_KEXT_OVERRIDE; 48 | UInt8 configRead8(IOPCIAddressSpace space, UInt8 offset) APPLE_KEXT_OVERRIDE; 49 | void configWrite8(IOPCIAddressSpace space, UInt8 offset, UInt8 data) APPLE_KEXT_OVERRIDE; 50 | 51 | IOPCIAddressSpace getBridgeSpace() APPLE_KEXT_OVERRIDE { 52 | IOPCIAddressSpace space = { 0 }; 53 | return space; 54 | } 55 | UInt8 firstBusNum() APPLE_KEXT_OVERRIDE { return _pciBusNumber; } 56 | UInt8 lastBusNum() APPLE_KEXT_OVERRIDE { return _pciBusNumber; } 57 | }; 58 | 59 | #endif 60 | -------------------------------------------------------------------------------- /MacHyperVSupport/IntegrationComponents/FileCopy/HyperVFileCopy.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // HyperVFileCopy.cpp 3 | // Hyper-V file copy driver 4 | // 5 | // Copyright © 2022 flagers. All rights reserved. 6 | // 7 | 8 | #include "HyperVFileCopy.hpp" 9 | 10 | OSDefineMetaClassAndStructors(HyperVFileCopy, super); 11 | 12 | static const VMBusVersion fileCopyVersions[] = { 13 | { kHyperVFileCopyVersionV1_1 } 14 | }; 15 | 16 | bool HyperVFileCopy::start(IOService *provider) { 17 | if (HVCheckOffArg()) { 18 | HVSYSLOG("Disabling Hyper-V File Copy due to boot arg"); 19 | return false; 20 | } 21 | 22 | if (!super::start(provider)) { 23 | HVSYSLOG("super::start() returned false"); 24 | return false; 25 | } 26 | 27 | HVCheckDebugArgs(); 28 | setICDebug(debugEnabled); 29 | 30 | HVDBGLOG("Initializing Hyper-V File Copy"); 31 | registerService(); 32 | return true; 33 | } 34 | 35 | void HyperVFileCopy::stop(IOService *provider) { 36 | HVDBGLOG("Stopping Hyper-V File Copy"); 37 | super::stop(provider); 38 | } 39 | 40 | bool HyperVFileCopy::open(IOService *forClient, IOOptionBits options, void *arg) { 41 | HyperVFileCopyUserClient *hvFileCopyUserClient = OSDynamicCast(HyperVFileCopyUserClient, forClient); 42 | if (hvFileCopyUserClient == nullptr || _userClientInstance != nullptr) { 43 | return false; 44 | } 45 | 46 | if (!super::open(forClient, options, arg)) { 47 | return false; 48 | } 49 | 50 | _userClientInstance = hvFileCopyUserClient; 51 | _userClientInstance->retain(); 52 | return true; 53 | } 54 | 55 | void HyperVFileCopy::close(IOService *forClient, IOOptionBits options) { 56 | OSSafeReleaseNULL(_userClientInstance); 57 | super::close(forClient, options); 58 | } 59 | 60 | void HyperVFileCopy::handlePacket(VMBusPacketHeader *pktHeader, UInt32 pktHeaderLength, UInt8 *pktData, UInt32 pktDataLength) { 61 | HyperVFileCopyMessage *fileCopyMsg = (HyperVFileCopyMessage*) pktData; 62 | IOReturn status; 63 | 64 | switch (fileCopyMsg->icHeader.type) { 65 | case kVMBusICMessageTypeNegotiate: 66 | // 67 | // Determine supported protocol version and communicate back to Hyper-V. 68 | // 69 | if (!processNegotiationResponse(&fileCopyMsg->negotiate, fileCopyVersions, arrsize(fileCopyVersions))) { 70 | HVSYSLOG("Failed to determine a supported Hyper-V File Copy version"); 71 | fileCopyMsg->icHeader.status = kHyperVStatusFailure; 72 | } 73 | break; 74 | 75 | case kVMBusICMessageTypeFileCopy: 76 | HVDBGLOG("Attempting file copy operation of type 0x%X", fileCopyMsg->fileCopyHeader.type); 77 | if (_userClientInstance == nullptr) { 78 | HVSYSLOG("Unable to start file copy (file copy daemon is not running)"); 79 | fileCopyMsg->icHeader.status = kHyperVStatusFailure; 80 | break; 81 | } 82 | 83 | switch (fileCopyMsg->fileCopyHeader.type) { 84 | case kHyperVFileCopyMessageTypeStartCopy: 85 | status = _userClientInstance->startFileCopy(fileCopyMsg->startCopy.fileName, fileCopyMsg->startCopy.filePath, 86 | fileCopyMsg->startCopy.flags, fileCopyMsg->startCopy.fileSize); 87 | break; 88 | 89 | case kHyperVFileCopyMessageTypeWriteToFile: 90 | status = _userClientInstance->writeFileFragment(fileCopyMsg->dataFragment.offset, 91 | fileCopyMsg->dataFragment.data, fileCopyMsg->dataFragment.size); 92 | break; 93 | 94 | case kHyperVFileCopyMessageTypeCompleteCopy: 95 | status = _userClientInstance->completeFileCopy(); 96 | break; 97 | 98 | case kHyperVFileCopyMessageTypeCancelCopy: 99 | status = _userClientInstance->cancelFileCopy(); 100 | break; 101 | 102 | default: 103 | status = kIOReturnUnsupported; 104 | break; 105 | } 106 | 107 | switch (status) { 108 | case kIOReturnSuccess: 109 | fileCopyMsg->icHeader.status = kHyperVStatusSuccess; 110 | break; 111 | 112 | case kIOReturnStillOpen: 113 | fileCopyMsg->icHeader.status = kHyperVStatusAlreadyExists; 114 | break; 115 | 116 | case kIOReturnNoSpace: 117 | fileCopyMsg->icHeader.status = kHyperVStatusDiskFull; 118 | break; 119 | 120 | default: 121 | fileCopyMsg->icHeader.status = kHyperVStatusFailure; 122 | break; 123 | } 124 | break; 125 | 126 | default: 127 | HVDBGLOG("Unknown file copy message type %u", fileCopyMsg->fileCopyHeader.type); 128 | fileCopyMsg->icHeader.status = kHyperVStatusFailure; 129 | break; 130 | } 131 | 132 | // 133 | // Send response back to Hyper-V. The packet size will always be the same as the original inbound one. 134 | // 135 | fileCopyMsg->icHeader.flags = kVMBusICFlagTransaction | kVMBusICFlagResponse; 136 | _hvDevice->writeInbandPacket(fileCopyMsg, pktDataLength, false); 137 | } 138 | -------------------------------------------------------------------------------- /MacHyperVSupport/IntegrationComponents/FileCopy/HyperVFileCopy.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // HyperVFileCopy.hpp 3 | // Hyper-V file copy driver 4 | // 5 | // Copyright © 2022-2025 flagers. All rights reserved. 6 | // 7 | 8 | #ifndef HyperVFileCopy_hpp 9 | #define HyperVFileCopy_hpp 10 | 11 | #include "HyperVICService.hpp" 12 | #include "HyperVFileCopyRegs.hpp" 13 | #include "HyperVFileCopyUserClientInternal.hpp" 14 | 15 | class HyperVFileCopy : public HyperVICService { 16 | OSDeclareDefaultStructors(HyperVFileCopy); 17 | HVDeclareLogFunctionsVMBusChild("fcopy"); 18 | typedef HyperVICService super; 19 | 20 | protected: 21 | void handlePacket(VMBusPacketHeader *pktHeader, UInt32 pktHeaderLength, UInt8 *pktData, UInt32 pktDataLength) APPLE_KEXT_OVERRIDE; 22 | UInt32 txBufferSize() APPLE_KEXT_OVERRIDE { return kHyperVFileCopyBufferSize; }; 23 | UInt32 rxBufferSize() APPLE_KEXT_OVERRIDE { return kHyperVFileCopyBufferSize; }; 24 | 25 | private: 26 | HyperVFileCopyUserClient *_userClientInstance = nullptr; 27 | 28 | public: 29 | // 30 | // IOService overrides. 31 | // 32 | bool start(IOService *provider) APPLE_KEXT_OVERRIDE; 33 | void stop(IOService *provider) APPLE_KEXT_OVERRIDE; 34 | bool open(IOService *forClient, IOOptionBits options, void *arg) APPLE_KEXT_OVERRIDE; 35 | void close(IOService *forClient, IOOptionBits options) APPLE_KEXT_OVERRIDE; 36 | }; 37 | 38 | #endif 39 | -------------------------------------------------------------------------------- /MacHyperVSupport/IntegrationComponents/FileCopy/HyperVFileCopyRegs.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // HyperVFileCopyRegs.hpp 3 | // Hyper-V file copy driver 4 | // 5 | // Copyright © 2022 flagers. All rights reserved. 6 | // 7 | 8 | #ifndef HyperVFileCopyRegs_hpp 9 | #define HyperVFileCopyRegs_hpp 10 | 11 | #include "HyperVIC.hpp" 12 | #include "HyperVFileCopyRegsUser.h" 13 | 14 | #define kHyperVFileCopyBufferSize (8 * PAGE_SIZE) 15 | 16 | #if __MAC_OS_X_VERSION_MIN_REQUIRED >= __MAC_10_5 17 | #define kHyperVFileCopyMaxDataSize kHyperVFileCopyFragmentSize 18 | #else 19 | #define kHyperVFileCopyMaxDataSize sizeof(io_struct_inband_t) 20 | #endif 21 | 22 | // 23 | // File copy versions. 24 | // 25 | #define kHyperVFileCopyVersionV1_1 VMBUS_VERSION(1, 1) 26 | 27 | // 28 | // File copy messages. 29 | // 30 | typedef struct __attribute__((packed)) { 31 | VMBusICMessageHeader icHeader; 32 | 33 | HyperVFileCopyMessageType type; 34 | uuid_t unused1; 35 | uuid_t unused2; 36 | } HyperVFileCopyMessageHeader; 37 | 38 | typedef struct __attribute__((packed)) { 39 | HyperVFileCopyMessageHeader header; 40 | 41 | // 42 | // fileName & filePath are UTF-16 strings. 43 | // 44 | UInt16 fileName[kHyperVFileCopyMaxPath]; 45 | UInt16 filePath[kHyperVFileCopyMaxPath]; 46 | 47 | HyperVFileCopyMessageFlags flags; 48 | UInt64 fileSize; 49 | } HyperVFileCopyMessageStartCopy; 50 | 51 | typedef struct __attribute__((packed)) { 52 | HyperVFileCopyMessageHeader header; 53 | 54 | UInt32 reserved; 55 | UInt64 offset; 56 | UInt32 size; 57 | UInt8 data[kHyperVFileCopyFragmentSize]; 58 | } HyperVFileCopyMessageDataFragment; 59 | 60 | typedef struct __attribute__((packed)) { 61 | union { 62 | VMBusICMessageHeader icHeader; 63 | HyperVFileCopyMessageHeader fileCopyHeader; 64 | 65 | VMBusICMessageNegotiate negotiate; 66 | HyperVFileCopyMessageStartCopy startCopy; 67 | HyperVFileCopyMessageDataFragment dataFragment; 68 | }; 69 | } HyperVFileCopyMessage; 70 | 71 | #endif 72 | -------------------------------------------------------------------------------- /MacHyperVSupport/IntegrationComponents/FileCopy/HyperVFileCopyRegsUser.h: -------------------------------------------------------------------------------- 1 | // 2 | // HyperVFileCopyRegsUser.h 3 | // Hyper-V file copy driver 4 | // 5 | // Copyright © 2022 Goldfish64. All rights reserved. 6 | // 7 | 8 | #ifndef HyperVFileCopyRegsUser_h 9 | #define HyperVFileCopyRegsUser_h 10 | 11 | #define kHyperVFileCopyMaxPath 260 12 | #define kHyperVFileCopyFragmentSize (6 * 1024) 13 | 14 | // 15 | // File copy message types. 16 | // 17 | typedef enum : UInt32 { 18 | kHyperVFileCopyMessageTypeStartCopy = 0, 19 | kHyperVFileCopyMessageTypeWriteToFile = 1, 20 | kHyperVFileCopyMessageTypeCompleteCopy = 2, 21 | kHyperVFileCopyMessageTypeCancelCopy = 3 22 | } HyperVFileCopyMessageType; 23 | 24 | // 25 | // File copy flags. 26 | // 27 | typedef enum : UInt32 { 28 | kHyperVFileCopyMessageFlagsOverwrite = 1, 29 | kHyperVFileCopyMessageFlagsCreatePath = 2 30 | } HyperVFileCopyMessageFlags; 31 | 32 | #endif 33 | -------------------------------------------------------------------------------- /MacHyperVSupport/IntegrationComponents/FileCopy/HyperVFileCopyUserClient.h: -------------------------------------------------------------------------------- 1 | // 2 | // HyperVFileCopyUserClient.h 3 | // Hyper-V file copy user client 4 | // 5 | // Copyright © 2022 flagers, Goldfish64. All rights reserved. 6 | // 7 | 8 | #ifndef HyperVFileCopyUserClient_h 9 | #define HyperVFileCopyUserClient_h 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | #include "HyperVFileCopyRegsUser.h" 16 | 17 | typedef enum : UInt32 { 18 | kHyperVFileCopyUserClientMethodGetFilePath, 19 | kHyperVFileCopyUserClientMethodGetNextDataFragment, 20 | kHyperVFileCopyUserClientMethodCompleteIO, 21 | 22 | kHyperVFileCopyUserClientMethodNumberOfMethods 23 | } HyperVFileCopyUserClientMethod; 24 | 25 | typedef struct { 26 | UInt8 fileName[NAME_MAX]; 27 | UInt8 filePath[PATH_MAX]; 28 | } HyperVFileCopyUserClientStartCopyData; 29 | 30 | typedef struct { 31 | HyperVFileCopyMessageFlags flags; 32 | UInt64 fileSize; 33 | UInt32 maxFragmentSize; 34 | } HyperVFileCopyUserClientNotificationMessageStartCopy; 35 | 36 | typedef struct { 37 | UInt64 offset; 38 | UInt32 size; 39 | } HyperVFileCopyUserClientNotificationMessageDataFragment; 40 | 41 | typedef struct { 42 | mach_msg_header_t header; 43 | union { 44 | HyperVFileCopyUserClientNotificationMessageStartCopy startCopy; 45 | HyperVFileCopyUserClientNotificationMessageDataFragment dataFragment; 46 | }; 47 | HyperVFileCopyMessageType type; 48 | 49 | #ifndef KERNEL 50 | mach_msg_trailer_t trailer; 51 | #endif 52 | } HyperVFileCopyUserClientNotificationMessage; 53 | 54 | #endif 55 | -------------------------------------------------------------------------------- /MacHyperVSupport/IntegrationComponents/FileCopy/HyperVFileCopyUserClientInternal.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // HyperVFileCopyUserClientInternal.hpp 3 | // Hyper-V file copy user client 4 | // 5 | // Copyright © 2022-2025 flagers, Goldfish64. All rights reserved. 6 | // 7 | 8 | #ifndef HyperVFileCopyUserClient_hpp 9 | #define HyperVFileCopyUserClient_hpp 10 | 11 | #include "HyperVICUserClient.hpp" 12 | #include "HyperVFileCopyRegs.hpp" 13 | #include "HyperVFileCopyUserClient.h" 14 | 15 | class HyperVFileCopyUserClient : public HyperVICUserClient { 16 | OSDeclareDefaultStructors(HyperVFileCopyUserClient); 17 | HVDeclareLogFunctions("fcopyuser"); 18 | typedef HyperVICUserClient super; 19 | 20 | private: 21 | UInt8 _currentFileName[PATH_MAX]; 22 | UInt8 _currentFilePath[PATH_MAX]; 23 | UInt8 *_currentFileData = nullptr; 24 | 25 | // 26 | // Userspace communication methods. 27 | // 28 | bool convertUnicodePath(UInt16 *inputStr, UInt8 *outputStr, size_t outputStrSize); 29 | IOReturn notifyFileCopyClientApplication(HyperVFileCopyUserClientNotificationMessage *notificationMsg); 30 | IOReturn getFilePath(void *output, UInt32 *outputSize); 31 | IOReturn getNextDataFragment(IOMemoryDescriptor *memoryDesc, void *output, UInt32 *outputSize); 32 | IOReturn completeIO(IOReturn status); 33 | 34 | #if __MAC_OS_X_VERSION_MIN_REQUIRED >= __MAC_10_5 35 | static IOReturn sDispatchMethodGetFilePath(HyperVFileCopyUserClient *target, void *ref, IOExternalMethodArguments *args); 36 | static IOReturn sDispatchMethodGetNextDataFragment(HyperVFileCopyUserClient *target, void *ref, IOExternalMethodArguments *args); 37 | static IOReturn sDispatchMethodCompleteIO(HyperVFileCopyUserClient *target, void *ref, IOExternalMethodArguments *args); 38 | #else 39 | #if (defined(__i386__) && defined(__clang__)) 40 | static IOReturn sMethodGetFilePath(HyperVFileCopyUserClient *that, void *output, UInt32 *outputSize); 41 | static IOReturn sMethodGetNextDataFragment(HyperVFileCopyUserClient *that, void *output, UInt32 *outputSize); 42 | static IOReturn sMethodCompleteIO(HyperVFileCopyUserClient *that, UInt32 status); 43 | #else 44 | IOReturn methodGetFilePath(void *output, UInt32 *outputSize); 45 | IOReturn methodGetNextDataFragment(void *output, UInt32 *outputSize); 46 | IOReturn methodCompleteIO(UInt32 status); 47 | #endif 48 | #endif 49 | 50 | public: 51 | // 52 | // IOService overrides. 53 | // 54 | bool start(IOService *provider) APPLE_KEXT_OVERRIDE; 55 | void stop(IOService *provider) APPLE_KEXT_OVERRIDE; 56 | 57 | // 58 | // IOUserClient overrides. 59 | // 60 | #if __MAC_OS_X_VERSION_MIN_REQUIRED >= __MAC_10_5 61 | IOReturn externalMethod(uint32_t selector, IOExternalMethodArguments *arguments, 62 | IOExternalMethodDispatch *dispatch, OSObject *target, 63 | void *reference) APPLE_KEXT_OVERRIDE; 64 | #else 65 | IOExternalMethod *getTargetAndMethodForIndex(IOService **target, UInt32 index) APPLE_KEXT_OVERRIDE; 66 | #endif 67 | 68 | // 69 | // User client methods. 70 | // 71 | IOReturn startFileCopy(UInt16 *fileName, UInt16 *filePath, HyperVFileCopyMessageFlags flags, UInt64 fileSize); 72 | IOReturn writeFileFragment(UInt64 offset, UInt8 *data, UInt32 dataSize); 73 | IOReturn completeFileCopy(); 74 | IOReturn cancelFileCopy(); 75 | }; 76 | 77 | #endif 78 | -------------------------------------------------------------------------------- /MacHyperVSupport/IntegrationComponents/FileCopy/HyperVFileCopyUserClientPrivate.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // HyperVFileCopyUserClientPrivate.cpp 3 | // Hyper-V file copy user client 4 | // 5 | // Copyright © 2022-2025 flagers, Goldfish64. All rights reserved. 6 | // 7 | 8 | #include "HyperVFileCopyUserClientInternal.hpp" 9 | #include 10 | 11 | bool HyperVFileCopyUserClient::convertUnicodePath(UInt16 *inputStr, UInt8 *outputStr, size_t outputStrSize) { 12 | int result; 13 | UInt8 unicodeNul[3] = { 0xE2, 0x90, 0x80 }; 14 | void *unicodeNulLoc; 15 | size_t nlen; 16 | 17 | bzero(outputStr, outputStrSize); 18 | result = utf8_encodestr(inputStr, kHyperVFileCopyMaxPath * sizeof (UInt16), outputStr, &nlen, outputStrSize, '/', UTF_LITTLE_ENDIAN); 19 | if (result != 0) { 20 | HVDBGLOG("Failed to encode UTF8 string with result %d", result); 21 | return false; 22 | } 23 | 24 | unicodeNulLoc = lilu_os_memmem(outputStr, outputStrSize, &unicodeNul, sizeof (unicodeNul)); 25 | if (!unicodeNulLoc) { 26 | return false; 27 | } 28 | *(char *)unicodeNulLoc = 0x00; 29 | 30 | return true; 31 | } 32 | 33 | IOReturn HyperVFileCopyUserClient::notifyFileCopyClientApplication(HyperVFileCopyUserClientNotificationMessage *notificationMsg) { 34 | if (_notificationPort == MACH_PORT_NULL) { 35 | HVDBGLOG("Notification port is not open"); 36 | return kIOReturnNotFound; 37 | } 38 | 39 | HVDBGLOG("Sending file copy notification type %u", notificationMsg->type); 40 | notificationMsg->header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0); 41 | notificationMsg->header.msgh_size = sizeof (*notificationMsg); 42 | notificationMsg->header.msgh_remote_port = _notificationPort; 43 | notificationMsg->header.msgh_local_port = MACH_PORT_NULL; 44 | notificationMsg->header.msgh_reserved = 0; 45 | notificationMsg->header.msgh_id = 0; 46 | 47 | return mach_msg_send_from_kernel(¬ificationMsg->header, notificationMsg->header.msgh_size); 48 | } 49 | 50 | IOReturn HyperVFileCopyUserClient::getFilePath(void *output, UInt32 *outputSize) { 51 | HyperVFileCopyUserClientStartCopyData *startCopyData; 52 | 53 | HVDBGLOG("Filename and path requested by userspace daemon"); 54 | if (output == nullptr || *outputSize != sizeof (*startCopyData)) { 55 | HVDBGLOG("Invalid struct, null: %u size: %u bytes", output == nullptr, *outputSize); 56 | return kIOReturnBadArgument; 57 | } 58 | startCopyData = (HyperVFileCopyUserClientStartCopyData *) output; 59 | 60 | // 61 | // Copy filename and file path to userspace. 62 | // 63 | memcpy(startCopyData->fileName, _currentFileName, sizeof (startCopyData->fileName)); 64 | memcpy(startCopyData->filePath, _currentFilePath, sizeof (startCopyData->filePath)); 65 | HVDBGLOG("Returning filename %s and path %s to userspace daemon", startCopyData->fileName, startCopyData->filePath); 66 | return kIOReturnSuccess; 67 | } 68 | 69 | IOReturn HyperVFileCopyUserClient::getNextDataFragment(IOMemoryDescriptor *memoryDesc, void *output, UInt32 *outputSize) { 70 | IOReturn status; 71 | IOByteCount bytesWritten; 72 | 73 | // 74 | // Structures larger than 4KB are passed in with an IOMemoryDescriptor that must be mapped instead. 75 | // This only applies to 10.5 and newer. 76 | // 77 | HVDBGLOG("File data requested by userspace daemon"); 78 | if (memoryDesc != nullptr) { 79 | status = memoryDesc->prepare(); 80 | if (status != kIOReturnSuccess) { 81 | HVDBGLOG("Failed to prepare data descriptor with status 0x%X", status); 82 | return status; 83 | } 84 | 85 | bytesWritten = memoryDesc->writeBytes(0, _currentFileData, *outputSize); // TODO: is this right output size? 86 | memoryDesc->complete(); 87 | 88 | if (bytesWritten != kHyperVFileCopyFragmentSize) { 89 | HVDBGLOG("Failed to write all bytes to descriptor, only wrote %u bytes", bytesWritten); 90 | return kIOReturnIOError; 91 | } 92 | } else { 93 | if (output == nullptr || *outputSize != kHyperVFileCopyMaxDataSize) { 94 | HVDBGLOG("Invalid struct, null: %u, size %u bytes", output == nullptr, *outputSize); 95 | return kIOReturnBadArgument; 96 | } 97 | 98 | // 99 | // Copy file data to userspace. 100 | // 101 | memcpy(output, _currentFileData, *outputSize); 102 | } 103 | 104 | return kIOReturnSuccess; 105 | } 106 | 107 | IOReturn HyperVFileCopyUserClient::completeIO(IOReturn status) { 108 | HVDBGLOG("Completing I/O with status 0x%X", status); 109 | wakeThread(status); 110 | return kIOReturnSuccess; 111 | } 112 | 113 | #if __MAC_OS_X_VERSION_MIN_REQUIRED >= __MAC_10_5 114 | IOReturn HyperVFileCopyUserClient::sDispatchMethodGetFilePath(HyperVFileCopyUserClient *target, void *ref, IOExternalMethodArguments *args) { 115 | return target->getFilePath(args->structureOutput, &args->structureOutputSize); 116 | } 117 | 118 | IOReturn HyperVFileCopyUserClient::sDispatchMethodGetNextDataFragment(HyperVFileCopyUserClient *target, void *ref, IOExternalMethodArguments *args) { 119 | return target->getNextDataFragment(args->structureOutputDescriptor, args->structureOutput, &args->structureOutputSize); 120 | } 121 | 122 | IOReturn HyperVFileCopyUserClient::sDispatchMethodCompleteIO(HyperVFileCopyUserClient *target, void *ref, IOExternalMethodArguments *args) { 123 | return target->completeIO(static_cast(args->scalarInput[0])); 124 | } 125 | #else 126 | #if (defined(__i386__) && defined(__clang__)) 127 | IOReturn HyperVFileCopyUserClient::sMethodGetFilePath(HyperVFileCopyUserClient *that, void *output, UInt32 *outputSize) { 128 | return that->getFilePath(output, outputSize); 129 | } 130 | 131 | IOReturn HyperVFileCopyUserClient::sMethodGetNextDataFragment(HyperVFileCopyUserClient *that, void *output, UInt32 *outputSize) { 132 | return that->getNextDataFragment(nullptr, output, outputSize); 133 | } 134 | 135 | IOReturn HyperVFileCopyUserClient::sMethodCompleteIO(HyperVFileCopyUserClient *that, UInt32 status) { 136 | return that->completeIO(static_cast(status)); 137 | } 138 | #else 139 | IOReturn HyperVFileCopyUserClient::methodGetFilePath(void *output, UInt32 *outputSize) { 140 | return getFilePath(output, outputSize); 141 | } 142 | 143 | IOReturn HyperVFileCopyUserClient::methodGetNextDataFragment(void *output, UInt32 *outputSize) { 144 | return getNextDataFragment(nullptr, output, outputSize); 145 | } 146 | 147 | IOReturn HyperVFileCopyUserClient::methodCompleteIO(UInt32 status) { 148 | return completeIO(static_cast(status)); 149 | } 150 | #endif 151 | #endif 152 | 153 | 154 | -------------------------------------------------------------------------------- /MacHyperVSupport/IntegrationComponents/Heartbeat/HyperVHeartbeat.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // HyperVHeartbeat.cpp 3 | // Hyper-V heartbeat driver 4 | // 5 | // Copyright © 2021-2022 Goldfish64. All rights reserved. 6 | // 7 | 8 | #include "HyperVHeartbeat.hpp" 9 | 10 | OSDefineMetaClassAndStructors(HyperVHeartbeat, super); 11 | 12 | static const VMBusVersion heartbeatVersions[] = { 13 | { kHyperVHeartbeatVersionV3 }, 14 | { kHyperVHeartbeatVersionV1 } 15 | }; 16 | 17 | bool HyperVHeartbeat::start(IOService *provider) { 18 | if (HVCheckOffArg()) { 19 | HVSYSLOG("Disabling Hyper-V Heartbeat due to boot arg"); 20 | return false; 21 | } 22 | 23 | if (!super::start(provider)) { 24 | HVSYSLOG("super::start() returned false"); 25 | return false; 26 | } 27 | 28 | HVCheckDebugArgs(); 29 | setICDebug(debugEnabled); 30 | 31 | HVDBGLOG("Initializing Hyper-V Heartbeat"); 32 | return true; 33 | } 34 | 35 | void HyperVHeartbeat::stop(IOService *provider) { 36 | HVDBGLOG("Stopping Hyper-V Heartbeat"); 37 | super::stop(provider); 38 | } 39 | 40 | void HyperVHeartbeat::handlePacket(VMBusPacketHeader *pktHeader, UInt32 pktHeaderLength, UInt8 *pktData, UInt32 pktDataLength) { 41 | VMBusICMessageHeartbeat *heartbeatMsg = (VMBusICMessageHeartbeat*) pktData; 42 | UInt32 packetSize; 43 | 44 | switch (heartbeatMsg->header.type) { 45 | case kVMBusICMessageTypeNegotiate: 46 | // 47 | // Determine supported protocol version and communicate back to Hyper-V. 48 | // 49 | firstHeartbeatReceived = false; 50 | if (!processNegotiationResponse(&heartbeatMsg->negotiate, heartbeatVersions, arrsize(heartbeatVersions))) { 51 | HVSYSLOG("Failed to determine a supported Hyper-V Heartbeat version"); 52 | heartbeatMsg->header.status = kHyperVStatusFailure; 53 | } 54 | break; 55 | 56 | case kVMBusICMessageTypeHeartbeat: 57 | // 58 | // Normal heartbeat packet. 59 | // The sequence number is incremented twice per cycle, once by the guest and once by Hyper-V. 60 | // 61 | packetSize = heartbeatMsg->header.dataSize + sizeof (heartbeatMsg->header); 62 | if (packetSize < __offsetof (VMBusICMessageHeartbeatSequence, sequence)) { 63 | HVSYSLOG("Heartbeat packet is invalid size (%u bytes)", packetSize); 64 | heartbeatMsg->header.status = kHyperVStatusFailure; 65 | break; 66 | } 67 | 68 | HVDBGLOG("Got heartbeat, seq = %u", heartbeatMsg->heartbeat.sequence); 69 | heartbeatMsg->heartbeat.sequence++; 70 | 71 | if (!firstHeartbeatReceived) { 72 | firstHeartbeatReceived = true; 73 | HVDBGLOG("Initialized Hyper-V Heartbeat"); 74 | } 75 | break; 76 | 77 | default: 78 | HVDBGLOG("Unknown heartbeat message type %u", heartbeatMsg->header.type); 79 | heartbeatMsg->header.status = kHyperVStatusFailure; 80 | break; 81 | } 82 | 83 | // 84 | // Send response back to Hyper-V. The packet size will always be the same as the original inbound one. 85 | // 86 | heartbeatMsg->header.flags = kVMBusICFlagTransaction | kVMBusICFlagResponse; 87 | _hvDevice->writeInbandPacket(heartbeatMsg, pktDataLength, false); 88 | } 89 | -------------------------------------------------------------------------------- /MacHyperVSupport/IntegrationComponents/Heartbeat/HyperVHeartbeat.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // HyperVHeartbeat.hpp 3 | // Hyper-V heartbeat driver 4 | // 5 | // Copyright © 2021 Goldfish64. All rights reserved. 6 | // 7 | 8 | #ifndef HyperVHeartbeat_hpp 9 | #define HyperVHeartbeat_hpp 10 | 11 | #include "HyperVICService.hpp" 12 | #include "HyperVHeartbeatRegs.hpp" 13 | 14 | class HyperVHeartbeat : public HyperVICService { 15 | OSDeclareDefaultStructors(HyperVHeartbeat); 16 | HVDeclareLogFunctionsVMBusChild("heart"); 17 | typedef HyperVICService super; 18 | 19 | private: 20 | bool firstHeartbeatReceived = false; 21 | 22 | protected: 23 | void handlePacket(VMBusPacketHeader *pktHeader, UInt32 pktHeaderLength, UInt8 *pktData, UInt32 pktDataLength) APPLE_KEXT_OVERRIDE; 24 | 25 | public: 26 | // 27 | // IOService overrides. 28 | // 29 | bool start(IOService *provider) APPLE_KEXT_OVERRIDE; 30 | void stop(IOService *provider) APPLE_KEXT_OVERRIDE; 31 | }; 32 | 33 | #endif 34 | -------------------------------------------------------------------------------- /MacHyperVSupport/IntegrationComponents/Heartbeat/HyperVHeartbeatRegs.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // HyperVHeartbeatRegs.hpp 3 | // Hyper-V heartbeat driver 4 | // 5 | // Copyright © 2022 Goldfish64. All rights reserved. 6 | // 7 | 8 | #ifndef HyperVHeartbeatRegs_hpp 9 | #define HyperVHeartbeatRegs_hpp 10 | 11 | #include "HyperVIC.hpp" 12 | 13 | // 14 | // Heartbeat versions. 15 | // 16 | #define kHyperVHeartbeatVersionV1 VMBUS_VERSION(1, 0) 17 | #define kHyperVHeartbeatVersionV3 VMBUS_VERSION(3, 0) 18 | 19 | // 20 | // Heartbeat messages. 21 | // 22 | typedef struct __attribute__((packed)) { 23 | VMBusICMessageHeader header; 24 | 25 | UInt64 sequence; 26 | UInt32 reserved[8]; 27 | } VMBusICMessageHeartbeatSequence; 28 | 29 | typedef struct __attribute__((packed)) { 30 | union { 31 | VMBusICMessageHeader header; 32 | VMBusICMessageNegotiate negotiate; 33 | VMBusICMessageHeartbeatSequence heartbeat; 34 | }; 35 | } VMBusICMessageHeartbeat; 36 | 37 | #endif 38 | -------------------------------------------------------------------------------- /MacHyperVSupport/IntegrationComponents/HyperVIC.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // HyperVIC.hpp 3 | // Hyper-V IC base class 4 | // 5 | // Copyright © 2021 Goldfish64. All rights reserved. 6 | // 7 | 8 | #ifndef HyperVIC_hpp 9 | #define HyperVIC_hpp 10 | 11 | #include "HyperV.hpp" 12 | 13 | // 14 | // Framework versions. 15 | // 16 | #define kHyperVICVersion2008 VMBUS_VERSION(1, 0) 17 | #define kHyperVICVersionV3 VMBUS_VERSION(3, 0) 18 | 19 | // 20 | // Message types. 21 | // 22 | typedef enum : UInt16 { 23 | kVMBusICMessageTypeNegotiate = 0, 24 | kVMBusICMessageTypeHeartbeat = 1, 25 | kVMBusICMessageTypeKVPExchange = 2, 26 | kVMBusICMessageTypeShutdown = 3, 27 | kVMBusICMessageTypeTimeSync = 4, 28 | kVMBusICMessageTypeVSS = 5, 29 | kVMBusICMessageTypeFileCopy = 7 30 | } VMBusICMessageType; 31 | 32 | #define kVMBusICFlagTransaction 1 33 | #define kVMBusICFlagRequest 2 34 | #define kVMBusICFlagResponse 4 35 | 36 | // 37 | // Header and common negotiation message. 38 | // 39 | typedef struct __attribute__((packed)) { 40 | UInt32 pipeFlags; 41 | UInt32 pipeMsgs; 42 | 43 | VMBusVersion frameworkVersion; 44 | VMBusICMessageType type; 45 | VMBusVersion msgVersion; 46 | UInt16 dataSize; 47 | HyperVStatus status; 48 | UInt8 transactionId; 49 | UInt8 flags; 50 | UInt16 reserved; 51 | } VMBusICMessageHeader; 52 | 53 | typedef struct __attribute__((packed)) { 54 | VMBusICMessageHeader header; 55 | 56 | UInt16 frameworkVersionCount; 57 | UInt16 messageVersionCount; 58 | UInt32 reserved; 59 | VMBusVersion versions[]; 60 | } VMBusICMessageNegotiate; 61 | 62 | #endif 63 | -------------------------------------------------------------------------------- /MacHyperVSupport/IntegrationComponents/HyperVICService.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // HyperVICService.cpp 3 | // Hyper-V IC base class 4 | // 5 | // Copyright © 2021 Goldfish64. All rights reserved. 6 | // 7 | 8 | #include "HyperVICService.hpp" 9 | 10 | OSDefineMetaClassAndAbstractStructors(HyperVICService, super); 11 | 12 | static const VMBusVersion frameworkVersions[] = { 13 | { kHyperVICVersionV3 }, 14 | { kHyperVICVersion2008 } 15 | }; 16 | 17 | bool HyperVICService::start(IOService *provider) { 18 | bool result = false; 19 | IOReturn status; 20 | 21 | // 22 | // Get parent VMBus device object. 23 | // 24 | _hvDevice = OSDynamicCast(HyperVVMBusDevice, provider); 25 | if (_hvDevice == nullptr) { 26 | HVSYSLOG("Provider is not HyperVVMBusDevice"); 27 | return false; 28 | } 29 | _hvDevice->retain(); 30 | 31 | HVCheckDebugArgs(); 32 | HVDBGLOG("Initializing Hyper-V Integration Component"); 33 | 34 | if (!super::start(provider)) { 35 | HVSYSLOG("super::start() returned false"); 36 | OSSafeReleaseNULL(_hvDevice); 37 | return false; 38 | } 39 | 40 | do { 41 | // 42 | // Install packet handler. 43 | // 44 | status = _hvDevice->installPacketActions(this, OSMemberFunctionCast(HyperVVMBusDevice::PacketReadyAction, this, &HyperVICService::handlePacket), 45 | nullptr, kHyperVICBufferSize, true, false); 46 | if (status != kIOReturnSuccess) { 47 | HVSYSLOG("Failed to install packet handler with status 0x%X", status); 48 | break; 49 | } 50 | 51 | // 52 | // Open VMBus channel 53 | // 54 | status = _hvDevice->openVMBusChannel(txBufferSize(), rxBufferSize()); 55 | if (status != kIOReturnSuccess) { 56 | HVSYSLOG("Failed to open VMBus channel with status 0x%X", status); 57 | break; 58 | } 59 | 60 | result = true; 61 | } while (false); 62 | 63 | if (!result) { 64 | stop(provider); 65 | } 66 | return result; 67 | } 68 | 69 | void HyperVICService::stop(IOService *provider) { 70 | // 71 | // Close channel and remove handler. 72 | // 73 | if (_hvDevice != nullptr) { 74 | _hvDevice->closeVMBusChannel(); 75 | _hvDevice->uninstallPacketActions(); 76 | OSSafeReleaseNULL(_hvDevice); 77 | } 78 | 79 | super::stop(provider); 80 | } 81 | 82 | bool HyperVICService::processNegotiationResponse(VMBusICMessageNegotiate *negMsg, const VMBusVersion *msgVersions, 83 | UInt32 msgVersionsCount, VMBusVersion *msgVersionUsed) { 84 | UInt32 versionCount; 85 | UInt32 packetSize; 86 | 87 | bool foundFwMatch = false; 88 | bool foundMsgMatch = false; 89 | 90 | VMBusVersion frameworkVersion = { }; 91 | VMBusVersion msgVersion = { }; 92 | 93 | if (negMsg->frameworkVersionCount == 0 || negMsg->messageVersionCount == 0) { 94 | HVDBGLOG("Invalid framework or message version count"); 95 | return false; 96 | } 97 | 98 | versionCount = negMsg->frameworkVersionCount + negMsg->messageVersionCount; 99 | packetSize = negMsg->header.dataSize + sizeof (negMsg->header); 100 | if (packetSize < __offsetof (VMBusICMessageNegotiate, versions[versionCount])) { 101 | HVSYSLOG("Negotiate packet is invalid size (%u bytes)", packetSize); 102 | return false; 103 | } 104 | 105 | // 106 | // Determine highest supported framework version. 107 | // 108 | for (UInt32 i = 0; i < arrsize(frameworkVersions); i++) { 109 | for (UInt32 j = 0; j < negMsg->frameworkVersionCount; j++) { 110 | HVDBGLOG("Checking framework version %u.%u against version %u.%u", 111 | frameworkVersions[i].major, frameworkVersions[i].minor, negMsg->versions[j].major, negMsg->versions[j].minor); 112 | if ((frameworkVersions[i].major == negMsg->versions[j].major) 113 | && (frameworkVersions[i].minor == negMsg->versions[j].minor)) { 114 | frameworkVersion = negMsg->versions[j]; 115 | foundFwMatch = true; 116 | break; 117 | } 118 | } 119 | 120 | if (foundFwMatch) { 121 | break; 122 | } 123 | } 124 | 125 | // 126 | // Determine highest supported message version. 127 | // 128 | for (UInt32 i = 0; i < msgVersionsCount; i++) { 129 | for (UInt32 j = negMsg->frameworkVersionCount; j < versionCount; j++) { 130 | HVDBGLOG("Checking message version %u.%u against version %u.%u", 131 | msgVersions[i].major, msgVersions[i].minor, negMsg->versions[j].major, negMsg->versions[j].minor); 132 | if ((msgVersions[i].major == negMsg->versions[j].major) 133 | && (msgVersions[i].minor == negMsg->versions[j].minor)) { 134 | msgVersion = negMsg->versions[j]; 135 | foundMsgMatch = true; 136 | break; 137 | } 138 | } 139 | 140 | if (foundMsgMatch) { 141 | break; 142 | } 143 | } 144 | 145 | if (foundFwMatch && foundMsgMatch) { 146 | HVDBGLOG("Found supported fw version %u.%u and msg version %u.%u", 147 | frameworkVersion.major, frameworkVersion.minor, msgVersion.major, msgVersion.minor); 148 | negMsg->frameworkVersionCount = 1; 149 | negMsg->messageVersionCount = 1; 150 | 151 | negMsg->versions[0] = frameworkVersion; 152 | negMsg->versions[1] = msgVersion; 153 | 154 | if (msgVersionUsed != nullptr) { 155 | *msgVersionUsed = msgVersion; 156 | } 157 | } else { 158 | HVDBGLOG("Unsupported fw/msg version"); 159 | negMsg->frameworkVersionCount = 0; 160 | negMsg->messageVersionCount = 0; 161 | } 162 | 163 | return foundFwMatch && foundMsgMatch; 164 | } 165 | -------------------------------------------------------------------------------- /MacHyperVSupport/IntegrationComponents/HyperVICService.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // HyperVICService.hpp 3 | // Hyper-V IC base class 4 | // 5 | // Copyright © 2021 Goldfish64. All rights reserved. 6 | // 7 | 8 | #ifndef HyperVICService_hpp 9 | #define HyperVICService_hpp 10 | 11 | #include 12 | 13 | #include "HyperVVMBusDevice.hpp" 14 | #include "HyperVIC.hpp" 15 | 16 | #define kHyperVICBufferSize PAGE_SIZE 17 | 18 | class HyperVICService : public IOService { 19 | OSDeclareDefaultStructors(HyperVICService); 20 | HVDeclareLogFunctionsVMBusChild("ic"); 21 | typedef IOService super; 22 | 23 | protected: 24 | HyperVVMBusDevice *_hvDevice = nullptr; 25 | void setICDebug(bool debug) { debugEnabled = debug; } 26 | 27 | virtual void handlePacket(VMBusPacketHeader *pktHeader, UInt32 pktHeaderLength, UInt8 *pktData, UInt32 pktDataLength) = 0; 28 | virtual UInt32 txBufferSize() { return kHyperVICBufferSize; }; 29 | virtual UInt32 rxBufferSize() { return kHyperVICBufferSize; }; 30 | bool processNegotiationResponse(VMBusICMessageNegotiate *negMsg, const VMBusVersion *msgVersions, 31 | UInt32 msgVersionsCount, VMBusVersion *msgVersionUsed = nullptr); 32 | 33 | public: 34 | // 35 | // IOService overrides. 36 | // 37 | virtual bool start(IOService *provider) APPLE_KEXT_OVERRIDE; 38 | virtual void stop(IOService *provider) APPLE_KEXT_OVERRIDE; 39 | }; 40 | 41 | #endif 42 | -------------------------------------------------------------------------------- /MacHyperVSupport/IntegrationComponents/HyperVICUserClient.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // HyperVICUserClient.cpp 3 | // Hyper-V IC user client base class 4 | // 5 | // Copyright © 2022 Goldfish64. All rights reserved. 6 | // 7 | 8 | #include "HyperVICUserClient.hpp" 9 | 10 | OSDefineMetaClassAndAbstractStructors(HyperVICUserClient, super); 11 | 12 | bool HyperVICUserClient::start(IOService *provider) { 13 | // 14 | // Get parent HyperVICService object. 15 | // 16 | _hvICProvider = OSDynamicCast(HyperVICService, provider); 17 | if (_hvICProvider == nullptr) { 18 | HVSYSLOG("Provider is not HyperVICService"); 19 | return false; 20 | } 21 | _hvICProvider->retain(); 22 | 23 | HVCheckDebugArgs(); 24 | HVDBGLOG("Initializing Hyper-V user client"); 25 | 26 | if (!super::start(provider)) { 27 | HVSYSLOG("super::start() returned false"); 28 | OSSafeReleaseNULL(_hvICProvider); 29 | return false; 30 | } 31 | 32 | // 33 | // Should only be one user client active at a time. 34 | // 35 | if (_hvICProvider->isOpen() || !_hvICProvider->open(this)) { 36 | HVSYSLOG("Unable to open additional user clients, only one at a time is allowed"); 37 | stop(provider); 38 | return false; 39 | } 40 | 41 | _sleepLock = IOLockAlloc(); 42 | if (_sleepLock == nullptr) { 43 | HVSYSLOG("Failed to allocate sleeping lock"); 44 | stop(provider); 45 | return false; 46 | } 47 | 48 | // 49 | // User client needs to be registered for daemons to connect. 50 | // 51 | registerService(); 52 | HVDBGLOG("Initialized Hyper-V user client"); 53 | return true; 54 | } 55 | 56 | void HyperVICUserClient::stop(IOService *provider) { 57 | HVDBGLOG("Stopping Hyper-V user client"); 58 | 59 | if (_sleepLock != nullptr) { 60 | IOLockFree(_sleepLock); 61 | } 62 | 63 | if (_hvICProvider != nullptr) { 64 | _hvICProvider->close(this); 65 | OSSafeReleaseNULL(_hvICProvider); 66 | } 67 | 68 | super::stop(provider); 69 | } 70 | 71 | IOReturn HyperVICUserClient::message(UInt32 type, IOService *provider, void *argument) { 72 | if (OSDynamicCast(HyperVICService, provider) == _hvICProvider) { 73 | HVDBGLOG("Message from HyperVICService of type 0x%X received", type); 74 | switch (type) { 75 | case kIOMessageServiceIsTerminated: 76 | _hvICProvider->close(this); 77 | break; 78 | 79 | default: 80 | break; 81 | } 82 | } 83 | 84 | return super::message(type, provider, argument); 85 | } 86 | 87 | IOReturn HyperVICUserClient::clientClose() { 88 | HVDBGLOG("Hyper-V user client is closing"); 89 | terminate(); 90 | return kIOReturnSuccess; 91 | } 92 | 93 | bool HyperVICUserClient::initWithTask(task_t owningTask, void *securityToken, UInt32 type, OSDictionary *properties) { 94 | if (owningTask == nullptr) { 95 | return false; 96 | } 97 | 98 | // 99 | // Clients must be administrator level. 100 | // 101 | if (clientHasPrivilege(securityToken, kIOClientPrivilegeAdministrator) != kIOReturnSuccess) { 102 | return false; 103 | }; 104 | 105 | if (!super::initWithTask(owningTask, securityToken, type)) 106 | return false; 107 | 108 | _task = owningTask; 109 | return true; 110 | } 111 | 112 | IOReturn HyperVICUserClient::registerNotificationPort(mach_port_t port, UInt32 type, UInt32 refCon) { 113 | if (_hvICProvider == nullptr) { 114 | return kIOReturnNotReady; 115 | } 116 | 117 | HVDBGLOG("Registering notification port 0x%p", port); 118 | _notificationPort = port; 119 | return kIOReturnSuccess; 120 | } 121 | 122 | IOReturn HyperVICUserClient::sleepThread() { 123 | int result = THREAD_AWAKENED; 124 | AbsoluteTime deadline; 125 | 126 | clock_interval_to_deadline(15, kSecondScale, &deadline); 127 | 128 | IOLockLock(_sleepLock); 129 | while (_isSleeping && result != THREAD_TIMED_OUT) { 130 | result = IOLockSleepDeadline(_sleepLock, &_isSleeping, deadline, THREAD_INTERRUPTIBLE); 131 | } 132 | if (_isSleeping) { 133 | _sleepStatus = kIOReturnTimeout; 134 | } 135 | _isSleeping = false; 136 | IOLockUnlock(_sleepLock); 137 | 138 | HVDBGLOG("Thread woken up with status 0x%X", _sleepStatus); 139 | return _sleepStatus; 140 | } 141 | 142 | void HyperVICUserClient::wakeThread(IOReturn status) { 143 | IOLockLock(_sleepLock); 144 | _sleepStatus = status; 145 | _isSleeping = false; 146 | IOLockUnlock(_sleepLock); 147 | IOLockWakeup(_sleepLock, &_isSleeping, true); 148 | } 149 | -------------------------------------------------------------------------------- /MacHyperVSupport/IntegrationComponents/HyperVICUserClient.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // HyperVICUserClient.hpp 3 | // Hyper-V IC user client base class 4 | // 5 | // Copyright © 2022 Goldfish64. All rights reserved. 6 | // 7 | 8 | #ifndef HyperVICUserClient_hpp 9 | #define HyperVICUserClient_hpp 10 | 11 | #include 12 | 13 | #include "HyperV.hpp" 14 | #include "HyperVICService.hpp" 15 | 16 | class HyperVICUserClient : public IOUserClient { 17 | OSDeclareDefaultStructors(HyperVICUserClient); 18 | HVDeclareLogFunctions("icuser"); 19 | typedef IOUserClient super; 20 | 21 | private: 22 | HyperVICService *_hvICProvider = nullptr; 23 | 24 | protected: 25 | task_t _task = nullptr; 26 | IOLock *_sleepLock = nullptr; 27 | bool _isSleeping = false; 28 | IOReturn _sleepStatus = kIOReturnSuccess; 29 | mach_port_t _notificationPort = MACH_PORT_NULL; 30 | 31 | void setICDebug(bool debug) { debugEnabled = debug; } 32 | IOReturn sleepThread(); 33 | void wakeThread(IOReturn status); 34 | 35 | public: 36 | // 37 | // IOService overrides. 38 | // 39 | bool start(IOService *provider) APPLE_KEXT_OVERRIDE; 40 | void stop(IOService *provider) APPLE_KEXT_OVERRIDE; 41 | IOReturn message(UInt32 type, IOService *provider, void *argument = NULL) APPLE_KEXT_OVERRIDE; 42 | 43 | // 44 | // IOUserClient overrides. 45 | // 46 | bool initWithTask(task_t owningTask, void *securityToken, UInt32 type, OSDictionary *properties) APPLE_KEXT_OVERRIDE; 47 | IOReturn clientClose() APPLE_KEXT_OVERRIDE; 48 | IOReturn registerNotificationPort(mach_port_t port, UInt32 type, UInt32 refCon) APPLE_KEXT_OVERRIDE; 49 | }; 50 | 51 | #endif 52 | -------------------------------------------------------------------------------- /MacHyperVSupport/IntegrationComponents/Shutdown/HyperVShutdown.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // HyperVShutdown.cpp 3 | // Hyper-V guest shutdown driver 4 | // 5 | // Copyright © 2021-2022 Goldfish64. All rights reserved. 6 | // 7 | 8 | #include "HyperVShutdown.hpp" 9 | 10 | OSDefineMetaClassAndStructors(HyperVShutdown, super); 11 | 12 | static const VMBusVersion shutdownVersions[] = { 13 | { kHyperVShutdownVersionV3_2 }, 14 | { kHyperVShutdownVersionV3_1 }, 15 | { kHyperVShutdownVersionV3_0 }, 16 | { kHyperVShutdownVersionV1_0 } 17 | }; 18 | 19 | bool HyperVShutdown::start(IOService *provider) { 20 | if (HVCheckOffArg()) { 21 | HVSYSLOG("Disabling Hyper-V Guest Shutdown due to boot arg"); 22 | return false; 23 | } 24 | 25 | if (!super::start(provider)) { 26 | HVSYSLOG("super::start() returned false"); 27 | return false; 28 | } 29 | 30 | HVCheckDebugArgs(); 31 | setICDebug(debugEnabled); 32 | 33 | registerService(); 34 | HVDBGLOG("Initialized Hyper-V Guest Shutdown"); 35 | return true; 36 | } 37 | 38 | void HyperVShutdown::stop(IOService *provider) { 39 | HVDBGLOG("Stopping Hyper-V Guest Shutdown"); 40 | super::stop(provider); 41 | } 42 | 43 | bool HyperVShutdown::open(IOService *forClient, IOOptionBits options, void *arg) { 44 | HyperVShutdownUserClient *hvShutdownUserClient = OSDynamicCast(HyperVShutdownUserClient, forClient); 45 | if (hvShutdownUserClient == nullptr || _userClientInstance != nullptr) { 46 | return false; 47 | } 48 | 49 | if (!super::open(forClient, options, arg)) { 50 | return false; 51 | } 52 | 53 | _userClientInstance = hvShutdownUserClient; 54 | return true; 55 | } 56 | 57 | void HyperVShutdown::close(IOService *forClient, IOOptionBits options) { 58 | _userClientInstance = nullptr; 59 | super::close(forClient, options); 60 | } 61 | 62 | void HyperVShutdown::handlePacket(VMBusPacketHeader *pktHeader, UInt32 pktHeaderLength, UInt8 *pktData, UInt32 pktDataLength) { 63 | VMBusICMessageShutdown *shutdownMsg = (VMBusICMessageShutdown*) pktData; 64 | 65 | bool doShutdown = false; 66 | switch (shutdownMsg->header.type) { 67 | case kVMBusICMessageTypeNegotiate: 68 | // 69 | // Determine supported protocol version and communicate back to Hyper-V. 70 | // 71 | if (!processNegotiationResponse(&shutdownMsg->negotiate, shutdownVersions, arrsize(shutdownVersions))) { 72 | HVSYSLOG("Failed to determine a supported Hyper-V Guest Shutdown version"); 73 | shutdownMsg->header.status = kHyperVStatusFailure; 74 | } 75 | break; 76 | 77 | case kVMBusICMessageTypeShutdown: 78 | // 79 | // Shutdown/restart request. 80 | // 81 | doShutdown = handleShutdown(&shutdownMsg->shutdown); 82 | break; 83 | 84 | default: 85 | HVDBGLOG("Unknown shutdown message type %u", shutdownMsg->header.type); 86 | shutdownMsg->header.status = kHyperVStatusFailure; 87 | break; 88 | } 89 | 90 | // 91 | // Send response back to Hyper-V. The packet size will always be the same as the original inbound one. 92 | // 93 | shutdownMsg->header.flags = kVMBusICFlagTransaction | kVMBusICFlagResponse; 94 | _hvDevice->writeInbandPacket(shutdownMsg, pktDataLength, false); 95 | 96 | // 97 | // Shutdown/restart machine if requested. This should not return. 98 | // 99 | if (doShutdown) { 100 | HVDBGLOG("Shutdown request received, notifying userspace"); 101 | performShutdown(&shutdownMsg->shutdown); 102 | } 103 | } 104 | 105 | bool HyperVShutdown::handleShutdown(VMBusICMessageShutdownData *shutdownData) { 106 | bool result = false; 107 | UInt32 packetSize = shutdownData->header.dataSize + sizeof (shutdownData->header); 108 | 109 | if (packetSize < __offsetof (VMBusICMessageShutdownData, reason)) { 110 | HVSYSLOG("Shutdown packet is invalid size (%u bytes)", packetSize); 111 | return false; 112 | } 113 | HVDBGLOG("Shutdown request received: flags 0x%X, reason 0x%X", shutdownData->flags, shutdownData->reason); 114 | 115 | result = checkShutdown(shutdownData); 116 | shutdownData->header.status = result ? kHyperVStatusSuccess : kHyperVStatusFailure; 117 | return result; 118 | } 119 | 120 | bool HyperVShutdown::checkShutdown(VMBusICMessageShutdownData *shutdownData) { 121 | switch (shutdownData->flags) { 122 | case kVMBusICShutdownFlagsShutdown: 123 | case kVMBusICShutdownFlagsShutdownForced: 124 | case kVMBusICShutdownFlagsRestart: 125 | case kVMBusICShutdownFlagsRestartForced: 126 | if (_userClientInstance != nullptr) { 127 | return _userClientInstance->canShutdown(); 128 | } else { 129 | HVSYSLOG("Unable to request shutdown (shutdown daemon is not running)"); 130 | } 131 | break; 132 | 133 | default: 134 | HVSYSLOG("Invalid shutdown flags %u"); 135 | break; 136 | } 137 | 138 | return false; 139 | } 140 | 141 | void HyperVShutdown::performShutdown(VMBusICMessageShutdownData *shutdownData) { 142 | switch (shutdownData->flags) { 143 | case kVMBusICShutdownFlagsShutdown: 144 | case kVMBusICShutdownFlagsShutdownForced: 145 | HVDBGLOG("Performing shutdown"); 146 | _userClientInstance->doShutdown(false); 147 | break; 148 | 149 | case kVMBusICShutdownFlagsRestart: 150 | case kVMBusICShutdownFlagsRestartForced: 151 | HVDBGLOG("Performing restart"); 152 | _userClientInstance->doShutdown(true); 153 | break; 154 | 155 | default: 156 | HVSYSLOG("Invalid shutdown flags %u"); 157 | break; 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /MacHyperVSupport/IntegrationComponents/Shutdown/HyperVShutdown.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // HyperVShutdown.hpp 3 | // Hyper-V guest shutdown driver 4 | // 5 | // Copyright © 2021-2022 Goldfish64. All rights reserved. 6 | // 7 | 8 | #ifndef HyperVShutdown_hpp 9 | #define HyperVShutdown_hpp 10 | 11 | #include "HyperVICService.hpp" 12 | #include "HyperVShutdownRegs.hpp" 13 | #include "HyperVShutdownUserClientInternal.hpp" 14 | 15 | class HyperVShutdown : public HyperVICService { 16 | OSDeclareDefaultStructors(HyperVShutdown); 17 | HVDeclareLogFunctionsVMBusChild("shut"); 18 | typedef HyperVICService super; 19 | 20 | private: 21 | HyperVShutdownUserClient *_userClientInstance = nullptr; 22 | 23 | bool handleShutdown(VMBusICMessageShutdownData *shutdownData); 24 | bool checkShutdown(VMBusICMessageShutdownData *shutdownData); 25 | void performShutdown(VMBusICMessageShutdownData *shutdownData); 26 | 27 | protected: 28 | void handlePacket(VMBusPacketHeader *pktHeader, UInt32 pktHeaderLength, UInt8 *pktData, UInt32 pktDataLength) APPLE_KEXT_OVERRIDE; 29 | 30 | public: 31 | // 32 | // IOService overrides. 33 | // 34 | bool start(IOService *provider) APPLE_KEXT_OVERRIDE; 35 | void stop(IOService *provider) APPLE_KEXT_OVERRIDE; 36 | bool open(IOService *forClient, IOOptionBits options, void *arg) APPLE_KEXT_OVERRIDE; 37 | void close(IOService *forClient, IOOptionBits options) APPLE_KEXT_OVERRIDE; 38 | }; 39 | 40 | #endif 41 | -------------------------------------------------------------------------------- /MacHyperVSupport/IntegrationComponents/Shutdown/HyperVShutdownRegs.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // HyperVShutdownRegs.hpp 3 | // Hyper-V guest shutdown driver 4 | // 5 | // Copyright © 2022 Goldfish64. All rights reserved. 6 | // 7 | 8 | #ifndef HyperVShutdownRegs_hpp 9 | #define HyperVShutdownRegs_hpp 10 | 11 | #include "HyperVIC.hpp" 12 | 13 | // 14 | // Shutdown versions. 15 | // 16 | #define kHyperVShutdownVersionV1_0 VMBUS_VERSION(1, 0) 17 | #define kHyperVShutdownVersionV3_0 VMBUS_VERSION(3, 0) 18 | #define kHyperVShutdownVersionV3_1 VMBUS_VERSION(3, 1) 19 | #define kHyperVShutdownVersionV3_2 VMBUS_VERSION(3, 2) 20 | 21 | // 22 | // Shutdown flags. 23 | // 24 | typedef enum : UInt32 { 25 | kVMBusICShutdownFlagsShutdown = 0, 26 | kVMBusICShutdownFlagsShutdownForced = 1, 27 | kVMBusICShutdownFlagsRestart = 2, 28 | kVMBusICShutdownFlagsRestartForced = 3, 29 | kVMBusICShutdownFlagsHibernate = 4, 30 | kVMBusICShutdownFlagsHibernateForced = 5 31 | } VMBusICShutdownFlags; 32 | 33 | // 34 | // Shutdown messages. 35 | // 36 | typedef struct __attribute__((packed)) { 37 | VMBusICMessageHeader header; 38 | 39 | UInt32 reason; 40 | UInt32 timeoutSeconds; 41 | VMBusICShutdownFlags flags; 42 | char displayMessage[2048]; 43 | } VMBusICMessageShutdownData; 44 | 45 | typedef struct __attribute__((packed)) { 46 | union { 47 | VMBusICMessageHeader header; 48 | VMBusICMessageNegotiate negotiate; 49 | VMBusICMessageShutdownData shutdown; 50 | }; 51 | } VMBusICMessageShutdown; 52 | 53 | #endif 54 | -------------------------------------------------------------------------------- /MacHyperVSupport/IntegrationComponents/Shutdown/HyperVShutdownUserClient.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // HyperVShutdownUserClient.cpp 3 | // Hyper-V guest shutdown user client 4 | // 5 | // Copyright © 2022-2025 Goldfish64. All rights reserved. 6 | // 7 | 8 | #include "HyperVShutdownUserClientInternal.hpp" 9 | 10 | OSDefineMetaClassAndStructors(HyperVShutdownUserClient, super); 11 | 12 | bool HyperVShutdownUserClient::start(IOService *provider) { 13 | if (!super::start(provider)) { 14 | HVSYSLOG("super::start() returned false"); 15 | return false; 16 | } 17 | 18 | HVCheckDebugArgs(); 19 | setICDebug(debugEnabled); 20 | 21 | HVDBGLOG("Initialized Hyper-V Guest Shutdown user client"); 22 | return true; 23 | } 24 | 25 | void HyperVShutdownUserClient::stop(IOService *provider) { 26 | HVDBGLOG("Stopping Hyper-V Guest Shutdown user client"); 27 | super::stop(provider); 28 | } 29 | 30 | #if __MAC_OS_X_VERSION_MIN_REQUIRED >= __MAC_10_5 31 | IOReturn HyperVShutdownUserClient::externalMethod(uint32_t selector, IOExternalMethodArguments *arguments, IOExternalMethodDispatch *dispatch, 32 | OSObject *target, void *reference) { 33 | static const IOExternalMethodDispatch methods[kHyperVShutdownUserClientMethodNumberOfMethods] = { 34 | { // kHyperVShutdownUserClientMethodReportShutdownAbility 35 | reinterpret_cast(&HyperVShutdownUserClient::sDispatchMethodReportShutdownAbility), // Method pointer 36 | 1, // Num of scalar input values 37 | 0, // Size of struct input 38 | 0, // Num of scalar output values 39 | 0 // Size of struct output 40 | } 41 | }; 42 | 43 | if (selector >= kHyperVShutdownUserClientMethodNumberOfMethods) { 44 | return kIOReturnUnsupported; 45 | } 46 | dispatch = const_cast(&methods[selector]); 47 | 48 | target = this; 49 | reference = nullptr; 50 | 51 | return super::externalMethod(selector, arguments, dispatch, target, reference); 52 | } 53 | #else 54 | IOExternalMethod* HyperVShutdownUserClient::getTargetAndMethodForIndex(IOService **target, UInt32 index) { 55 | static const IOExternalMethod methods[kHyperVShutdownUserClientMethodNumberOfMethods] = { 56 | { // kHyperVShutdownUserClientMethodReportShutdownAbility 57 | NULL, // Target pointer 58 | #if (defined(__i386__) && defined(__clang__)) 59 | // Required to match GCC behavior on 32-bit when building with clang 60 | kIOExternalMethodACID32Padding, 61 | reinterpret_cast(&HyperVShutdownUserClient::sMethodReportShutdownAbility), // Static method pointer 62 | #else 63 | reinterpret_cast(&HyperVShutdownUserClient::methodReportShutdownAbility), // Instance method pointer 64 | #endif 65 | kIOUCScalarIScalarO, // Method type 66 | 1, // Num of scalar input values or size of struct input 67 | 0 // Num of scalar output values or size of struct output 68 | } 69 | }; 70 | 71 | if (index >= kHyperVShutdownUserClientMethodNumberOfMethods) { 72 | return nullptr; 73 | } 74 | 75 | *target = this; 76 | return (IOExternalMethod *) &methods[index]; 77 | } 78 | #endif 79 | 80 | bool HyperVShutdownUserClient::canShutdown() { 81 | IOReturn status; 82 | 83 | // 84 | // Check if userspace daemon is running and responsive. 85 | // 86 | _isSleeping = true; 87 | status = notifyShutdownClient(kHyperVShutdownUserClientNotificationTypeCheck); 88 | if (status != kIOReturnSuccess) { 89 | HVSYSLOG("Failed to notify shutdown daemon with status 0x%X", status); 90 | return false; 91 | } 92 | status = sleepThread(); 93 | if (status == kIOReturnTimeout) { 94 | HVSYSLOG("Timed out while waiting for shutdown daemon"); 95 | return false; 96 | } 97 | 98 | HVDBGLOG("Shutdown supported: %u", status == kIOReturnSuccess); 99 | return status == kIOReturnSuccess; 100 | } 101 | 102 | void HyperVShutdownUserClient::doShutdown(bool restart) { 103 | IOReturn status = notifyShutdownClient(restart ? 104 | kHyperVShutdownUserClientNotificationTypePerformRestart : 105 | kHyperVShutdownUserClientNotificationTypePerformShutdown); 106 | if (status != kIOReturnSuccess) { 107 | HVSYSLOG("Failed to notify shutdown daemon with status 0x%X", status); 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /MacHyperVSupport/IntegrationComponents/Shutdown/HyperVShutdownUserClient.h: -------------------------------------------------------------------------------- 1 | // 2 | // HyperVShutdownUserClient.h 3 | // Hyper-V guest shutdown user client 4 | // 5 | // Copyright © 2022 Goldfish64. All rights reserved. 6 | // 7 | 8 | #ifndef HyperVShutdownUserClient_h 9 | #define HyperVShutdownUserClient_h 10 | 11 | #include 12 | #include 13 | 14 | #define kHyperVShutdownMagic 0x66697368 15 | 16 | typedef enum : UInt32 { 17 | kHyperVShutdownUserClientMethodReportShutdownAbility, 18 | 19 | kHyperVShutdownUserClientMethodNumberOfMethods 20 | } HyperVShutdownUserClientMethod; 21 | 22 | typedef enum : UInt32 { 23 | kHyperVShutdownUserClientNotificationTypeCheck, 24 | kHyperVShutdownUserClientNotificationTypePerformShutdown, 25 | kHyperVShutdownUserClientNotificationTypePerformRestart, 26 | } HyperVShutdownUserClientNotificationType; 27 | 28 | typedef struct { 29 | mach_msg_header_t header; 30 | HyperVShutdownUserClientNotificationType type; 31 | #ifndef KERNEL 32 | mach_msg_trailer_t trailer; 33 | #endif 34 | } HyperVShutdownUserClientNotificationMessage; 35 | 36 | #endif 37 | -------------------------------------------------------------------------------- /MacHyperVSupport/IntegrationComponents/Shutdown/HyperVShutdownUserClientInternal.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // HyperVShutdownUserClientInternal.hpp 3 | // Hyper-V guest shutdown user client 4 | // 5 | // Copyright © 2022-2025 Goldfish64. All rights reserved. 6 | // 7 | 8 | #ifndef HyperVShutdownUserClientInternal_hpp 9 | #define HyperVShutdownUserClientInternal_hpp 10 | 11 | #include "HyperVICUserClient.hpp" 12 | #include "HyperVShutdownUserClient.h" 13 | 14 | class HyperVShutdownUserClient : public HyperVICUserClient { 15 | OSDeclareDefaultStructors(HyperVShutdownUserClient); 16 | HVDeclareLogFunctions("shutuser"); 17 | typedef HyperVICUserClient super; 18 | 19 | private: 20 | // 21 | // Userspace communication methods. 22 | // 23 | IOReturn notifyShutdownClient(HyperVShutdownUserClientNotificationType type); 24 | IOReturn reportShutdownAbility(UInt32 arg); 25 | 26 | #if __MAC_OS_X_VERSION_MIN_REQUIRED >= __MAC_10_5 27 | static IOReturn sDispatchMethodReportShutdownAbility(HyperVShutdownUserClient *target, void *ref, IOExternalMethodArguments *args); 28 | #else 29 | #if (defined(__i386__) && defined(__clang__)) 30 | static IOReturn sMethodReportShutdownAbility(HyperVShutdownUserClient* that, UInt32 arg); 31 | #else 32 | IOReturn methodReportShutdownAbility(UInt32 arg); 33 | #endif 34 | #endif 35 | 36 | public: 37 | // 38 | // IOService overrides. 39 | // 40 | bool start(IOService *provider) APPLE_KEXT_OVERRIDE; 41 | void stop(IOService *provider) APPLE_KEXT_OVERRIDE; 42 | 43 | // 44 | // IOUserClient overrides. 45 | // 46 | #if __MAC_OS_X_VERSION_MIN_REQUIRED >= __MAC_10_5 47 | IOReturn externalMethod(uint32_t selector, IOExternalMethodArguments *arguments, 48 | IOExternalMethodDispatch *dispatch, OSObject *target, 49 | void *reference) APPLE_KEXT_OVERRIDE; 50 | #else 51 | IOExternalMethod *getTargetAndMethodForIndex(IOService **target, UInt32 index) APPLE_KEXT_OVERRIDE; 52 | #endif 53 | 54 | // 55 | // User client methods. 56 | // 57 | bool canShutdown(); 58 | void doShutdown(bool restart); 59 | }; 60 | 61 | #endif 62 | -------------------------------------------------------------------------------- /MacHyperVSupport/IntegrationComponents/Shutdown/HyperVShutdownUserClientPrivate.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // HyperVShutdownUserClientPrivate.cpp 3 | // Hyper-V guest shutdown user client 4 | // 5 | // Copyright © 2025 Goldfish64. All rights reserved. 6 | // 7 | 8 | #include "HyperVShutdownUserClientInternal.hpp" 9 | 10 | IOReturn HyperVShutdownUserClient::notifyShutdownClient(HyperVShutdownUserClientNotificationType type) { 11 | HyperVShutdownUserClientNotificationMessage notificationMsg = { }; 12 | 13 | if (_notificationPort == MACH_PORT_NULL) { 14 | HVDBGLOG("Notification port is not open"); 15 | return kIOReturnNotFound; 16 | } 17 | 18 | HVDBGLOG("Sending shutdown notification type %u", type); 19 | notificationMsg.header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0); 20 | notificationMsg.header.msgh_size = sizeof (notificationMsg); 21 | notificationMsg.header.msgh_remote_port = _notificationPort; 22 | notificationMsg.header.msgh_local_port = MACH_PORT_NULL; 23 | notificationMsg.header.msgh_reserved = 0; 24 | notificationMsg.header.msgh_id = 0; 25 | notificationMsg.type = type; 26 | 27 | return mach_msg_send_from_kernel(¬ificationMsg.header, notificationMsg.header.msgh_size); 28 | } 29 | 30 | IOReturn HyperVShutdownUserClient::reportShutdownAbility(UInt32 arg) { 31 | wakeThread((arg == kHyperVShutdownMagic) ? kIOReturnSuccess : kIOReturnUnsupported); 32 | return kIOReturnSuccess; 33 | } 34 | 35 | #if __MAC_OS_X_VERSION_MIN_REQUIRED >= __MAC_10_5 36 | IOReturn HyperVShutdownUserClient::sDispatchMethodReportShutdownAbility(HyperVShutdownUserClient *target, void *ref, IOExternalMethodArguments *args) { 37 | return target->reportShutdownAbility((UInt32)args->scalarInput[0]); 38 | } 39 | #else 40 | #if (defined(__i386__) && defined(__clang__)) 41 | IOReturn HyperVShutdownUserClient::sMethodReportShutdownAbility(HyperVShutdownUserClient* that, UInt32 arg) { 42 | that->wakeThread((arg == kHyperVShutdownMagic) ? kIOReturnSuccess : kIOReturnUnsupported); 43 | return that->reportShutdownAbility(arg); 44 | } 45 | #else 46 | IOReturn HyperVShutdownUserClient::methodReportShutdownAbility(UInt32 arg) { 47 | return reportShutdownAbility(arg); 48 | } 49 | #endif 50 | #endif 51 | -------------------------------------------------------------------------------- /MacHyperVSupport/IntegrationComponents/TimeSync/HyperVTimeSync.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // HyperVTimeSync.hpp 3 | // Hyper-V time synchronization driver 4 | // 5 | // Copyright © 2022 Goldfish64. All rights reserved. 6 | // 7 | 8 | #ifndef HyperVTimeSync_hpp 9 | #define HyperVTimeSync_hpp 10 | 11 | #include "HyperVICService.hpp" 12 | #include "HyperVTimeSyncRegs.hpp" 13 | #include "HyperVTimeSyncUserClientInternal.hpp" 14 | 15 | class HyperVTimeSync : public HyperVICService { 16 | OSDeclareDefaultStructors(HyperVTimeSync); 17 | HVDeclareLogFunctionsVMBusChild("time"); 18 | typedef HyperVICService super; 19 | 20 | private: 21 | HyperVTimeSyncUserClient *_userClientInstance = nullptr; 22 | 23 | VMBusVersion _timeSyncCurrentVersion = { kHyperVTimeSyncVersionV1_0 }; 24 | void handleTimeAdjust(UInt64 hostTime, UInt64 referenceTime, VMBusICTimeSyncFlags flags); 25 | 26 | protected: 27 | void handlePacket(VMBusPacketHeader *pktHeader, UInt32 pktHeaderLength, UInt8 *pktData, UInt32 pktDataLength) APPLE_KEXT_OVERRIDE; 28 | 29 | public: 30 | // 31 | // IOService overrides. 32 | // 33 | bool start(IOService *provider) APPLE_KEXT_OVERRIDE; 34 | void stop(IOService *provider) APPLE_KEXT_OVERRIDE; 35 | bool open(IOService *forClient, IOOptionBits options, void *arg) APPLE_KEXT_OVERRIDE; 36 | void close(IOService *forClient, IOOptionBits options) APPLE_KEXT_OVERRIDE; 37 | }; 38 | 39 | #endif 40 | -------------------------------------------------------------------------------- /MacHyperVSupport/IntegrationComponents/TimeSync/HyperVTimeSyncRegs.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // HyperVTimeSyncRegs.hpp 3 | // Hyper-V time synchronization driver 4 | // 5 | // Copyright © 2022 Goldfish64. All rights reserved. 6 | // 7 | 8 | #ifndef HyperVTimeSyncRegs_hpp 9 | #define HyperVTimeSyncRegs_hpp 10 | 11 | #include "HyperVIC.hpp" 12 | 13 | // 14 | // Time sync versions. 15 | // 16 | #define kHyperVTimeSyncVersionV1_0 VMBUS_VERSION(1, 0) 17 | #define kHyperVTimeSyncVersionV3_0 VMBUS_VERSION(3, 0) 18 | #define kHyperVTimeSyncVersionV4_0 VMBUS_VERSION(4, 0) 19 | 20 | #define kHyperVTimeSyncTimeBase 116444736000000000ULL 21 | 22 | // 23 | // Threshold is 500ms. 24 | // 25 | #define kHyperVTimeSyncDiffThreshold (500 * NSEC_PER_MSEC) 26 | 27 | // 28 | // Time sync flags. 29 | // 30 | typedef enum : UInt8 { 31 | kVMBusICTimeSyncFlagsProbe = 0, 32 | kVMBusICTimeSyncFlagsSync = 1, 33 | kVMBusICTimeSyncFlagsSample = 2 34 | } VMBusICTimeSyncFlags; 35 | 36 | // 37 | // Time sync messages. 38 | // 39 | // Used in v1 and v3. 40 | // 41 | typedef struct __attribute__((packed)) { 42 | VMBusICMessageHeader header; 43 | 44 | UInt64 parentTime; 45 | UInt64 childTime; 46 | UInt64 roundTripTime; 47 | VMBusICTimeSyncFlags flags; 48 | } VMBusICMessageTimeSyncData; 49 | 50 | // 51 | // Used in v4. 52 | // 53 | typedef struct __attribute__((packed)) { 54 | VMBusICMessageHeader header; 55 | 56 | UInt64 parentTime; 57 | UInt64 referenceTime; 58 | VMBusICTimeSyncFlags flags; 59 | UInt8 leapFlags; 60 | UInt8 stratum; 61 | UInt8 reserved[3]; 62 | } VMBusICMessageTimeSyncRefData; 63 | 64 | typedef struct __attribute__((packed)) { 65 | union { 66 | VMBusICMessageHeader header; 67 | VMBusICMessageNegotiate negotiate; 68 | VMBusICMessageTimeSyncData timeSync; 69 | VMBusICMessageTimeSyncRefData timeSyncRef; 70 | }; 71 | } VMBusICMessageTimeSync; 72 | 73 | #endif 74 | -------------------------------------------------------------------------------- /MacHyperVSupport/IntegrationComponents/TimeSync/HyperVTimeSyncUserClient.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // HyperVTimeSyncUserClient.cpp 3 | // Hyper-V time synchronization user client 4 | // 5 | // Copyright © 2022-2025 Goldfish64. All rights reserved. 6 | // 7 | 8 | #include "HyperVTimeSyncUserClientInternal.hpp" 9 | 10 | OSDefineMetaClassAndStructors(HyperVTimeSyncUserClient, super); 11 | 12 | bool HyperVTimeSyncUserClient::start(IOService *provider) { 13 | if (!super::start(provider)) { 14 | HVSYSLOG("super::start() returned false"); 15 | return false; 16 | } 17 | 18 | HVCheckDebugArgs(); 19 | setICDebug(debugEnabled); 20 | 21 | HVDBGLOG("Initialized Hyper-V Time Synchronization user client"); 22 | return true; 23 | } 24 | 25 | void HyperVTimeSyncUserClient::stop(IOService *provider) { 26 | HVDBGLOG("Stopping Hyper-V Time Synchronization user client"); 27 | super::stop(provider); 28 | } 29 | 30 | IOReturn HyperVTimeSyncUserClient::doTimeSync(UInt64 seconds, UInt32 microseconds) { 31 | HyperVTimeSyncUserClientNotificationMessage notificationMsg = { }; 32 | 33 | if (_notificationPort == MACH_PORT_NULL) { 34 | HVDBGLOG("Notification port is not open"); 35 | return kIOReturnNotFound; 36 | } 37 | 38 | HVDBGLOG("Sending time sync notification with %llu seconds and %u microseconds", seconds, microseconds); 39 | notificationMsg.header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0); 40 | notificationMsg.header.msgh_size = sizeof (notificationMsg); 41 | notificationMsg.header.msgh_remote_port = _notificationPort; 42 | notificationMsg.header.msgh_local_port = MACH_PORT_NULL; 43 | notificationMsg.header.msgh_reserved = 0; 44 | notificationMsg.header.msgh_id = 0; 45 | notificationMsg.seconds = seconds; 46 | notificationMsg.microseconds = microseconds; 47 | 48 | return mach_msg_send_from_kernel(¬ificationMsg.header, notificationMsg.header.msgh_size); 49 | } 50 | -------------------------------------------------------------------------------- /MacHyperVSupport/IntegrationComponents/TimeSync/HyperVTimeSyncUserClient.h: -------------------------------------------------------------------------------- 1 | // 2 | // HyperVTimeSyncUserClient.h 3 | // Hyper-V time synchronization user client 4 | // 5 | // Copyright © 2022 Goldfish64. All rights reserved. 6 | // 7 | 8 | #ifndef HyperVTimeSyncUserClient_h 9 | #define HyperVTimeSyncUserClient_h 10 | 11 | #include 12 | #include 13 | 14 | typedef struct { 15 | mach_msg_header_t header; 16 | UInt64 seconds; 17 | UInt32 microseconds; 18 | 19 | #ifndef KERNEL 20 | mach_msg_trailer_t trailer; 21 | #endif 22 | } HyperVTimeSyncUserClientNotificationMessage; 23 | 24 | #endif 25 | -------------------------------------------------------------------------------- /MacHyperVSupport/IntegrationComponents/TimeSync/HyperVTimeSyncUserClientInternal.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // HyperVTimeSyncUserClientInternal.hpp 3 | // Hyper-V time synchronization user client 4 | // 5 | // Copyright © 2022-2025 Goldfish64. All rights reserved. 6 | // 7 | 8 | #ifndef HyperVTimeSyncUserClient_hpp 9 | #define HyperVTimeSyncUserClient_hpp 10 | 11 | #include "HyperVICUserClient.hpp" 12 | #include "HyperVTimeSyncUserClient.h" 13 | 14 | class HyperVTimeSyncUserClient : public HyperVICUserClient { 15 | OSDeclareDefaultStructors(HyperVTimeSyncUserClient); 16 | HVDeclareLogFunctions("timeuser"); 17 | typedef HyperVICUserClient super; 18 | 19 | public: 20 | // 21 | // IOService overrides. 22 | // 23 | bool start(IOService *provider) APPLE_KEXT_OVERRIDE; 24 | void stop(IOService *provider) APPLE_KEXT_OVERRIDE; 25 | 26 | // 27 | // User client methods. 28 | // 29 | IOReturn doTimeSync(UInt64 seconds, UInt32 microseconds); 30 | }; 31 | 32 | #endif 33 | -------------------------------------------------------------------------------- /MacHyperVSupport/InterruptController/HyperVInterruptController.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // HyperVInterruptController.cpp 3 | // Hyper-V synthetic interrupt controller. 4 | // 5 | // Copyright © 2022 Goldfish64. All rights reserved. 6 | // 7 | 8 | #include "HyperVInterruptController.hpp" 9 | 10 | #ifndef kVectorCountKey 11 | #define kVectorCountKey "Vector Count" 12 | #endif 13 | 14 | #ifndef kInterruptControllerNameKey 15 | #define kInterruptControllerNameKey "InterruptControllerName" 16 | #endif 17 | 18 | OSDefineMetaClassAndStructors(HyperVInterruptController, super); 19 | 20 | bool HyperVInterruptController::init(UInt32 numVectors) { 21 | if (!super::init()) { 22 | HVSYSLOG("Superclass init function failed"); 23 | return false; 24 | } 25 | 26 | // 27 | // Allocate vectors. 28 | // 29 | vectorCount = numVectors; 30 | setProperty(kVectorCountKey, vectorCount, 32); 31 | HVDBGLOG("Initializing Hyper-V interrupt controller with %u vector slots", vectorCount); 32 | 33 | vectors = IONew(IOInterruptVector, vectorCount); 34 | if (vectors == nullptr) { 35 | HVSYSLOG("Failed to allocate vectors"); 36 | return false; 37 | } 38 | bzero(vectors, sizeof (IOInterruptVector) * vectorCount); 39 | 40 | // Allocate lock for each vector. 41 | for (UInt32 i = 0; i < vectorCount; i++) { 42 | vectors[i].interruptLock = IOLockAlloc(); 43 | if (vectors[i].interruptLock == nullptr) { 44 | HVSYSLOG("Failed to allocate vector %u lock", i); 45 | return false; 46 | } 47 | } 48 | 49 | // 50 | // Attach to platform expert. 51 | // 52 | attach(getPlatform()); 53 | const OSSymbol *symName = copyName(); 54 | setProperty(kInterruptControllerNameKey, (OSObject*) symName); 55 | getPlatform()->registerInterruptController((OSSymbol*) symName, this); 56 | symName->release(); 57 | 58 | registerService(); 59 | HVDBGLOG("Initialized Hyper-V interrupt controller"); 60 | return true; 61 | } 62 | 63 | IOReturn HyperVInterruptController::handleInterrupt(void *refCon, IOService *nub, int source) { 64 | IOInterruptVector *vector; 65 | 66 | if (source < 0 || source > vectorCount) { 67 | return kIOReturnSuccess; 68 | } 69 | 70 | vector = &vectors[source]; 71 | vector->interruptActive = 1; 72 | if (vector->interruptRegistered && !vector->interruptDisabledHard) { 73 | vector->handler(vector->target, vector->refCon, vector->nub, vector->source); 74 | } 75 | vector->interruptActive = 0; 76 | 77 | return kIOReturnSuccess; 78 | } 79 | 80 | int HyperVInterruptController::getVectorType(IOInterruptVectorNumber vectorNumber, IOInterruptVector *vector) { 81 | return kIOInterruptTypeEdge | kIOInterruptTypeHyperV; 82 | } 83 | -------------------------------------------------------------------------------- /MacHyperVSupport/InterruptController/HyperVInterruptController.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // HyperVInterruptController.hpp 3 | // Hyper-V synthetic interrupt controller. 4 | // 5 | // Copyright © 2022 Goldfish64. All rights reserved. 6 | // 7 | 8 | #ifndef HyperVInterruptController_hpp 9 | #define HyperVInterruptController_hpp 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | #include "HyperV.hpp" 16 | 17 | #define kIOInterruptTypeHyperV 0x10000000 18 | 19 | class HyperVInterruptController : public IOInterruptController { 20 | OSDeclareDefaultStructors(HyperVInterruptController); 21 | HVDeclareLogFunctions("intc"); 22 | typedef IOInterruptController super; 23 | 24 | private: 25 | UInt32 vectorCount = 0; 26 | 27 | public: 28 | bool init(UInt32 numVectors); 29 | 30 | // 31 | // IOInterruptController overrides. 32 | // 33 | IOReturn handleInterrupt(void *refCon, IOService *nub, int source) APPLE_KEXT_OVERRIDE; 34 | int getVectorType(IOInterruptVectorNumber vectorNumber, IOInterruptVector *vector) APPLE_KEXT_OVERRIDE; 35 | }; 36 | 37 | #endif 38 | -------------------------------------------------------------------------------- /MacHyperVSupport/Keyboard/HyperVKeyboard.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // HyperVKeyboard.cpp 3 | // Hyper-V keyboard driver 4 | // 5 | // Copyright © 2021-2022 Goldfish64. All rights reserved. 6 | // 7 | 8 | #include "HyperVKeyboard.hpp" 9 | #include "HyperVADBMaps.hpp" 10 | 11 | OSDefineMetaClassAndStructors(HyperVKeyboard, super); 12 | 13 | bool HyperVKeyboard::start(IOService *provider) { 14 | bool result = false; 15 | IOReturn status; 16 | 17 | // 18 | // Get parent VMBus device object. 19 | // 20 | _hvDevice = OSDynamicCast(HyperVVMBusDevice, provider); 21 | if (_hvDevice == nullptr) { 22 | HVSYSLOG("Provider is not HyperVVMBusDevice"); 23 | return false; 24 | } 25 | _hvDevice->retain(); 26 | 27 | HVCheckDebugArgs(); 28 | HVDBGLOG("Initializing Hyper-V Synthetic Keyboard"); 29 | 30 | if (HVCheckOffArg()) { 31 | HVSYSLOG("Disabling Hyper-V Synthetic Keyboard due to boot arg"); 32 | OSSafeReleaseNULL(_hvDevice); 33 | return false; 34 | } 35 | 36 | if (!super::start(provider)) { 37 | HVSYSLOG("super::start() returned false"); 38 | OSSafeReleaseNULL(_hvDevice); 39 | return false; 40 | } 41 | 42 | do { 43 | // 44 | // Install packet handler. 45 | // 46 | status = _hvDevice->installPacketActions(this, OSMemberFunctionCast(HyperVVMBusDevice::PacketReadyAction, this, &HyperVKeyboard::handlePacket), 47 | nullptr, kHyperVKeyboardResponsePacketSize); 48 | if (status != kIOReturnSuccess) { 49 | HVSYSLOG("Failed to install packet handler with status 0x%X", status); 50 | break; 51 | } 52 | 53 | // 54 | // Open VMBus channel and connect to keyboard. 55 | // 56 | status = _hvDevice->openVMBusChannel(kHyperVKeyboardRingBufferSize, kHyperVKeyboardRingBufferSize); 57 | if (status != kIOReturnSuccess) { 58 | HVSYSLOG("Failed to open VMBus channel with status 0x%X", status); 59 | break; 60 | } 61 | 62 | status = connectKeyboard(); 63 | if (status != kIOReturnSuccess) { 64 | HVSYSLOG("Failed to connect to keyboard device with status 0x%X", status); 65 | break; 66 | } 67 | 68 | HVDBGLOG("Initialized Hyper-V Synthetic Keyboard"); 69 | result = true; 70 | } while (false); 71 | 72 | if (!result) { 73 | stop(provider); 74 | } 75 | return result; 76 | } 77 | 78 | void HyperVKeyboard::stop(IOService *provider) { 79 | HVDBGLOG("Stopping Hyper-V Synthetic Keyboard"); 80 | 81 | if (_hvDevice != nullptr) { 82 | _hvDevice->closeVMBusChannel(); 83 | _hvDevice->uninstallPacketActions(); 84 | OSSafeReleaseNULL(_hvDevice); 85 | } 86 | 87 | super::stop(provider); 88 | } 89 | 90 | IOReturn HyperVKeyboard::connectKeyboard() { 91 | HVDBGLOG("Connecting to keyboard interface"); 92 | 93 | HyperVKeyboardMessageProtocolRequest requestMsg; 94 | requestMsg.header.type = kHyperVKeyboardMessageTypeProtocolRequest; 95 | requestMsg.versionRequested = kHyperVKeyboardVersion; 96 | 97 | return _hvDevice->writeInbandPacket(&requestMsg, sizeof (requestMsg), true); 98 | } 99 | 100 | void HyperVKeyboard::dispatchUnicodeKeyboardEvent(UInt16 unicodeChar, bool isBreak) { 101 | UInt16 keyCode; 102 | UInt64 time; 103 | 104 | // 105 | // Ignore break codes as the simulated key goes down and back up within this function. 106 | // 107 | if (isBreak) { 108 | return; 109 | } 110 | 111 | if (unicodeChar >= arrsize(UnicodeToADBMap)) { 112 | HVDBGLOG("Unknown Unicode character 0x%X break: %u", unicodeChar, isBreak); 113 | return; 114 | } 115 | keyCode = UnicodeToADBMap[unicodeChar]; 116 | HVDBGLOG("Handling Unicode character 0x%X keycode: 0x%X shift: %u", unicodeChar, 117 | keyCode, (keyCode & kADBUnicodeShift) ? 1 : 0); 118 | 119 | // 120 | // Simulate shift key press for shifted characters. 121 | // 122 | if (keyCode & kADBUnicodeShift) { 123 | clock_get_uptime(&time); 124 | dispatchKeyboardEvent(kADBKeyCodeShift, true, *(AbsoluteTime*)&time); 125 | } 126 | 127 | // 128 | // Simulate key press and release for character. 129 | // 130 | clock_get_uptime(&time); 131 | dispatchKeyboardEvent((UInt8)keyCode, true, *(AbsoluteTime*)&time); 132 | clock_get_uptime(&time); 133 | dispatchKeyboardEvent((UInt8)keyCode, false, *(AbsoluteTime*)&time); 134 | 135 | // 136 | // Simulate shift key release for shifted characters. 137 | // 138 | if (keyCode & kADBUnicodeShift) { 139 | clock_get_uptime(&time); 140 | dispatchKeyboardEvent(kADBKeyCodeShift, false, *(AbsoluteTime*)&time); 141 | } 142 | } 143 | 144 | inline UInt32 getKeyCode(HyperVKeyboardMessageKeystroke *keyEvent) { 145 | return PS2ToADBMapStock[keyEvent->makeCode + (keyEvent->isE0 ? kADBConverterExStart : 0)]; 146 | } 147 | 148 | void HyperVKeyboard::handlePacket(VMBusPacketHeader *pktHeader, UInt32 pktHeaderLength, UInt8 *pktData, UInt32 pktDataLength) { 149 | HyperVKeyboardMessage *keyboardMsg = (HyperVKeyboardMessage*) pktData; 150 | UInt64 time; 151 | 152 | switch (keyboardMsg->header.type) { 153 | case kHyperVKeyboardMessageTypeProtocolResponse: 154 | HVDBGLOG("Keyboard protocol status %u %u", keyboardMsg->protocolResponse.header.type, keyboardMsg->protocolResponse.status); 155 | break; 156 | 157 | case kHyperVKeyboardMessageTypeEvent: 158 | HVDBGLOG("Got make code 0x%X (E0: %u, E1: %u, break: %u, Unicode: %u)", keyboardMsg->keystroke.makeCode, 159 | keyboardMsg->keystroke.isE0, keyboardMsg->keystroke.isE1, keyboardMsg->keystroke.isBreak, keyboardMsg->keystroke.isUnicode); 160 | if (keyboardMsg->keystroke.isUnicode) { 161 | dispatchUnicodeKeyboardEvent(keyboardMsg->keystroke.makeCode, keyboardMsg->keystroke.isBreak); 162 | } else { 163 | clock_get_uptime(&time); 164 | dispatchKeyboardEvent(getKeyCode(&keyboardMsg->keystroke), !keyboardMsg->keystroke.isBreak, *(AbsoluteTime*)&time); 165 | } 166 | break; 167 | 168 | default: 169 | HVDBGLOG("Unknown message type %u, size %u", keyboardMsg->header.type, pktDataLength); 170 | break; 171 | } 172 | } 173 | -------------------------------------------------------------------------------- /MacHyperVSupport/Keyboard/HyperVKeyboard.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // HyperVKeyboard.hpp 3 | // Hyper-V keyboard driver 4 | // 5 | // Copyright © 2021-2022 Goldfish64. All rights reserved. 6 | // 7 | 8 | #ifndef HyperVKeyboard_hpp 9 | #define HyperVKeyboard_hpp 10 | 11 | #include 12 | 13 | #include "HyperVKeyboardBase.hpp" 14 | #include "HyperVVMBusDevice.hpp" 15 | #include "HyperVKeyboardRegs.hpp" 16 | 17 | class HyperVKeyboard : public HyperVKeyboardBase { 18 | OSDeclareDefaultStructors(HyperVKeyboard); 19 | HVDeclareLogFunctionsVMBusChild("kbd"); 20 | typedef HyperVKeyboardBase super; 21 | 22 | private: 23 | HyperVVMBusDevice *_hvDevice = nullptr; 24 | 25 | void handlePacket(VMBusPacketHeader *pktHeader, UInt32 pktHeaderLength, UInt8 *pktData, UInt32 pktDataLength); 26 | IOReturn connectKeyboard(); 27 | void dispatchUnicodeKeyboardEvent(UInt16 unicodeChar, bool isBreak); 28 | 29 | public: 30 | // 31 | // IOService overrides. 32 | // 33 | bool start(IOService *provider) APPLE_KEXT_OVERRIDE; 34 | void stop(IOService *provider) APPLE_KEXT_OVERRIDE; 35 | }; 36 | 37 | #endif 38 | -------------------------------------------------------------------------------- /MacHyperVSupport/Keyboard/HyperVKeyboardBase.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // HyperVKeyboardBase.hpp 3 | // Hyper-V keyboard base driver 4 | // 5 | // Copyright © 2025 Goldfish64. All rights reserved. 6 | // 7 | 8 | #ifndef HyperVKeyboardBase_hpp 9 | #define HyperVKeyboardBase_hpp 10 | 11 | #include 12 | 13 | #include "HyperV.hpp" 14 | 15 | class HyperVKeyboardBase : public IOHIKeyboard { 16 | OSDeclareAbstractStructors(HyperVKeyboardBase); 17 | typedef IOHIKeyboard super; 18 | 19 | protected: 20 | // 21 | // IOHIKeyboard overrides. 22 | // 23 | virtual const unsigned char * defaultKeymapOfLength(UInt32 * length) APPLE_KEXT_OVERRIDE; 24 | virtual UInt32 maxKeyCodes() APPLE_KEXT_OVERRIDE; 25 | 26 | public: 27 | // 28 | // IOHIKeyboard overrides. 29 | // 30 | virtual UInt32 deviceType() APPLE_KEXT_OVERRIDE; 31 | virtual UInt32 interfaceID() APPLE_KEXT_OVERRIDE; 32 | }; 33 | 34 | #endif 35 | -------------------------------------------------------------------------------- /MacHyperVSupport/Keyboard/HyperVKeyboardRegs.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // HyperVKeyboardRegs.hpp 3 | // Hyper-V keyboard driver 4 | // 5 | // Copyright © 2021-2022 Goldfish64. All rights reserved. 6 | // 7 | 8 | #ifndef HyperVKeyboardRegs_hpp 9 | #define HyperVKeyboardRegs_hpp 10 | 11 | #define kHyperVKeyboardRingBufferSize 0x8000 12 | #define kHyperVKeyboardResponsePacketSize 128 13 | 14 | // 15 | // Current keyboard protocol is 1.0. 16 | // First used in Windows Server 2008. 17 | // 18 | #define kHyperVKeyboardVersionMajor 1 19 | #define kHyperVKeyboardVersionMinor 0 20 | #define kHyperVKeyboardVersion (kHyperVKeyboardVersionMinor | (kHyperVKeyboardVersionMajor << 16)) 21 | 22 | // 23 | // Keyboard message types. 24 | // 25 | typedef enum : UInt32 { 26 | kHyperVKeyboardMessageTypeProtocolRequest = 1, 27 | kHyperVKeyboardMessageTypeProtocolResponse = 2, 28 | kHyperVKeyboardMessageTypeEvent = 3, 29 | kHyperVKeyboardMessageTypeLEDIndicators = 4 30 | } HyperVKeyboardMessageType; 31 | 32 | // 33 | // Message structures. 34 | // 35 | // Header 36 | typedef struct __attribute__((packed)) { 37 | HyperVKeyboardMessageType type; 38 | } HyperVKeyboardMessageHeader; 39 | 40 | // Protocol request 41 | typedef struct __attribute__((packed)) { 42 | HyperVKeyboardMessageHeader header; 43 | 44 | UInt32 versionRequested; 45 | } HyperVKeyboardMessageProtocolRequest; 46 | 47 | // Protocol response 48 | #define kHyperVKeyboardProtocolResponseAccepted 1 49 | typedef struct __attribute__((packed)) { 50 | HyperVKeyboardMessageHeader header; 51 | 52 | UInt32 status; 53 | } HyperVKeyboardMessageProtocolResponse; 54 | 55 | // Keystroke 56 | typedef struct __attribute__((packed)) { 57 | HyperVKeyboardMessageHeader header; 58 | 59 | UInt16 makeCode; 60 | UInt16 reserved; 61 | UInt32 isUnicode : 1; 62 | UInt32 isBreak : 1; 63 | UInt32 isE0 : 1; 64 | UInt32 isE1 : 1; 65 | UInt32 reserved2 : 28; 66 | } HyperVKeyboardMessageKeystroke; 67 | 68 | // 69 | // All messages. 70 | // 71 | typedef union __attribute__((packed)) { 72 | HyperVKeyboardMessageHeader header; 73 | HyperVKeyboardMessageProtocolRequest protocolRequest; 74 | HyperVKeyboardMessageProtocolResponse protocolResponse; 75 | HyperVKeyboardMessageKeystroke keystroke; 76 | } HyperVKeyboardMessage; 77 | 78 | #endif 79 | -------------------------------------------------------------------------------- /MacHyperVSupport/Keyboard/HyperVPS2Keyboard.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // HyperVPS2Keyboard.hpp 3 | // Hyper-V PS/2 keyboard driver 4 | // 5 | // Copyright © 2025 Goldfish64. All rights reserved. 6 | // 7 | 8 | #ifndef HyperVPS2Keyboard_hpp 9 | #define HyperVPS2Keyboard_hpp 10 | 11 | #include 12 | #include 13 | 14 | #include "HyperVKeyboardBase.hpp" 15 | 16 | class HyperVPS2Keyboard : public HyperVKeyboardBase { 17 | OSDeclareDefaultStructors(HyperVPS2Keyboard); 18 | HVDeclareLogFunctions("ps2kbd"); 19 | typedef HyperVKeyboardBase super; 20 | 21 | private: 22 | IOACPIPlatformDevice *_acpiDevice = nullptr; 23 | IOWorkLoop *_workLoop = nullptr; 24 | IOInterruptEventSource *_intEventSource = nullptr; 25 | 26 | void interruptOccurred(OSObject *owner, IOInterruptEventSource *sender, int count); 27 | IOReturn connectPS2Keyboard(); 28 | void writeCommandPort(UInt8 byte); 29 | bool readDataPort(UInt8 *byte); 30 | void writeDataPort(UInt8 byte); 31 | void flushDataPort(); 32 | IOReturn resetPS2Controller(); 33 | IOReturn resetPS2Keyboard(); 34 | 35 | public: 36 | // 37 | // IOService overrides. 38 | // 39 | bool start(IOService *provider) APPLE_KEXT_OVERRIDE; 40 | void stop(IOService *provider) APPLE_KEXT_OVERRIDE; 41 | }; 42 | 43 | #endif 44 | -------------------------------------------------------------------------------- /MacHyperVSupport/Keyboard/HyperVPS2KeyboardRegs.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // HyperVPS2KeyboardRegs.hpp 3 | // Hyper-V PS/2 keyboard driver 4 | // 5 | // Copyright © 2025 Goldfish64. All rights reserved. 6 | // 7 | 8 | #ifndef HyperVPS2KeyboardRegs_hpp 9 | #define HyperVPS2KeyboardRegs_hpp 10 | 11 | #define kHyperVPS2DataPort 0x60 12 | #define kHyperVPS2CommandPort 0x64 13 | 14 | #define kHyperVPS2Delay 10 15 | 16 | #define kHyperVPS2SelfTestPassResult 0x55 17 | #define kHyperVPS2KeyboardPortPassResult 0x00 18 | 19 | // 20 | // PS/2 status bits. 21 | // 22 | #define kHyperVPS2StatusOutputBusy BIT(0) 23 | #define kHyperVPS2StatusInputBusy BIT(1) 24 | #define kHyperVPS2StatusSystem BIT(2) 25 | #define kHyperVPS2StatusControllerData BIT(3) 26 | #define kHyperVPS2StatusTimeout BIT(6) 27 | #define kHyperVPS2StatusParity BIT(7) 28 | 29 | // 30 | // PS/2 controller configuration register bits. 31 | // 32 | #define kHyperVPS2ConfigEnableKeyboardIRQ BIT(0) 33 | #define kHyperVPS2ConfigEnableMouseIRQ BIT(1) 34 | #define kHyperVPS2ConfigSystem BIT(2) 35 | #define kHyperVPS2ConfigKeyboardDisableClock BIT(4) 36 | #define kHyperVPS2ConfigMouseDisableClock BIT(5) 37 | #define kHyperVPS2ConfigKeyboardTranslation BIT(6) 38 | 39 | // 40 | // PS/2 controller commands. 41 | // 42 | #define kHyperVPS2CommandReadByte 0x20 43 | #define kHyperVPS2CommandReadByteBase 0x21 44 | #define kHyperVPS2CommandWriteByte 0x60 45 | #define kHyperVPS2CommandWriteByteBase 0x61 46 | #define kHyperVPS2CommandDisableMousePort 0xA7 47 | #define kHyperVPS2CommandEnableMousePort 0xA8 48 | #define kHyperVPS2CommandTestMousePort 0xA9 49 | #define kHyperVPS2CommandTestController 0xAA 50 | #define kHyperVPS2CommandTestKeyboardPort 0xAB 51 | #define kHyperVPS2CommandDiagnosticDump 0xAC 52 | #define kHyperVPS2CommandDisableKeyboardPort 0xAD 53 | #define kHyperVPS2CommandEnableKeyboardPort 0xAE 54 | #define kHyperVPS2CommandReadInput 0xC0 55 | #define kHyperVPS2CommandPollInputLow 0xC1 56 | #define kHyperVPS2CommandPollInputHigh 0xC2 57 | #define kHyperVPS2CommandReadOutput 0xD0 58 | #define kHyperVPS2CommandWriteOutput 0xD1 59 | #define kHyperVPS2CommandWriteOutputKeyboard 0xD2 60 | #define kHyperVPS2CommandWriteOutputMouse 0xD3 61 | #define kHyperVPS2CommandWriteInputMouse 0xD4 62 | #define kHyperVPS2CommandPulseOutputBase 0xF0 63 | 64 | // 65 | // PS/2 keyboard commands. 66 | // 67 | #define kHyperVPS2KeyboardAck 0xFA 68 | 69 | #define kHyperVPS2KeyboardCommandEnable 0xF4 70 | #define kHyperVPS2KeyboardCommandSetDefaults 0xF6 71 | 72 | // 73 | // PS/2 scancode modifiers. 74 | // 75 | #define kHyperVPS2KeyboardScancodeBreak 0x80 76 | #define kHyperVPS2KeyboardE0 0xE0 77 | #define kHyperVPS2KeyboardE1 0xE1 78 | 79 | #endif 80 | -------------------------------------------------------------------------------- /MacHyperVSupport/Mouse/HyperVMouse.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // HyperVMouse.cpp 3 | // Hyper-V mouse driver 4 | // 5 | // Copyright © 2021-2022 Goldfish64. All rights reserved. 6 | // 7 | 8 | #include "HyperVMouse.hpp" 9 | 10 | OSDefineMetaClassAndStructors(HyperVMouse, super); 11 | 12 | bool HyperVMouse::handleStart(IOService *provider) { 13 | bool result = false; 14 | IOReturn status; 15 | 16 | // 17 | // Get parent VMBus device object. 18 | // 19 | _hvDevice = OSDynamicCast(HyperVVMBusDevice, provider); 20 | if (_hvDevice == nullptr) { 21 | HVSYSLOG("Provider is not HyperVVMBusDevice"); 22 | return false; 23 | } 24 | _hvDevice->retain(); 25 | 26 | HVCheckDebugArgs(); 27 | HVDBGLOG("Initializing Hyper-V Synthetic Mouse"); 28 | 29 | if (HVCheckOffArg()) { 30 | HVSYSLOG("Disabling Hyper-V Synthetic Mouse due to boot arg"); 31 | OSSafeReleaseNULL(_hvDevice); 32 | return false; 33 | } 34 | 35 | if (!super::handleStart(provider)) { 36 | HVSYSLOG("super::handleStart() returned false"); 37 | OSSafeReleaseNULL(_hvDevice); 38 | return false; 39 | } 40 | 41 | // 42 | // HIDDefaultBehavior needs to be set to Mouse for the device to 43 | // get exposed as a mouse to userspace. 44 | // 45 | setProperty("HIDDefaultBehavior", "Mouse"); 46 | 47 | do { 48 | // 49 | // Install packet handler. 50 | // 51 | status = _hvDevice->installPacketActions(this, OSMemberFunctionCast(HyperVVMBusDevice::PacketReadyAction, this, &HyperVMouse::handlePacket), 52 | nullptr, kHyperVMouseResponsePacketSize); 53 | if (status != kIOReturnSuccess) { 54 | HVSYSLOG("Failed to install packet handler with status 0x%X", status); 55 | break; 56 | } 57 | 58 | // 59 | // Open VMBus channel. 60 | // 61 | status = _hvDevice->openVMBusChannel(kHyperVMouseRingBufferSize, kHyperVMouseRingBufferSize); 62 | if (status != kIOReturnSuccess) { 63 | HVSYSLOG("Failed to open VMBus channel with status 0x%X", status); 64 | break; 65 | } 66 | 67 | // 68 | // Configure Hyper-V mouse device. 69 | // 70 | if (!setupMouse()) { 71 | HVSYSLOG("Unable to setup mouse device"); 72 | break; 73 | } 74 | 75 | HVDBGLOG("Initialized Hyper-V Synthetic Mouse"); 76 | result = true; 77 | } while (false); 78 | 79 | if (!result) { 80 | _hvDevice->closeVMBusChannel(); 81 | _hvDevice->uninstallPacketActions(); 82 | OSSafeReleaseNULL(_hvDevice); 83 | } 84 | 85 | return result; 86 | } 87 | 88 | void HyperVMouse::handleStop(IOService *provider) { 89 | HVDBGLOG("Hyper-V Synthetic Mouse is stopping"); 90 | 91 | if (_hvDevice != nullptr) { 92 | _hvDevice->closeVMBusChannel(); 93 | _hvDevice->uninstallPacketActions(); 94 | OSSafeReleaseNULL(_hvDevice); 95 | } 96 | 97 | super::handleStop(provider); 98 | } 99 | 100 | OSString* HyperVMouse::newTransportString() const { 101 | return OSString::withCStringNoCopy("VMBus"); 102 | } 103 | 104 | OSString* HyperVMouse::newManufacturerString() const { 105 | return OSString::withCStringNoCopy("Microsoft"); 106 | } 107 | 108 | OSString* HyperVMouse::newProductString() const { 109 | return OSString::withCStringNoCopy("Hyper-V Mouse"); 110 | } 111 | 112 | OSNumber* HyperVMouse::newVendorIDNumber() const { 113 | return OSNumber::withNumber(_mouseInfo.vendor, 16); 114 | } 115 | 116 | OSNumber* HyperVMouse::newProductIDNumber() const { 117 | return OSNumber::withNumber(_mouseInfo.product, 16); 118 | } 119 | 120 | OSNumber* HyperVMouse::newVersionNumber() const { 121 | return OSNumber::withNumber(_mouseInfo.version, 16); 122 | } 123 | 124 | IOReturn HyperVMouse::newReportDescriptor(IOMemoryDescriptor **descriptor) const { 125 | IOBufferMemoryDescriptor *bufferDesc = IOBufferMemoryDescriptor::withBytes(_hidDescriptor, _hidDescriptorLength, kIODirectionNone); 126 | if (bufferDesc == nullptr) { 127 | HVSYSLOG("Failed to allocate report descriptor buffer descriptor"); 128 | return kIOReturnNoResources; 129 | } 130 | 131 | *descriptor = bufferDesc; 132 | return kIOReturnSuccess; 133 | } 134 | -------------------------------------------------------------------------------- /MacHyperVSupport/Mouse/HyperVMouse.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // HyperVMouse.hpp 3 | // Hyper-V mouse driver 4 | // 5 | // Copyright © 2021-2022 Goldfish64. All rights reserved. 6 | // 7 | 8 | #ifndef HyperVMouse_hpp 9 | #define HyperVMouse_hpp 10 | 11 | #include 12 | 13 | #include "HyperVVMBusDevice.hpp" 14 | #include "HyperVMouseRegs.hpp" 15 | 16 | class HyperVMouse : public IOHIDDevice { 17 | OSDeclareDefaultStructors(HyperVMouse); 18 | HVDeclareLogFunctionsVMBusChild("mouse"); 19 | typedef IOHIDDevice super; 20 | 21 | private: 22 | HyperVVMBusDevice *_hvDevice = nullptr; 23 | 24 | // 25 | // HID structures. 26 | // 27 | HyperVMouseDeviceInfo _mouseInfo = { }; 28 | void *_hidDescriptor = nullptr; 29 | size_t _hidDescriptorLength = 0; 30 | 31 | void handlePacket(VMBusPacketHeader *pktHeader, UInt32 pktHeaderLength, UInt8 *pktData, UInt32 pktDataLength); 32 | bool setupMouse(); 33 | void handleProtocolResponse(HyperVMouseMessageProtocolResponse *response); 34 | void handleDeviceInfo(HyperVMouseMessageInitialDeviceInfo *deviceInfo); 35 | void handleInputReport(HyperVMouseMessageInputReport *inputReport); 36 | 37 | protected: 38 | // 39 | // IOHIDDevice overrides. 40 | // 41 | bool handleStart(IOService *provider) APPLE_KEXT_OVERRIDE; 42 | void handleStop(IOService *provider) APPLE_KEXT_OVERRIDE; 43 | 44 | public: 45 | // 46 | // IOHIDDevice overrides. 47 | // 48 | OSString *newTransportString() const APPLE_KEXT_OVERRIDE; 49 | OSString *newManufacturerString() const APPLE_KEXT_OVERRIDE; 50 | OSString *newProductString() const APPLE_KEXT_OVERRIDE; 51 | OSNumber *newVendorIDNumber() const APPLE_KEXT_OVERRIDE; 52 | OSNumber *newProductIDNumber() const APPLE_KEXT_OVERRIDE; 53 | OSNumber *newVersionNumber() const APPLE_KEXT_OVERRIDE; 54 | 55 | IOReturn newReportDescriptor(IOMemoryDescriptor **descriptor) const APPLE_KEXT_OVERRIDE; 56 | }; 57 | 58 | #endif 59 | -------------------------------------------------------------------------------- /MacHyperVSupport/Mouse/HyperVMousePrivate.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // HyperVMousePrivate.cpp 3 | // Hyper-V mouse driver 4 | // 5 | // Copyright © 2021-2022 Goldfish64. All rights reserved. 6 | // 7 | 8 | #include "HyperVMouse.hpp" 9 | 10 | void HyperVMouse::handlePacket(VMBusPacketHeader *pktHeader, UInt32 pktHeaderLength, UInt8 *pktData, UInt32 pktDataLength) { 11 | HyperVMousePipeIncomingMessage *mouseMsg = (HyperVMousePipeIncomingMessage*) pktData; 12 | 13 | switch (mouseMsg->header.type) { 14 | case kHyperVMouseMessageTypeProtocolResponse: 15 | // 16 | // Initial protocol response during startup. 17 | // 18 | handleProtocolResponse(&mouseMsg->response); 19 | break; 20 | 21 | case kHyperVMouseMessageTypeInitialDeviceInfo: 22 | // 23 | // Device info result. 24 | // 25 | handleDeviceInfo(&mouseMsg->deviceInfo); 26 | break; 27 | 28 | case kHyperVMouseMessageTypeInputReport: 29 | // 30 | // Normal input packets. 31 | // 32 | handleInputReport(&mouseMsg->inputReport); 33 | break; 34 | 35 | default: 36 | HVDBGLOG("Unknown message type %u, size %u", mouseMsg->header.type, mouseMsg->header.size); 37 | break; 38 | } 39 | } 40 | 41 | bool HyperVMouse::setupMouse() { 42 | // 43 | // Send mouse request. 44 | // Fixed transaction ID is used for tracking as the response is always zero. 45 | // 46 | HyperVMousePipeMessage message; 47 | HyperVMouseMessageProtocolResponse protoResponse; 48 | 49 | message.type = kHyperVPipeMessageTypeData; 50 | message.size = sizeof (HyperVMouseMessageProtocolRequest); 51 | 52 | message.request.header.type = kHyperVMouseMessageTypeProtocolRequest; 53 | message.request.header.size = sizeof (UInt32); 54 | message.request.versionRequested = kHyperVMouseVersion; 55 | 56 | HVDBGLOG("Sending mouse protocol request"); 57 | if (_hvDevice->writeInbandPacketWithTransactionId(&message, sizeof (message), kHyperVMouseProtocolRequestTransactionID, true, &protoResponse, sizeof (protoResponse)) != kIOReturnSuccess) { 58 | return false; 59 | } 60 | 61 | HVDBGLOG("Got mouse protocol response of %u, version 0x%X", protoResponse.status, protoResponse.versionRequested); 62 | if (protoResponse.status == 0) { 63 | return false; 64 | } 65 | 66 | // 67 | // Wait for device info packet to be received. 68 | // The VMBusDevice nub reserves a special transaction ID 0 for general purpose waiting. 69 | // 70 | // The device info packet is sent unsolicitated by Hyper-V and we cannot continue until that is received. 71 | // 72 | _hvDevice->sleepThreadZero(); 73 | return true; 74 | } 75 | 76 | void HyperVMouse::handleProtocolResponse(HyperVMouseMessageProtocolResponse *response) { 77 | void *responseBuffer; 78 | UInt32 responseLength; 79 | 80 | // 81 | // The protocol response packet always has a transaction ID of 0, wake using fixed transaction ID. 82 | // This assumes the response length is of the correct size, set before the initial request. 83 | // 84 | if (_hvDevice->getPendingTransaction(kHyperVMouseProtocolRequestTransactionID, &responseBuffer, &responseLength)) { 85 | memcpy(responseBuffer, response, responseLength); 86 | _hvDevice->wakeTransaction(kHyperVMouseProtocolRequestTransactionID); 87 | } 88 | } 89 | 90 | void HyperVMouse::handleDeviceInfo(HyperVMouseMessageInitialDeviceInfo *deviceInfo) { 91 | IOReturn status; 92 | 93 | if (deviceInfo->header.size < sizeof (*deviceInfo) 94 | || deviceInfo->info.size < sizeof (deviceInfo->info)) { 95 | return; 96 | } 97 | 98 | memcpy(&_mouseInfo, &deviceInfo->info, sizeof (_mouseInfo)); 99 | HVDBGLOG("Hyper-V Mouse ID %04X:%04X, version 0x%X", 100 | _mouseInfo.vendor, _mouseInfo.product, _mouseInfo.version); 101 | 102 | // 103 | // Store HID descriptor. 104 | // 105 | _hidDescriptorLength = deviceInfo->hidDescriptor.hidDescriptorLength; 106 | HVDBGLOG("HID descriptor is %u bytes", _hidDescriptorLength); 107 | 108 | _hidDescriptor = IOMalloc(_hidDescriptorLength); 109 | if (_hidDescriptor == nullptr) { 110 | HVSYSLOG("Failed to allocate HID descriptor"); 111 | return; 112 | } 113 | memcpy(_hidDescriptor, deviceInfo->hidDescriptorData, _hidDescriptorLength); 114 | 115 | // 116 | // Send device info ack message. 117 | // 118 | HyperVMousePipeMessage message; 119 | message.type = kHyperVPipeMessageTypeData; 120 | message.size = sizeof (message.deviceInfoAck); 121 | 122 | message.deviceInfoAck.header.type = kHyperVMouseMessageTypeInitialDeviceInfoAck; 123 | message.deviceInfoAck.header.size = sizeof (message.deviceInfoAck) - sizeof (message.deviceInfoAck.header); 124 | message.deviceInfoAck.reserved = 0; 125 | 126 | HVDBGLOG("Sending device info ack"); 127 | status = _hvDevice->writeInbandPacket(&message, sizeof (message), false); 128 | if (status != kIOReturnSuccess) { 129 | HVSYSLOG("Failed to send device info ack with status 0x%X", status); 130 | } 131 | _hvDevice->wakeThreadZero(); 132 | } 133 | 134 | void HyperVMouse::handleInputReport(HyperVMouseMessageInputReport *inputReport) { 135 | #if DEBUG 136 | typedef struct __attribute__((packed)) { 137 | UInt8 buttons; 138 | UInt16 x; 139 | UInt16 y; 140 | SInt8 wheel; 141 | SInt8 pan; 142 | } HIDABSReport; 143 | 144 | HIDABSReport *report = (HIDABSReport*)inputReport->data; 145 | HVDBGLOG("Got mouse input buttons %u X: %u Y: %u wheel: %d pan: %d", report->buttons, report->x, report->y, report->wheel, report->pan); 146 | #endif 147 | 148 | // 149 | // Send new report to HID system. 150 | // 151 | IOBufferMemoryDescriptor *memDesc = IOBufferMemoryDescriptor::withBytes(inputReport->data, inputReport->header.size, kIODirectionNone); 152 | if (memDesc == nullptr) { 153 | HVSYSLOG("Failed to allocate input report descriptor"); 154 | return; 155 | } 156 | 157 | handleReport(memDesc); 158 | memDesc->release(); 159 | } 160 | -------------------------------------------------------------------------------- /MacHyperVSupport/Mouse/HyperVMouseRegs.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // HyperVMouseRegs.hpp 3 | // Hyper-V mouse driver 4 | // 5 | // Copyright © 2021 Goldfish64. All rights reserved. 6 | // 7 | 8 | #ifndef HyperVMouseRegs_hpp 9 | #define HyperVMouseRegs_hpp 10 | 11 | #define kHyperVMouseRingBufferSize 0x8000 12 | #define kHyperVMouseResponsePacketSize 128 13 | 14 | #define kHyperVMouseInitTimeout 10000 15 | 16 | #define kHyperVMouseProtocolRequestTransactionID 0xCAFECAFE 17 | 18 | // 19 | // Current mouse protocol is 2.0. 20 | // First used in Windows Server 2008. 21 | // 22 | #define kHyperVMouseVersionMajor 2 23 | #define kHyperVMouseVersionMinor 0 24 | #define kHyperVMouseVersion (kHyperVMouseVersionMinor | (kHyperVMouseVersionMajor << 16)) 25 | 26 | // 27 | // HID descriptor 28 | // 29 | typedef struct __attribute__((packed)) { 30 | UInt8 descLen; 31 | UInt8 descType; 32 | UInt16 descVersNum; 33 | UInt8 hidCountryCode; 34 | UInt8 hidNumDescriptors; 35 | UInt8 hidDescriptorType; 36 | UInt16 hidDescriptorLength; 37 | } HyperVHIDDescriptor; 38 | 39 | typedef enum : UInt32 { 40 | kHyperVPipeMessageTypeInvalid = 0, 41 | kHyperVPipeMessageTypeData = 1 42 | } HyperVPipeMessageType; 43 | 44 | // 45 | // Mouse message types. 46 | // 47 | typedef enum : UInt32 { 48 | kHyperVMouseMessageTypeProtocolRequest = 0, 49 | kHyperVMouseMessageTypeProtocolResponse = 1, 50 | kHyperVMouseMessageTypeInitialDeviceInfo = 2, 51 | kHyperVMouseMessageTypeInitialDeviceInfoAck = 3, 52 | kHyperVMouseMessageTypeInputReport = 4, 53 | } HyperVMouseMessageType; 54 | 55 | // 56 | // Mouse info 57 | // 58 | typedef struct __attribute__((packed)) { 59 | UInt32 size; 60 | UInt16 vendor; 61 | UInt16 product; 62 | UInt16 version; 63 | UInt16 reserved[11]; 64 | } HyperVMouseDeviceInfo; 65 | 66 | // 67 | // Message structures. 68 | // 69 | // Header 70 | typedef struct __attribute__((packed)) { 71 | HyperVMouseMessageType type; 72 | UInt32 size; 73 | } HyperVMouseMessageHeader; 74 | 75 | // Protocol request 76 | typedef struct __attribute__((packed)) { 77 | HyperVMouseMessageHeader header; 78 | 79 | UInt32 versionRequested; 80 | } HyperVMouseMessageProtocolRequest; 81 | 82 | // Protocol response 83 | typedef struct __attribute__((packed)) { 84 | HyperVMouseMessageHeader header; 85 | 86 | UInt32 versionRequested; 87 | UInt8 status; 88 | UInt8 reserved[3]; 89 | } HyperVMouseMessageProtocolResponse; 90 | 91 | // Device info 92 | typedef struct __attribute__((packed)) { 93 | HyperVMouseMessageHeader header; 94 | 95 | HyperVMouseDeviceInfo info; 96 | HyperVHIDDescriptor hidDescriptor; 97 | UInt8 hidDescriptorData[]; 98 | } HyperVMouseMessageInitialDeviceInfo; 99 | 100 | // Device info ack 101 | typedef struct __attribute__((packed)) { 102 | HyperVMouseMessageHeader header; 103 | 104 | UInt8 reserved; 105 | } HyperVMouseMessageInitialDeviceInfoAck; 106 | 107 | // Input report 108 | typedef struct __attribute__((packed)) { 109 | HyperVMouseMessageHeader header; 110 | 111 | UInt8 data[]; 112 | } HyperVMouseMessageInputReport; 113 | 114 | // 115 | // Used for outgoing messages. 116 | // 117 | typedef struct __attribute__((packed)) { 118 | HyperVPipeMessageType type; 119 | UInt32 size; 120 | union { 121 | HyperVMouseMessageProtocolRequest request; 122 | HyperVMouseMessageProtocolResponse response; 123 | HyperVMouseMessageInitialDeviceInfoAck deviceInfoAck; 124 | }; 125 | } HyperVMousePipeMessage; 126 | 127 | // 128 | // Used for incoming messages. 129 | // 130 | typedef struct __attribute__((packed)) { 131 | HyperVPipeMessageType type; 132 | UInt32 size; 133 | union { 134 | HyperVMouseMessageHeader header; 135 | HyperVMouseMessageProtocolRequest request; 136 | HyperVMouseMessageProtocolResponse response; 137 | HyperVMouseMessageInitialDeviceInfo deviceInfo; 138 | HyperVMouseMessageInitialDeviceInfoAck deviceInfoAck; 139 | HyperVMouseMessageInputReport inputReport; 140 | }; 141 | } HyperVMousePipeIncomingMessage; 142 | 143 | #endif /* HyperVMouseRegs_hpp */ 144 | -------------------------------------------------------------------------------- /MacHyperVSupport/Network/HyperVNetwork.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // HyperVNetwork.cpp 3 | // Hyper-V network driver 4 | // 5 | // Copyright © 2021-2022 Goldfish64. All rights reserved. 6 | // 7 | 8 | #ifndef HyperVNetwork_hpp 9 | #define HyperVNetwork_hpp 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include "HyperVVMBusDevice.hpp" 19 | #include "HyperVNetworkRegs.hpp" 20 | 21 | extern "C" { 22 | #include 23 | } 24 | 25 | typedef struct HyperVNetworkRNDISRequest { 26 | HyperVNetworkRNDISMessage message; 27 | UInt8 messageOverflow[PAGE_SIZE]; 28 | 29 | HyperVNetworkRNDISRequest *next; 30 | IOLock *lock; 31 | bool isSleeping; 32 | 33 | HyperVDMABuffer dmaBuffer; 34 | } HyperVNetworkRNDISRequest; 35 | 36 | class HyperVNetwork : public IOEthernetController { 37 | OSDeclareDefaultStructors(HyperVNetwork); 38 | HVDeclareLogFunctionsVMBusChild("net"); 39 | typedef IOEthernetController super; 40 | 41 | private: 42 | HyperVVMBusDevice *_hvDevice = nullptr; 43 | IOWorkLoop *_workLoop = nullptr; 44 | 45 | // 46 | // Network structures. 47 | // 48 | HyperVNetworkProtocolVersion _netVersion; 49 | bool _isNetworkEnabled = false; 50 | IOEthernetInterface *_ethInterface = nullptr; 51 | IOEthernetAddress _ethAddress = { }; 52 | bool _isLinkUp = false; 53 | 54 | UInt32 _packetFilterAdditional = 0; 55 | 56 | // 57 | // Receive buffer. 58 | // 59 | HyperVDMABuffer _receiveBuffer = { }; 60 | UInt32 _receiveBufferSize = 0; 61 | UInt32 _receiveGpadlHandle = kHyperVGpadlNullHandle; 62 | 63 | // 64 | // Send buffer and tracking info. 65 | // 66 | HyperVDMABuffer _sendBuffer = { }; 67 | UInt32 _sendBufferSize = 0; 68 | UInt32 _sendGpadlHandle = kHyperVGpadlNullHandle; 69 | UInt32 _sendSectionSize = 0; 70 | UInt32 _sendSectionCount = 0; 71 | UInt32 *_sendIndexMap = nullptr; 72 | size_t _sendIndexMapSize = 0; 73 | UInt32 _sendIndexesOutstanding = 0; 74 | UInt32 oldSends = 0; 75 | UInt64 totalbytes = 0; 76 | UInt64 totalRX = 0; 77 | UInt64 preCycle = 0; 78 | UInt64 midCycle = 0; 79 | UInt64 postCycle = 0; 80 | UInt64 stalls = 0; 81 | 82 | IOLock *_rndisLock = nullptr; 83 | UInt32 _rndisTransId = 0; 84 | HyperVNetworkRNDISRequest *_rndisRequests; 85 | 86 | 87 | 88 | 89 | 90 | void handleTimer(); 91 | bool wakePacketHandler(VMBusPacketHeader *pktHeader, UInt32 pktHeaderLength, UInt8 *pktData, UInt32 pktDataLength); 92 | void handlePacket(VMBusPacketHeader *pktHeader, UInt32 pktHeaderLength, UInt8 *pktData, UInt32 pktDataLength); 93 | 94 | 95 | IOReturn negotiateProtocol(HyperVNetworkProtocolVersion protocolVersion); 96 | 97 | // 98 | // Send/receive buffers. 99 | // 100 | IOReturn initSendReceiveBuffers(); 101 | void freeSendReceiveBuffers(); 102 | UInt32 getNextSendIndex(); 103 | UInt32 getFreeSendIndexCount(); 104 | void releaseSendIndex(UInt32 sendIndex); 105 | 106 | bool connectNetwork(); 107 | 108 | void handleRNDISRanges(VMBusPacketTransferPages *pktPages, UInt32 pktLength); 109 | void handleCompletion(void *pktData, UInt32 pktLength); 110 | 111 | bool processRNDISPacket(UInt8 *data, UInt32 dataLength); 112 | void processIncoming(UInt8 *data, UInt32 dataLength); 113 | 114 | // 115 | // RNDIS setup and operations. 116 | // 117 | HyperVNetworkRNDISRequest *allocateRNDISRequest(size_t additionalLength = 0); 118 | void freeRNDISRequest(HyperVNetworkRNDISRequest *rndisRequest); 119 | UInt32 getNextRNDISTransId(); 120 | bool sendRNDISRequest(HyperVNetworkRNDISRequest *rndisRequest, bool waitResponse = false); 121 | 122 | IOReturn initializeRNDIS(); 123 | IOReturn getRNDISOID(HyperVNetworkRNDISOID oid, void *value, UInt32 *valueSize); 124 | IOReturn setRNDISOID(HyperVNetworkRNDISOID oid, void *value, UInt32 valueSize); 125 | 126 | // 127 | // Private 128 | // 129 | bool addNetworkMedium(OSDictionary* mediumDict, IOMediumType type); 130 | bool createMediumDictionary(); 131 | IOReturn readMACAddress(); 132 | IOReturn setPacketFilter(UInt32 filter); 133 | void updateLinkState(HyperVNetworkRNDISMessageIndicateStatus *indicateStatus); 134 | 135 | public: 136 | // 137 | // IOService overrides. 138 | // 139 | bool start(IOService *provider) APPLE_KEXT_OVERRIDE; 140 | void stop(IOService *provider) APPLE_KEXT_OVERRIDE; 141 | IOWorkLoop* getWorkLoop() const APPLE_KEXT_OVERRIDE { 142 | return _workLoop; 143 | }; 144 | 145 | // 146 | // IONetworkController overrides. 147 | // 148 | bool createWorkLoop() APPLE_KEXT_OVERRIDE; 149 | bool configureInterface(IONetworkInterface *interface) APPLE_KEXT_OVERRIDE; 150 | const OSString* newVendorString() const APPLE_KEXT_OVERRIDE { 151 | return OSString::withCString(kHyperVNetworkVendor); 152 | }; 153 | const OSString* newModelString() const APPLE_KEXT_OVERRIDE { 154 | return OSString::withCString(kHyperVNetworkModel); 155 | } 156 | IOReturn enable(IONetworkInterface *interface) APPLE_KEXT_OVERRIDE; 157 | IOReturn disable(IONetworkInterface *interface) APPLE_KEXT_OVERRIDE; 158 | IOReturn setMulticastMode(bool active) APPLE_KEXT_OVERRIDE; 159 | IOReturn setPromiscuousMode(bool active) APPLE_KEXT_OVERRIDE; 160 | 161 | // 162 | // IOEthernetController overrides. 163 | // 164 | IOReturn getHardwareAddress(IOEthernetAddress *addrP) APPLE_KEXT_OVERRIDE; 165 | UInt32 outputPacket(mbuf_t m, void *param) APPLE_KEXT_OVERRIDE; 166 | }; 167 | 168 | #endif 169 | -------------------------------------------------------------------------------- /MacHyperVSupport/PCIBridge/DevicePathPropertyDatabase.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // DevicePathPropertyDatabase.hpp 3 | // Device Path Property Database definitions 4 | // 5 | // Copyright © 2023 Goldfish64. All rights reserved. 6 | // 7 | 8 | #ifndef DevicePathPropertyDatabase_h 9 | #define DevicePathPropertyDatabase_h 10 | 11 | #define EFI_DEVICE_PATH_PROPERTY_DATABASE_VERSION 1 12 | 13 | /** 14 | This protocol can be used on any device handle to obtain generic path/location 15 | information concerning the physical device or logical device. If the handle does 16 | not logically map to a physical device, the handle may not necessarily support 17 | the device path protocol. The device path describes the location of the device 18 | the handle is for. The size of the Device Path can be determined from the structures 19 | that make up the Device Path. 20 | **/ 21 | #define HARDWARE_DEVICE_PATH 0x01 22 | #define HW_VENDOR_DP 0x04 23 | 24 | #define END_DEVICE_PATH 0x7F 25 | #define END_DEVICE_PATH_SUBTYPE 0xFF 26 | 27 | // 28 | // Generic EFI device path node. 29 | // 30 | typedef struct { 31 | UInt8 type; 32 | UInt8 subType; 33 | UInt8 length[2]; 34 | } EfiDevicePathProtocol; 35 | 36 | // 37 | // Hyper-V vendor-specific EFI device path node. 38 | // 39 | typedef struct { 40 | EfiDevicePathProtocol header; 41 | 42 | uuid_t vendorGuid; 43 | uuid_t deviceTypeGuid; 44 | uuid_t instanceGuid; 45 | } HyperVDevPropEfiDevicePathProtocol; 46 | 47 | // 48 | // Device path property data set. 49 | // There should be a pair of these for each property present, 50 | // one for the property name (in unicode) and one for the data. 51 | // 52 | typedef struct { 53 | UInt32 Size; 54 | UInt8 Data[]; 55 | } EfiDevicePathPropertyData; 56 | 57 | // 58 | // Device path property node header. 59 | // 60 | typedef struct { 61 | UInt32 Size; 62 | UInt32 NumberOfProperties; 63 | } EfiDevicePathPropertyBufferNodeHeader; 64 | 65 | // 66 | // Device path property node. 67 | // 68 | typedef struct { 69 | EfiDevicePathPropertyBufferNodeHeader Hdr; 70 | HyperVDevPropEfiDevicePathProtocol DevicePath; // Variable length 71 | //EfiDevicePathPropertyData Data; // Variable length 72 | } EfiDevicePathPropertyBufferNode; 73 | 74 | // 75 | // Device path property buffer. 76 | // 77 | typedef struct { 78 | UInt32 Size; 79 | UInt32 Version; 80 | UInt32 NumberOfNodes; 81 | 82 | EfiDevicePathPropertyBufferNode Nodes[]; // Each node is variable in length. 83 | } EfiDevicePathPropertyBuffer; 84 | 85 | #endif 86 | -------------------------------------------------------------------------------- /MacHyperVSupport/PCIBridge/HyperVPCIBridge.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // HyperVPCIBridge.hpp 3 | // Hyper-V PCI passthrough device support 4 | // 5 | // Copyright © 2022 Goldfish64. All rights reserved. 6 | // 7 | 8 | #ifndef HyperVPCIBridge_hpp 9 | #define HyperVPCIBridge_hpp 10 | 11 | #include 12 | 13 | #include "HyperVVMBusDevice.hpp" 14 | #include "HyperVPCIBridgeRegs.hpp" 15 | #include "HyperVPCIRoot.hpp" 16 | 17 | class HyperVPCIBridge : public HV_PCIBRIDGE_CLASS { 18 | OSDeclareDefaultStructors(HyperVPCIBridge); 19 | HVDeclareLogFunctionsVMBusChild("pcib"); 20 | typedef HV_PCIBRIDGE_CLASS super; 21 | 22 | private: 23 | // 24 | // Parent VMBus device and PCI state. 25 | // 26 | HyperVVMBusDevice *_hvDevice = nullptr; 27 | HyperVPCIRoot *_hvPCIRoot = nullptr; 28 | UInt8 _pciBusNumber = 0; 29 | IOSimpleLock *_pciLock = nullptr; 30 | 31 | HyperVPCIBridgeProtocolVersion currentPciVersion; 32 | 33 | // 34 | // MMIO. 35 | // 36 | IORangeScalar _pciConfigSpace = 0; 37 | IOMemoryDescriptor *_pciConfigMemoryDescriptor = nullptr; 38 | IOMemoryMap *_pciConfigMemoryMap = nullptr; 39 | UInt64 _bars[kHyperVPCIBarCount] = { }; 40 | UInt64 _barSizes[kHyperVPCIBarCount] = { }; 41 | 42 | UInt32 _pciFunctionsCount = 0; 43 | HyperVPCIFunctionDescription *_pciFunctions = nullptr; 44 | 45 | // 46 | // Packet handling. 47 | // 48 | bool wakePacketHandler(VMBusPacketHeader *pktHeader, UInt32 pktHeaderLength, UInt8 *pktData, UInt32 pktDataLength); 49 | void handlePacket(VMBusPacketHeader *pktHeader, UInt32 pktHeaderLength, UInt8 *pktData, UInt32 pktDataLength); 50 | void handleIncomingPCIMessage(HyperVPCIBridgeIncomingMessageHeader *pciMsgHeader, UInt32 msgSize); 51 | 52 | // 53 | // PCI bus setup. 54 | // 55 | IOReturn connectPCIBus(); 56 | IOReturn negotiateProtocolVersion(); 57 | IOReturn allocatePCIConfigWindow(); 58 | IOReturn queryBusRelations(); 59 | IOReturn enterPCID0(); 60 | IOReturn queryResourceRequirements(); 61 | IOReturn sendResourcesAssigned(UInt32 slot); 62 | 63 | inline UInt64 getBarSize(UInt64 barValue) { 64 | if (barValue < UINT32_MAX) { 65 | barValue |= 0xFFFFFFFF00000000; 66 | } 67 | return roundup((1 + ~(barValue & kHyperVPCIBarMemoryMask)), PAGE_SIZE); 68 | } 69 | 70 | // 71 | // Device properties handling. 72 | // 73 | IOReturn mergePropertiesFromDT(UInt32 slot, OSDictionary *dict); 74 | 75 | // 76 | // PCI config space read/write. 77 | // 78 | UInt32 readPCIConfig(UInt32 offset, UInt8 size); 79 | void writePCIConfig(UInt32 offset, UInt8 size, UInt32 value); 80 | 81 | public: 82 | // 83 | // IOService overrides. 84 | // 85 | bool start(IOService *provider) APPLE_KEXT_OVERRIDE; 86 | IOReturn callPlatformFunction(const OSSymbol *functionName, bool waitForFunction, 87 | void *param1, void *param2, void *param3, void *param4) APPLE_KEXT_OVERRIDE; 88 | 89 | // 90 | // IOPCIBridge overrides. 91 | // 92 | bool configure(IOService *provider) APPLE_KEXT_OVERRIDE; 93 | IODeviceMemory *ioDeviceMemory() APPLE_KEXT_OVERRIDE { HVDBGLOG("start"); return NULL; } 94 | UInt32 configRead32(IOPCIAddressSpace space, UInt8 offset) APPLE_KEXT_OVERRIDE; 95 | void configWrite32(IOPCIAddressSpace space, UInt8 offset, UInt32 data) APPLE_KEXT_OVERRIDE; 96 | UInt16 configRead16(IOPCIAddressSpace space, UInt8 offset) APPLE_KEXT_OVERRIDE; 97 | void configWrite16(IOPCIAddressSpace space, UInt8 offset, UInt16 data) APPLE_KEXT_OVERRIDE; 98 | UInt8 configRead8(IOPCIAddressSpace space, UInt8 offset) APPLE_KEXT_OVERRIDE; 99 | void configWrite8(IOPCIAddressSpace space, UInt8 offset, UInt8 data) APPLE_KEXT_OVERRIDE; 100 | 101 | IOPCIAddressSpace getBridgeSpace() APPLE_KEXT_OVERRIDE { 102 | HVDBGLOG("start"); 103 | IOPCIAddressSpace space = { 0 }; 104 | return space; 105 | } 106 | 107 | UInt8 firstBusNum() APPLE_KEXT_OVERRIDE { 108 | HVDBGLOG("start"); 109 | return _pciBusNumber; 110 | } 111 | 112 | UInt8 lastBusNum() APPLE_KEXT_OVERRIDE { 113 | HVDBGLOG("start"); 114 | return _pciBusNumber; 115 | } 116 | 117 | bool initializeNub(IOPCIDevice *nub, OSDictionary *from) APPLE_KEXT_OVERRIDE; 118 | }; 119 | 120 | #endif 121 | -------------------------------------------------------------------------------- /MacHyperVSupport/PCIRoot/HyperVPCIRoot.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // HyperVPCIRoot.hpp 3 | // Hyper-V PCI root bridge driver 4 | // 5 | // Copyright © 2021-2025 Goldfish64. All rights reserved. 6 | // 7 | 8 | #ifndef HyperVPCIRoot_hpp 9 | #define HyperVPCIRoot_hpp 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | #include "HyperV.hpp" 16 | 17 | class HyperVPCIRoot : public HV_PCIBRIDGE_CLASS { 18 | OSDeclareDefaultStructors(HyperVPCIRoot); 19 | HVDeclareLogFunctions("pcir"); 20 | typedef HV_PCIBRIDGE_CLASS super; 21 | 22 | private: 23 | // 24 | // IOPlatformExpert::setConsoleInfo wrapping 25 | // 26 | static HyperVPCIRoot *_instance; 27 | mach_vm_address_t _setConsoleInfoAddr = 0; 28 | UInt64 _setConsoleInfoOrg[2] {}; 29 | static IOReturn wrapSetConsoleInfo(IOPlatformExpert *that, PE_Video *consoleInfo, unsigned int op); 30 | 31 | IOSimpleLock *_pciLock = NULL; 32 | 33 | // 34 | // Child PCI bridges. 35 | // 36 | IOPCIBridge *pciBridges[256] {}; 37 | 38 | // 39 | // Range allocators for low and high MMIO allocations. 40 | // 41 | bool canAllocateMMIO = false; 42 | IORangeAllocator *_rangeAllocatorLow = nullptr; 43 | IORangeAllocator *_rangeAllocatorHigh = nullptr; 44 | bool reserveFramebufferArea(); 45 | 46 | public: 47 | // 48 | // IOService overrides. 49 | // 50 | bool start(IOService *provider) APPLE_KEXT_OVERRIDE; 51 | void stop(IOService *provider) APPLE_KEXT_OVERRIDE; 52 | 53 | // 54 | // IOPCIBridge overrides. 55 | // 56 | bool configure(IOService *provider) APPLE_KEXT_OVERRIDE; 57 | IODeviceMemory *ioDeviceMemory() APPLE_KEXT_OVERRIDE { return nullptr; } 58 | 59 | UInt32 configRead32(IOPCIAddressSpace space, UInt8 offset) APPLE_KEXT_OVERRIDE; 60 | void configWrite32(IOPCIAddressSpace space, UInt8 offset, UInt32 data) APPLE_KEXT_OVERRIDE; 61 | UInt16 configRead16(IOPCIAddressSpace space, UInt8 offset) APPLE_KEXT_OVERRIDE; 62 | void configWrite16(IOPCIAddressSpace space, UInt8 offset, UInt16 data) APPLE_KEXT_OVERRIDE; 63 | UInt8 configRead8(IOPCIAddressSpace space, UInt8 offset) APPLE_KEXT_OVERRIDE; 64 | void configWrite8(IOPCIAddressSpace space, UInt8 offset, UInt8 data) APPLE_KEXT_OVERRIDE; 65 | 66 | IOPCIAddressSpace getBridgeSpace() APPLE_KEXT_OVERRIDE { 67 | IOPCIAddressSpace space = { 0 }; 68 | return space; 69 | } 70 | UInt8 firstBusNum() APPLE_KEXT_OVERRIDE { return 0; } 71 | UInt8 lastBusNum() APPLE_KEXT_OVERRIDE { return 0; } 72 | 73 | static HyperVPCIRoot* getPCIRootInstance(); 74 | IOReturn registerChildPCIBridge(IOPCIBridge *pciBridge, UInt8 *busNumber); 75 | 76 | bool isHyperVGen2(); 77 | IORangeScalar allocateRange(IORangeScalar size, IORangeScalar alignment, IORangeScalar maxAddress); 78 | void freeRange(IORangeScalar start, IORangeScalar size); 79 | }; 80 | 81 | #endif 82 | -------------------------------------------------------------------------------- /MacHyperVSupport/PlatformProvider/HyperVPlatformProvider.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // HyperVPlatformProvider.cpp 3 | // Hyper-V platform functions provider 4 | // 5 | // Copyright © 2021-2025 Goldfish64. All rights reserved. 6 | // 7 | 8 | #include "HyperVPlatformProvider.hpp" 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | HyperVPlatformProvider *HyperVPlatformProvider::_instance; 15 | 16 | void HyperVPlatformProvider::init() { 17 | HVCheckDebugArgs(); 18 | HVDBGLOG("Initializing provider"); 19 | 20 | // 21 | // Lilu is used for function hooking/patching, register patcher callback. 22 | // 23 | lilu.onPatcherLoadForce([](void *user, KernelPatcher &patcher) { 24 | static_cast(user)->onLiluPatcherLoad(patcher); 25 | }, this); 26 | } 27 | 28 | void HyperVPlatformProvider::onLiluPatcherLoad(KernelPatcher &patcher) { 29 | HVDBGLOG("Patcher loaded"); 30 | } 31 | -------------------------------------------------------------------------------- /MacHyperVSupport/PlatformProvider/HyperVPlatformProvider.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // HyperVPlatformProvider.hpp 3 | // Hyper-V platform functions provider 4 | // 5 | // Copyright © 2021-2025 Goldfish64. All rights reserved. 6 | // 7 | 8 | #ifndef HyperVPlatformProvider_hpp 9 | #define HyperVPlatformProvider_hpp 10 | 11 | #include 12 | #include 13 | 14 | #include "HyperV.hpp" 15 | 16 | class HyperVPlatformProvider { 17 | HVDeclareLogFunctionsNonIOKit("prov", "HyperVPlatformProvider"); 18 | 19 | private: 20 | // 21 | // Global instance. 22 | // 23 | static HyperVPlatformProvider *_instance; 24 | 25 | // 26 | // Initialization function. 27 | // 28 | void init(); 29 | void onLiluPatcherLoad(KernelPatcher &patcher); 30 | 31 | public: 32 | // 33 | // Instance creator. 34 | // 35 | static HyperVPlatformProvider *getInstance() { 36 | if (_instance == nullptr) { 37 | _instance = new HyperVPlatformProvider; 38 | if (_instance != nullptr) { 39 | _instance->init(); 40 | } 41 | } 42 | 43 | return _instance; 44 | } 45 | }; 46 | 47 | #endif 48 | -------------------------------------------------------------------------------- /MacHyperVSupport/Storage/HyperVStorage.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // HyperVStorage.hpp 3 | // Hyper-V storage driver 4 | // 5 | // Copyright © 2021-2022 Goldfish64. All rights reserved. 6 | // 7 | 8 | #ifndef HyperVStorage_hpp 9 | #define HyperVStorage_hpp 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #pragma clang diagnostic push 17 | #pragma clang diagnostic ignored "-Wdocumentation" 18 | #include 19 | #pragma clang diagnostic pop 20 | 21 | #include "HyperVVMBusDevice.hpp" 22 | #include "HyperVStorageRegs.hpp" 23 | 24 | class HyperVStorage : public IOSCSIParallelInterfaceController { 25 | OSDeclareDefaultStructors(HyperVStorage); 26 | HVDeclareLogFunctionsVMBusChild("stor"); 27 | typedef IOSCSIParallelInterfaceController super; 28 | 29 | private: 30 | HyperVVMBusDevice *_hvDevice = nullptr; 31 | 32 | // 33 | // Storage protocol. 34 | // 35 | UInt32 _protocolVersion = 0; 36 | UInt32 _senseBufferSize = 0; 37 | UInt32 _packetSizeDelta = 0; 38 | bool _isIDE = false; 39 | UInt32 _maxLuns = kHyperVStorageMaxLunsSCSI; 40 | UInt8 _targetId = 0; 41 | 42 | bool _subChannelsSupported = false; 43 | UInt16 _maxSubChannels = 0; 44 | UInt32 _maxTransferBytes = 0; 45 | UInt32 _maxPageSegments = 0; 46 | 47 | // 48 | // Segments for DMA transfers. 49 | // 50 | IODMACommand::Segment64 *_segs64 = nullptr; 51 | 52 | // 53 | // Thread for disk enumeration. 54 | // 55 | thread_call_t _scanSCSIDiskThread = nullptr; 56 | 57 | // 58 | // Packets and I/O. 59 | // 60 | bool wakePacketHandler(VMBusPacketHeader *pktHeader, UInt32 pktHeaderLength, UInt8 *pktData, UInt32 pktDataLength); 61 | void handlePacket(VMBusPacketHeader *pktHeader, UInt32 pktHeaderLength, UInt8 *pktData, UInt32 pktDataLength); 62 | void handleIOCompletion(UInt64 transactionId, HyperVStoragePacket *packet); 63 | IOReturn sendStorageCommand(HyperVStoragePacket *packet, bool checkCompletion); 64 | IOReturn prepareDataTransfer(SCSIParallelTaskIdentifier parallelRequest, VMBusPacketMultiPageBuffer **pagePacket, UInt32 *pagePacketLength); 65 | void completeDataTransfer(SCSIParallelTaskIdentifier parallelRequest, HyperVStoragePacket *packet); 66 | 67 | // 68 | // Disk enumeration and misc. 69 | // 70 | void setHBAInfo(); 71 | IOReturn connectStorage(); 72 | bool checkSCSIDiskPresent(UInt8 diskId); 73 | void startDiskEnumeration(); 74 | void scanSCSIDisks(); 75 | 76 | protected: 77 | // 78 | // IOSCSIParallelInterfaceController overrides. 79 | // 80 | bool InitializeController() APPLE_KEXT_OVERRIDE; 81 | void TerminateController() APPLE_KEXT_OVERRIDE; 82 | bool StartController() APPLE_KEXT_OVERRIDE; 83 | void StopController() APPLE_KEXT_OVERRIDE; 84 | bool DoesHBAPerformDeviceManagement() APPLE_KEXT_OVERRIDE; 85 | void HandleInterruptRequest() APPLE_KEXT_OVERRIDE; 86 | SCSIInitiatorIdentifier ReportInitiatorIdentifier() APPLE_KEXT_OVERRIDE; 87 | SCSIDeviceIdentifier ReportHighestSupportedDeviceID() APPLE_KEXT_OVERRIDE; 88 | UInt32 ReportMaximumTaskCount() APPLE_KEXT_OVERRIDE; 89 | UInt32 ReportHBASpecificTaskDataSize() APPLE_KEXT_OVERRIDE; 90 | UInt32 ReportHBASpecificDeviceDataSize() APPLE_KEXT_OVERRIDE; 91 | IOInterruptEventSource *CreateDeviceInterrupt(IOInterruptEventSource::Action action, 92 | IOFilterInterruptEventSource::Filter filter, 93 | IOService *provider) APPLE_KEXT_OVERRIDE; 94 | bool InitializeDMASpecification(IODMACommand *command) APPLE_KEXT_OVERRIDE; 95 | 96 | public: 97 | // 98 | // IOSCSIParallelInterfaceController overrides. 99 | // 100 | bool DoesHBASupportSCSIParallelFeature(SCSIParallelFeature theFeature) APPLE_KEXT_OVERRIDE; 101 | bool InitializeTargetForID(SCSITargetIdentifier targetID) APPLE_KEXT_OVERRIDE; 102 | SCSILogicalUnitNumber ReportHBAHighestLogicalUnitNumber() APPLE_KEXT_OVERRIDE; 103 | SCSIServiceResponse AbortTaskRequest(SCSITargetIdentifier theT, SCSILogicalUnitNumber theL, SCSITaggedTaskIdentifier theQ) APPLE_KEXT_OVERRIDE; 104 | SCSIServiceResponse AbortTaskSetRequest(SCSITargetIdentifier theT, SCSILogicalUnitNumber theL) APPLE_KEXT_OVERRIDE; 105 | SCSIServiceResponse ClearACARequest(SCSITargetIdentifier theT, SCSILogicalUnitNumber theL) APPLE_KEXT_OVERRIDE; 106 | SCSIServiceResponse ClearTaskSetRequest(SCSITargetIdentifier theT, SCSILogicalUnitNumber theL) APPLE_KEXT_OVERRIDE; 107 | SCSIServiceResponse LogicalUnitResetRequest(SCSITargetIdentifier theT, SCSILogicalUnitNumber theL) APPLE_KEXT_OVERRIDE; 108 | SCSIServiceResponse TargetResetRequest(SCSITargetIdentifier theT) APPLE_KEXT_OVERRIDE; 109 | SCSIServiceResponse ProcessParallelTask(SCSIParallelTaskIdentifier parallelRequest) APPLE_KEXT_OVERRIDE; 110 | void ReportHBAConstraints(OSDictionary *constraints) APPLE_KEXT_OVERRIDE; 111 | }; 112 | 113 | #endif 114 | -------------------------------------------------------------------------------- /MacHyperVSupport/VMBus/HyperVVMBus.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // HyperVVMBus.hpp 3 | // Hyper-V VMBus controller 4 | // 5 | // Copyright © 2021-2022 Goldfish64. All rights reserved. 6 | // 7 | 8 | #ifndef HyperVVMBus_hpp 9 | #define HyperVVMBus_hpp 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include "HyperV.hpp" 17 | #include "VMBus.hpp" 18 | 19 | #include "HyperVController.hpp" 20 | 21 | #define kVMBusArrayInitialChildrenCount 10 22 | 23 | class HyperVVMBusDevice; 24 | class VMBusInterruptProcessor; 25 | 26 | 27 | // 28 | // Channel status. 29 | // 30 | typedef enum { 31 | kVMBusChannelStatusNotPresent = 0, 32 | kVMBusChannelStatusClosed, 33 | kVMBusChannelStatusOpen 34 | } VMBusChannelStatus; 35 | 36 | // 37 | // Used for per-channel tracking of buffers and stats. 38 | // 39 | typedef struct { 40 | VMBusChannelStatus status; 41 | uuid_string_t typeGuidString; 42 | uuid_t instanceId; 43 | VMBusChannelMessageChannelOffer offerMessage; 44 | bool useDedicatedInterrupt; 45 | UInt32 connectionSignalId; 46 | 47 | // 48 | // Unique GPADL handle for this channel. 49 | // 50 | UInt32 dataGpadlHandle; 51 | HyperVDMABuffer dataBuffer; 52 | HyperVDMABuffer eventBuffer; 53 | 54 | // 55 | // Index into ring buffer where receive pages begin. 56 | // 57 | UInt16 rxPageIndex; 58 | 59 | VMBusRingBuffer *txBuffer; 60 | VMBusRingBuffer *rxBuffer; 61 | 62 | // 63 | // I/O Kit nub for VMBus device. 64 | // 65 | HyperVVMBusDevice *deviceNub; 66 | } VMBusChannel; 67 | 68 | class HyperVVMBus : public IOService { 69 | OSDeclareDefaultStructors(HyperVVMBus); 70 | HVDeclareLogFunctions("vmbus"); 71 | typedef IOService super; 72 | 73 | friend class HyperVVMBusDevice; 74 | friend class VMBusInterruptProcessor; 75 | 76 | 77 | private: 78 | // 79 | // Parent controller. 80 | // 81 | HyperVController *hvController = nullptr; 82 | 83 | // 84 | // Interrupt event sources for VMBus management packets. 85 | // This may arrive on any CPU, so one is used for each CPU. 86 | // 87 | UInt32 vmbusInterruptProcsCount = 0; 88 | VMBusInterruptProcessor **vmbusInterruptProcs = nullptr; 89 | 90 | // 91 | // VMBus event flags. 92 | // 93 | bool useLegacyEventFlags = false; 94 | HyperVDMABuffer vmbusEventFlags; 95 | HyperVEventFlags *vmbusRxEventFlags; 96 | HyperVEventFlags *vmbusTxEventFlags; 97 | HyperVDMABuffer _vmbusMnf1 = { }; 98 | HyperVDMABuffer _vmbusMnf2 = { }; 99 | 100 | // 101 | // Flag used for waiting for incoming message response. 102 | // 0 = disable. 103 | // 104 | UInt8 _vmbusWaitForMessageType = 0; 105 | UInt32 _vmbusWaitMessageCpu = 0; 106 | HyperVMessage _vmbusWaitMessage = { }; 107 | 108 | IOCommandGate *_cmdGate = nullptr; 109 | bool _cmdShouldWake = false; 110 | bool _cmdGateEvent = false; 111 | 112 | 113 | UInt32 _nextGpadlHandle = kHyperVGpadlNullHandle; 114 | UInt32 _vmbusVersion = 0; 115 | UInt16 _vmbusMsgConnectionId = 0; 116 | public: 117 | VMBusChannel _vmbusChannels[kVMBusMaxChannels] = { }; 118 | 119 | 120 | private: 121 | 122 | // 123 | // Interrupt handling for VMBus management packets. 124 | // 125 | void handleDirectInterrupt(OSObject *target, void *refCon, IOService *nub, int source); 126 | bool allocateInterruptEventSources(); 127 | void freeInterruptEventSources(); 128 | 129 | void processIncomingVMBusMessage(UInt32 cpu); 130 | // 131 | // VMBus functions. 132 | // 133 | bool allocateVMBusBuffers(); 134 | bool sendVMBusMessage(VMBusChannelMessage *message, VMBusChannelMessageType responseType = kVMBusChannelMessageTypeInvalid, VMBusChannelMessage *response = NULL); 135 | bool sendVMBusMessageWithSize(VMBusChannelMessage *message, UInt32 messageSize, VMBusChannelMessageType responseType = kVMBusChannelMessageTypeInvalid, VMBusChannelMessage *response = NULL); 136 | IOReturn sendVMBusMessageGated(VMBusChannelMessage *message, UInt32 *messageSize, VMBusChannelMessageType *responseType, VMBusChannelMessage *response); 137 | bool connectVMBus(); 138 | bool negotiateVMBus(UInt32 version); 139 | bool scanVMBus(); 140 | bool addVMBusDevice(VMBusChannelMessageChannelOffer *offerMessage); 141 | void removeVMBusDevice(VMBusChannelMessageChannelRescindOffer *rescindOfferMessage); 142 | bool registerVMBusDevice(VMBusChannel *channel); 143 | void cleanupVMBusDevice(VMBusChannel *channel); 144 | 145 | // 146 | // VMBus channel management. 147 | // 148 | 149 | 150 | void freeVMBusChannel(UInt32 channelId); 151 | 152 | public: 153 | // 154 | // IOService overrides. 155 | // 156 | bool attach(IOService *provider) APPLE_KEXT_OVERRIDE; 157 | 158 | // 159 | // Misc functions. 160 | // 161 | inline HyperVController *getHvController() { return hvController; } 162 | inline UInt32 getVersion() { return _vmbusVersion; } 163 | 164 | // 165 | // VMBus channel management. 166 | // 167 | VMBusChannelStatus getVMBusChannelStatus(UInt32 channelId); 168 | IOReturn openVMBusChannel(UInt32 channelId, UInt32 txBufferSize, VMBusRingBuffer **txBuffer, UInt32 rxBufferSize, VMBusRingBuffer **rxBuffer); 169 | IOReturn closeVMBusChannel(UInt32 channelId); 170 | IOReturn initVMBusChannelGPADL(UInt32 channelId, HyperVDMABuffer *dmaBuffer, UInt32 *gpadlHandle); 171 | IOReturn freeVMBusChannelGPADL(UInt32 channelId, UInt32 gpadlHandle); 172 | void signalVMBusChannel(UInt32 channelId); 173 | }; 174 | 175 | #endif 176 | -------------------------------------------------------------------------------- /MacHyperVSupport/VMBus/HyperVVMBusInterrupts.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // HyperVVMBusInterrupts.cpp 3 | // Hyper-V VMBus controller 4 | // 5 | // Copyright © 2022 Goldfish64. All rights reserved. 6 | // 7 | 8 | #include "HyperVVMBus.hpp" 9 | 10 | class VMBusInterruptProcessor : public OSObject { 11 | OSDeclareDefaultStructors(VMBusInterruptProcessor) 12 | 13 | private: 14 | UInt32 _cpuIndex = 0; 15 | HyperVVMBus *_vmbus = nullptr; 16 | IOInterruptEventSource *_interruptEventSource = nullptr; 17 | 18 | void handleInterrupt(OSObject *owner, IOInterruptEventSource *sender, int count); 19 | 20 | public: 21 | static VMBusInterruptProcessor *vmbusInterruptProcessor(UInt32 cpuIndex, HyperVVMBus *vmbus); 22 | 23 | bool setupInterrupt(); 24 | void teardownInterrupt(); 25 | void triggerInterrupt(); 26 | }; 27 | 28 | OSDefineMetaClassAndStructors(VMBusInterruptProcessor, OSObject); 29 | 30 | VMBusInterruptProcessor *VMBusInterruptProcessor::vmbusInterruptProcessor(UInt32 cpuIndex, HyperVVMBus *vmbus) { 31 | VMBusInterruptProcessor *me = new VMBusInterruptProcessor; 32 | if (me == nullptr) { 33 | return nullptr; 34 | } 35 | 36 | me->_cpuIndex = cpuIndex; 37 | me->_vmbus = vmbus; 38 | return me; 39 | } 40 | 41 | bool VMBusInterruptProcessor::setupInterrupt() { 42 | _interruptEventSource = IOInterruptEventSource::interruptEventSource(this, OSMemberFunctionCast(IOInterruptEventAction, this, &VMBusInterruptProcessor::handleInterrupt)); 43 | if (_interruptEventSource == nullptr) { 44 | return false; 45 | } 46 | _interruptEventSource->enable(); 47 | _vmbus->getWorkLoop()->addEventSource(_interruptEventSource); 48 | return true; 49 | } 50 | 51 | void VMBusInterruptProcessor::teardownInterrupt() { 52 | _vmbus->getWorkLoop()->removeEventSource(_interruptEventSource); 53 | _interruptEventSource->disable(); 54 | _interruptEventSource->release(); 55 | } 56 | 57 | void VMBusInterruptProcessor::triggerInterrupt() { 58 | _interruptEventSource->interruptOccurred(0, 0, 0); 59 | } 60 | 61 | void VMBusInterruptProcessor::handleInterrupt(OSObject *owner, IOInterruptEventSource *sender, int count) { 62 | // 63 | // Process message on main VMBus class. 64 | // 65 | _vmbus->processIncomingVMBusMessage(_cpuIndex); 66 | } 67 | 68 | void HyperVVMBus::handleDirectInterrupt(OSObject *target, void *refCon, IOService *nub, int source) { 69 | vmbusInterruptProcs[cpu_number()]->triggerInterrupt(); 70 | } 71 | 72 | bool HyperVVMBus::allocateInterruptEventSources() { 73 | vmbusInterruptProcsCount = real_ncpus; 74 | 75 | vmbusInterruptProcs = IONew(VMBusInterruptProcessor*, vmbusInterruptProcsCount); 76 | bzero(vmbusInterruptProcs, sizeof (VMBusInterruptProcessor*) * vmbusInterruptProcsCount); 77 | for (UInt32 cpuIndex = 0; cpuIndex < vmbusInterruptProcsCount; cpuIndex++) { 78 | VMBusInterruptProcessor *vmbusInterruptProcessor = VMBusInterruptProcessor::vmbusInterruptProcessor(cpuIndex, this); 79 | if (vmbusInterruptProcessor == nullptr) { 80 | return false; 81 | } 82 | vmbusInterruptProcessor->setupInterrupt(); 83 | vmbusInterruptProcs[cpuIndex] = vmbusInterruptProcessor; 84 | } 85 | 86 | if (registerInterrupt(0, this, OSMemberFunctionCast(IOInterruptAction, this, &HyperVVMBus::handleDirectInterrupt)) != kIOReturnSuccess 87 | || enableInterrupt(0) != kIOReturnSuccess) { 88 | HVSYSLOG("Failed to setup interrupt handler"); 89 | return false; 90 | } 91 | 92 | return true; 93 | } 94 | -------------------------------------------------------------------------------- /MacHyperVSupport/VMBus/HyperVVMBusPrivate.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // HyperVVMBusPrivate.cpp 3 | // Hyper-V VMBus controller 4 | // 5 | // Copyright © 2021-2022 Goldfish64. All rights reserved. 6 | // 7 | 8 | #include "HyperVVMBus.hpp" 9 | 10 | 11 | 12 | bool HyperVVMBus::allocateVMBusBuffers() { 13 | // 14 | // Allocate common VMBus structures. 15 | // 16 | getHvController()->allocateDmaBuffer(&vmbusEventFlags, PAGE_SIZE); 17 | getHvController()->allocateDmaBuffer(&_vmbusMnf1, PAGE_SIZE); 18 | getHvController()->allocateDmaBuffer(&_vmbusMnf2, PAGE_SIZE); 19 | 20 | // 21 | // Event flag bits primarily used on Windows Server 2008 R2 and older. 22 | // 23 | vmbusRxEventFlags = (HyperVEventFlags*)vmbusEventFlags.buffer; 24 | vmbusTxEventFlags = (HyperVEventFlags*)((UInt8*)vmbusEventFlags.buffer + PAGE_SIZE / 2); 25 | 26 | return true; 27 | } 28 | -------------------------------------------------------------------------------- /MacHyperVSupport/kern_start.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // kern_start.cpp 3 | // Hyper-V integration support Lilu configuration 4 | // 5 | // Copyright © 2021 Goldfish64. All rights reserved. 6 | // 7 | // This kext is not a plugin, but requires an early start to 8 | // properly register with Lilu's patcher functions. 9 | // 10 | // 11 | 12 | #include 13 | #include 14 | 15 | #include "HyperVPlatformProvider.hpp" 16 | 17 | PluginConfiguration ADDPR(config) { 18 | xStringify(PRODUCT_NAME), 19 | parseModuleVersion(xStringify(MODULE_VERSION)), 20 | LiluAPI::AllowNormal | LiluAPI::AllowInstallerRecovery | LiluAPI::AllowSafeMode, 21 | NULL, 22 | 0, 23 | NULL, 24 | 0, 25 | NULL, 26 | 0, 27 | KernelVersion::Tiger, 28 | KernelVersion::Sequoia, 29 | []() { 30 | HyperVPlatformProvider::getInstance(); 31 | } 32 | }; 33 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | MacHyperVSupport 2 | ================ 3 | 4 | [![Build Status](https://github.com/acidanthera/MacHyperVSupport/actions/workflows/main.yml/badge.svg?branch=master)](https://github.com/acidanthera/MacHyperVSupport/actions) [![Scan Status](https://scan.coverity.com/projects/23212/badge.svg?flat=1)](https://scan.coverity.com/projects/23212) 5 | 6 | Hyper-V integration services for macOS running on Windows Server 2008 R2 or higher (generation 1) or Windows 8.1 / Windows Server 2012 R2 or higher (generation 2). Windows Server 2016 is currently unsupported when using a generation 2 VM. 7 | 8 | All Intel macOS versions are supported. 9 | 10 | ### Supported Hyper-V devices and services 11 | - Heartbeat 12 | - Guest shutdown (with daemon) 13 | - Time synchronization (with daemon) 14 | - Host to guest file copy (with daemon) 15 | - PCI passthrough (partial support) 16 | - PS/2 keyboard (applies to generation 1 VMs only) 17 | - Synthetic graphics (full support requires HyperVFramebuffer.kext to be loaded or installed) 18 | - Synthetic keyboard 19 | - Synthetic mouse 20 | - Synthetic network controller 21 | - Synthetic IDE controller (applies to generation 1 VMs only, currently only virtual hard disks) 22 | - Synthetic SCSI controller 23 | 24 | ### Binaries 25 | - MacHyperVSupport.kext: Core Hyper-V support kext for macOS 10.4 to 11.0. 26 | - MacHyperVSupportMonterey.kext: Core Hyper-V support kext for macOS 12.0 and newer. 27 | - MacHyperVFramebuffer.kext: Basic framebuffer support kext for all macOS versions. This extension must be installed and kext signing disabled in SIP on macOS 11.0 and newer due to macOS requirements. 28 | - hvfilecopyd: File copy userspace daemon. 29 | - hvshutdownd: Shutdown userspace daemon. 30 | - hvtimesyncd: Time synchronization userspace daemon. 31 | 32 | ### OpenCore configuration 33 | #### ACPI 34 | - [SSDT-HV-VMBUS](https://github.com/acidanthera/OpenCorePkg/blob/master/Docs/AcpiSamples/Source/SSDT-HV-VMBUS.dsl): Enables correct Startup Disk operation, ensure patches described within are also configured. 35 | - [SSDT-HV-DEV](https://github.com/acidanthera/OpenCorePkg/blob/master/Docs/AcpiSamples/Source/SSDT-HV-DEV.dsl): Required on Windows Server 2019 / Windows 10 and newer, provides proper processor objects and disables incompatible virtual devices under macOS. 36 | - [SSDT-HV-PLUG](https://github.com/acidanthera/OpenCorePkg/blob/master/Docs/AcpiSamples/Source/SSDT-HV-PLUG.dsl): Ensures VMPlatformPlugin loads on Big Sur and above, avoids freezes with the default PlatformPlugin. 37 | * Ensure all SSDTs are added in the order above. Legacy iasl will need to be used when using older versions of macOS prior to 10.7. 38 | * Ensure all patches described in above SSDTs are present in `ACPI->Patch`. 39 | 40 | #### Booter quirks 41 | - `AllowRelocationBlock` - required for macOS 10.7 and older 42 | - `AvoidRuntimeDefrag` - required 43 | - `ClearTaskSwitchBit` - required for macOS 10.7 and older 44 | - `ForceExitBootServices` - required for macOS 10.7 and older 45 | - `ProvideCustomSlide` - required 46 | - `RebuildAppleMemoryMap` - required for macOS 10.6 and older 47 | 48 | #### Kernel 49 | - Quirks 50 | - `ProvideCurrentCpuInfo` - required for proper TSC/FSB values and CPU topology values. 51 | - The following additional kernel extensions are required: 52 | - [Lilu](https://github.com/acidanthera/Lilu) - patching and library functions 53 | - [VirtualSMC](https://github.com/acidanthera/VirtualSMC) - SMC emulator 54 | - Force 55 | - On older versions of macOS, the following kernel extensions may need to be Force injected. Refer to the OpenCore Configuration manual for details. 56 | - IONetworkingFamily (`com.apple.iokit.IONetworkingFamily`) 57 | - IOSCSIParallelFamily (`com.apple.iokit.IOSCSIParallelFamily`) 58 | - If injecting MacHyperVFramebuffer on supported versions, IOGraphicsFamily (`com.apple.iokit.IOGraphicsFamily`) must also be injected with `Force` 59 | - Patch 60 | - Disable _hpet_init 61 | - Arch = `i386` 62 | - Base = `_hpet_init` 63 | - Comment = `Disables _hpet_init due to no HPET hardware present` 64 | - Count = `1` 65 | - Identifier = `kernel` 66 | - MaxKernel = `9.5.99` 67 | - Replace = `C3` 68 | - Disable IOHIDDeviceShim::newTransportString() 69 | - Arch = `i386` 70 | - Base = `__ZNK15IOHIDDeviceShim18newTransportStringEv` 71 | - Comment = `Fix crash in IOHIDDeviceShim::newTransportString() caused by NULL _deviceType` 72 | - Count = `1` 73 | - Identifier = `com.apple.iokit.IOHIDFamily` 74 | - MaxKernel = `9.6.99` 75 | - MinKernel = `9.4.0` 76 | - Replace = `31C0C3` 77 | - Disable scaling factor for X/Y mouse movement 78 | - Arch = `i386` 79 | - Base = `__ZN16IOHIDEventDriver21handleInterruptReportE12UnsignedWideP18IOMemoryDescriptor15IOHIDReportTypem` 80 | - Comment = `Workaround for absence of AbsoluteAxisBoundsRemovalPercentage in 10.4` 81 | - Identifier = `com.apple.iokit.IOHIDFamily` 82 | - Find = `BA1F85EB51` 83 | - MaxKernel = `8.11.99` 84 | - MinKernel = `8.0.0` 85 | - Replace = `BA00000000` 86 | - Emulate 87 | - DummyPowerManagement and CPU spoofing may be required depending on the host CPU for older versions of macOS. 88 | 89 | #### UEFI 90 | - Quirks 91 | - `DisableSecurityPolicy` - required on Windows Server 2019 / Windows 10 and newer 92 | 93 | ### Installer image creation 94 | - Installer images can either be passed in from USB hard disks, or converted from a DMG to a VHD/VHDX image using `qemu-img`: 95 | - DMGs need to be in a read/write format first. 96 | - VHD: `qemu-img convert -f raw -O vpc Installer.dmg Installer.vhd` 97 | - VHDX: `qemu-img convert -f raw -O vhdx Installer.dmg Installer.vhdx` 98 | 99 | ### Boot arguments 100 | See the [module list](Docs/modules.md) for boot arguments for each module. 101 | 102 | ### Credits 103 | - [Apple](https://www.apple.com) for macOS 104 | - [Goldfish64](https://github.com/Goldfish64) for this software 105 | - [vit9696](https://github.com/vit9696) for [Lilu.kext](https://github.com/acidanthera/Lilu) and providing assistance 106 | - [flagers](https://github.com/flagersgit) for file copy implementation and providing assistance 107 | - [Microsoft Hypervisor Top-Level Functional Specification](https://learn.microsoft.com/en-us/virtualization/hyper-v-on-windows/reference/tlfs) 108 | - [Linux](https://github.com/torvalds/linux/tree/master/drivers/hv) and [FreeBSD](https://github.com/freebsd/freebsd-src/tree/main/sys/dev/hyperv) Hyper-V integration services 109 | -------------------------------------------------------------------------------- /Tools/Daemons/hvdebug.h: -------------------------------------------------------------------------------- 1 | // 2 | // hvdebug.h 3 | // Hyper-V userspace debugging support 4 | // 5 | // Copyright © 2022 Goldfish64. All rights reserved. 6 | // 7 | 8 | #ifndef hvdebug_h 9 | #define hvdebug_h 10 | 11 | #import 12 | #include 13 | 14 | #if DEBUG 15 | void HVLOG_PRINT(const char *func, FILE *file, const char *str, ...); 16 | 17 | #define HVDeclareLogFunctionsUser(appName) \ 18 | void HVLOG_PRINT(const char *func, FILE *file, const char *str, ...) { \ 19 | char tmp[256]; \ 20 | tmp[0] = '\0'; \ 21 | va_list args; \ 22 | va_start(args, str); \ 23 | vsnprintf(tmp, sizeof (tmp), str, args); \ 24 | fprintf(file, "%s: %s(): %s\n", appName, func, tmp); \ 25 | va_end(args); \ 26 | } 27 | 28 | #define HVDBGLOG(file, str, ...) HVLOG_PRINT(__FUNCTION__, file, str, ## __VA_ARGS__) 29 | #define HVSYSLOG(file, str, ...) HVLOG_PRINT(__FUNCTION__, file, str, ## __VA_ARGS__) 30 | 31 | #else 32 | void HVLOG_PRINT(FILE *file, const char *str, ...); 33 | 34 | #define HVDeclareLogFunctionsUser(appName) \ 35 | void HVLOG_PRINT(FILE *file, const char *str, ...) { \ 36 | char tmp[256]; \ 37 | tmp[0] = '\0'; \ 38 | va_list args; \ 39 | va_start(args, str); \ 40 | vsnprintf(tmp, sizeof (tmp), str, args); \ 41 | fprintf(file, "%s: %s\n", appName, tmp); \ 42 | va_end(args); \ 43 | } 44 | 45 | #define HVDBGLOG(file, str, ...) {} 46 | #define HVSYSLOG(file, str, ...) HVLOG_PRINT(file, str, ## __VA_ARGS__) 47 | #endif 48 | 49 | #endif 50 | -------------------------------------------------------------------------------- /Tools/Daemons/hvfilecopyd/fish.goldfish64.hvfilecopyd.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Disabled 6 | 7 | KeepAlive 8 | 9 | Label 10 | fish.goldfish64.hvfilecopyd 11 | ProgramArguments 12 | 13 | /Library/Application Support/MacHyperVSupport/hvfilecopyd 14 | 15 | RunAtLoad 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /Tools/Daemons/hvfilecopyd/postinstall: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # postinstall 4 | # MacHyperVSupport hvfilecopyd daemon package postinstall script 5 | # 6 | launchctl load /Library/LaunchDaemons/fish.goldfish64.hvfilecopyd.plist || exit 1 7 | -------------------------------------------------------------------------------- /Tools/Daemons/hviokit.h: -------------------------------------------------------------------------------- 1 | // 2 | // hviokit.h 3 | // Hyper-V userspace I/O Kit support 4 | // 5 | // Copyright © 2022 Goldfish64. All rights reserved. 6 | // 7 | 8 | #ifndef hviokit_h 9 | #define hviokit_h 10 | 11 | #include 12 | #include 13 | 14 | void hvIOKitNotificationHandler(io_connect_t connection, CFMachPortRef port, void *msg, CFIndex size, void *info); 15 | IOReturn hvIOKitSetupIOKitNotifications(const char *name); 16 | 17 | 18 | #endif 19 | -------------------------------------------------------------------------------- /Tools/Daemons/hvshutdownd/fish.goldfish64.hvshutdownd.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Disabled 6 | 7 | KeepAlive 8 | 9 | Label 10 | fish.goldfish64.hvshutdownd 11 | ProgramArguments 12 | 13 | /Library/Application Support/MacHyperVSupport/hvshutdownd 14 | 15 | RunAtLoad 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /Tools/Daemons/hvshutdownd/hvshutdownd.c: -------------------------------------------------------------------------------- 1 | // 2 | // hvshutdownd.c 3 | // Hyper-V guest shutdown userspace daemon 4 | // 5 | // Copyright © 2022-2025 Goldfish64. All rights reserved. 6 | // 7 | 8 | #include "HyperVShutdownUserClient.h" 9 | #include "hvdebug.h" 10 | #include "hviokit.h" 11 | 12 | #define HYPERV_SHUTDOWN_KERNEL_SERVICE "HyperVShutdown" 13 | #define SHUTDOWN_BIN_PATH "/sbin/shutdown" 14 | 15 | HVDeclareLogFunctionsUser("hvshutdownd"); 16 | 17 | void hvShutdownDoShutdownCheck(io_connect_t connection) { 18 | #if __MAC_OS_X_VERSION_MIN_REQUIRED >= __MAC_10_5 19 | // 20 | // Call into user client with standard API. 21 | // 22 | UInt64 input64 = kHyperVShutdownMagic; 23 | IOConnectCallScalarMethod(connection, kHyperVShutdownUserClientMethodReportShutdownAbility, &input64, 1, NULL, NULL); 24 | #else 25 | // 26 | // Call into user client with legacy API. 27 | // 28 | IOConnectMethodScalarIScalarO(connection, kHyperVShutdownUserClientMethodReportShutdownAbility, 1, 0, kHyperVShutdownMagic); 29 | #endif 30 | } 31 | 32 | void hvShutdownDoShutdown(bool restart) { 33 | HVSYSLOG(stdout, "Shutdown request received, performing shutdown (restart %u)", restart); 34 | 35 | // 36 | // Shutdown/restart has been requested, invoke /sbin/shutdown. 37 | // 38 | char *shutdownArgs[] = { 39 | SHUTDOWN_BIN_PATH, 40 | restart ? "-r" : "-h", 41 | "now", 42 | "Hyper-V Guest Shutdown initiated", 43 | NULL 44 | }; 45 | 46 | // 47 | // This should not return. 48 | // 49 | int ret = execv(shutdownArgs[0], shutdownArgs); 50 | if (ret == -1) { 51 | HVSYSLOG(stderr, "Failed to execute %s", shutdownArgs[0]); 52 | } 53 | } 54 | 55 | void hvIOKitNotificationHandler(io_connect_t connection, CFMachPortRef port, void *msg, CFIndex size, void *info) { 56 | HyperVShutdownUserClientNotificationMessage *shutdownMsg = (HyperVShutdownUserClientNotificationMessage *) msg; 57 | 58 | if (size < __offsetof(HyperVShutdownUserClientNotificationMessage, type)) { 59 | HVSYSLOG(stderr, "Invalid message size %u received, should be at least %u", 60 | size, __offsetof(HyperVShutdownUserClientNotificationMessage, type)); 61 | return; 62 | } 63 | 64 | HVDBGLOG(stdout, "Received notification of type 0x%X", shutdownMsg->type); 65 | switch (shutdownMsg->type) { 66 | // 67 | // Always returns magic value, means daemon is alive and can handle a shutdown request. 68 | // 69 | case kHyperVShutdownUserClientNotificationTypeCheck: 70 | hvShutdownDoShutdownCheck(connection); 71 | break; 72 | 73 | case kHyperVShutdownUserClientNotificationTypePerformShutdown: 74 | case kHyperVShutdownUserClientNotificationTypePerformRestart: 75 | hvShutdownDoShutdown(shutdownMsg->type == kHyperVShutdownUserClientNotificationTypePerformRestart); 76 | break; 77 | 78 | default: 79 | HVDBGLOG(stdout, "Unknown notification type 0x%X", shutdownMsg->type); 80 | break; 81 | } 82 | } 83 | 84 | int main(int argc, const char * argv[]) { 85 | // 86 | // Setup I/O Kit notifications. 87 | // 88 | if (hvIOKitSetupIOKitNotifications(HYPERV_SHUTDOWN_KERNEL_SERVICE) != kIOReturnSuccess) { 89 | return -1; 90 | } 91 | 92 | // 93 | // Run main loop, this should not return. 94 | // 95 | CFRunLoopRun(); 96 | return 0; 97 | } 98 | -------------------------------------------------------------------------------- /Tools/Daemons/hvshutdownd/postinstall: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # postinstall 4 | # MacHyperVSupport hvshutdownd daemon package postinstall script 5 | # 6 | launchctl load /Library/LaunchDaemons/fish.goldfish64.hvshutdownd.plist || exit 1 7 | -------------------------------------------------------------------------------- /Tools/Daemons/hvtimesyncd/fish.goldfish64.hvtimesyncd.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Disabled 6 | 7 | KeepAlive 8 | 9 | Label 10 | fish.goldfish64.hvtimesyncd 11 | ProgramArguments 12 | 13 | /Library/Application Support/MacHyperVSupport/hvtimesyncd 14 | 15 | RunAtLoad 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /Tools/Daemons/hvtimesyncd/hvtimesyncd.c: -------------------------------------------------------------------------------- 1 | // 2 | // hvtimesyncd.c 3 | // Hyper-V time synchronization userspace daemon 4 | // 5 | // Copyright © 2022 Goldfish64. All rights reserved. 6 | // 7 | 8 | #include "HyperVTimeSyncUserClient.h" 9 | #include "hvdebug.h" 10 | #include "hviokit.h" 11 | 12 | #include 13 | 14 | #define HYPERV_TIME_SYNC_KERNEL_SERVICE "HyperVTimeSync" 15 | 16 | HVDeclareLogFunctionsUser("hvtimesyncd"); 17 | 18 | void hvIOKitNotificationHandler(io_connect_t connection, CFMachPortRef port, void *msg, CFIndex size, void *info) { 19 | HyperVTimeSyncUserClientNotificationMessage *timeSyncMsg = (HyperVTimeSyncUserClientNotificationMessage *) msg; 20 | struct timeval timeValData; 21 | 22 | if (size < __offsetof(HyperVTimeSyncUserClientNotificationMessage, microseconds)) { 23 | HVSYSLOG(stderr, "Invalid message size %u received, should be at least %u", 24 | size, __offsetof(HyperVTimeSyncUserClientNotificationMessage, microseconds)); 25 | return; 26 | } 27 | 28 | HVDBGLOG(stdout, "Got new time data (seconds: %llu, microseconds: %u)", timeSyncMsg->seconds, timeSyncMsg->microseconds); 29 | 30 | timeValData.tv_sec = timeSyncMsg->seconds; 31 | timeValData.tv_usec = timeSyncMsg->microseconds; 32 | settimeofday(&timeValData, NULL); 33 | } 34 | 35 | int main(int argc, const char * argv[]) { 36 | // 37 | // Setup I/O Kit notifications. 38 | // 39 | if (hvIOKitSetupIOKitNotifications(HYPERV_TIME_SYNC_KERNEL_SERVICE) != kIOReturnSuccess) { 40 | return -1; 41 | } 42 | 43 | // 44 | // Run main loop, this should not return. 45 | // 46 | CFRunLoopRun(); 47 | return 0; 48 | } 49 | -------------------------------------------------------------------------------- /Tools/Daemons/hvtimesyncd/postinstall: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # postinstall 4 | # MacHyperVSupport hvtimesyncd daemon package postinstall script 5 | # 6 | launchctl load /Library/LaunchDaemons/fish.goldfish64.hvtimesyncd.plist || exit 1 7 | -------------------------------------------------------------------------------- /Tools/package/Localizable_EN.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/acidanthera/MacHyperVSupport/8a2d1bbc1772861b723ed8e872266c8d443ab8c3/Tools/package/Localizable_EN.strings -------------------------------------------------------------------------------- /Tools/package/scripts.js: -------------------------------------------------------------------------------- 1 | function installCheckScript() { 2 | try { 3 | var hvController = system.ioregistry.matchingClass('HyperVController'); 4 | if (hvController.length > 0) { 5 | return true; 6 | } 7 | } catch(e) { 8 | system.log('installCheckScript threw exception ' + e); 9 | } 10 | 11 | // If HyperVController is not present, fail as this is not on a Hyper-V platform. 12 | my.result.message = system.localizedStringWithFormat('ERR_NOT_HYPERV'); 13 | my.result.type = 'Fatal'; 14 | return false; 15 | } 16 | 17 | function checkIfTiger() { 18 | try { 19 | if(system.compareVersions(my.target.systemVersion.ProductVersion, '10.4') != -1) { 20 | if(system.compareVersions(my.target.systemVersion.ProductVersion, '10.5') == -1) { 21 | return true; 22 | } 23 | } 24 | } catch(err) { } 25 | 26 | return false; 27 | } 28 | --------------------------------------------------------------------------------