1 /*
2  * PROJECT:     ReactOS Bochs graphics card driver
3  * LICENSE:     GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
4  * PURPOSE:     Bochs graphics card driver
5  * COPYRIGHT:   Copyright 2022 Hervé Poussineau <hpoussin@reactos.org>
6  */
7 
8 #include "bochsmp.h"
9 
10 static const BOCHS_SIZE BochsAvailableResolutions[] = {
11     { 640, 480 },   // VGA
12     { 800, 600 },   // SVGA
13     { 1024, 600 },  // WSVGA
14     { 1024, 768 },  // XGA
15     { 1152, 864 },  // XGA+
16     { 1280, 720 },  // WXGA-H
17     { 1280, 768 },  // WXGA
18     { 1280, 960 },  // SXGA-
19     { 1280, 1024 }, // SXGA
20     { 1368, 768 },  // HD ready
21     { 1400, 1050 }, // SXGA+
22     { 1440, 900 },  // WSXGA
23     { 1600, 900 },  // HD+
24     { 1600, 1200 }, // UXGA
25     { 1680, 1050 }, // WSXGA+
26     { 1920, 1080 }, // FHD
27     { 2048, 1536 }, // QXGA
28     { 2560, 1440 }, // WQHD
29     { 2560, 1600 }, // WQXGA
30     { 2560, 2048 }, // QSXGA
31     { 2800, 2100 }, // QSXGA+
32     { 3200, 2400 }, // QUXGA
33     { 3840, 2160 }, // 4K UHD-1
34 };
35 
36 CODE_SEG("PAGE")
37 static VOID
38 BochsFreeResources(
39     _Inout_ PBOCHS_DEVICE_EXTENSION DeviceExtension)
40 {
41     if (DeviceExtension->AvailableModeInfo)
42     {
43         VideoPortFreePool(DeviceExtension, DeviceExtension->AvailableModeInfo);
44         DeviceExtension->AvailableModeInfo = NULL;
45     }
46 }
47 
48 CODE_SEG("PAGE")
49 static VOID
50 BochsWriteDispI(
51     _In_ PBOCHS_DEVICE_EXTENSION DeviceExtension,
52     _In_ ULONG Index,
53     _In_ USHORT Value)
54 {
55     if (DeviceExtension->IoPorts.RangeInIoSpace)
56     {
57         VideoPortWritePortUshort((PUSHORT)(DeviceExtension->IoPorts.Mapped - VBE_DISPI_IOPORT_INDEX + VBE_DISPI_IOPORT_INDEX), Index);
58         VideoPortWritePortUshort((PUSHORT)(DeviceExtension->IoPorts.Mapped - VBE_DISPI_IOPORT_INDEX + VBE_DISPI_IOPORT_DATA), Value);
59     }
60     else
61     {
62         VideoPortWriteRegisterUshort((PUSHORT)(DeviceExtension->IoPorts.Mapped + 0x500 + Index * 2), Value);
63     }
64 }
65 
66 CODE_SEG("PAGE")
67 static USHORT
68 BochsReadDispI(
69     _In_ PBOCHS_DEVICE_EXTENSION DeviceExtension,
70     _In_ ULONG Index)
71 {
72     if (DeviceExtension->IoPorts.RangeInIoSpace)
73     {
74         VideoPortWritePortUshort((PUSHORT)(DeviceExtension->IoPorts.Mapped - VBE_DISPI_IOPORT_INDEX + VBE_DISPI_IOPORT_INDEX), Index);
75         return VideoPortReadPortUshort((PUSHORT)(DeviceExtension->IoPorts.Mapped - VBE_DISPI_IOPORT_INDEX + VBE_DISPI_IOPORT_DATA));
76     }
77     else
78     {
79         return VideoPortReadRegisterUshort((PUSHORT)(DeviceExtension->IoPorts.Mapped + 0x500 + Index * 2));
80     }
81 }
82 
83 CODE_SEG("PAGE")
84 static BOOLEAN
85 BochsWriteDispIAndCheck(
86     _In_ PBOCHS_DEVICE_EXTENSION DeviceExtension,
87     _In_ ULONG Index,
88     _In_ USHORT Value)
89 {
90     BochsWriteDispI(DeviceExtension, Index, Value);
91     return BochsReadDispI(DeviceExtension, Index) == Value;
92 }
93 
94 CODE_SEG("PAGE")
95 static BOOLEAN
96 BochsInitializeSuitableModeInfo(
97     _In_ PBOCHS_DEVICE_EXTENSION DeviceExtension,
98     _In_ ULONG PotentialModeCount)
99 {
100     ULONG i, ModeCount = 0;
101 
102     for (i = 0; i < ARRAYSIZE(BochsAvailableResolutions) && ModeCount < PotentialModeCount; i++)
103     {
104         if (BochsAvailableResolutions[i].XResolution > DeviceExtension->MaxXResolution)
105             continue;
106         if (BochsAvailableResolutions[i].YResolution > DeviceExtension->MaxYResolution)
107             continue;
108         if (BochsAvailableResolutions[i].XResolution * BochsAvailableResolutions[i].YResolution * 4 > DeviceExtension->VramSize64K * 64 * 1024)
109             continue;
110         DeviceExtension->AvailableModeInfo[ModeCount++] = BochsAvailableResolutions[i];
111     }
112 
113     if (ModeCount == 0)
114     {
115         VideoDebugPrint((Error, "Bochs: no suitable modes available!\n"));
116         return FALSE;
117     }
118 
119     DeviceExtension->AvailableModeCount = ModeCount;
120     return TRUE;
121 }
122 
123 CODE_SEG("PAGE")
124 static BOOLEAN
125 BochsGetControllerInfo(
126     _Inout_ PBOCHS_DEVICE_EXTENSION DeviceExtension)
127 {
128     USHORT Version;
129     WCHAR ChipType[5];
130     ULONG SizeInBytes;
131 
132     /* Detect DISPI version */
133     for (Version = VBE_DISPI_ID5; Version >= VBE_DISPI_ID0; Version--)
134     {
135         if (BochsWriteDispIAndCheck(DeviceExtension, VBE_DISPI_INDEX_ID, Version))
136             break;
137     }
138     if (Version < VBE_DISPI_ID0)
139     {
140         VideoDebugPrint((Error, "Bochs: VBE extension signature incorrect\n"));
141         return FALSE;
142     }
143     VideoDebugPrint((Error, "Bochs: detected version 0x%04x\n", Version));
144     if (Version < VBE_DISPI_ID2)
145     {
146         /* Too old (no 32 bpp support, no linear frame buffer) */
147         VideoDebugPrint((Error, "Bochs: VBE extension too old (0x%04x)\n", Version));
148         return FALSE;
149     }
150 
151     if (Version <= VBE_DISPI_ID2)
152     {
153         DeviceExtension->MaxXResolution = 1024;
154         DeviceExtension->MaxYResolution = 768;
155     }
156     else
157     {
158         BochsWriteDispI(DeviceExtension, VBE_DISPI_INDEX_ENABLE, VBE_DISPI_GETCAPS);
159         DeviceExtension->MaxXResolution = BochsReadDispI(DeviceExtension, VBE_DISPI_INDEX_XRES);
160         DeviceExtension->MaxYResolution = BochsReadDispI(DeviceExtension, VBE_DISPI_INDEX_YRES);
161         BochsWriteDispI(DeviceExtension, VBE_DISPI_INDEX_ENABLE, VBE_DISPI_DISABLED);
162         /* Workaround bug in QEMU bochs-display */
163         if (DeviceExtension->MaxXResolution == 0 && DeviceExtension->MaxYResolution == 0)
164         {
165             DeviceExtension->MaxXResolution = 1024;
166             DeviceExtension->MaxYResolution = 768;
167         }
168     }
169     if (Version < VBE_DISPI_ID4)
170     {
171         DeviceExtension->VramSize64K = 4 * 1024 / 64; /* 4 MB */
172     }
173     else if (Version == VBE_DISPI_ID4)
174     {
175         DeviceExtension->VramSize64K = 8 * 1024 / 64; /* 8 MB */
176     }
177     else
178     {
179         DeviceExtension->VramSize64K = BochsReadDispI(DeviceExtension, VBE_DISPI_INDEX_VIDEO_MEMORY_64K);
180     }
181     VideoDebugPrint((Info, "Bochs: capabilities %dx%d (%d MB)\n",
182                      DeviceExtension->MaxXResolution,
183                      DeviceExtension->MaxYResolution,
184                      DeviceExtension->VramSize64K * 64 / 1024));
185 
186     /* Store information in registry */
187 #define HEX(c) (((c) >= 0 && (c) <= 9) ? (c) + L'0' : (c) - 10 + L'A')
188     ChipType[0] = HEX((Version >> 12) & 0xf);
189     ChipType[1] = HEX((Version >> 8) & 0xf);
190     ChipType[2] = HEX((Version >> 4) & 0xf);
191     ChipType[3] = HEX(Version & 0xf);
192     ChipType[4] = UNICODE_NULL;
193     VideoPortSetRegistryParameters(DeviceExtension, L"HardwareInformation.ChipType", ChipType, sizeof(ChipType));
194     SizeInBytes = DeviceExtension->VramSize64K * 64 * 1024;
195     VideoPortSetRegistryParameters(DeviceExtension, L"HardwareInformation.MemorySize", &SizeInBytes, sizeof(SizeInBytes));
196     return TRUE;
197 }
198 
199 CODE_SEG("PAGE")
200 static VOID
201 BochsGetModeInfo(
202     _In_ PBOCHS_SIZE AvailableModeInfo,
203     _Out_ PVIDEO_MODE_INFORMATION ModeInfo,
204     _In_ ULONG Index)
205 {
206     VideoDebugPrint((Info, "Bochs: Filling details of mode #%d\n", Index));
207 
208     ModeInfo->Length = sizeof(*ModeInfo);
209     ModeInfo->ModeIndex = Index;
210     ModeInfo->VisScreenWidth = AvailableModeInfo->XResolution;
211     ModeInfo->VisScreenHeight = AvailableModeInfo->YResolution;
212     ModeInfo->ScreenStride = AvailableModeInfo->XResolution * 4;
213     ModeInfo->NumberOfPlanes = 1;
214     ModeInfo->BitsPerPlane = 32;
215     ModeInfo->Frequency = 60;
216 
217     /* 960 DPI appears to be common */
218     ModeInfo->XMillimeter = AvailableModeInfo->XResolution * 254 / 960;
219     ModeInfo->YMillimeter = AvailableModeInfo->YResolution * 254 / 960;
220     ModeInfo->NumberRedBits = 8;
221     ModeInfo->NumberGreenBits = 8;
222     ModeInfo->NumberBlueBits = 8;
223     ModeInfo->RedMask = 0xff0000;
224     ModeInfo->GreenMask = 0x00ff00;
225     ModeInfo->BlueMask = 0x0000ff;
226 
227     ModeInfo->AttributeFlags = VIDEO_MODE_GRAPHICS | VIDEO_MODE_COLOR | VIDEO_MODE_NO_OFF_SCREEN;
228     ModeInfo->VideoMemoryBitmapWidth = AvailableModeInfo->XResolution;
229     ModeInfo->VideoMemoryBitmapHeight = AvailableModeInfo->YResolution;
230 }
231 
232 CODE_SEG("PAGE")
233 static BOOLEAN
234 BochsMapVideoMemory(
235     _In_ PBOCHS_DEVICE_EXTENSION DeviceExtension,
236     _In_ PVIDEO_MEMORY RequestedAddress,
237     _Out_ PVIDEO_MEMORY_INFORMATION MapInformation,
238     _Out_ PSTATUS_BLOCK StatusBlock)
239 {
240     VP_STATUS Status;
241     PHYSICAL_ADDRESS VideoMemory;
242     ULONG MemSpace = VIDEO_MEMORY_SPACE_MEMORY;
243 
244     VideoDebugPrint((Info, "Bochs: BochsMapVideoMemory Entry\n"));
245 
246     VideoMemory = DeviceExtension->FrameBuffer.RangeStart;
247     MapInformation->VideoRamBase = RequestedAddress->RequestedVirtualAddress;
248     MapInformation->VideoRamLength = 4 *
249         DeviceExtension->AvailableModeInfo[DeviceExtension->CurrentMode].XResolution *
250         DeviceExtension->AvailableModeInfo[DeviceExtension->CurrentMode].YResolution;
251 
252     Status = VideoPortMapMemory(DeviceExtension,
253                                 VideoMemory,
254                                 &MapInformation->VideoRamLength,
255                                 &MemSpace,
256                                 &MapInformation->VideoRamBase);
257     if (Status != NO_ERROR)
258     {
259         VideoDebugPrint((Error, "BochsMapVideoMemory - VideoPortMapMemory failed status:%x\n", Status));
260         StatusBlock->Status = Status;
261         return FALSE;
262     }
263 
264     MapInformation->FrameBufferBase = MapInformation->VideoRamBase;
265     MapInformation->FrameBufferLength = MapInformation->VideoRamLength;
266     StatusBlock->Information = sizeof(*MapInformation);
267     StatusBlock->Status = NO_ERROR;
268 
269     VideoDebugPrint((Info, "Bochs:BochsMapVideoMemory Exit VideoRamBase: %p VideoRamLength: 0x%x PhysBasePtr: 0x%x\n",
270                      MapInformation->VideoRamBase, MapInformation->VideoRamLength, (ULONG)VideoMemory.QuadPart));
271     return TRUE;
272 }
273 
274 CODE_SEG("PAGE")
275 static BOOLEAN NTAPI
276 BochsUnmapVideoMemory(
277     _In_ PBOCHS_DEVICE_EXTENSION DeviceExtension,
278     _In_ PVIDEO_MEMORY VideoMemory,
279     _Out_ PSTATUS_BLOCK StatusBlock)
280 {
281     VP_STATUS Status;
282 
283     VideoDebugPrint((Info, "Bochs: BochsUnmapVideoMemory Entry VideoRamBase:%p\n", VideoMemory->RequestedVirtualAddress));
284 
285     Status = VideoPortUnmapMemory(DeviceExtension, VideoMemory->RequestedVirtualAddress, NULL);
286     if (Status != NO_ERROR)
287     {
288         VideoDebugPrint((Error, "Bochs: BochsUnmapVideoMemory Failed to unmap memory:%p Status:%x\n",
289                          VideoMemory->RequestedVirtualAddress,
290                          Status));
291     }
292 
293     StatusBlock->Status = Status;
294 
295     VideoDebugPrint((Info, "Bochs: BochsUnmapVideoMemory Exit status:%x\n", Status));
296     return (Status == NO_ERROR);
297 }
298 
299 CODE_SEG("PAGE")
300 static BOOLEAN
301 BochsQueryNumAvailableModes(
302     _In_ PBOCHS_DEVICE_EXTENSION DeviceExtension,
303     _Out_ PVIDEO_NUM_MODES AvailableModes,
304     _Out_ PSTATUS_BLOCK StatusBlock)
305 {
306     AvailableModes->NumModes = DeviceExtension->AvailableModeCount;
307     AvailableModes->ModeInformationLength = sizeof(VIDEO_MODE_INFORMATION);
308 
309     StatusBlock->Information = sizeof(*AvailableModes);
310     StatusBlock->Status = NO_ERROR;
311     return TRUE;
312 }
313 
314 CODE_SEG("PAGE")
315 static BOOLEAN
316 BochsQueryAvailableModes(
317     _In_ PBOCHS_DEVICE_EXTENSION DeviceExtension,
318     _Out_ PVIDEO_MODE_INFORMATION ReturnedModes,
319     _Out_ PSTATUS_BLOCK StatusBlock)
320 {
321     ULONG Count;
322     PBOCHS_SIZE AvailableModeInfo;
323     PVIDEO_MODE_INFORMATION ModeInfo;
324 
325     for (Count = 0, AvailableModeInfo = DeviceExtension->AvailableModeInfo, ModeInfo = ReturnedModes;
326         Count < DeviceExtension->AvailableModeCount;
327         Count++, AvailableModeInfo++, ModeInfo++)
328     {
329         VideoPortZeroMemory(ModeInfo, sizeof(*ModeInfo));
330         BochsGetModeInfo(AvailableModeInfo, ModeInfo, Count);
331     }
332 
333     StatusBlock->Information = sizeof(VIDEO_MODE_INFORMATION) * DeviceExtension->AvailableModeCount;
334     StatusBlock->Status = NO_ERROR;
335 
336     return TRUE;
337 }
338 
339 CODE_SEG("PAGE")
340 static BOOLEAN
341 BochsSetCurrentMode(
342     _In_ PBOCHS_DEVICE_EXTENSION DeviceExtension,
343     _In_ PVIDEO_MODE RequestedMode,
344     _Out_ PSTATUS_BLOCK StatusBlock)
345 {
346     PBOCHS_SIZE AvailableModeInfo;
347     /* Mask the two high-order bits, which can be set to request special behavior */
348     ULONG ModeRequested = RequestedMode->RequestedMode & 0x3fffffff;
349     BOOLEAN Ret;
350 
351     VideoDebugPrint((Info, "Bochs:BochsSetCurrentMode Entry\n"));
352 
353     if (ModeRequested >= DeviceExtension->AvailableModeCount)
354     {
355         VideoDebugPrint((Error, "Bochs: set current mode - invalid parameter\n"));
356         StatusBlock->Status = ERROR_INVALID_PARAMETER;
357         return FALSE;
358     }
359 
360     AvailableModeInfo = &DeviceExtension->AvailableModeInfo[ModeRequested];
361 
362     /* Set the mode characteristics */
363     BochsWriteDispI(DeviceExtension, VBE_DISPI_INDEX_ENABLE, VBE_DISPI_DISABLED);
364     Ret = BochsWriteDispIAndCheck(DeviceExtension, VBE_DISPI_INDEX_XRES, AvailableModeInfo->XResolution) &&
365           BochsWriteDispIAndCheck(DeviceExtension, VBE_DISPI_INDEX_YRES, AvailableModeInfo->YResolution) &&
366           BochsWriteDispIAndCheck(DeviceExtension, VBE_DISPI_INDEX_BPP, 32);
367     /* Always enable screen, even if display settings change failed */
368     BochsWriteDispI(DeviceExtension, VBE_DISPI_INDEX_ENABLE, VBE_DISPI_LFB_ENABLED | VBE_DISPI_ENABLED);
369     if (!Ret)
370     {
371         VideoDebugPrint((Error, "Bochs: failed to change mode\n"));
372         return FALSE;
373     }
374 
375     /* Enable VGA (QEMU secondary-vga disables it by default) */
376     if (!DeviceExtension->IoPorts.RangeInIoSpace)
377     {
378         /* Discard AR flip-flip */
379         (VOID)VideoPortReadRegisterUshort((PUSHORT)(DeviceExtension->IoPorts.Mapped + 0x41A));
380         /* Enable display */
381         VideoPortWriteRegisterUshort((PUSHORT)(DeviceExtension->IoPorts.Mapped + 0x400), 0x20);
382     }
383 
384     DeviceExtension->CurrentMode = (USHORT)ModeRequested;
385     StatusBlock->Status = NO_ERROR;
386 
387     VideoDebugPrint((Info, "Bochs:BochsSetCurrentMode Exit Mode:%d\n", ModeRequested));
388     return TRUE;
389 }
390 
391 CODE_SEG("PAGE")
392 static BOOLEAN
393 BochsQueryCurrentMode(
394     _In_ PBOCHS_DEVICE_EXTENSION DeviceExtension,
395     _Out_ PVIDEO_MODE_INFORMATION VideoModeInfo,
396     _Out_ PSTATUS_BLOCK StatusBlock)
397 {
398     PBOCHS_SIZE AvailableModeInfo;
399 
400     if (DeviceExtension->CurrentMode > DeviceExtension->AvailableModeCount)
401     {
402         StatusBlock->Status = ERROR_INVALID_PARAMETER;
403         return FALSE;
404     }
405 
406     AvailableModeInfo = &DeviceExtension->AvailableModeInfo[DeviceExtension->CurrentMode];
407     VideoPortZeroMemory(VideoModeInfo, sizeof(*VideoModeInfo));
408     BochsGetModeInfo(AvailableModeInfo, VideoModeInfo, DeviceExtension->CurrentMode);
409 
410     StatusBlock->Information = sizeof(*VideoModeInfo);
411     StatusBlock->Status = NO_ERROR;
412     return TRUE;
413 }
414 
415 CODE_SEG("PAGE")
416 static BOOLEAN
417 BochsResetDevice(
418     _In_ PBOCHS_DEVICE_EXTENSION DeviceExtension,
419     _Out_ PSTATUS_BLOCK StatusBlock)
420 {
421     VideoDebugPrint((Info, "Bochs:BochsResetDevice Entry\n"));
422 
423     StatusBlock->Status = NO_ERROR;
424 
425     VideoDebugPrint((Info, "Bochs:BochsResetDevice Exit\n"));
426     return TRUE;
427 }
428 
429 CODE_SEG("PAGE")
430 static BOOLEAN
431 BochsGetChildState(
432     _In_ PBOCHS_DEVICE_EXTENSION DeviceExtension,
433     _Out_ PULONG pChildState,
434     _Out_ PSTATUS_BLOCK StatusBlock)
435 {
436     *pChildState = VIDEO_CHILD_ACTIVE;
437 
438     StatusBlock->Information = sizeof(*pChildState);
439     StatusBlock->Status = NO_ERROR;
440     return TRUE;
441 }
442 
443 CODE_SEG("PAGE")
444 VP_STATUS NTAPI
445 BochsFindAdapter(
446     _In_ PVOID HwDeviceExtension,
447     _In_ PVOID HwContext,
448     _In_ PWSTR ArgumentString,
449     _In_ PVIDEO_PORT_CONFIG_INFO ConfigInfo,
450     _In_ PUCHAR Again)
451 {
452     PBOCHS_DEVICE_EXTENSION DeviceExtension = HwDeviceExtension;
453     VIDEO_ACCESS_RANGE AccessRanges[2] = {0};
454 
455     if (ConfigInfo->Length < sizeof(*ConfigInfo))
456         return ERROR_INVALID_PARAMETER;
457 
458     if (VideoPortGetAccessRanges(DeviceExtension, 0, NULL, ARRAYSIZE(AccessRanges), AccessRanges, NULL, NULL, NULL) != NO_ERROR)
459     {
460         VideoDebugPrint((Error, "Bochs: failed to get access ranges\n"));
461         return ERROR_DEV_NOT_EXIST;
462     }
463 
464     /* Framebuffer */
465     DeviceExtension->FrameBuffer.RangeStart = AccessRanges[0].RangeStart;
466     DeviceExtension->FrameBuffer.RangeLength = AccessRanges[0].RangeLength;
467     DeviceExtension->FrameBuffer.RangeInIoSpace = AccessRanges[0].RangeInIoSpace;
468 
469     /* I/O ports */
470     if (AccessRanges[1].RangeLength == 0)
471     {
472         /* Set default values */
473         AccessRanges[1].RangeStart.LowPart = VBE_DISPI_IOPORT_INDEX;
474         AccessRanges[1].RangeLength = 2;
475         AccessRanges[1].RangeInIoSpace = TRUE;
476         if (VideoPortVerifyAccessRanges(DeviceExtension, 1, &AccessRanges[1]) != NO_ERROR)
477         {
478             VideoDebugPrint((Error, "Bochs: failed to claim I/O range 0x%x-0x%x\n",
479                             VBE_DISPI_IOPORT_INDEX,
480                             VBE_DISPI_IOPORT_INDEX + 1));
481             return ERROR_DEV_NOT_EXIST;
482         }
483     }
484     else if (AccessRanges[1].RangeLength != 0x1000)
485     {
486         VideoDebugPrint((Error, "Bochs: invalid access ranges (size 0x%x)\n", AccessRanges[1].RangeLength));
487         return ERROR_DEV_NOT_EXIST;
488     }
489     DeviceExtension->IoPorts.RangeStart = AccessRanges[1].RangeStart;
490     DeviceExtension->IoPorts.RangeLength = AccessRanges[1].RangeLength;
491     DeviceExtension->IoPorts.RangeInIoSpace = AccessRanges[1].RangeInIoSpace;
492 
493     DeviceExtension->IoPorts.Mapped = VideoPortGetDeviceBase(DeviceExtension,
494                                                              DeviceExtension->IoPorts.RangeStart,
495                                                              DeviceExtension->IoPorts.RangeLength,
496                                                              DeviceExtension->IoPorts.RangeInIoSpace
497                                                                  ? VIDEO_MEMORY_SPACE_IO
498                                                                  : VIDEO_MEMORY_SPACE_MEMORY);
499     if (!DeviceExtension->IoPorts.Mapped)
500     {
501         VideoDebugPrint((Error, "Bochs: failed to map dispi interface\n"));
502         return ERROR_DEV_NOT_EXIST;
503     }
504     VideoDebugPrint((Info, "Bochs: address 0x%x mapped to 0x%p\n",
505                      DeviceExtension->IoPorts.RangeStart.LowPart,
506                      DeviceExtension->IoPorts.Mapped));
507 
508     return NO_ERROR;
509 }
510 
511 CODE_SEG("PAGE")
512 BOOLEAN NTAPI
513 BochsInitialize(
514     _In_ PVOID HwDeviceExtension)
515 {
516     ULONG PotentialModeCount = 0;
517     PBOCHS_DEVICE_EXTENSION DeviceExtension = HwDeviceExtension;
518 
519     VideoDebugPrint((Info, "Bochs: BochsInitialize\n"));
520 
521     if (!BochsGetControllerInfo(DeviceExtension))
522     {
523         BochsFreeResources(DeviceExtension);
524         return FALSE;
525     }
526 
527     PotentialModeCount = ARRAYSIZE(BochsAvailableResolutions);
528     DeviceExtension->AvailableModeInfo = VideoPortAllocatePool(HwDeviceExtension,
529                                                                VpPagedPool,
530                                                                PotentialModeCount * sizeof(BOCHS_SIZE),
531                                                                BOCHS_TAG);
532     if (!DeviceExtension->AvailableModeInfo)
533     {
534         VideoDebugPrint((Error, "Bochs: insufficient resources\n"));
535         BochsFreeResources(DeviceExtension);
536         return FALSE;
537     }
538 
539     if (!BochsInitializeSuitableModeInfo(DeviceExtension, PotentialModeCount))
540     {
541         BochsFreeResources(DeviceExtension);
542         return FALSE;
543     }
544 
545     return TRUE;
546 }
547 
548 CODE_SEG("PAGE")
549 BOOLEAN NTAPI
550 BochsStartIO(
551     _In_ PVOID HwDeviceExtension,
552     _Inout_ PVIDEO_REQUEST_PACKET RequestPacket)
553 {
554     PBOCHS_DEVICE_EXTENSION DeviceExtension = HwDeviceExtension;
555 
556     VideoDebugPrint((Info, "Bochs: BochsStartIO\n"));
557     RequestPacket->StatusBlock->Status = ERROR_INVALID_FUNCTION;
558 
559     switch (RequestPacket->IoControlCode)
560     {
561         case IOCTL_VIDEO_MAP_VIDEO_MEMORY:
562         {
563             VideoDebugPrint((Info, "BochsStartIO - Map video memory\n"));
564             if (RequestPacket->InputBufferLength < sizeof(VIDEO_MEMORY))
565             {
566                 VideoDebugPrint((Error, "BochsStartIO - invalid input parameter\n"));
567                 RequestPacket->StatusBlock->Status = ERROR_INSUFFICIENT_BUFFER;
568                 return FALSE;
569             }
570             if (RequestPacket->OutputBufferLength < sizeof(VIDEO_MEMORY_INFORMATION))
571             {
572                 VideoDebugPrint((Error, "BochsStartIO - Insufficent output buffer\n"));
573                 RequestPacket->StatusBlock->Status = ERROR_INSUFFICIENT_BUFFER;
574                 return FALSE;
575             }
576             return BochsMapVideoMemory(DeviceExtension,
577                                        (PVIDEO_MEMORY)RequestPacket->InputBuffer,
578                                        (PVIDEO_MEMORY_INFORMATION)RequestPacket->OutputBuffer,
579                                        RequestPacket->StatusBlock);
580         }
581 
582         case IOCTL_VIDEO_UNMAP_VIDEO_MEMORY:
583         {
584             VideoDebugPrint((Info, "BochsStartIO - Unmap video memory\n"));
585             if (RequestPacket->InputBufferLength < sizeof(VIDEO_MEMORY))
586             {
587                 RequestPacket->StatusBlock->Status = ERROR_INSUFFICIENT_BUFFER;
588                 return FALSE;
589             }
590             return BochsUnmapVideoMemory(DeviceExtension,
591                                          (PVIDEO_MEMORY)RequestPacket->InputBuffer,
592                                          RequestPacket->StatusBlock);
593         }
594 
595         case IOCTL_VIDEO_QUERY_NUM_AVAIL_MODES:
596         {
597             VideoDebugPrint((Info, "BochsStartIO - Query num available modes\n"));
598             if (RequestPacket->OutputBufferLength < sizeof(VIDEO_NUM_MODES))
599             {
600                 RequestPacket->StatusBlock->Status = ERROR_INSUFFICIENT_BUFFER;
601                 return FALSE;
602             }
603             return BochsQueryNumAvailableModes(DeviceExtension,
604                                                (PVIDEO_NUM_MODES)RequestPacket->OutputBuffer,
605                                                RequestPacket->StatusBlock);
606         }
607 
608         case IOCTL_VIDEO_QUERY_AVAIL_MODES:
609         {
610             VideoDebugPrint((Info, "BochsStartIO - Query available modes\n"));
611             if (RequestPacket->OutputBufferLength < DeviceExtension->AvailableModeCount * sizeof(VIDEO_MODE_INFORMATION))
612             {
613                 RequestPacket->StatusBlock->Status = ERROR_INSUFFICIENT_BUFFER;
614                 return FALSE;
615             }
616             return BochsQueryAvailableModes(DeviceExtension,
617                                             (PVIDEO_MODE_INFORMATION)RequestPacket->OutputBuffer,
618                                             RequestPacket->StatusBlock);
619         }
620 
621         case IOCTL_VIDEO_SET_CURRENT_MODE:
622         {
623             VideoDebugPrint((Info, "BochsStartIO - Set current mode\n"));
624             if (RequestPacket->InputBufferLength < sizeof(VIDEO_MODE))
625             {
626                 VideoDebugPrint((Error, "Bochs: set current mode - invalid parameter\n"));
627                 RequestPacket->StatusBlock->Status = ERROR_INSUFFICIENT_BUFFER;
628                 return FALSE;
629             }
630             return BochsSetCurrentMode(DeviceExtension,
631                                        (PVIDEO_MODE)RequestPacket->InputBuffer,
632                                        RequestPacket->StatusBlock);
633         }
634 
635         case IOCTL_VIDEO_QUERY_CURRENT_MODE:
636         {
637             VideoDebugPrint((Info, "BochsStartIO - Query current mode\n"));
638             if (RequestPacket->OutputBufferLength < sizeof(VIDEO_MODE_INFORMATION))
639             {
640                 RequestPacket->StatusBlock->Status = ERROR_INSUFFICIENT_BUFFER;
641                 return FALSE;
642             }
643             return BochsQueryCurrentMode(DeviceExtension,
644                                          (PVIDEO_MODE_INFORMATION)RequestPacket->OutputBuffer,
645                                          RequestPacket->StatusBlock);
646         }
647 
648         case IOCTL_VIDEO_RESET_DEVICE:
649         {
650             VideoDebugPrint((Info, "BochsStartIO - Reset device\n"));
651             return BochsResetDevice(DeviceExtension,
652                                     RequestPacket->StatusBlock);
653         }
654 
655         case IOCTL_VIDEO_GET_CHILD_STATE:
656         {
657             VideoDebugPrint((Info, "BochsStartIO - Get child state\n"));
658             if (RequestPacket->OutputBufferLength < sizeof(ULONG))
659             {
660                 RequestPacket->StatusBlock->Status = ERROR_INSUFFICIENT_BUFFER;
661                 return FALSE;
662             }
663             return BochsGetChildState(DeviceExtension,
664                                       (PULONG)RequestPacket->OutputBuffer,
665                                       RequestPacket->StatusBlock);
666         }
667 
668         default:
669         {
670             VideoDebugPrint((Warn, "BochsStartIO - Unknown IOCTL - 0x%08x\n",
671                              RequestPacket->IoControlCode));
672             break;
673         }
674     }
675 
676     return FALSE;
677 }
678 
679 CODE_SEG("PAGE")
680 VP_STATUS NTAPI
681 BochsSetPowerState(
682     _In_ PVOID HwDeviceExtension,
683     _In_ ULONG HwId,
684     _In_ PVIDEO_POWER_MANAGEMENT VideoPowerControl)
685 {
686     return NO_ERROR;
687 }
688 
689 CODE_SEG("PAGE")
690 VP_STATUS NTAPI
691 BochsGetPowerState(
692     _In_ PVOID HwDeviceExtension,
693     _In_ ULONG HwId,
694     _Out_ PVIDEO_POWER_MANAGEMENT VideoPowerControl)
695 {
696     return ERROR_DEVICE_REINITIALIZATION_NEEDED;
697 }
698 
699 CODE_SEG("PAGE")
700 VP_STATUS NTAPI
701 BochsGetVideoChildDescriptor(
702     _In_ PVOID HwDeviceExtension,
703     _In_ PVIDEO_CHILD_ENUM_INFO ChildEnumInfo,
704     _Out_ PVIDEO_CHILD_TYPE VideoChildType,
705     _Out_ PUCHAR pChildDescriptor,
706     _Out_ PULONG UId,
707     _Out_ PULONG pUnused)
708 {
709     PBOCHS_DEVICE_EXTENSION DeviceExtension = HwDeviceExtension;
710 
711     VideoDebugPrint((Info, "Bochs: BochsGetVideoChildDescriptor Entry\n"));
712 
713     if (ChildEnumInfo->Size < sizeof(*VideoChildType))
714         return VIDEO_ENUM_NO_MORE_DEVICES;
715 
716     if (ChildEnumInfo->ChildIndex == 0)
717     {
718         /* Ignore ACPI enumerations */
719         return VIDEO_ENUM_INVALID_DEVICE;
720     }
721 
722     *pUnused = 0;
723     if (ChildEnumInfo->ChildIndex == DISPLAY_ADAPTER_HW_ID)
724     {
725         *VideoChildType = VideoChip;
726         return VIDEO_ENUM_MORE_DEVICES;
727     }
728 
729     if (ChildEnumInfo->ChildIndex != 1)
730         return VIDEO_ENUM_NO_MORE_DEVICES;
731 
732     *UId = 0;
733     *VideoChildType = Monitor;
734 
735     if (pChildDescriptor &&
736         ChildEnumInfo->ChildDescriptorSize >= VBE_EDID_SIZE &&
737         !DeviceExtension->IoPorts.RangeInIoSpace)
738     {
739         memcpy(pChildDescriptor,
740                DeviceExtension->IoPorts.Mapped,
741                VBE_EDID_SIZE);
742     }
743 
744     VideoDebugPrint((Info, "Bochs: BochsGetVideoChildDescriptor Exit Uid:%d\n", ChildEnumInfo->ChildIndex));
745 
746     return VIDEO_ENUM_MORE_DEVICES;
747 }
748 
749 ULONG NTAPI
750 DriverEntry(PVOID Context1, PVOID Context2)
751 {
752     VIDEO_HW_INITIALIZATION_DATA VideoInitData;
753 
754     VideoDebugPrint((Info, "Bochs: DriverEntry\n"));
755     VideoPortZeroMemory(&VideoInitData, sizeof(VideoInitData));
756     VideoInitData.HwInitDataSize = sizeof(VideoInitData);
757     VideoInitData.HwFindAdapter = BochsFindAdapter;
758     VideoInitData.HwInitialize = BochsInitialize;
759     VideoInitData.HwStartIO = BochsStartIO;
760     VideoInitData.HwDeviceExtensionSize = sizeof(BOCHS_DEVICE_EXTENSION);
761     VideoInitData.HwSetPowerState = BochsSetPowerState;
762     VideoInitData.HwGetPowerState = BochsGetPowerState;
763     VideoInitData.HwGetVideoChildDescriptor = BochsGetVideoChildDescriptor;
764 
765     return VideoPortInitialize(Context1, Context2, &VideoInitData, NULL);
766 }
767