1 /* $Id: bootvid.c,v 1.4 2003/11/14 17:13:24 weiden Exp $
3 * COPYRIGHT: See COPYING in the top level directory
4 * PROJECT: ReactOS kernel
5 * FILE: ntoskrnl/inbv/bootvid.c
6 * PURPOSE: Boot video support
7 * PROGRAMMER: Casper S. Hornstrup (chorns@users.sourceforge.net)
9 * 12-07-2003 CSH Created
12 /* INCLUDES ******************************************************************/
14 #include <ddk/ntddk.h>
15 #include <ddk/ntbootvid.h>
16 #include <reactos/resource.h>
18 #include "../../../ntoskrnl/include/internal/v86m.h"
25 typedef struct tagRGBQUAD
{
26 unsigned char rgbBlue
;
27 unsigned char rgbGreen
;
29 unsigned char rgbReserved
;
32 typedef long FXPT2DOT30
;
34 typedef struct tagCIEXYZ
{
39 typedef CIEXYZ
* LPCIEXYZ
;
41 typedef struct tagCIEXYZTRIPLE
{
46 typedef CIEXYZTRIPLE
*LPCIEXYZTRIPLE
;
56 LONG bV5XPelsPerMeter
;
57 LONG bV5YPelsPerMeter
;
59 DWORD bV5ClrImportant
;
65 CIEXYZTRIPLE bV5Endpoints
;
73 } BITMAPV5HEADER
, *PBITMAPV5HEADER
;
79 #define GRAPHICS 0x3ce
88 } FADER_PALETTE_ENTRY
;
92 InbvPutPixels(int x
, int y
, unsigned long c
);
94 /* GLOBALS *******************************************************************/
98 /* Must be 4 bytes per entry */
102 static HANDLE BitmapThreadHandle
;
103 static CLIENT_ID BitmapThreadId
;
104 static BOOLEAN BitmapIsDrawn
;
105 static PUCHAR BootimageBitmap
;
106 static BOOLEAN InGraphicsMode
= FALSE
;
108 /* DATA **********************************************************************/
110 static BOOLEAN VideoAddressSpaceInitialized
= FALSE
;
111 static PVOID NonBiosBaseAddress
;
112 static PDRIVER_OBJECT BootVidDriverObject
= NULL
;
114 /* FUNCTIONS *****************************************************************/
119 PIMAGE_RESOURCE_DATA_ENTRY ResourceDataEntry
;
120 LDR_RESOURCE_INFO ResourceInfo
;
122 PVOID BaseAddress
= BootVidDriverObject
->DriverStart
;
125 ResourceInfo
.Type
= RT_BITMAP
;
126 ResourceInfo
.Name
= IDB_BOOTIMAGE
;
127 ResourceInfo
.Language
= 0x09;
129 Status
= LdrFindResource_U(BaseAddress
,
133 if (!NT_SUCCESS(Status
))
135 DPRINT("LdrFindResource_U() failed with status 0x%.08x\n", Status
);
139 Status
= LdrAccessResource(BaseAddress
,
141 (PVOID
*)&BootimageBitmap
,
143 if (!NT_SUCCESS(Status
))
145 DPRINT("LdrAccessResource() failed with status 0x%.08x\n", Status
);
154 InbvInitializeVideoAddressSpace(VOID
)
156 OBJECT_ATTRIBUTES ObjectAttributes
;
157 UNICODE_STRING PhysMemName
;
159 HANDLE PhysMemHandle
;
161 LARGE_INTEGER Offset
;
165 PVOID start
= (PVOID
)0x0;
168 * Open the physical memory section
170 RtlInitUnicodeStringFromLiteral(&PhysMemName
, L
"\\Device\\PhysicalMemory");
171 InitializeObjectAttributes(&ObjectAttributes
,
176 Status
= ZwOpenSection(&PhysMemHandle
, SECTION_ALL_ACCESS
,
178 if (!NT_SUCCESS(Status
))
180 DPRINT("Couldn't open \\Device\\PhysicalMemory\n");
185 * Map the BIOS and device registers into the address space
187 Offset
.QuadPart
= 0xa0000;
188 ViewSize
= 0x100000 - 0xa0000;
189 BaseAddress
= (PVOID
)0xa0000;
190 Status
= NtMapViewOfSection(PhysMemHandle
,
199 PAGE_EXECUTE_READWRITE
);
200 if (!NT_SUCCESS(Status
))
202 DPRINT("Couldn't map physical memory (%x)\n", Status
);
203 NtClose(PhysMemHandle
);
206 NtClose(PhysMemHandle
);
207 if (BaseAddress
!= (PVOID
)0xa0000)
209 DPRINT("Couldn't map physical memory at the right address "
210 "(was %x)\n", BaseAddress
);
215 * Map some memory to use for the non-BIOS parts of the v86 mode address
218 NonBiosBaseAddress
= (PVOID
)0x1;
219 ViewSize
= 0xa0000 - 0x1000;
220 Status
= NtAllocateVirtualMemory(NtCurrentProcess(),
225 PAGE_EXECUTE_READWRITE
);
226 if (!NT_SUCCESS(Status
))
228 DPRINT("Failed to allocate virtual memory (Status %x)\n", Status
);
231 if (NonBiosBaseAddress
!= (PVOID
)0x0)
233 DPRINT("Failed to allocate virtual memory at right address "
234 "(was %x)\n", NonBiosBaseAddress
);
239 * Get the real mode IVT from the kernel
241 Status
= NtVdmControl(0, IVT
);
242 if (!NT_SUCCESS(Status
))
244 DPRINT("NtVdmControl failed (status %x)\n", Status
);
249 * Copy the real mode IVT into the right place
251 memcpy(start
, IVT
, 1024);
254 * Get the BDA from the kernel
256 Status
= NtVdmControl(1, BDA
);
257 if (!NT_SUCCESS(Status
))
259 DPRINT("NtVdmControl failed (status %x)\n", Status
);
264 * Copy the BDA into the right place
266 memcpy((PVOID
)0x400, BDA
, 256);
273 InbvDeinitializeVideoAddressSpace(VOID
)
278 RegionSize
= 0xa0000 - 0x1000;
279 NtFreeVirtualMemory(NtCurrentProcess(),
284 ViewBase
= (PUCHAR
) 0xa0000;
285 ZwUnmapViewOfSection(NtCurrentProcess(), ViewBase
);
296 for(j
= 0; j
< 80; j
++)
298 maskbit
[j
* 8 + 0] = 128;
299 maskbit
[j
* 8 + 1] = 64;
300 maskbit
[j
* 8 + 2] = 32;
301 maskbit
[j
* 8 + 3] = 16;
302 maskbit
[j
* 8 + 4] = 8;
303 maskbit
[j
* 8 + 5] = 4;
304 maskbit
[j
* 8 + 6] = 2;
305 maskbit
[j
* 8 + 7] = 1;
307 for(j
= 0; j
< 480; j
++)
309 y80
[j
] = j
* 80; /* 80 = 640 / 8 = Number of bytes per scanline */
315 InbvInitVGAMode(VOID
)
317 KV86M_REGISTERS Regs
;
321 vidmem
= (char *)(0xd0000000 + 0xa0000);
322 memset(&Regs
, 0, sizeof(Regs
));
324 Status
= Ke386CallBios(0x10, &Regs
);
325 assert(NT_SUCCESS(Status
));
327 /* Get VGA registers into the correct state */
328 /* Reset the internal flip-flop. */
329 (VOID
)READ_PORT_UCHAR((PUCHAR
)FEATURE
);
330 /* Write the 16 palette registers. */
331 for (i
= 0; i
< 16; i
++)
333 WRITE_PORT_UCHAR((PUCHAR
)ATTRIB
, i
);
334 WRITE_PORT_UCHAR((PUCHAR
)ATTRIB
, i
);
336 /* Write the mode control register - graphics mode; 16 color DAC. */
337 WRITE_PORT_UCHAR((PUCHAR
)ATTRIB
, 0x10);
338 WRITE_PORT_UCHAR((PUCHAR
)ATTRIB
, 0x81);
339 /* Write the color select register - select first 16 DAC registers. */
340 WRITE_PORT_UCHAR((PUCHAR
)ATTRIB
, 0x14);
341 WRITE_PORT_UCHAR((PUCHAR
)ATTRIB
, 0x00);
343 /* Get the VGA into the mode we want to work with */
344 WRITE_PORT_UCHAR((PUCHAR
)0x3ce,0x08); /* Set */
345 WRITE_PORT_UCHAR((PUCHAR
)0x3cf,0); /* the MASK */
346 WRITE_PORT_USHORT((PUSHORT
)0x3ce,0x0205); /* write mode = 2 (bits 0,1) read mode = 0 (bit 3) */
347 (UCHAR
) READ_REGISTER_UCHAR(vidmem
); /* Update bit buffer */
348 WRITE_REGISTER_UCHAR(vidmem
, 0); /* Write the pixel */
349 WRITE_PORT_UCHAR((PUCHAR
)0x3ce,0x08);
350 WRITE_PORT_UCHAR((PUCHAR
)0x3cf,0xff);
352 /* Set the PEL mask. */
353 WRITE_PORT_UCHAR((PUCHAR
)0x3c6, 0xff);
355 /* Zero out video memory (clear a possibly trashed screen) */
356 RtlZeroMemory(vidmem
, 64000);
364 VidResetDisplay(VOID
)
367 We are only using standard VGA facilities so we can rely on the HAL 'int10mode3'
368 reset to cleanup the hardware state.
370 InGraphicsMode
= FALSE
;
381 We are only using standard VGA facilities so we can rely on the HAL 'int10mode3'
382 reset to cleanup the hardware state.
384 InGraphicsMode
= FALSE
;
388 static __inline__ VOID
389 InbvSetColor(int cindex
, unsigned char red
, unsigned char green
, unsigned char blue
)
391 red
= (red
* 63) / 255;
392 green
= (green
* 63) / 255;
393 blue
= (blue
* 63) / 255;
395 WRITE_PORT_UCHAR((PUCHAR
)0x03c8, cindex
);
396 (VOID
)READ_PORT_UCHAR((PUCHAR
)FEATURE
);
397 (VOID
)READ_PORT_UCHAR((PUCHAR
)FEATURE
);
398 WRITE_PORT_UCHAR((PUCHAR
)0x03c9, red
);
399 (VOID
)READ_PORT_UCHAR((PUCHAR
)FEATURE
);
400 (VOID
)READ_PORT_UCHAR((PUCHAR
)FEATURE
);
401 WRITE_PORT_UCHAR((PUCHAR
)0x03c9, green
);
402 (VOID
)READ_PORT_UCHAR((PUCHAR
)FEATURE
);
403 (VOID
)READ_PORT_UCHAR((PUCHAR
)FEATURE
);
404 WRITE_PORT_UCHAR((PUCHAR
)0x03c9, blue
);
405 (VOID
)READ_PORT_UCHAR((PUCHAR
)FEATURE
);
406 (VOID
)READ_PORT_UCHAR((PUCHAR
)FEATURE
);
410 static __inline__ VOID
411 InbvSetBlackPalette()
413 register ULONG r
= 0;
415 /* Disable screen and enable palette access. */
416 (VOID
)READ_PORT_UCHAR((PUCHAR
)FEATURE
);
417 WRITE_PORT_UCHAR((PUCHAR
)0x3c0, 0x00);
418 for (r
= 0; r
< 16; r
++)
420 InbvSetColor(r
, 0, 0, 0);
422 /* Enable screen and enable palette access. */
423 (VOID
)READ_PORT_UCHAR((PUCHAR
)FEATURE
);
424 WRITE_PORT_UCHAR((PUCHAR
)0x3c0, 0x20);
429 InbvDisplayBitmap(ULONG Width
, ULONG Height
, PCHAR ImageData
)
437 for (y
= 0; y
< Height
; y
++)
439 for (j
= 0; j
< 8; j
++)
444 * Loop through the line and process every 8th pixel.
445 * This way we can get a way with using the same bit mask
446 * for several pixels and thus not need to do as much I/O
455 c
= ImageData
[k
+ x
];
456 for (i
= 1; i
< 4; i
++)
460 c
|= (ImageData
[k
+ x
+ i
* 8] << i
* 8);
465 InbvPutPixels(x
, 479 - y
, c
);
475 InbvDisplayCompressedBitmap()
477 PBITMAPV5HEADER bminfo
;
485 bminfo
= (PBITMAPV5HEADER
) &BootimageBitmap
[0];
486 DPRINT("bV5Size = %d\n", bminfo
->bV5Size
);
487 DPRINT("bV5Width = %d\n", bminfo
->bV5Width
);
488 DPRINT("bV5Height = %d\n", bminfo
->bV5Height
);
489 DPRINT("bV5Planes = %d\n", bminfo
->bV5Planes
);
490 DPRINT("bV5BitCount = %d\n", bminfo
->bV5BitCount
);
491 DPRINT("bV5Compression = %d\n", bminfo
->bV5Compression
);
492 DPRINT("bV5SizeImage = %d\n", bminfo
->bV5SizeImage
);
493 DPRINT("bV5XPelsPerMeter = %d\n", bminfo
->bV5XPelsPerMeter
);
494 DPRINT("bV5YPelsPerMeter = %d\n", bminfo
->bV5YPelsPerMeter
);
495 DPRINT("bV5ClrUsed = %d\n", bminfo
->bV5ClrUsed
);
496 DPRINT("bV5ClrImportant = %d\n", bminfo
->bV5ClrImportant
);
498 bfOffBits
= bminfo
->bV5Size
+ bminfo
->bV5ClrUsed
* sizeof(RGBQUAD
);
499 DPRINT("bfOffBits = %d\n", bfOffBits
);
500 DPRINT("size of color indices = %d\n", bminfo
->bV5ClrUsed
* sizeof(RGBQUAD
));
501 DPRINT("first byte of data = %d\n", BootimageBitmap
[bfOffBits
]);
503 InbvSetBlackPalette();
505 ImageData
= ExAllocatePool(NonPagedPool
, bminfo
->bV5Width
* bminfo
->bV5Height
);
506 RtlZeroMemory(ImageData
, bminfo
->bV5Width
* bminfo
->bV5Height
);
509 * ImageData has 1 pixel per byte.
510 * bootimage has 2 pixels per byte.
513 if (bminfo
->bV5Compression
== 2)
517 while ((j
< bminfo
->bV5SizeImage
) && (k
< (ULONG
) (bminfo
->bV5Width
* bminfo
->bV5Height
)))
521 clen
= BootimageBitmap
[bfOffBits
+ j
];
528 b
= BootimageBitmap
[bfOffBits
+ j
];
531 for (i
= 0; i
< (clen
/ 2); i
++)
533 ImageData
[k
] = (b
& 0xf0) >> 4;
535 ImageData
[k
] = b
& 0xf;
540 ImageData
[k
] = (b
& 0xf0) >> 4;
547 b
= BootimageBitmap
[bfOffBits
+ j
];
561 x
= BootimageBitmap
[bfOffBits
+ j
];
563 y
= BootimageBitmap
[bfOffBits
+ j
];
565 curx
= k
% bminfo
->bV5Width
;
566 cury
= k
/ bminfo
->bV5Width
;
567 k
= (cury
+ y
) * bminfo
->bV5Width
+ (curx
+ x
);
573 DPRINT("Unaligned copy!\n");
577 for (i
= 0; i
< (clen
/ 2); i
++)
579 b
= BootimageBitmap
[bfOffBits
+ j
];
582 ImageData
[k
] = (b
& 0xf0) >> 4;
584 ImageData
[k
] = b
& 0xf;
589 b
= BootimageBitmap
[bfOffBits
+ j
];
591 ImageData
[k
] = (b
& 0xf0) >> 4;
600 InbvDisplayBitmap(bminfo
->bV5Width
, bminfo
->bV5Height
, ImageData
);
604 DbgPrint("Warning boot image need to be compressed using RLE4\n");
607 ExFreePool(ImageData
);
611 #define PALETTE_FADE_STEPS 20
612 #define PALETTE_FADE_TIME 20 * 10000 /* 20ms */
617 PBITMAPV5HEADER bminfo
;
622 LARGE_INTEGER Interval
;
623 FADER_PALETTE_ENTRY FaderPalette
[16];
624 FADER_PALETTE_ENTRY FaderPaletteDelta
[16];
626 RtlZeroMemory(&FaderPalette
, sizeof(FaderPalette
));
627 RtlZeroMemory(&FaderPaletteDelta
, sizeof(FaderPaletteDelta
));
629 bminfo
= (PBITMAPV5HEADER
) &BootimageBitmap
[0]; //sizeof(BITMAPFILEHEADER)];
630 Palette
= (PRGBQUAD
) &BootimageBitmap
[/* sizeof(BITMAPFILEHEADER) + */ bminfo
->bV5Size
];
632 for (i
= 0; i
< 16; i
++)
634 if (i
< bminfo
->bV5ClrUsed
)
636 FaderPaletteDelta
[i
].r
= ((Palette
[i
].rgbRed
<< 8) / PALETTE_FADE_STEPS
);
637 FaderPaletteDelta
[i
].g
= ((Palette
[i
].rgbGreen
<< 8) / PALETTE_FADE_STEPS
);
638 FaderPaletteDelta
[i
].b
= ((Palette
[i
].rgbBlue
<< 8) / PALETTE_FADE_STEPS
);
642 for (i
= 0; i
< PALETTE_FADE_STEPS
; i
++)
644 /* Disable screen and enable palette access. */
645 (VOID
)READ_PORT_UCHAR((PUCHAR
)FEATURE
);
646 WRITE_PORT_UCHAR((PUCHAR
)0x3c0, 0x00);
647 for (c
= 0; c
< bminfo
->bV5ClrUsed
; c
++)
650 FaderPalette
[c
].r
+= FaderPaletteDelta
[c
].r
;
651 FaderPalette
[c
].g
+= FaderPaletteDelta
[c
].g
;
652 FaderPalette
[c
].b
+= FaderPaletteDelta
[c
].b
;
654 /* Get the integer values */
655 r
= FaderPalette
[c
].r
>> 8;
656 g
= FaderPalette
[c
].g
>> 8;
657 b
= FaderPalette
[c
].b
>> 8;
659 /* Don't go too far */
660 if (r
> Palette
[c
].rgbRed
)
661 r
= Palette
[c
].rgbRed
;
662 if (g
> Palette
[c
].rgbGreen
)
663 g
= Palette
[c
].rgbGreen
;
664 if (b
> Palette
[c
].rgbBlue
)
665 b
= Palette
[c
].rgbBlue
;
667 /* Update the hardware */
668 InbvSetColor(c
, r
, g
, b
);
670 /* Enable screen and disable palette access. */
671 (VOID
)READ_PORT_UCHAR((PUCHAR
)FEATURE
);
672 WRITE_PORT_UCHAR((PUCHAR
)0x3c0, 0x20);
673 /* Wait for a bit. */
674 Interval
.QuadPart
= -PALETTE_FADE_TIME
;
675 KeDelayExecutionThread(KernelMode
, FALSE
, &Interval
);
680 InbvBitmapThreadMain(PVOID Ignored
)
682 if (InbvFindBootimage())
684 InbvDisplayCompressedBitmap();
689 DbgPrint("Warning: Cannot find boot image\n");
692 BitmapIsDrawn
= TRUE
;
698 VidIsBootDriverInstalled(VOID
)
700 return InGraphicsMode
;
710 if (!VideoAddressSpaceInitialized
)
712 InbvInitializeVideoAddressSpace();
717 InGraphicsMode
= TRUE
;
719 BitmapIsDrawn
= FALSE
;
721 Status
= PsCreateSystemThread(&BitmapThreadHandle
,
726 InbvBitmapThreadMain
,
728 if (!NT_SUCCESS(Status
))
732 NtClose(BitmapThreadHandle
);
734 InbvDeinitializeVideoAddressSpace();
735 VideoAddressSpaceInitialized
= FALSE
;
740 NTSTATUS STDCALL_FUNC
741 VidDispatch(IN PDEVICE_OBJECT DeviceObject
, IN PIRP Irp
)
743 PIO_STACK_LOCATION piosStack
= IoGetCurrentIrpStackLocation(Irp
);
745 NTBOOTVID_FUNCTION_TABLE
* FunctionTable
;
747 nErrCode
= STATUS_SUCCESS
;
749 switch(piosStack
->MajorFunction
)
751 /* opening and closing handles to the device */
756 case IRP_MJ_DEVICE_CONTROL
:
757 switch (piosStack
->Parameters
.DeviceIoControl
.IoControlCode
)
759 case IOCTL_BOOTVID_INITIALIZE
:
761 FunctionTable
= (NTBOOTVID_FUNCTION_TABLE
*)
762 Irp
->AssociatedIrp
.SystemBuffer
;
763 FunctionTable
->ResetDisplay
= VidResetDisplay
;
765 case IOCTL_BOOTVID_CLEANUP
:
769 nErrCode
= STATUS_NOT_IMPLEMENTED
;
774 /* unsupported operations */
776 nErrCode
= STATUS_NOT_IMPLEMENTED
;
779 Irp
->IoStatus
.Status
= nErrCode
;
780 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
787 DriverEntry(PDRIVER_OBJECT DriverObject
, PUNICODE_STRING RegistryPath
)
789 PDEVICE_OBJECT BootVidDevice
;
790 UNICODE_STRING DeviceName
;
793 BootVidDriverObject
= DriverObject
;
795 /* register driver routines */
796 DriverObject
->MajorFunction
[IRP_MJ_CLOSE
] = (PDRIVER_DISPATCH
)VidDispatch
;
797 DriverObject
->MajorFunction
[IRP_MJ_CREATE
] = (PDRIVER_DISPATCH
)VidDispatch
;
798 DriverObject
->MajorFunction
[IRP_MJ_DEVICE_CONTROL
] =
799 (PDRIVER_DISPATCH
)VidDispatch
;
800 DriverObject
->DriverUnload
= NULL
;
803 RtlInitUnicodeStringFromLiteral(&DeviceName
, L
"\\Device\\BootVid");
805 Status
= IoCreateDevice(DriverObject
, 0, &DeviceName
, FILE_DEVICE_BOOTVID
,
806 0, FALSE
, &BootVidDevice
);
807 if (! NT_SUCCESS(Status
))
812 BootVidDevice
->Flags
|= DO_BUFFERED_IO
;