Reactos
at listview 538 lines 17 kB view raw
1/* 2 * PROJECT: ReactOS Generic Framebuffer Boot Video Driver 3 * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) 4 * or MIT (https://spdx.org/licenses/MIT) 5 * PURPOSE: Main file 6 * COPYRIGHT: Copyright 2023-2026 Hermès Bélusca-Maïto <hermes.belusca-maito@reactos.org> 7 */ 8 9#include "precomp.h" 10 11#define NDEBUG 12#include <debug.h> 13 14/* Include the Boot-time (POST) display discovery helper functions */ 15#include <drivers/bootvid/framebuf.c> 16 17/* Scaling of the bootvid 640x480 default virtual screen to the larger video framebuffer */ 18#define SCALING_SUPPORT 19#define SCALING_PROPORTIONAL 20 21/* Keep borders black or controlled with palette */ 22// #define COLORED_BORDERS 23 24 25/* GLOBALS ********************************************************************/ 26 27#define BB_PIXEL(x, y) \ 28 ((PUCHAR)BackBuffer + (y) * SCREEN_WIDTH + (x)) 29 30#define FB_PIXEL(x, y) \ 31 ((PUCHAR)FrameBufferStart + (PanV + VidpYScale * (y)) * BytesPerScanLine \ 32 + (PanH + VidpXScale * (x)) * BytesPerPixel) 33 34static ULONG_PTR FrameBufferStart = 0; 35static ULONG FrameBufferSize; 36static ULONG ScreenWidth, ScreenHeight, BytesPerScanLine; 37static UCHAR BytesPerPixel; 38static PUCHAR BackBuffer = NULL; 39static SIZE_T BackBufferSize; 40 41#ifdef SCALING_SUPPORT 42static USHORT VidpXScale = 1; 43static USHORT VidpYScale = 1; 44#else 45#define VidpXScale 1 46#define VidpYScale 1 47#endif 48static ULONG PanH, PanV; 49 50static RGBQUAD CachedPalette[BV_MAX_COLORS]; 51 52 53/* PRIVATE FUNCTIONS *********************************************************/ 54 55static VOID 56ApplyPalette(VOID) 57{ 58 PULONG Frame = (PULONG)FrameBufferStart; 59 ULONG x, y; 60 61#ifdef COLORED_BORDERS 62 /* Top border */ 63 for (x = 0; x < PanV * ScreenWidth; ++x) 64 { 65 *Frame++ = CachedPalette[BV_COLOR_BLACK]; 66 } 67 68 /* Left border */ 69 for (y = 0; y < VidpYScale * SCREEN_HEIGHT; ++y) 70 { 71 // Frame = (PULONG)(FrameBufferStart + FB_OFFSET(-(LONG)PanH, y)); 72 Frame = (PULONG)(FrameBufferStart + (PanV + y) * BytesPerScanLine); 73 for (x = 0; x < PanH; ++x) 74 { 75 *Frame++ = CachedPalette[BV_COLOR_BLACK]; 76 } 77 } 78#endif // COLORED_BORDERS 79 80 /* Screen redraw */ 81 PUCHAR Back = BackBuffer; 82 for (y = 0; y < SCREEN_HEIGHT; ++y) 83 { 84 Frame = (PULONG)FB_PIXEL(0, y); 85 PULONG Pixel = Frame; 86 for (x = 0; x < SCREEN_WIDTH; ++x) 87 { 88 for (ULONG j = VidpXScale; j > 0; --j) 89 *Pixel++ = CachedPalette[*Back]; 90 Back++; 91 } 92 Pixel = Frame; 93 for (ULONG i = VidpYScale-1; i > 0; --i) 94 { 95 Pixel = (PULONG)((ULONG_PTR)Pixel + BytesPerScanLine); 96 RtlCopyMemory(Pixel, Frame, VidpXScale * SCREEN_WIDTH * BytesPerPixel); 97 } 98 } 99 100#ifdef COLORED_BORDERS 101 /* Right border */ 102 for (y = 0; y < VidpYScale * SCREEN_HEIGHT; ++y) 103 { 104 // Frame = (PULONG)(FrameBufferStart + FB_OFFSET(SCREEN_WIDTH, y)); 105 Frame = (PULONG)(FrameBufferStart + (PanV + y) * BytesPerScanLine + (PanH + VidpXScale * SCREEN_WIDTH) * BytesPerPixel); 106 for (x = 0; x < PanH; ++x) 107 { 108 *Frame++ = CachedPalette[BV_COLOR_BLACK]; 109 } 110 } 111 112 /* Bottom border */ 113 // Frame = (PULONG)(FrameBufferStart + FB_OFFSET(-(LONG)PanH, SCREEN_HEIGHT)); 114 Frame = (PULONG)(FrameBufferStart + (PanV + VidpYScale * SCREEN_HEIGHT) * BytesPerScanLine); 115 for (x = 0; x < PanV * ScreenWidth; ++x) 116 { 117 *Frame++ = CachedPalette[BV_COLOR_BLACK]; 118 } 119#endif // COLORED_BORDERS 120} 121 122/* PUBLIC FUNCTIONS **********************************************************/ 123 124BOOLEAN 125NTAPI 126VidInitialize( 127 _In_ BOOLEAN SetMode) 128{ 129 PHYSICAL_ADDRESS FrameBuffer; 130 PHYSICAL_ADDRESS VramAddress; 131 ULONG VramSize; 132 CM_FRAMEBUF_DEVICE_DATA VideoConfigData; /* Configuration data from hardware tree */ 133 INTERFACE_TYPE Interface; 134 ULONG BusNumber; 135 NTSTATUS Status; 136 137 /* Find boot-time framebuffer display information from the LoaderBlock */ 138 Status = FindBootDisplay(&VramAddress, 139 &VramSize, 140 &VideoConfigData, 141 NULL, // MonitorConfigData 142 &Interface, 143 &BusNumber); 144 if (!NT_SUCCESS(Status)) 145 { 146 DPRINT1("Boot framebuffer does not exist!\n"); 147 return FALSE; 148 } 149 150 /* The VRAM address must be page-aligned */ 151 if (VramAddress.QuadPart % PAGE_SIZE != 0) // DPRINTed for diagnostics on some systems 152 DPRINT1("** VramAddress 0x%I64X isn't PAGE_SIZE aligned\n", VramAddress.QuadPart); 153 ASSERT(VramAddress.QuadPart % PAGE_SIZE == 0); 154 if (VramSize % PAGE_SIZE != 0) 155 DPRINT1("** VramSize %lu (0x%lx) isn't multiple of PAGE_SIZE\n", VramSize, VramSize); 156 // ASSERT(VramSize % PAGE_SIZE == 0); // This assert may fail, e.g. 800x600@32bpp UEFI GOP display 157 158 /* Retrieve the framebuffer address, its visible screen dimensions, and its attributes */ 159 FrameBuffer.QuadPart = VramAddress.QuadPart + VideoConfigData.FrameBufferOffset; 160 ScreenWidth = VideoConfigData.ScreenWidth; 161 ScreenHeight = VideoConfigData.ScreenHeight; 162 if (ScreenWidth < SCREEN_WIDTH || ScreenHeight < SCREEN_HEIGHT) 163 { 164 DPRINT1("Unsupported screen resolution!\n"); 165 return FALSE; 166 } 167 168 BytesPerPixel = (VideoConfigData.BitsPerPixel + 7) / 8; // Round up to nearest byte. 169 ASSERT(BytesPerPixel >= 1 && BytesPerPixel <= 4); 170 if (BytesPerPixel != 4) 171 { 172 UNIMPLEMENTED; 173 DPRINT1("Unsupported BytesPerPixel = %u\n", BytesPerPixel); 174 return FALSE; 175 } 176 177 ASSERT(ScreenWidth <= VideoConfigData.PixelsPerScanLine); 178 BytesPerScanLine = VideoConfigData.PixelsPerScanLine * BytesPerPixel; 179 if (BytesPerScanLine < 1) 180 { 181 DPRINT1("Invalid BytesPerScanLine = %lu\n", BytesPerScanLine); 182 return FALSE; 183 } 184 185 /* Compute the visible framebuffer size */ 186 FrameBufferSize = ScreenHeight * BytesPerScanLine; 187 188 /* Verify that the framebuffer actually fits inside the video RAM */ 189 if (FrameBuffer.QuadPart + FrameBufferSize > VramAddress.QuadPart + VramSize) 190 { 191 DPRINT1("The framebuffer exceeds video memory bounds!\n"); 192 return FALSE; 193 } 194 195 /* Translate the framebuffer from bus-relative to physical address */ 196 PHYSICAL_ADDRESS TranslatedAddress; 197 ULONG AddressSpace = 0; /* MMIO space */ 198 if (!BootTranslateBusAddress(Interface, 199 BusNumber, 200 FrameBuffer, 201 &AddressSpace, 202 &TranslatedAddress)) 203 { 204 DPRINT1("Could not translate framebuffer bus address 0x%I64X\n", FrameBuffer.QuadPart); 205 return FALSE; 206 } 207 208 /* Map it into system space if necessary */ 209 ULONG MappedSize = 0; 210 PVOID FrameBufferBase = NULL; 211 if (AddressSpace == 0) 212 { 213 /* Calculate page-aligned address and size for MmMapIoSpace() */ 214 FrameBuffer.HighPart = TranslatedAddress.HighPart; 215 FrameBuffer.LowPart = ALIGN_DOWN_BY(TranslatedAddress.LowPart, PAGE_SIZE); 216 MappedSize = FrameBufferSize; 217 MappedSize += (ULONG)(TranslatedAddress.QuadPart - FrameBuffer.QuadPart); // BYTE_OFFSET() 218 MappedSize = ROUND_TO_PAGES(MappedSize); 219 /* Essentially MmMapVideoDisplay() */ 220 FrameBufferBase = MmMapIoSpace(FrameBuffer, MappedSize, MmFrameBufferCached); 221 if (!FrameBufferBase) 222 FrameBufferBase = MmMapIoSpace(FrameBuffer, MappedSize, MmNonCached); 223 if (!FrameBufferBase) 224 { 225 DPRINT1("Could not map framebuffer 0x%I64X (%lu bytes)\n", 226 FrameBuffer.QuadPart, MappedSize); 227 goto Failure; 228 } 229 FrameBufferStart = (ULONG_PTR)FrameBufferBase; 230 FrameBufferStart += (TranslatedAddress.QuadPart - FrameBuffer.QuadPart); // BYTE_OFFSET() 231 } 232 else 233 { 234 /* The base is the translated address, no need to map */ 235 FrameBufferStart = (ULONG_PTR)TranslatedAddress.QuadPart; 236 } 237 238 239 /* 240 * Reserve off-screen area for the backbuffer that contains 241 * 8-bit indexed color screen image, plus preserved row data. 242 */ 243 BackBufferSize = SCREEN_WIDTH * (SCREEN_HEIGHT + (BOOTCHAR_HEIGHT + 1)); 244 245 /* If there is enough video memory in the physical framebuffer, 246 * place the backbuffer in the hidden part of the framebuffer, 247 * otherwise allocate a zone for the backbuffer. */ 248 if (VideoConfigData.FrameBufferOffset + FrameBufferSize + BackBufferSize 249 <= ((AddressSpace == 0) ? MappedSize : VramSize)) 250 { 251 /* Backbuffer placed following the framebuffer in the hidden part */ 252 BackBuffer = (PUCHAR)(FrameBufferStart + FrameBufferSize); 253 // BackBuffer = (PUCHAR)(VramAddress + VramSize - BackBufferSize); // Or at the end of VRAM. 254 } 255 else 256 { 257 /* Allocate the backbuffer */ 258 PHYSICAL_ADDRESS NullAddress = {{0, 0}}; 259 PHYSICAL_ADDRESS HighestAddress = {{-1, -1}}; 260 BackBuffer = MmAllocateContiguousMemorySpecifyCache( 261 BackBufferSize, NullAddress, HighestAddress, 262 NullAddress, MmNonCached); 263 if (!BackBuffer) 264 { 265 DPRINT1("Could not allocate backbuffer (size: %lu)\n", (ULONG)BackBufferSize); 266 goto Failure; 267 } 268 } 269 270#ifdef SCALING_SUPPORT 271 /* Compute autoscaling; only integer (not fractional) scaling is supported */ 272 VidpXScale = ScreenWidth / SCREEN_WIDTH; 273 VidpYScale = ScreenHeight / SCREEN_HEIGHT; 274 ASSERT(VidpXScale >= 1); 275 ASSERT(VidpYScale >= 1); 276#ifdef SCALING_PROPORTIONAL 277 VidpXScale = min(VidpXScale, VidpYScale); 278 VidpYScale = VidpXScale; 279#endif 280 DPRINT1("Scaling X = %hu, Y = %hu\n", VidpXScale, VidpYScale); 281#endif // SCALING_SUPPORT 282 283 /* Calculate left/right and top/bottom border values 284 * to keep the displayed area centered on the screen */ 285 PanH = (ScreenWidth - VidpXScale * SCREEN_WIDTH) / 2; 286 PanV = (ScreenHeight - VidpYScale * SCREEN_HEIGHT) / 2; 287 DPRINT1("Borders X = %lu, Y = %lu\n", PanH, PanV); 288 289 /* Reset the video mode if requested */ 290 if (SetMode) 291 VidResetDisplay(TRUE); 292 293 return TRUE; 294 295Failure: 296 /* We failed somewhere; unmap the framebuffer if we mapped it */ 297 if (FrameBufferBase && (AddressSpace == 0)) 298 MmUnmapIoSpace(FrameBufferBase, MappedSize); 299 300 return FALSE; 301} 302 303VOID 304NTAPI 305VidCleanUp(VOID) 306{ 307 /* Just fill the screen black */ 308 VidSolidColorFill(0, 0, SCREEN_WIDTH - 1, SCREEN_HEIGHT - 1, BV_COLOR_BLACK); 309} 310 311VOID 312ResetDisplay( 313 _In_ BOOLEAN SetMode) 314{ 315 RtlZeroMemory(BackBuffer, BackBufferSize); 316 RtlZeroMemory((PVOID)FrameBufferStart, FrameBufferSize); 317 318 /* Re-initialize the palette and fill the screen black */ 319 InitializePalette(); 320 VidSolidColorFill(0, 0, SCREEN_WIDTH - 1, SCREEN_HEIGHT - 1, BV_COLOR_BLACK); 321} 322 323VOID 324InitPaletteWithTable( 325 _In_reads_(Count) const ULONG* Table, 326 _In_ ULONG Count) 327{ 328 const ULONG* Entry = Table; 329 ULONG i; 330 BOOLEAN HasChanged = FALSE; 331 332 for (i = 0; i < Count; i++, Entry++) 333 { 334 HasChanged |= !!((CachedPalette[i] ^ *Entry) & 0x00FFFFFF); 335 CachedPalette[i] = *Entry | 0xFF000000; 336 } 337 338 /* Re-apply the palette if it has changed */ 339 if (HasChanged) 340 ApplyPalette(); 341} 342 343VOID 344SetPixel( 345 _In_ ULONG Left, 346 _In_ ULONG Top, 347 _In_ UCHAR Color) 348{ 349 PUCHAR Back = BB_PIXEL(Left, Top); 350 PULONG Frame = (PULONG)FB_PIXEL(Left, Top); 351 352 *Back = Color; 353 for (ULONG i = VidpYScale; i > 0; --i) 354 { 355 PULONG Pixel = Frame; 356 for (ULONG j = VidpXScale; j > 0; --j) 357 *Pixel++ = CachedPalette[Color]; 358 Frame = (PULONG)((ULONG_PTR)Frame + BytesPerScanLine); 359 } 360} 361 362VOID 363PreserveRow( 364 _In_ ULONG CurrentTop, 365 _In_ ULONG TopDelta, 366 _In_ BOOLEAN Restore) 367{ 368 PUCHAR NewPosition, OldPosition; 369 370 /* Calculate the position in memory for the row */ 371 if (Restore) 372 { 373 /* Restore the row by copying back the contents saved off-screen */ 374 NewPosition = BB_PIXEL(0, CurrentTop); 375 OldPosition = BB_PIXEL(0, SCREEN_HEIGHT); 376 } 377 else 378 { 379 /* Preserve the row by saving its contents off-screen */ 380 NewPosition = BB_PIXEL(0, SCREEN_HEIGHT); 381 OldPosition = BB_PIXEL(0, CurrentTop); 382 } 383 384 /* Set the count and copy the pixel data back to the other position in the backbuffer */ 385 ULONG Count = TopDelta * SCREEN_WIDTH; 386 RtlCopyMemory(NewPosition, OldPosition, Count); 387 388 /* On restore, mirror the backbuffer changes to the framebuffer */ 389 if (Restore) 390 { 391 NewPosition = BB_PIXEL(0, CurrentTop); 392 for (ULONG y = 0; y < TopDelta; ++y) 393 { 394 PULONG Frame = (PULONG)FB_PIXEL(0, CurrentTop + y); 395 PULONG Pixel = Frame; 396 for (Count = 0; Count < SCREEN_WIDTH; ++Count) 397 { 398 for (ULONG j = VidpXScale; j > 0; --j) 399 *Pixel++ = CachedPalette[*NewPosition]; 400 NewPosition++; 401 } 402 Pixel = Frame; 403 for (ULONG i = VidpYScale-1; i > 0; --i) 404 { 405 Pixel = (PULONG)((ULONG_PTR)Pixel + BytesPerScanLine); 406 RtlCopyMemory(Pixel, Frame, VidpXScale * SCREEN_WIDTH * BytesPerPixel); 407 } 408 } 409 } 410} 411 412VOID 413DoScroll( 414 _In_ ULONG Scroll) 415{ 416 ULONG RowSize = VidpScrollRegion.Right - VidpScrollRegion.Left + 1; 417 418 /* Calculate the position in memory for the row */ 419 PUCHAR OldPosition = BB_PIXEL(VidpScrollRegion.Left, VidpScrollRegion.Top + Scroll); 420 PUCHAR NewPosition = BB_PIXEL(VidpScrollRegion.Left, VidpScrollRegion.Top); 421 422 /* Start loop */ 423 for (ULONG Top = VidpScrollRegion.Top; Top <= VidpScrollRegion.Bottom; ++Top) 424 { 425 /* Scroll the row */ 426 RtlCopyMemory(NewPosition, OldPosition, RowSize); 427 428 PULONG Frame = (PULONG)FB_PIXEL(VidpScrollRegion.Left, Top); 429 PULONG Pixel = Frame; 430 for (ULONG Count = 0; Count < RowSize; ++Count) 431 { 432 for (ULONG j = VidpXScale; j > 0; --j) 433 *Pixel++ = CachedPalette[NewPosition[Count]]; 434 } 435 Pixel = Frame; 436 for (ULONG i = VidpYScale-1; i > 0; --i) 437 { 438 Pixel = (PULONG)((ULONG_PTR)Pixel + BytesPerScanLine); 439 RtlCopyMemory(Pixel, Frame, VidpXScale * RowSize * BytesPerPixel); 440 } 441 442 OldPosition += SCREEN_WIDTH; 443 NewPosition += SCREEN_WIDTH; 444 } 445} 446 447VOID 448DisplayCharacter( 449 _In_ CHAR Character, 450 _In_ ULONG Left, 451 _In_ ULONG Top, 452 _In_ ULONG TextColor, 453 _In_ ULONG BackColor) 454{ 455 /* Get the font line for this character */ 456 const UCHAR* FontChar = GetFontPtr(Character); 457 458 /* Loop each pixel height */ 459 for (ULONG y = Top; y < Top + BOOTCHAR_HEIGHT; ++y, FontChar += FONT_PTR_DELTA) 460 { 461 /* Loop each pixel width */ 462 ULONG x = Left; 463 for (UCHAR bit = 1 << (BOOTCHAR_WIDTH - 1); bit > 0; bit >>= 1, ++x) 464 { 465 /* If we should draw this pixel, use the text color. Otherwise 466 * this is a background pixel, draw it unless it's transparent. */ 467 if (*FontChar & bit) 468 SetPixel(x, y, (UCHAR)TextColor); 469 else if (BackColor < BV_COLOR_NONE) 470 SetPixel(x, y, (UCHAR)BackColor); 471 } 472 } 473} 474 475VOID 476NTAPI 477VidSolidColorFill( 478 _In_ ULONG Left, 479 _In_ ULONG Top, 480 _In_ ULONG Right, 481 _In_ ULONG Bottom, 482 _In_ UCHAR Color) 483{ 484 for (; Top <= Bottom; ++Top) 485 { 486 PUCHAR Back = BB_PIXEL(Left, Top); 487 // NOTE: Assumes 32bpp 488 PULONG Frame = (PULONG)FB_PIXEL(Left, Top); 489 PULONG Pixel = Frame; 490 for (ULONG L = Left; L <= Right; ++L) 491 { 492 *Back++ = Color; 493 for (ULONG j = VidpXScale; j > 0; --j) 494 *Pixel++ = CachedPalette[Color]; 495 } 496 Pixel = Frame; 497 for (ULONG i = VidpYScale-1; i > 0; --i) 498 { 499 Pixel = (PULONG)((ULONG_PTR)Pixel + BytesPerScanLine); 500 RtlCopyMemory(Pixel, Frame, VidpXScale * (Right - Left + 1) * BytesPerPixel); 501 } 502 } 503} 504 505VOID 506NTAPI 507VidScreenToBufferBlt( 508 _Out_writes_bytes_all_(Delta * Height) PUCHAR Buffer, 509 _In_ ULONG Left, 510 _In_ ULONG Top, 511 _In_ ULONG Width, 512 _In_ ULONG Height, 513 _In_ ULONG Delta) 514{ 515 ULONG x, y; 516 517 /* Clear the destination buffer */ 518 RtlZeroMemory(Buffer, Delta * Height); 519 520 /* Start the outer Y height loop */ 521 for (y = 0; y < Height; ++y) 522 { 523 /* Set current scanline */ 524 PUCHAR Back = BB_PIXEL(Left, Top + y); 525 PUCHAR Buf = Buffer + y * Delta; 526 527 /* Start the X inner loop */ 528 for (x = 0; x < Width; x += sizeof(USHORT)) 529 { 530 /* Read the current value */ 531 *Buf = (*Back++ & 0xF) << 4; 532 *Buf |= *Back++ & 0xF; 533 Buf++; 534 } 535 } 536} 537 538/* EOF */