Skip to content

Commit

Permalink
GraphicsBridge: Simplify graphics bridge PCI stub
Browse files Browse the repository at this point in the history
  • Loading branch information
Goldfish64 committed Feb 18, 2025
1 parent 8ed533d commit 436ace9
Show file tree
Hide file tree
Showing 4 changed files with 96 additions and 138 deletions.
6 changes: 0 additions & 6 deletions MacHyperVSupport.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -218,8 +218,6 @@
41F2E43D2666E6A100CE26CE /* HyperVGraphicsBridge.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 41F2E43B2666E6A100CE26CE /* HyperVGraphicsBridge.hpp */; };
41F2E45C26683B2C00CE26CE /* HyperVPCIRoot.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 41F2E45A26683B2B00CE26CE /* HyperVPCIRoot.cpp */; };
41F2E45D26683B2C00CE26CE /* HyperVPCIRoot.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 41F2E45B26683B2C00CE26CE /* HyperVPCIRoot.hpp */; };
41F47BE9290EC06E00C0E3C5 /* HyperVGraphicsBridgePrivate.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 41F47BE8290EC06E00C0E3C5 /* HyperVGraphicsBridgePrivate.cpp */; };
41F47BEA290EC06E00C0E3C5 /* HyperVGraphicsBridgePrivate.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 41F47BE8290EC06E00C0E3C5 /* HyperVGraphicsBridgePrivate.cpp */; };
41F9B8F02849792200E0DCB2 /* HyperVPCIBridge.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 41F9B8EE2849792200E0DCB2 /* HyperVPCIBridge.cpp */; };
41F9B8F12849792200E0DCB2 /* HyperVPCIBridge.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 41F9B8EF2849792200E0DCB2 /* HyperVPCIBridge.hpp */; };
41F9B8F8284983FF00E0DCB2 /* HyperVPCIBridgePrivate.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 41F9B8F7284983FF00E0DCB2 /* HyperVPCIBridgePrivate.cpp */; };
Expand Down Expand Up @@ -656,7 +654,6 @@
41F2E43B2666E6A100CE26CE /* HyperVGraphicsBridge.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = HyperVGraphicsBridge.hpp; sourceTree = "<group>"; };
41F2E45A26683B2B00CE26CE /* HyperVPCIRoot.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = HyperVPCIRoot.cpp; sourceTree = "<group>"; };
41F2E45B26683B2C00CE26CE /* HyperVPCIRoot.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = HyperVPCIRoot.hpp; sourceTree = "<group>"; };
41F47BE8290EC06E00C0E3C5 /* HyperVGraphicsBridgePrivate.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = HyperVGraphicsBridgePrivate.cpp; sourceTree = "<group>"; };
41F9B8EE2849792200E0DCB2 /* HyperVPCIBridge.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = HyperVPCIBridge.cpp; sourceTree = "<group>"; };
41F9B8EF2849792200E0DCB2 /* HyperVPCIBridge.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = HyperVPCIBridge.hpp; sourceTree = "<group>"; };
41F9B8F7284983FF00E0DCB2 /* HyperVPCIBridgePrivate.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = HyperVPCIBridgePrivate.cpp; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1142,7 +1139,6 @@
children = (
41F2E43A2666E6A100CE26CE /* HyperVGraphicsBridge.cpp */,
41F2E43B2666E6A100CE26CE /* HyperVGraphicsBridge.hpp */,
41F47BE8290EC06E00C0E3C5 /* HyperVGraphicsBridgePrivate.cpp */,
);
path = GraphicsBridge;
sourceTree = "<group>";
Expand Down Expand Up @@ -1854,7 +1850,6 @@
4191F70C28F5057F00809232 /* HyperVFileCopyUserClient.cpp in Sources */,
417C576128C64B92003A177C /* HyperVVMBusInterrupts.cpp in Sources */,
416E418E2651E42E006DED6D /* HyperVMousePrivate.cpp in Sources */,
41F47BE9290EC06E00C0E3C5 /* HyperVGraphicsBridgePrivate.cpp in Sources */,
41AE1D0E289C95A9001A7B42 /* HyperVCPU.cpp in Sources */,
416E4180264A0D5D006DED6D /* HyperVStoragePrivate.cpp in Sources */,
41F2E43C2666E6A100CE26CE /* HyperVGraphicsBridge.cpp in Sources */,
Expand Down Expand Up @@ -1904,7 +1899,6 @@
4191F70D28F5057F00809232 /* HyperVFileCopyUserClient.cpp in Sources */,
417C576228C64B92003A177C /* HyperVVMBusInterrupts.cpp in Sources */,
41BF4615288CDF1200813670 /* HyperVMousePrivate.cpp in Sources */,
41F47BEA290EC06E00C0E3C5 /* HyperVGraphicsBridgePrivate.cpp in Sources */,
41AE1D0F289C95A9001A7B42 /* HyperVCPU.cpp in Sources */,
41BF4617288CDF1200813670 /* HyperVStoragePrivate.cpp in Sources */,
41BF4618288CDF1200813670 /* HyperVGraphicsBridge.cpp in Sources */,
Expand Down
181 changes: 90 additions & 91 deletions MacHyperVSupport/GraphicsBridge/HyperVGraphicsBridge.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,17 @@
// HyperVGraphicsBridge.cpp
// Hyper-V synthetic graphics bridge
//
// Copyright © 2021-2022 Goldfish64. All rights reserved.
// Copyright © 2021-2025 Goldfish64. All rights reserved.
//

