[FREELDR][XBOXVMP] Check only low 28 bits for framebuffer address (#2249)
[reactos.git] / win32ss / drivers / miniport / xboxvmp / xboxvmp.c
1 /*
2 * PROJECT: ReactOS Xbox miniport video driver
3 * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
4 * PURPOSE: Simple framebuffer driver for NVIDIA NV2A XGPU
5 * COPYRIGHT: Copyright 2004 Gé van Geldorp
6 * Copyright 2004 Filip Navara
7 * Copyright 2019 Stanislav Motylkov (x86corez@gmail.com)
8 *
9 * TODO:
10 * - Check input parameters everywhere.
11 * - Call VideoPortVerifyAccessRanges to reserve the memory we're about
12 * to map.
13 */
14
15 /* INCLUDES *******************************************************************/
16
17 #include "xboxvmp.h"
18
19 #include <debug.h>
20 #include <dpfilter.h>
21
22 /* PUBLIC AND PRIVATE FUNCTIONS ***********************************************/
23
24 ULONG
25 NTAPI
26 DriverEntry(
27 IN PVOID Context1,
28 IN PVOID Context2)
29 {
30 VIDEO_HW_INITIALIZATION_DATA InitData;
31
32 VideoPortZeroMemory(&InitData, sizeof(InitData));
33 InitData.AdapterInterfaceType = PCIBus;
34 InitData.HwInitDataSize = sizeof(VIDEO_HW_INITIALIZATION_DATA);
35 InitData.HwFindAdapter = XboxVmpFindAdapter;
36 InitData.HwInitialize = XboxVmpInitialize;
37 InitData.HwStartIO = XboxVmpStartIO;
38 InitData.HwResetHw = XboxVmpResetHw;
39 InitData.HwGetPowerState = XboxVmpGetPowerState;
40 InitData.HwSetPowerState = XboxVmpSetPowerState;
41 InitData.HwDeviceExtensionSize = sizeof(XBOXVMP_DEVICE_EXTENSION);
42
43 return VideoPortInitialize(Context1, Context2, &InitData, NULL);
44 }
45
46 /*
47 * XboxVmpFindAdapter
48 *
49 * Detects the Xbox Nvidia display adapter.
50 */
51
52 VP_STATUS
53 NTAPI
54 XboxVmpFindAdapter(
55 IN PVOID HwDeviceExtension,
56 IN PVOID HwContext,
57 IN PWSTR ArgumentString,
58 IN OUT PVIDEO_PORT_CONFIG_INFO ConfigInfo,
59 OUT PUCHAR Again)
60 {
61 PXBOXVMP_DEVICE_EXTENSION XboxVmpDeviceExtension;
62 VIDEO_ACCESS_RANGE AccessRanges[3];
63 VP_STATUS Status;
64 USHORT VendorId = 0x10DE; /* NVIDIA Corporation */
65 USHORT DeviceId = 0x02A0; /* NV2A XGPU */
66
67 TRACE_(IHVVIDEO, "XboxVmpFindAdapter\n");
68
69 XboxVmpDeviceExtension = (PXBOXVMP_DEVICE_EXTENSION)HwDeviceExtension;
70
71 Status = VideoPortGetAccessRanges(HwDeviceExtension, 0, NULL, 3, AccessRanges,
72 &VendorId, &DeviceId, NULL);
73
74 if (Status == NO_ERROR)
75 {
76 XboxVmpDeviceExtension->PhysControlStart = AccessRanges[0].RangeStart;
77 XboxVmpDeviceExtension->ControlLength = AccessRanges[0].RangeLength;
78 XboxVmpDeviceExtension->PhysFrameBufferStart = AccessRanges[1].RangeStart;
79 }
80
81 return Status;
82 }
83
84 /*
85 * XboxVmpInitialize
86 *
87 * Performs the first initialization of the adapter, after the HAL has given
88 * up control of the video hardware to the video port driver.
89 */
90
91 BOOLEAN
92 NTAPI
93 XboxVmpInitialize(
94 PVOID HwDeviceExtension)
95 {
96 PXBOXVMP_DEVICE_EXTENSION XboxVmpDeviceExtension;
97 ULONG inIoSpace = VIDEO_MEMORY_SPACE_MEMORY;
98 ULONG Length;
99
100 TRACE_(IHVVIDEO, "XboxVmpInitialize\n");
101
102 XboxVmpDeviceExtension = (PXBOXVMP_DEVICE_EXTENSION)HwDeviceExtension;
103
104 Length = XboxVmpDeviceExtension->ControlLength;
105 XboxVmpDeviceExtension->VirtControlStart = NULL;
106
107 if (VideoPortMapMemory(HwDeviceExtension,
108 XboxVmpDeviceExtension->PhysControlStart,
109 &Length,
110 &inIoSpace,
111 &XboxVmpDeviceExtension->VirtControlStart) != NO_ERROR)
112 {
113 ERR_(IHVVIDEO, "Failed to map control memory\n");
114 return FALSE;
115 }
116
117 INFO_(IHVVIDEO, "Mapped 0x%x bytes of control mem at 0x%x to virt addr 0x%x\n",
118 XboxVmpDeviceExtension->ControlLength,
119 XboxVmpDeviceExtension->PhysControlStart.u.LowPart,
120 XboxVmpDeviceExtension->VirtControlStart);
121
122 return TRUE;
123 }
124
125 /*
126 * XboxVmpStartIO
127 *
128 * Processes the specified Video Request Packet.
129 */
130
131 BOOLEAN
132 NTAPI
133 XboxVmpStartIO(
134 PVOID HwDeviceExtension,
135 PVIDEO_REQUEST_PACKET RequestPacket)
136 {
137 BOOLEAN Result;
138
139 RequestPacket->StatusBlock->Status = ERROR_INVALID_PARAMETER;
140
141 switch (RequestPacket->IoControlCode)
142 {
143 case IOCTL_VIDEO_SET_CURRENT_MODE:
144 {
145 TRACE_(IHVVIDEO, "XboxVmpStartIO IOCTL_VIDEO_SET_CURRENT_MODE\n");
146
147 if (RequestPacket->InputBufferLength < sizeof(VIDEO_MODE))
148 {
149 RequestPacket->StatusBlock->Status = ERROR_INSUFFICIENT_BUFFER;
150 return TRUE;
151 }
152
153 Result = XboxVmpSetCurrentMode(
154 (PXBOXVMP_DEVICE_EXTENSION)HwDeviceExtension,
155 (PVIDEO_MODE)RequestPacket->InputBuffer,
156 RequestPacket->StatusBlock);
157 break;
158 }
159
160 case IOCTL_VIDEO_RESET_DEVICE:
161 {
162 TRACE_(IHVVIDEO, "XboxVmpStartIO IOCTL_VIDEO_RESET_DEVICE\n");
163
164 Result = XboxVmpResetDevice(
165 (PXBOXVMP_DEVICE_EXTENSION)HwDeviceExtension,
166 RequestPacket->StatusBlock);
167 break;
168 }
169
170 case IOCTL_VIDEO_MAP_VIDEO_MEMORY:
171 {
172 TRACE_(IHVVIDEO, "XboxVmpStartIO IOCTL_VIDEO_MAP_VIDEO_MEMORY\n");
173
174 if (RequestPacket->OutputBufferLength < sizeof(VIDEO_MEMORY_INFORMATION) ||
175 RequestPacket->InputBufferLength < sizeof(VIDEO_MEMORY))
176 {
177 RequestPacket->StatusBlock->Status = ERROR_INSUFFICIENT_BUFFER;
178 return TRUE;
179 }
180
181 Result = XboxVmpMapVideoMemory(
182 (PXBOXVMP_DEVICE_EXTENSION)HwDeviceExtension,
183 (PVIDEO_MEMORY)RequestPacket->InputBuffer,
184 (PVIDEO_MEMORY_INFORMATION)RequestPacket->OutputBuffer,
185 RequestPacket->StatusBlock);
186 break;
187 }
188
189 case IOCTL_VIDEO_UNMAP_VIDEO_MEMORY:
190 {
191 TRACE_(IHVVIDEO, "XboxVmpStartIO IOCTL_VIDEO_UNMAP_VIDEO_MEMORY\n");
192
193 if (RequestPacket->InputBufferLength < sizeof(VIDEO_MEMORY))
194 {
195 RequestPacket->StatusBlock->Status = ERROR_INSUFFICIENT_BUFFER;
196 return TRUE;
197 }
198
199 Result = XboxVmpUnmapVideoMemory(
200 (PXBOXVMP_DEVICE_EXTENSION)HwDeviceExtension,
201 (PVIDEO_MEMORY)RequestPacket->InputBuffer,
202 RequestPacket->StatusBlock);
203 break;
204 }
205
206 case IOCTL_VIDEO_QUERY_NUM_AVAIL_MODES:
207 {
208 TRACE_(IHVVIDEO, "XboxVmpStartIO IOCTL_VIDEO_QUERY_NUM_AVAIL_MODES\n");
209
210 if (RequestPacket->OutputBufferLength < sizeof(VIDEO_NUM_MODES))
211 {
212 RequestPacket->StatusBlock->Status = ERROR_INSUFFICIENT_BUFFER;
213 return TRUE;
214 }
215
216 Result = XboxVmpQueryNumAvailModes(
217 (PXBOXVMP_DEVICE_EXTENSION)HwDeviceExtension,
218 (PVIDEO_NUM_MODES)RequestPacket->OutputBuffer,
219 RequestPacket->StatusBlock);
220 break;
221 }
222
223 case IOCTL_VIDEO_QUERY_AVAIL_MODES:
224 {
225 TRACE_(IHVVIDEO, "XboxVmpStartIO IOCTL_VIDEO_QUERY_AVAIL_MODES\n");
226
227 if (RequestPacket->OutputBufferLength < sizeof(VIDEO_MODE_INFORMATION))
228 {
229 RequestPacket->StatusBlock->Status = ERROR_INSUFFICIENT_BUFFER;
230 return TRUE;
231 }
232
233 Result = XboxVmpQueryAvailModes(
234 (PXBOXVMP_DEVICE_EXTENSION)HwDeviceExtension,
235 (PVIDEO_MODE_INFORMATION)RequestPacket->OutputBuffer,
236 RequestPacket->StatusBlock);
237 break;
238 }
239
240 case IOCTL_VIDEO_QUERY_CURRENT_MODE:
241 {
242 TRACE_(IHVVIDEO, "XboxVmpStartIO IOCTL_VIDEO_QUERY_CURRENT_MODE\n");
243
244 if (RequestPacket->OutputBufferLength < sizeof(VIDEO_MODE_INFORMATION))
245 {
246 RequestPacket->StatusBlock->Status = ERROR_INSUFFICIENT_BUFFER;
247 return TRUE;
248 }
249
250 Result = XboxVmpQueryCurrentMode(
251 (PXBOXVMP_DEVICE_EXTENSION)HwDeviceExtension,
252 (PVIDEO_MODE_INFORMATION)RequestPacket->OutputBuffer,
253 RequestPacket->StatusBlock);
254 break;
255 }
256
257 default:
258 {
259 WARN_(IHVVIDEO, "XboxVmpStartIO 0x%x not implemented\n", RequestPacket->IoControlCode);
260
261 RequestPacket->StatusBlock->Status = ERROR_INVALID_FUNCTION;
262 return FALSE;
263 }
264 }
265
266 if (Result)
267 {
268 RequestPacket->StatusBlock->Status = NO_ERROR;
269 }
270
271 return TRUE;
272 }
273
274 /*
275 * XboxVmpResetHw
276 *
277 * This function is called to reset the hardware to a known state.
278 */
279
280 BOOLEAN
281 NTAPI
282 XboxVmpResetHw(
283 PVOID DeviceExtension,
284 ULONG Columns,
285 ULONG Rows)
286 {
287 TRACE_(IHVVIDEO, "XboxVmpResetHw\n");
288
289 if (!XboxVmpResetDevice((PXBOXVMP_DEVICE_EXTENSION)DeviceExtension, NULL))
290 {
291 return FALSE;
292 }
293
294 return TRUE;
295 }
296
297 /*
298 * XboxVmpGetPowerState
299 *
300 * Queries whether the device can support the requested power state.
301 */
302
303 VP_STATUS
304 NTAPI
305 XboxVmpGetPowerState(
306 PVOID HwDeviceExtension,
307 ULONG HwId,
308 PVIDEO_POWER_MANAGEMENT VideoPowerControl)
309 {
310 ERR_(IHVVIDEO, "XboxVmpGetPowerState is not supported\n");
311
312 return ERROR_INVALID_FUNCTION;
313 }
314
315 /*
316 * XboxVmpSetPowerState
317 *
318 * Sets the power state of the specified device
319 */
320
321 VP_STATUS
322 NTAPI
323 XboxVmpSetPowerState(
324 PVOID HwDeviceExtension,
325 ULONG HwId,
326 PVIDEO_POWER_MANAGEMENT VideoPowerControl)
327 {
328 ERR_(IHVVIDEO, "XboxVmpSetPowerState not supported\n");
329
330 return ERROR_INVALID_FUNCTION;
331 }
332
333 /*
334 * VBESetCurrentMode
335 *
336 * Sets the adapter to the specified operating mode.
337 */
338
339 BOOLEAN
340 FASTCALL
341 XboxVmpSetCurrentMode(
342 PXBOXVMP_DEVICE_EXTENSION DeviceExtension,
343 PVIDEO_MODE RequestedMode,
344 PSTATUS_BLOCK StatusBlock)
345 {
346 if (RequestedMode->RequestedMode != 0)
347 {
348 return FALSE;
349 }
350
351 /* Nothing to do, really. We only support a single mode and we're already
352 * in that mode
353 */
354 return TRUE;
355 }
356
357 /*
358 * XboxVmpResetDevice
359 *
360 * Resets the video hardware to the default mode, to which it was initialized
361 * at system boot.
362 */
363
364 BOOLEAN
365 FASTCALL
366 XboxVmpResetDevice(
367 PXBOXVMP_DEVICE_EXTENSION DeviceExtension,
368 PSTATUS_BLOCK StatusBlock)
369 {
370 /* There is nothing to be done here */
371
372 return TRUE;
373 }
374
375 /*
376 * XboxVmpMapVideoMemory
377 *
378 * Maps the video hardware frame buffer and video RAM into the virtual address
379 * space of the requestor.
380 */
381
382 BOOLEAN
383 FASTCALL
384 XboxVmpMapVideoMemory(
385 PXBOXVMP_DEVICE_EXTENSION DeviceExtension,
386 PVIDEO_MEMORY RequestedAddress,
387 PVIDEO_MEMORY_INFORMATION MapInformation,
388 PSTATUS_BLOCK StatusBlock)
389 {
390 PHYSICAL_ADDRESS FrameBuffer;
391 ULONG inIoSpace = VIDEO_MEMORY_SPACE_MEMORY;
392
393 StatusBlock->Information = sizeof(VIDEO_MEMORY_INFORMATION);
394
395 /* Reuse framebuffer that was set up by firmware */
396 FrameBuffer.QuadPart = *((PULONG)((ULONG_PTR)DeviceExtension->VirtControlStart + NV2A_CONTROL_FRAMEBUFFER_ADDRESS_OFFSET));
397 /* Framebuffer address offset value is coming from the GPU within
398 * memory mapped I/O address space, so we're comparing only low
399 * 28 bits of the address within actual RAM address space */
400 FrameBuffer.QuadPart &= 0x0FFFFFFF;
401 if (FrameBuffer.QuadPart != 0x3C00000 && FrameBuffer.QuadPart != 0x7C00000)
402 {
403 /* Check framebuffer address (high 4 MB of either 64 or 128 MB RAM) */
404 WARN_(IHVVIDEO, "Non-standard framebuffer address 0x%p\n", FrameBuffer.QuadPart);
405 }
406 /* Verify that framebuffer address is page-aligned */
407 ASSERT(FrameBuffer.QuadPart % PAGE_SIZE == 0);
408
409 /* Return the address back to GPU memory mapped I/O */
410 FrameBuffer.QuadPart += DeviceExtension->PhysFrameBufferStart.QuadPart;
411 MapInformation->VideoRamBase = RequestedAddress->RequestedVirtualAddress;
412 /* FIXME: obtain fb size from firmware somehow (Cromwell reserves high 4 MB of RAM) */
413 MapInformation->VideoRamLength = NV2A_VIDEO_MEMORY_SIZE;
414
415 VideoPortMapMemory(
416 DeviceExtension,
417 FrameBuffer,
418 &MapInformation->VideoRamLength,
419 &inIoSpace,
420 &MapInformation->VideoRamBase);
421
422 MapInformation->FrameBufferBase = MapInformation->VideoRamBase;
423 MapInformation->FrameBufferLength = MapInformation->VideoRamLength;
424
425 /* Tell the nVidia controller about the framebuffer */
426 *((PULONG)((ULONG_PTR)DeviceExtension->VirtControlStart + NV2A_CONTROL_FRAMEBUFFER_ADDRESS_OFFSET)) = FrameBuffer.u.LowPart;
427
428 INFO_(IHVVIDEO, "Mapped 0x%x bytes of phys mem at 0x%lx to virt addr 0x%p\n",
429 MapInformation->VideoRamLength, FrameBuffer.u.LowPart, MapInformation->VideoRamBase);
430
431 return TRUE;
432 }
433
434 /*
435 * VBEUnmapVideoMemory
436 *
437 * Releases a mapping between the virtual address space and the adapter's
438 * frame buffer and video RAM.
439 */
440
441 BOOLEAN
442 FASTCALL
443 XboxVmpUnmapVideoMemory(
444 PXBOXVMP_DEVICE_EXTENSION DeviceExtension,
445 PVIDEO_MEMORY VideoMemory,
446 PSTATUS_BLOCK StatusBlock)
447 {
448 VideoPortUnmapMemory(
449 DeviceExtension,
450 VideoMemory->RequestedVirtualAddress,
451 NULL);
452
453 return TRUE;
454 }
455
456 /*
457 * XboxVmpQueryNumAvailModes
458 *
459 * Returns the number of video modes supported by the adapter and the size
460 * in bytes of the video mode information, which can be used to allocate a
461 * buffer for an IOCTL_VIDEO_QUERY_AVAIL_MODES request.
462 */
463
464 BOOLEAN
465 FASTCALL
466 XboxVmpQueryNumAvailModes(
467 PXBOXVMP_DEVICE_EXTENSION DeviceExtension,
468 PVIDEO_NUM_MODES Modes,
469 PSTATUS_BLOCK StatusBlock)
470 {
471 Modes->NumModes = 1;
472 Modes->ModeInformationLength = sizeof(VIDEO_MODE_INFORMATION);
473 StatusBlock->Information = sizeof(VIDEO_NUM_MODES);
474 return TRUE;
475 }
476
477 /*
478 * XboxVmpQueryAvailModes
479 *
480 * Returns information about each video mode supported by the adapter.
481 */
482
483 BOOLEAN
484 FASTCALL
485 XboxVmpQueryAvailModes(
486 PXBOXVMP_DEVICE_EXTENSION DeviceExtension,
487 PVIDEO_MODE_INFORMATION VideoMode,
488 PSTATUS_BLOCK StatusBlock)
489 {
490 return XboxVmpQueryCurrentMode(DeviceExtension, VideoMode, StatusBlock);
491 }
492
493 UCHAR
494 NvGetCrtc(
495 PXBOXVMP_DEVICE_EXTENSION DeviceExtension,
496 UCHAR Index)
497 {
498 *((PUCHAR)((ULONG_PTR)DeviceExtension->VirtControlStart + NV2A_CRTC_REGISTER_INDEX)) = Index;
499 return *((PUCHAR)((ULONG_PTR)DeviceExtension->VirtControlStart + NV2A_CRTC_REGISTER_VALUE));
500 }
501
502 UCHAR
503 NvGetBytesPerPixel(
504 PXBOXVMP_DEVICE_EXTENSION DeviceExtension,
505 ULONG ScreenWidth)
506 {
507 UCHAR BytesPerPixel;
508
509 /* Get BPP directly from NV2A CRTC (magic constants are from Cromwell) */
510 BytesPerPixel = 8 * (((NvGetCrtc(DeviceExtension, 0x19) & 0xE0) << 3) | (NvGetCrtc(DeviceExtension, 0x13) & 0xFF)) / ScreenWidth;
511
512 if (BytesPerPixel == 4)
513 {
514 ASSERT((NvGetCrtc(DeviceExtension, 0x28) & 0xF) == BytesPerPixel - 1);
515 }
516 else
517 {
518 ASSERT((NvGetCrtc(DeviceExtension, 0x28) & 0xF) == BytesPerPixel);
519 }
520
521 return BytesPerPixel;
522 }
523
524 /*
525 * VBEQueryCurrentMode
526 *
527 * Returns information about current video mode.
528 */
529
530 BOOLEAN
531 FASTCALL
532 XboxVmpQueryCurrentMode(
533 PXBOXVMP_DEVICE_EXTENSION DeviceExtension,
534 PVIDEO_MODE_INFORMATION VideoMode,
535 PSTATUS_BLOCK StatusBlock)
536 {
537 UCHAR BytesPerPixel;
538
539 VideoMode->Length = sizeof(VIDEO_MODE_INFORMATION);
540 VideoMode->ModeIndex = 0;
541
542 VideoMode->VisScreenWidth = *((PULONG)((ULONG_PTR)DeviceExtension->VirtControlStart + NV2A_RAMDAC_FP_HVALID_END)) + 1;
543 VideoMode->VisScreenHeight = *((PULONG)((ULONG_PTR)DeviceExtension->VirtControlStart + NV2A_RAMDAC_FP_VVALID_END)) + 1;
544
545 if (VideoMode->VisScreenWidth <= 1 || VideoMode->VisScreenHeight <= 1)
546 {
547 ERR_(IHVVIDEO, "Cannot obtain current screen resolution!\n");
548 return FALSE;
549 }
550
551 BytesPerPixel = NvGetBytesPerPixel(DeviceExtension, VideoMode->VisScreenWidth);
552 ASSERT(BytesPerPixel >= 1 && BytesPerPixel <= 4);
553
554 VideoMode->ScreenStride = VideoMode->VisScreenWidth * BytesPerPixel;
555 VideoMode->NumberOfPlanes = 1;
556 VideoMode->BitsPerPlane = BytesPerPixel * 8;
557 VideoMode->Frequency = 1;
558 VideoMode->XMillimeter = 0; /* FIXME */
559 VideoMode->YMillimeter = 0; /* FIXME */
560 if (BytesPerPixel >= 3)
561 {
562 VideoMode->NumberRedBits = 8;
563 VideoMode->NumberGreenBits = 8;
564 VideoMode->NumberBlueBits = 8;
565 VideoMode->RedMask = 0xFF0000;
566 VideoMode->GreenMask = 0x00FF00;
567 VideoMode->BlueMask = 0x0000FF;
568 }
569 else
570 {
571 /* FIXME: not implemented */
572 WARN_(IHVVIDEO, "BytesPerPixel %d - not implemented\n", BytesPerPixel);
573 }
574 VideoMode->VideoMemoryBitmapWidth = VideoMode->VisScreenWidth;
575 VideoMode->VideoMemoryBitmapHeight = VideoMode->VisScreenHeight;
576 VideoMode->AttributeFlags = VIDEO_MODE_GRAPHICS | VIDEO_MODE_COLOR |
577 VIDEO_MODE_NO_OFF_SCREEN;
578 VideoMode->DriverSpecificAttributeFlags = 0;
579
580 StatusBlock->Information = sizeof(VIDEO_MODE_INFORMATION);
581
582 /* Verify that screen fits framebuffer size */
583 if (VideoMode->VisScreenWidth * VideoMode->VisScreenHeight * (VideoMode->BitsPerPlane / 8) > NV2A_VIDEO_MEMORY_SIZE)
584 {
585 ERR_(IHVVIDEO, "Current screen resolution exceeds video memory bounds!\n");
586 return FALSE;
587 }
588
589 return TRUE;
590 }
591
592 /* EOF */