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;
791 UNICODE_STRING DosName;
794 BootVidDriverObject = DriverObject;
796 /* register driver routines */
797 DriverObject->MajorFunction[IRP_MJ_CLOSE] = (PDRIVER_DISPATCH)VidDispatch;
798 DriverObject->MajorFunction[IRP_MJ_CREATE] = (PDRIVER_DISPATCH)VidDispatch;
799 DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] =
800 (PDRIVER_DISPATCH)VidDispatch;
801 DriverObject->DriverUnload = NULL;
804 RtlInitUnicodeStringFromLiteral(&DeviceName, L"\\Device\\BootVid");
806 Status = IoCreateDevice(DriverObject, 0, &DeviceName, FILE_DEVICE_BOOTVID,
807 0, FALSE, &BootVidDevice);
808 if (! NT_SUCCESS(Status))
813 BootVidDevice->Flags |= DO_BUFFERED_IO;