#include "HyperVGraphicsBridge.hpp"
#include "HyperVPCIRoot.hpp"

#include <IOKit/IOPlatformExpert.h>

OSDefineMetaClassAndStructors(HyperVGraphicsBridge, super);

bool HyperVGraphicsBridge::start(IOService *provider) {
bool result = false;
PE_Video consoleInfo = { };
HyperVPCIRoot *hvPCIRoot;
IOReturn status;

HVCheckDebugArgs();
Expand All @@ -25,200 +24,200 @@ bool HyperVGraphicsBridge::start(IOService *provider) {
}

//
// Locate root PCI bus instance.
// Pull console info.
//
_hvPCIRoot = HyperVPCIRoot::getPCIRootInstance();
if (_hvPCIRoot == nullptr) {
if (getPlatform()->getConsoleInfo(&consoleInfo) != kIOReturnSuccess) {
HVSYSLOG("Failed to get console info");
return false;
}
HVDBGLOG("Console is at 0x%X (%ux%u, bpp: %u, bytes/row: %u)",
consoleInfo.v_baseAddr, consoleInfo.v_height, consoleInfo.v_width, consoleInfo.v_depth, consoleInfo.v_rowBytes);
_fbInitialBase = (UInt32)consoleInfo.v_baseAddr;
_fbInitialLength = (UInt32)(consoleInfo.v_height * consoleInfo.v_rowBytes);

//
// Do not start on Gen1 VMs.
// Ensure parent is HyperVGraphics object.
//
if (!_hvPCIRoot->isHyperVGen2()) {
HVDBGLOG("Not starting on Hyper-V Gen1 VM");
if (OSDynamicCast(HyperVGraphics, provider) == nullptr) {
HVSYSLOG("Provider is not HyperVGraphics");
return false;
}

//
// Register with root PCI bridge.
// Locate root PCI bus instance.
//
if (_hvPCIRoot->registerChildPCIBridge(this, &_pciBusNumber) != kIOReturnSuccess) {
HVSYSLOG("Failed to register with root PCI bus instance");
hvPCIRoot = HyperVPCIRoot::getPCIRootInstance();
if (hvPCIRoot == nullptr) {
HVSYSLOG("Failed to find root PCI bridge instance");
return false;
}

//
// Pull console info.
// TODO: Use actual info from Hyper-V VMBus device for this.
// Do not start on Gen1 VMs.
//
if (getPlatform()->getConsoleInfo(&_consoleInfo) != kIOReturnSuccess) {
HVSYSLOG("Failed to get console info");
if (!hvPCIRoot->isHyperVGen2()) {
HVDBGLOG("Not starting on Hyper-V Gen1 VM");
return false;
}
HVDBGLOG("Console is at 0x%X (%ux%u, bpp: %u, bytes/row: %u)",
_consoleInfo.v_baseAddr, _consoleInfo.v_height, _consoleInfo.v_width, _consoleInfo.v_depth, _consoleInfo.v_rowBytes);

//
// Allocate PCI lock and register with root PCI bridge.
//
_pciLock = IOSimpleLockAlloc();
fillFakePCIDeviceSpace();
if (_pciLock == nullptr) {
HVSYSLOG("Failed to allocate PCI lock");
return false;
}

if (!super::start(provider)) {
HVSYSLOG("super::start() returned false");
status = hvPCIRoot->registerChildPCIBridge(this, &_pciBusNumber);
if (status != kIOReturnSuccess) {
HVSYSLOG("Failed to register with root PCI bus instance");
IOSimpleLockFree(_pciLock);
return false;
}

//
// Add a friendly name to the child device produced.
// Fill PCI device config space.
//
// PCI bridge will contain a single PCI graphics device
// with the framebuffer memory at BAR0. The vendor/device ID is
// the same as what a generation 1 Hyper-V VM uses for the
// emulated graphics.
//
OSIterator *childIterator = getChildIterator(gIOServicePlane);
if (childIterator != NULL) {
childIterator->reset();

IOService *childService = OSDynamicCast(IOService, childIterator->getNextObject());
if (childService != NULL) {
HVDBGLOG("Found child %s", childService->getName());
childService->setProperty("model", "Hyper-V Graphics");
}
bzero(_fakePCIDeviceSpace, sizeof (_fakePCIDeviceSpace));
OSWriteLittleInt16(_fakePCIDeviceSpace, kIOPCIConfigVendorID, kHyperVPCIVendorMicrosoft);
OSWriteLittleInt16(_fakePCIDeviceSpace, kIOPCIConfigDeviceID, kHyperVPCIDeviceHyperVVideo);
OSWriteLittleInt32(_fakePCIDeviceSpace, kIOPCIConfigRevisionID, 0x3000000);
OSWriteLittleInt16(_fakePCIDeviceSpace, kIOPCIConfigSubSystemVendorID, kHyperVPCIVendorMicrosoft);
OSWriteLittleInt16(_fakePCIDeviceSpace, kIOPCIConfigSubSystemID, kHyperVPCIDeviceHyperVVideo);
OSWriteLittleInt32(_fakePCIDeviceSpace, kIOPCIConfigBaseAddress0, (UInt32)_fbInitialBase);

childIterator->release();
if (!super::start(provider)) {
HVSYSLOG("super::start() returned false");
IOSimpleLockFree(_pciLock);
return false;
}

do {


HVDBGLOG("Initialized Hyper-V Synthetic Graphics Bridge");
result = true;
} while (false);

if (!result) {
stop(provider);
}
return result;
HVDBGLOG("Initialized Hyper-V Synthetic Graphics Bridge");
return true;
}

void HyperVGraphicsBridge::stop(IOService *provider) {
HVDBGLOG("Hyper-V Synthetic Graphics Bridge is stopping");

if (_pciLock != nullptr) {
IOSimpleLockFree(_pciLock);
}

super::stop(provider);
}

bool HyperVGraphicsBridge::configure(IOService *provider) {
//
// Add framebuffer memory range to bridge.
//
UInt32 fbSize = (UInt32)(_consoleInfo.v_height * _consoleInfo.v_rowBytes);
addBridgeMemoryRange(_consoleInfo.v_baseAddr, fbSize, true);
HVDBGLOG("Adding framebuffer memory 0x%X length 0x%X to PCI bridge", _fbInitialBase, _fbInitialLength);
addBridgeMemoryRange(_fbInitialBase, _fbInitialLength, true);
return super::configure(provider);
}

UInt32 HyperVGraphicsBridge::configRead32(IOPCIAddressSpace space, UInt8 offset) {
HVDBGLOG("Bus: %u, device: %u, function: %u, offset %X", space.es.busNum, space.es.deviceNum, space.es.functionNum, offset);

UInt32 data;
IOInterruptState ints;

if (space.es.deviceNum != 0 || space.es.functionNum != 0) {
return 0xFFFFFFFF;
}

ints = IOSimpleLockLockDisableInterrupt(_pciLock);
data = OSReadLittleInt32(_fakePCIDeviceSpace, offset);

if (offset == kIOPCIConfigurationOffsetBaseAddress0) {
HVDBGLOG("gonna read %X", data);
}

IOSimpleLockUnlockEnableInterrupt(_pciLock, ints);

HVDBGLOG("Read 32-bit value %u from offset 0x%X", data, offset);
return data;
}

void HyperVGraphicsBridge::configWrite32(IOPCIAddressSpace space, UInt8 offset, UInt32 data) {
HVDBGLOG("Bus: %u, device: %u, function: %u, offset %X", space.es.busNum, space.es.deviceNum, space.es.functionNum, offset);

IOInterruptState ints;

if (space.es.deviceNum != 0 || space.es.functionNum != 0 || (offset > kIOPCIConfigurationOffsetBaseAddress0 && offset <= kIOPCIConfigurationOffsetBaseAddress5) || offset == kIOPCIConfigurationOffsetExpansionROMBase) {
HVDBGLOG("ignoring offset %X", offset);
if (space.es.deviceNum != 0 || space.es.functionNum != 0
|| (offset > kIOPCIConfigurationOffsetBaseAddress0 && offset <= kIOPCIConfigurationOffsetBaseAddress5)
|| offset == kIOPCIConfigurationOffsetExpansionROMBase) {
return;
}

if (offset == kIOPCIConfigurationOffsetBaseAddress0) {
HVDBGLOG("gonna write %X", data);
}

HVDBGLOG("Writing 32-bit value %u to offset 0x%X", data, offset);

//
// Return BAR0 size if requested.
//
if (offset == kIOPCIConfigurationOffsetBaseAddress0 && data == 0xFFFFFFFF) {
HVDBGLOG("Got bar size request");
UInt32 fbSize = (UInt32)(_consoleInfo.v_height * _consoleInfo.v_rowBytes);
OSWriteLittleInt32(_fakePCIDeviceSpace, offset, (0xFFFFFFFF - fbSize) + 1);
OSWriteLittleInt32(_fakePCIDeviceSpace, offset, (0xFFFFFFFF - _fbInitialLength) + 1);
return;
}

ints = IOSimpleLockLockDisableInterrupt(_pciLock);
OSWriteLittleInt32(_fakePCIDeviceSpace, offset, data);

IOSimpleLockUnlockEnableInterrupt(_pciLock, ints);
}

UInt16 HyperVGraphicsBridge::configRead16(IOPCIAddressSpace space, UInt8 offset) {
HVDBGLOG("Bus: %u, device: %u, function: %u, offset %X", space.es.busNum, space.es.deviceNum, space.es.functionNum, offset);

UInt16 data;
IOInterruptState ints;

if (space.es.deviceNum != 0 || space.es.functionNum != 0) {
return 0xFFFF;
}

ints = IOSimpleLockLockDisableInterrupt(_pciLock);
data = OSReadLittleInt16(_fakePCIDeviceSpace, offset);

IOSimpleLockUnlockEnableInterrupt(_pciLock, ints);

HVDBGLOG("Read 16-bit value %u from offset 0x%X", data, offset);
return data;
}

void HyperVGraphicsBridge::configWrite16(IOPCIAddressSpace space, UInt8 offset, UInt16 data) {
HVDBGLOG("Bus: %u, device: %u, function: %u, offset %X", space.es.busNum, space.es.deviceNum, space.es.functionNum, offset);

IOInterruptState ints;

if (space.es.deviceNum != 0 || space.es.functionNum != 0 || (offset >= kIOPCIConfigurationOffsetBaseAddress0 && offset <= kIOPCIConfigurationOffsetBaseAddress5) || offset == kIOPCIConfigurationOffsetExpansionROMBase) {

if (space.es.deviceNum != 0 || space.es.functionNum != 0
|| (offset >= kIOPCIConfigurationOffsetBaseAddress0 && offset <= kIOPCIConfigurationOffsetBaseAddress5)
|| offset == kIOPCIConfigurationOffsetExpansionROMBase) {
return;
}

HVDBGLOG("Writing 16-bit value %u to offset 0x%X", data, offset);

ints = IOSimpleLockLockDisableInterrupt(_pciLock);
OSWriteLittleInt16(_fakePCIDeviceSpace, offset, data);

IOSimpleLockUnlockEnableInterrupt(_pciLock, ints);
}

UInt8 HyperVGraphicsBridge::configRead8(IOPCIAddressSpace space, UInt8 offset) {
HVDBGLOG("Bus: %u, device: %u, function: %u, offset %X", space.es.busNum, space.es.deviceNum, space.es.functionNum, offset);

UInt8 data;
IOInterruptState ints;

if (space.es.deviceNum != 0 || space.es.functionNum != 0) {
return 0xFF;
}

ints = IOSimpleLockLockDisableInterrupt(_pciLock);
data = _fakePCIDeviceSpace[offset];

IOSimpleLockUnlockEnableInterrupt(_pciLock, ints);

HVDBGLOG("Read 8-bit value %u from offset 0x%X", data, offset);
return data;
}

void HyperVGraphicsBridge::configWrite8(IOPCIAddressSpace space, UInt8 offset, UInt8 data) {
HVDBGLOG("Bus: %u, device: %u, function: %u, offset %X", space.es.busNum, space.es.deviceNum, space.es.functionNum, offset);

IOInterruptState ints;

if (space.es.deviceNum != 0 || space.es.functionNum != 0 || (offset >= kIOPCIConfigurationOffsetBaseAddress0 && offset <= kIOPCIConfigurationOffsetBaseAddress5) || offset == kIOPCIConfigurationOffsetExpansionROMBase) {

if (space.es.deviceNum != 0 || space.es.functionNum != 0
|| (offset >= kIOPCIConfigurationOffsetBaseAddress0 && offset <= kIOPCIConfigurationOffsetBaseAddress5)
|| offset == kIOPCIConfigurationOffsetExpansionROMBase) {
return;
}

HVDBGLOG("Writing 8-bit value %u to offset 0x%X", data, offset);

ints = IOSimpleLockLockDisableInterrupt(_pciLock);
_fakePCIDeviceSpace[offset] = data;

IOSimpleLockUnlockEnableInterrupt(_pciLock, ints);
}
14 changes: 6 additions & 8 deletions MacHyperVSupport/GraphicsBridge/HyperVGraphicsBridge.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,16 @@
// HyperVGraphicsBridge.hpp
// Hyper-V synthetic graphics bridge
//
// Copyright © 2021-2022 Goldfish64. All rights reserved.
// Copyright © 2021-2025 Goldfish64. All rights reserved.
//

#ifndef HyperVGraphicsBridge_hpp
#define HyperVGraphicsBridge_hpp

#include <IOKit/pci/IOPCIBridge.h>

#include "HyperVVMBusDevice.hpp"
#include "HyperVGraphicsRegs.hpp"
#include "HyperVGraphics.hpp"
#include "HyperVPCIRoot.hpp"

class HyperVGraphicsBridge : public HV_PCIBRIDGE_CLASS {
Expand All @@ -19,18 +20,15 @@ class HyperVGraphicsBridge : public HV_PCIBRIDGE_CLASS {
typedef HV_PCIBRIDGE_CLASS super;

private:
HyperVPCIRoot *_hvPCIRoot = nullptr;
VMBusVersion _currentGraphicsVersion = { };
UInt8 _pciBusNumber = 0;

//
// Fake PCI structures.
//
IOSimpleLock *_pciLock = nullptr;
IOSimpleLock *_pciLock = nullptr;
UInt8 _fakePCIDeviceSpace[256];
PE_Video _consoleInfo = { };

void fillFakePCIDeviceSpace();
UInt32 _fbInitialBase = 0;
UInt32 _fbInitialLength = 0;

public:
//
Expand Down
Loading

0 comments on commit 436ace9

Please sign in to comment.