22// HyperVGraphicsBridge.cpp
33// Hyper-V synthetic graphics bridge
44//
5- // Copyright © 2021-2022 Goldfish64. All rights reserved.
5+ // Copyright © 2021-2025 Goldfish64. All rights reserved.
66//
77
88#include " HyperVGraphicsBridge.hpp"
99#include " HyperVPCIRoot.hpp"
1010
11- #include < IOKit/IOPlatformExpert.h>
12-
1311OSDefineMetaClassAndStructors (HyperVGraphicsBridge, super);
1412
1513bool HyperVGraphicsBridge::start (IOService *provider) {
16- bool result = false ;
14+ PE_Video consoleInfo = { };
15+ HyperVPCIRoot *hvPCIRoot;
1716 IOReturn status;
1817
1918 HVCheckDebugArgs ();
@@ -25,200 +24,200 @@ bool HyperVGraphicsBridge::start(IOService *provider) {
2524 }
2625
2726 //
28- // Locate root PCI bus instance .
27+ // Pull console info .
2928 //
30- _hvPCIRoot = HyperVPCIRoot::getPCIRootInstance ();
31- if (_hvPCIRoot == nullptr ) {
29+ if ( getPlatform ()-> getConsoleInfo (&consoleInfo) != kIOReturnSuccess ) {
30+ HVSYSLOG ( " Failed to get console info " );
3231 return false ;
3332 }
33+ HVDBGLOG (" Console is at 0x%X (%ux%u, bpp: %u, bytes/row: %u)" ,
34+ consoleInfo.v_baseAddr , consoleInfo.v_height , consoleInfo.v_width , consoleInfo.v_depth , consoleInfo.v_rowBytes );
35+ _fbInitialBase = (UInt32)consoleInfo.v_baseAddr ;
36+ _fbInitialLength = (UInt32)(consoleInfo.v_height * consoleInfo.v_rowBytes );
3437
3538 //
36- // Do not start on Gen1 VMs .
39+ // Ensure parent is HyperVGraphics object .
3740 //
38- if (!_hvPCIRoot-> isHyperVGen2 () ) {
39- HVDBGLOG ( " Not starting on Hyper-V Gen1 VM " );
41+ if (OSDynamicCast (HyperVGraphics, provider) == nullptr ) {
42+ HVSYSLOG ( " Provider is not HyperVGraphics " );
4043 return false ;
4144 }
4245
4346 //
44- // Register with root PCI bridge .
47+ // Locate root PCI bus instance .
4548 //
46- if (_hvPCIRoot->registerChildPCIBridge (this , &_pciBusNumber) != kIOReturnSuccess ) {
47- HVSYSLOG (" Failed to register with root PCI bus instance" );
49+ hvPCIRoot = HyperVPCIRoot::getPCIRootInstance ();
50+ if (hvPCIRoot == nullptr ) {
51+ HVSYSLOG (" Failed to find root PCI bridge instance" );
4852 return false ;
4953 }
5054
5155 //
52- // Pull console info.
53- // TODO: Use actual info from Hyper-V VMBus device for this.
56+ // Do not start on Gen1 VMs.
5457 //
55- if (getPlatform ()-> getConsoleInfo (&_consoleInfo) != kIOReturnSuccess ) {
56- HVSYSLOG ( " Failed to get console info " );
58+ if (!hvPCIRoot-> isHyperVGen2 () ) {
59+ HVDBGLOG ( " Not starting on Hyper-V Gen1 VM " );
5760 return false ;
5861 }
59- HVDBGLOG (" Console is at 0x%X (%ux%u, bpp: %u, bytes/row: %u)" ,
60- _consoleInfo.v_baseAddr , _consoleInfo.v_height , _consoleInfo.v_width , _consoleInfo.v_depth , _consoleInfo.v_rowBytes );
6162
63+ //
64+ // Allocate PCI lock and register with root PCI bridge.
65+ //
6266 _pciLock = IOSimpleLockAlloc ();
63- fillFakePCIDeviceSpace ();
67+ if (_pciLock == nullptr ) {
68+ HVSYSLOG (" Failed to allocate PCI lock" );
69+ return false ;
70+ }
6471
65- if (!super::start (provider)) {
66- HVSYSLOG (" super::start() returned false" );
72+ status = hvPCIRoot->registerChildPCIBridge (this , &_pciBusNumber);
73+ if (status != kIOReturnSuccess ) {
74+ HVSYSLOG (" Failed to register with root PCI bus instance" );
75+ IOSimpleLockFree (_pciLock);
6776 return false ;
6877 }
6978
7079 //
71- // Add a friendly name to the child device produced.
80+ // Fill PCI device config space.
81+ //
82+ // PCI bridge will contain a single PCI graphics device
83+ // with the framebuffer memory at BAR0. The vendor/device ID is
84+ // the same as what a generation 1 Hyper-V VM uses for the
85+ // emulated graphics.
7286 //
73- OSIterator *childIterator = getChildIterator (gIOServicePlane );
74- if (childIterator != NULL ) {
75- childIterator->reset ();
76-
77- IOService *childService = OSDynamicCast (IOService, childIterator->getNextObject ());
78- if (childService != NULL ) {
79- HVDBGLOG (" Found child %s" , childService->getName ());
80- childService->setProperty (" model" , " Hyper-V Graphics" );
81- }
87+ bzero (_fakePCIDeviceSpace, sizeof (_fakePCIDeviceSpace));
88+ OSWriteLittleInt16 (_fakePCIDeviceSpace, kIOPCIConfigVendorID , kHyperVPCIVendorMicrosoft );
89+ OSWriteLittleInt16 (_fakePCIDeviceSpace, kIOPCIConfigDeviceID , kHyperVPCIDeviceHyperVVideo );
90+ OSWriteLittleInt32 (_fakePCIDeviceSpace, kIOPCIConfigRevisionID , 0x3000000 );
91+ OSWriteLittleInt16 (_fakePCIDeviceSpace, kIOPCIConfigSubSystemVendorID , kHyperVPCIVendorMicrosoft );
92+ OSWriteLittleInt16 (_fakePCIDeviceSpace, kIOPCIConfigSubSystemID , kHyperVPCIDeviceHyperVVideo );
93+ OSWriteLittleInt32 (_fakePCIDeviceSpace, kIOPCIConfigBaseAddress0 , (UInt32)_fbInitialBase);
8294
83- childIterator->release ();
95+ if (!super::start (provider)) {
96+ HVSYSLOG (" super::start() returned false" );
97+ IOSimpleLockFree (_pciLock);
98+ return false ;
8499 }
85100
86- do {
87-
88-
89- HVDBGLOG (" Initialized Hyper-V Synthetic Graphics Bridge" );
90- result = true ;
91- } while (false );
92-
93- if (!result) {
94- stop (provider);
95- }
96- return result;
101+ HVDBGLOG (" Initialized Hyper-V Synthetic Graphics Bridge" );
102+ return true ;
97103}
98104
99105void HyperVGraphicsBridge::stop (IOService *provider) {
100106 HVDBGLOG (" Hyper-V Synthetic Graphics Bridge is stopping" );
101107
108+ if (_pciLock != nullptr ) {
109+ IOSimpleLockFree (_pciLock);
110+ }
111+
102112 super::stop (provider);
103113}
104114
105115bool HyperVGraphicsBridge::configure (IOService *provider) {
106116 //
107117 // Add framebuffer memory range to bridge.
108118 //
109- UInt32 fbSize = (UInt32)(_consoleInfo. v_height * _consoleInfo. v_rowBytes );
110- addBridgeMemoryRange (_consoleInfo. v_baseAddr , fbSize , true );
119+ HVDBGLOG ( " Adding framebuffer memory 0x%X length 0x%X to PCI bridge " , _fbInitialBase, _fbInitialLength );
120+ addBridgeMemoryRange (_fbInitialBase, _fbInitialLength , true );
111121 return super::configure (provider);
112122}
113123
114124UInt32 HyperVGraphicsBridge::configRead32 (IOPCIAddressSpace space, UInt8 offset) {
115- HVDBGLOG (" Bus: %u, device: %u, function: %u, offset %X" , space.es .busNum , space.es .deviceNum , space.es .functionNum , offset);
116-
117125 UInt32 data;
118126 IOInterruptState ints;
119-
127+
120128 if (space.es .deviceNum != 0 || space.es .functionNum != 0 ) {
121129 return 0xFFFFFFFF ;
122130 }
123-
131+
124132 ints = IOSimpleLockLockDisableInterrupt (_pciLock);
125133 data = OSReadLittleInt32 (_fakePCIDeviceSpace, offset);
126-
127- if (offset == kIOPCIConfigurationOffsetBaseAddress0 ) {
128- HVDBGLOG (" gonna read %X" , data);
129- }
130-
131134 IOSimpleLockUnlockEnableInterrupt (_pciLock, ints);
135+
136+ HVDBGLOG (" Read 32-bit value %u from offset 0x%X" , data, offset);
132137 return data;
133138}
134139
135140void HyperVGraphicsBridge::configWrite32 (IOPCIAddressSpace space, UInt8 offset, UInt32 data) {
136- HVDBGLOG (" Bus: %u, device: %u, function: %u, offset %X" , space.es .busNum , space.es .deviceNum , space.es .functionNum , offset);
137-
138141 IOInterruptState ints;
139142
140- if (space.es .deviceNum != 0 || space.es .functionNum != 0 || (offset > kIOPCIConfigurationOffsetBaseAddress0 && offset <= kIOPCIConfigurationOffsetBaseAddress5 ) || offset == kIOPCIConfigurationOffsetExpansionROMBase ) {
141- HVDBGLOG (" ignoring offset %X" , offset);
143+ if (space.es .deviceNum != 0 || space.es .functionNum != 0
144+ || (offset > kIOPCIConfigurationOffsetBaseAddress0 && offset <= kIOPCIConfigurationOffsetBaseAddress5 )
145+ || offset == kIOPCIConfigurationOffsetExpansionROMBase ) {
142146 return ;
143147 }
144-
145- if (offset == kIOPCIConfigurationOffsetBaseAddress0 ) {
146- HVDBGLOG ( " gonna write %X " , data);
147- }
148-
148+ HVDBGLOG ( " Writing 32-bit value %u to offset 0x%X " , data, offset);
149+
150+ //
151+ // Return BAR0 size if requested.
152+ //
149153 if (offset == kIOPCIConfigurationOffsetBaseAddress0 && data == 0xFFFFFFFF ) {
150- HVDBGLOG (" Got bar size request" );
151- UInt32 fbSize = (UInt32)(_consoleInfo.v_height * _consoleInfo.v_rowBytes );
152- OSWriteLittleInt32 (_fakePCIDeviceSpace, offset, (0xFFFFFFFF - fbSize) + 1 );
154+ OSWriteLittleInt32 (_fakePCIDeviceSpace, offset, (0xFFFFFFFF - _fbInitialLength) + 1 );
153155 return ;
154156 }
155-
157+
156158 ints = IOSimpleLockLockDisableInterrupt (_pciLock);
157159 OSWriteLittleInt32 (_fakePCIDeviceSpace, offset, data);
158-
159160 IOSimpleLockUnlockEnableInterrupt (_pciLock, ints);
160161}
161162
162163UInt16 HyperVGraphicsBridge::configRead16 (IOPCIAddressSpace space, UInt8 offset) {
163- HVDBGLOG (" Bus: %u, device: %u, function: %u, offset %X" , space.es .busNum , space.es .deviceNum , space.es .functionNum , offset);
164-
165164 UInt16 data;
166165 IOInterruptState ints;
167-
166+
168167 if (space.es .deviceNum != 0 || space.es .functionNum != 0 ) {
169168 return 0xFFFF ;
170169 }
171-
170+
172171 ints = IOSimpleLockLockDisableInterrupt (_pciLock);
173172 data = OSReadLittleInt16 (_fakePCIDeviceSpace, offset);
174-
175173 IOSimpleLockUnlockEnableInterrupt (_pciLock, ints);
174+
175+ HVDBGLOG (" Read 16-bit value %u from offset 0x%X" , data, offset);
176176 return data;
177177}
178178
179179void HyperVGraphicsBridge::configWrite16 (IOPCIAddressSpace space, UInt8 offset, UInt16 data) {
180- HVDBGLOG (" Bus: %u, device: %u, function: %u, offset %X" , space.es .busNum , space.es .deviceNum , space.es .functionNum , offset);
181-
182180 IOInterruptState ints;
183-
184- if (space.es .deviceNum != 0 || space.es .functionNum != 0 || (offset >= kIOPCIConfigurationOffsetBaseAddress0 && offset <= kIOPCIConfigurationOffsetBaseAddress5 ) || offset == kIOPCIConfigurationOffsetExpansionROMBase ) {
181+
182+ if (space.es .deviceNum != 0 || space.es .functionNum != 0
183+ || (offset >= kIOPCIConfigurationOffsetBaseAddress0 && offset <= kIOPCIConfigurationOffsetBaseAddress5 )
184+ || offset == kIOPCIConfigurationOffsetExpansionROMBase ) {
185185 return ;
186186 }
187-
187+ HVDBGLOG (" Writing 16-bit value %u to offset 0x%X" , data, offset);
188+
188189 ints = IOSimpleLockLockDisableInterrupt (_pciLock);
189190 OSWriteLittleInt16 (_fakePCIDeviceSpace, offset, data);
190-
191191 IOSimpleLockUnlockEnableInterrupt (_pciLock, ints);
192192}
193193
194194UInt8 HyperVGraphicsBridge::configRead8 (IOPCIAddressSpace space, UInt8 offset) {
195- HVDBGLOG (" Bus: %u, device: %u, function: %u, offset %X" , space.es .busNum , space.es .deviceNum , space.es .functionNum , offset);
196-
197195 UInt8 data;
198196 IOInterruptState ints;
199-
197+
200198 if (space.es .deviceNum != 0 || space.es .functionNum != 0 ) {
201199 return 0xFF ;
202200 }
203-
201+
204202 ints = IOSimpleLockLockDisableInterrupt (_pciLock);
205203 data = _fakePCIDeviceSpace[offset];
206-
207204 IOSimpleLockUnlockEnableInterrupt (_pciLock, ints);
205+
206+ HVDBGLOG (" Read 8-bit value %u from offset 0x%X" , data, offset);
208207 return data;
209208}
210209
211210void HyperVGraphicsBridge::configWrite8 (IOPCIAddressSpace space, UInt8 offset, UInt8 data) {
212- HVDBGLOG (" Bus: %u, device: %u, function: %u, offset %X" , space.es .busNum , space.es .deviceNum , space.es .functionNum , offset);
213-
214211 IOInterruptState ints;
215-
216- if (space.es .deviceNum != 0 || space.es .functionNum != 0 || (offset >= kIOPCIConfigurationOffsetBaseAddress0 && offset <= kIOPCIConfigurationOffsetBaseAddress5 ) || offset == kIOPCIConfigurationOffsetExpansionROMBase ) {
212+
213+ if (space.es .deviceNum != 0 || space.es .functionNum != 0
214+ || (offset >= kIOPCIConfigurationOffsetBaseAddress0 && offset <= kIOPCIConfigurationOffsetBaseAddress5 )
215+ || offset == kIOPCIConfigurationOffsetExpansionROMBase ) {
217216 return ;
218217 }
219-
218+ HVDBGLOG (" Writing 8-bit value %u to offset 0x%X" , data, offset);
219+
220220 ints = IOSimpleLockLockDisableInterrupt (_pciLock);
221221 _fakePCIDeviceSpace[offset] = data;
222-
223222 IOSimpleLockUnlockEnableInterrupt (_pciLock, ints);
224223}
0 commit comments