1 /* 2 * PROJECT: ReactOS Console Text-Mode Device Driver 3 * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+) 4 * PURPOSE: Driver Management Functions. 5 * COPYRIGHT: Copyright 1999 Boudewijn Dekker 6 * Copyright 1999-2019 Eric Kohl 7 * Copyright 2006 Filip Navara 8 * Copyright 2019 Hermes Belusca-Maito 9 */ 10 11 /* INCLUDES ******************************************************************/ 12 13 #include "blue.h" 14 #include <ndk/inbvfuncs.h> 15 16 #define NDEBUG 17 #include <debug.h> 18 19 /* NOTES ******************************************************************/ 20 /* 21 * [[character][attribute]][[character][attribute]].... 22 */ 23 24 /* TYPEDEFS ***************************************************************/ 25 26 typedef struct _DEVICE_EXTENSION 27 { 28 PUCHAR VideoMemory; /* Pointer to video memory */ 29 SIZE_T VideoMemorySize; 30 BOOLEAN Enabled; 31 PUCHAR ScreenBuffer; /* Pointer to screenbuffer */ 32 SIZE_T ScreenBufferSize; 33 ULONG CursorSize; 34 INT CursorVisible; 35 USHORT CharAttribute; 36 ULONG Mode; 37 UCHAR ScanLines; /* Height of a text line */ 38 USHORT Rows; /* Number of rows */ 39 USHORT Columns; /* Number of columns */ 40 USHORT CursorX, CursorY; /* Cursor position */ 41 PUCHAR FontBitfield; /* Specifies the font. If NULL, use CodePage */ 42 ULONG CodePage; /* Specifies the font associated to this code page */ 43 } DEVICE_EXTENSION, *PDEVICE_EXTENSION; 44 45 typedef struct _VGA_REGISTERS 46 { 47 UCHAR CRT[24]; 48 UCHAR Attribute[21]; 49 UCHAR Graphics[9]; 50 UCHAR Sequencer[5]; 51 UCHAR Misc; 52 } VGA_REGISTERS, *PVGA_REGISTERS; 53 54 static const VGA_REGISTERS VidpMode3Regs = 55 { 56 /* CRT Controller Registers */ 57 {0x5F, 0x4F, 0x50, 0x82, 0x55, 0x81, 0xBF, 0x1F, 0x00, 0x47, 0x1E, 0x00, 58 0x00, 0x00, 0x05, 0xF0, 0x9C, 0x8E, 0x8F, 0x28, 0x1F, 0x96, 0xB9, 0xA3}, 59 /* Attribute Controller Registers */ 60 {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x14, 0x07, 0x38, 0x39, 0x3A, 0x3B, 61 0x3C, 0x3D, 0x3E, 0x3F, 0x0C, 0x00, 0x0F, 0x08, 0x00}, 62 /* Graphics Controller Registers */ 63 {0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x0E, 0x00, 0xFF}, 64 /* Sequencer Registers */ 65 {0x03, 0x00, 0x03, 0x00, 0x02}, 66 /* Misc Output Register */ 67 0x67 68 }; 69 70 static const UCHAR DefaultPalette[] = 71 { 72 0, 0, 0, 73 0, 0, 0xC0, 74 0, 0xC0, 0, 75 0, 0xC0, 0xC0, 76 0xC0, 0, 0, 77 0xC0, 0, 0xC0, 78 0xC0, 0xC0, 0, 79 0xC0, 0xC0, 0xC0, 80 0x80, 0x80, 0x80, 81 0, 0, 0xFF, 82 0, 0xFF, 0, 83 0, 0xFF, 0xFF, 84 0xFF, 0, 0, 85 0xFF, 0, 0xFF, 86 0xFF, 0xFF, 0, 87 0xFF, 0xFF, 0xFF 88 }; 89 90 /* INBV MANAGEMENT FUNCTIONS **************************************************/ 91 92 static BOOLEAN 93 ScrResetScreen( 94 _In_ PDEVICE_EXTENSION DeviceExtension, 95 _In_ BOOLEAN FullReset, 96 _In_ BOOLEAN Enable); 97 98 static PDEVICE_EXTENSION ResetDisplayParametersDeviceExtension = NULL; 99 static HANDLE InbvThreadHandle = NULL; 100 static BOOLEAN InbvMonitoring = FALSE; 101 102 /* 103 * Reinitialize the display to base VGA mode. 104 * 105 * Returns TRUE if it completely resets the adapter to the given character mode. 106 * Returns FALSE otherwise, indicating that the HAL should perform the VGA mode 107 * reset itself after HwVidResetHw() returns control. 108 * 109 * This callback has been registered with InbvNotifyDisplayOwnershipLost() 110 * and is called by InbvAcquireDisplayOwnership(), typically when the bugcheck 111 * code regains display access. Therefore this routine can be called at any 112 * IRQL, and in particular at IRQL = HIGH_LEVEL. This routine must also reside 113 * completely in non-paged pool, and cannot perform the following actions: 114 * Allocate memory, access pageable memory, use any synchronization mechanisms 115 * or call any routine that must execute at IRQL = DISPATCH_LEVEL or below. 116 */ 117 static BOOLEAN 118 NTAPI 119 ScrResetDisplayParametersEx( 120 _In_ ULONG Columns, 121 _In_ ULONG Rows, 122 _In_ BOOLEAN CalledByInbv) 123 { 124 PDEVICE_EXTENSION DeviceExtension; 125 126 /* Bail out early if we don't have any resettable adapter */ 127 if (!ResetDisplayParametersDeviceExtension) 128 return FALSE; // No adapter found: request HAL to perform a full reset. 129 130 /* 131 * If we have been unexpectedly called via a callback from 132 * InbvAcquireDisplayOwnership(), start monitoring INBV. 133 */ 134 if (CalledByInbv) 135 InbvMonitoring = TRUE; 136 137 DeviceExtension = ResetDisplayParametersDeviceExtension; 138 ASSERT(DeviceExtension); 139 140 /* Disable the screen but don't reset all screen settings (OK at high IRQL) */ 141 return ScrResetScreen(DeviceExtension, FALSE, FALSE); 142 } 143 144 /* This callback is registered with InbvNotifyDisplayOwnershipLost() */ 145 static BOOLEAN 146 NTAPI 147 ScrResetDisplayParameters( 148 _In_ ULONG Columns, 149 _In_ ULONG Rows) 150 { 151 /* Call the extended function, specifying we were called by INBV */ 152 return ScrResetDisplayParametersEx(Columns, Rows, TRUE); 153 } 154 155 /* 156 * (Adapted for ReactOS/Win2k3 from an original comment 157 * by Gé van Geldorp, June 2003, r4937) 158 * 159 * DISPLAY OWNERSHIP 160 * 161 * So, who owns the physical display and is allowed to write to it? 162 * 163 * In NT 5.x (Win2k/Win2k3), upon boot INBV/BootVid owns the display, unless 164 * /NOGUIBOOT has been specified in the boot command line. Later in the boot 165 * sequence, WIN32K.SYS opens the DISPLAY device. This open call ends up in 166 * VIDEOPRT.SYS. This component takes ownership of the display by calling 167 * InbvNotifyDisplayOwnershipLost() -- effectively telling INBV to release 168 * ownership of the display it previously had. From that moment on, the display 169 * is owned by that component and can be switched to graphics mode. The display 170 * is not supposed to return to text mode, except in case of a bugcheck. 171 * The bugcheck code calls InbvAcquireDisplayOwnership() so as to make INBV 172 * re-take display ownership, and calls back the function previously registered 173 * by VIDEOPRT.SYS with InbvNotifyDisplayOwnershipLost(). After the bugcheck, 174 * execution is halted. So, under NT, the only possible sequence of display 175 * modes is text mode -> graphics mode -> text mode (the latter hopefully 176 * happening very infrequently). 177 * 178 * In ReactOS things are a little bit different. We want to have a functional 179 * interactive text mode. We should be able to switch back and forth from 180 * text mode to graphics mode when a GUI app is started and then finished. 181 * Also, when the system bugchecks in graphics mode we want to switch back to 182 * text mode and show the bugcheck information. Last but not least, when using 183 * KDBG in /DEBUGPORT=SCREEN mode, breaking into the debugger would trigger a 184 * switch to text mode, and the user would expect that by continuing execution 185 * a switch back to graphics mode is done. 186 */ 187 static VOID 188 NTAPI 189 InbvMonitorThread( 190 _In_ PVOID Context) 191 { 192 LARGE_INTEGER Delay; 193 USHORT i; 194 195 KeSetPriorityThread(KeGetCurrentThread(), LOW_REALTIME_PRIORITY); 196 197 while (TRUE) 198 { 199 /* 200 * During one second, check the INBV status each 100 milliseconds, 201 * then revert to 1 second delay. 202 */ 203 i = 10; 204 Delay.QuadPart = (LONGLONG)-100*1000*10; // 100 millisecond delay 205 while (!InbvMonitoring) 206 { 207 KeDelayExecutionThread(KernelMode, FALSE, &Delay); 208 209 if ((i > 0) && (--i == 0)) 210 Delay.QuadPart = (LONGLONG)-1*1000*1000*10; // 1 second delay 211 } 212 213 /* 214 * Loop while the display is owned by INBV. We cannot do anything else 215 * than polling since INBV does not offer a proper notification system. 216 * 217 * During one second, check the INBV status each 100 milliseconds, 218 * then revert to 1 second delay. 219 */ 220 i = 10; 221 Delay.QuadPart = (LONGLONG)-100*1000*10; // 100 millisecond delay 222 while (InbvCheckDisplayOwnership()) 223 { 224 KeDelayExecutionThread(KernelMode, FALSE, &Delay); 225 226 if ((i > 0) && (--i == 0)) 227 Delay.QuadPart = (LONGLONG)-1*1000*1000*10; // 1 second delay 228 } 229 230 /* Reset the monitoring */ 231 InbvMonitoring = FALSE; 232 233 /* 234 * Somebody released INBV display ownership, usually by invoking 235 * InbvNotifyDisplayOwnershipLost(). However the caller of this 236 * function certainly specified a different callback than ours. 237 * As we are going to be the only owner of the active display, 238 * we need to re-register our own display reset callback. 239 */ 240 InbvNotifyDisplayOwnershipLost(ScrResetDisplayParameters); 241 242 /* Re-enable the screen, keeping the original screen settings */ 243 if (ResetDisplayParametersDeviceExtension) 244 ScrResetScreen(ResetDisplayParametersDeviceExtension, FALSE, TRUE); 245 } 246 247 // FIXME: See ScrInbvCleanup(). 248 // PsTerminateSystemThread(STATUS_SUCCESS); 249 } 250 251 static NTSTATUS 252 ScrInbvInitialize(VOID) 253 { 254 /* Create the INBV monitoring thread if needed */ 255 if (!InbvThreadHandle) 256 { 257 NTSTATUS Status; 258 OBJECT_ATTRIBUTES ObjectAttributes = RTL_CONSTANT_OBJECT_ATTRIBUTES(NULL, OBJ_KERNEL_HANDLE); 259 260 Status = PsCreateSystemThread(&InbvThreadHandle, 261 0, 262 &ObjectAttributes, 263 NULL, 264 NULL, 265 InbvMonitorThread, 266 NULL); 267 if (!NT_SUCCESS(Status)) 268 InbvThreadHandle = NULL; 269 } 270 271 /* Re-register the display reset callback with INBV */ 272 InbvNotifyDisplayOwnershipLost(ScrResetDisplayParameters); 273 274 return STATUS_SUCCESS; 275 } 276 277 static NTSTATUS 278 ScrInbvCleanup(VOID) 279 { 280 // HANDLE ThreadHandle; 281 282 // ResetDisplayParametersDeviceExtension = NULL; 283 if (ResetDisplayParametersDeviceExtension) 284 { 285 InbvNotifyDisplayOwnershipLost(NULL); 286 ScrResetDisplayParametersEx(80, 50, FALSE); 287 // or InbvAcquireDisplayOwnership(); ? 288 } 289 290 #if 0 291 // TODO: Find the best way to communicate the request. 292 /* Signal the INBV monitoring thread and wait for it to terminate */ 293 ThreadHandle = InterlockedExchangePointer((PVOID*)&InbvThreadHandle, NULL); 294 if (ThreadHandle) 295 { 296 ZwWaitForSingleObject(ThreadHandle, Executive, KernelMode, FALSE, NULL); 297 /* Close its handle */ 298 ObCloseHandle(ThreadHandle, KernelMode); 299 } 300 #endif 301 302 return STATUS_SUCCESS; 303 } 304 305 /* FUNCTIONS **************************************************************/ 306 307 static VOID 308 FASTCALL 309 ScrSetRegisters(const VGA_REGISTERS *Registers) 310 { 311 UINT32 i; 312 313 /* Update misc output register */ 314 WRITE_PORT_UCHAR(MISC, Registers->Misc); 315 316 /* Synchronous reset on */ 317 WRITE_PORT_UCHAR(SEQ, 0x00); 318 WRITE_PORT_UCHAR(SEQDATA, 0x01); 319 320 /* Write sequencer registers */ 321 for (i = 1; i < sizeof(Registers->Sequencer); i++) 322 { 323 WRITE_PORT_UCHAR(SEQ, i); 324 WRITE_PORT_UCHAR(SEQDATA, Registers->Sequencer[i]); 325 } 326 327 /* Synchronous reset off */ 328 WRITE_PORT_UCHAR(SEQ, 0x00); 329 WRITE_PORT_UCHAR(SEQDATA, 0x03); 330 331 /* Deprotect CRT registers 0-7 */ 332 WRITE_PORT_UCHAR(CRTC, 0x11); 333 WRITE_PORT_UCHAR(CRTCDATA, Registers->CRT[0x11] & 0x7f); 334 335 /* Write CRT registers */ 336 for (i = 0; i < sizeof(Registers->CRT); i++) 337 { 338 WRITE_PORT_UCHAR(CRTC, i); 339 WRITE_PORT_UCHAR(CRTCDATA, Registers->CRT[i]); 340 } 341 342 /* Write graphics controller registers */ 343 for (i = 0; i < sizeof(Registers->Graphics); i++) 344 { 345 WRITE_PORT_UCHAR(GRAPHICS, i); 346 WRITE_PORT_UCHAR(GRAPHICSDATA, Registers->Graphics[i]); 347 } 348 349 /* Write attribute controller registers */ 350 for (i = 0; i < sizeof(Registers->Attribute); i++) 351 { 352 READ_PORT_UCHAR(STATUS); 353 WRITE_PORT_UCHAR(ATTRIB, i); 354 WRITE_PORT_UCHAR(ATTRIB, Registers->Attribute[i]); 355 } 356 357 /* Set the PEL mask */ 358 WRITE_PORT_UCHAR(PELMASK, 0xff); 359 } 360 361 static VOID 362 FASTCALL 363 ScrSetCursor( 364 _In_ PDEVICE_EXTENSION DeviceExtension) 365 { 366 ULONG Offset; 367 368 if (!DeviceExtension->VideoMemory) 369 return; 370 371 Offset = (DeviceExtension->CursorY * DeviceExtension->Columns) + DeviceExtension->CursorX; 372 373 _disable(); 374 WRITE_PORT_UCHAR(CRTC_COMMAND, CRTC_CURSORPOSLO); 375 WRITE_PORT_UCHAR(CRTC_DATA, Offset); 376 WRITE_PORT_UCHAR(CRTC_COMMAND, CRTC_CURSORPOSHI); 377 WRITE_PORT_UCHAR(CRTC_DATA, Offset >> 8); 378 _enable(); 379 } 380 381 static VOID 382 FASTCALL 383 ScrSetCursorShape( 384 _In_ PDEVICE_EXTENSION DeviceExtension) 385 { 386 ULONG size, height; 387 UCHAR data, value; 388 389 if (!DeviceExtension->VideoMemory) 390 return; 391 392 height = DeviceExtension->ScanLines; 393 data = (DeviceExtension->CursorVisible) ? 0x00 : 0x20; 394 395 size = (DeviceExtension->CursorSize * height) / 100; 396 if (size < 1) 397 size = 1; 398 399 data |= (UCHAR)(height - size); 400 401 _disable(); 402 WRITE_PORT_UCHAR(CRTC_COMMAND, CRTC_CURSORSTART); 403 WRITE_PORT_UCHAR(CRTC_DATA, data); 404 WRITE_PORT_UCHAR(CRTC_COMMAND, CRTC_CURSOREND); 405 value = READ_PORT_UCHAR(CRTC_DATA) & 0xE0; 406 WRITE_PORT_UCHAR(CRTC_DATA, value | (height - 1)); 407 _enable(); 408 } 409 410 static VOID 411 FASTCALL 412 ScrAcquireOwnership( 413 _In_ PDEVICE_EXTENSION DeviceExtension) 414 { 415 UCHAR data, value; 416 ULONG offset; 417 ULONG Index; 418 419 _disable(); 420 421 ScrSetRegisters(&VidpMode3Regs); 422 423 /* Disable screen and enable palette access */ 424 READ_PORT_UCHAR(STATUS); 425 WRITE_PORT_UCHAR(ATTRIB, 0x00); 426 427 for (Index = 0; Index < sizeof(DefaultPalette) / 3; Index++) 428 { 429 WRITE_PORT_UCHAR(PELINDEX, Index); 430 WRITE_PORT_UCHAR(PELDATA, DefaultPalette[Index * 3] >> 2); 431 WRITE_PORT_UCHAR(PELDATA, DefaultPalette[Index * 3 + 1] >> 2); 432 WRITE_PORT_UCHAR(PELDATA, DefaultPalette[Index * 3 + 2] >> 2); 433 } 434 435 /* Enable screen and disable palette access */ 436 READ_PORT_UCHAR(STATUS); 437 WRITE_PORT_UCHAR(ATTRIB, 0x20); 438 439 /* Switch blinking characters off */ 440 READ_PORT_UCHAR(ATTRC_INPST1); 441 value = READ_PORT_UCHAR(ATTRC_WRITEREG); 442 WRITE_PORT_UCHAR(ATTRC_WRITEREG, 0x10); 443 data = READ_PORT_UCHAR(ATTRC_READREG); 444 data = data & ~0x08; 445 WRITE_PORT_UCHAR(ATTRC_WRITEREG, data); 446 WRITE_PORT_UCHAR(ATTRC_WRITEREG, value); 447 READ_PORT_UCHAR(ATTRC_INPST1); 448 449 /* Read screen information from CRT controller */ 450 WRITE_PORT_UCHAR(CRTC_COMMAND, CRTC_COLUMNS); 451 DeviceExtension->Columns = READ_PORT_UCHAR(CRTC_DATA) + 1; 452 WRITE_PORT_UCHAR(CRTC_COMMAND, CRTC_ROWS); 453 DeviceExtension->Rows = READ_PORT_UCHAR(CRTC_DATA); 454 WRITE_PORT_UCHAR(CRTC_COMMAND, CRTC_OVERFLOW); 455 data = READ_PORT_UCHAR(CRTC_DATA); 456 DeviceExtension->Rows |= (((data & 0x02) << 7) | ((data & 0x40) << 3)); 457 DeviceExtension->Rows++; 458 WRITE_PORT_UCHAR(CRTC_COMMAND, CRTC_SCANLINES); 459 DeviceExtension->ScanLines = (READ_PORT_UCHAR(CRTC_DATA) & 0x1F) + 1; 460 461 /* Retrieve the current output cursor position */ 462 WRITE_PORT_UCHAR(CRTC_COMMAND, CRTC_CURSORPOSLO); 463 offset = READ_PORT_UCHAR(CRTC_DATA); 464 WRITE_PORT_UCHAR(CRTC_COMMAND, CRTC_CURSORPOSHI); 465 offset += (READ_PORT_UCHAR(CRTC_DATA) << 8); 466 467 /* Show blinking cursor */ 468 // FIXME: cursor block? Call ScrSetCursorShape() instead? 469 WRITE_PORT_UCHAR(CRTC_COMMAND, CRTC_CURSORSTART); 470 WRITE_PORT_UCHAR(CRTC_DATA, (DeviceExtension->ScanLines - 1) & 0x1F); 471 WRITE_PORT_UCHAR(CRTC_COMMAND, CRTC_CURSOREND); 472 data = READ_PORT_UCHAR(CRTC_DATA) & 0xE0; 473 WRITE_PORT_UCHAR(CRTC_DATA, 474 data | ((DeviceExtension->ScanLines - 1) & 0x1F)); 475 476 _enable(); 477 478 /* Calculate number of text rows */ 479 DeviceExtension->Rows = DeviceExtension->Rows / DeviceExtension->ScanLines; 480 481 /* Set the cursor position, clipping it to the screen */ 482 DeviceExtension->CursorX = (USHORT)(offset % DeviceExtension->Columns); 483 DeviceExtension->CursorY = (USHORT)(offset / DeviceExtension->Columns); 484 // DeviceExtension->CursorX = min(max(DeviceExtension->CursorX, 0), DeviceExtension->Columns - 1); 485 DeviceExtension->CursorY = min(max(DeviceExtension->CursorY, 0), DeviceExtension->Rows - 1); 486 487 if (DeviceExtension->FontBitfield) 488 { 489 ScrSetFont(DeviceExtension->FontBitfield); 490 } 491 else 492 { 493 /* Upload a default font for the current codepage */ 494 ScrLoadFontTable(DeviceExtension->CodePage); 495 } 496 497 DPRINT("%d Columns %d Rows %d Scanlines\n", 498 DeviceExtension->Columns, 499 DeviceExtension->Rows, 500 DeviceExtension->ScanLines); 501 } 502 503 static BOOLEAN 504 ScrResetScreen( 505 _In_ PDEVICE_EXTENSION DeviceExtension, 506 _In_ BOOLEAN FullReset, 507 _In_ BOOLEAN Enable) 508 { 509 #define FOREGROUND_LIGHTGRAY (FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED) 510 511 PHYSICAL_ADDRESS BaseAddress; 512 513 /* Allow resets to the same state only for full resets */ 514 if (!FullReset && (Enable == DeviceExtension->Enabled)) 515 return FALSE; // STATUS_INVALID_PARAMETER; STATUS_INVALID_DEVICE_REQUEST; 516 517 if (FullReset) 518 { 519 DeviceExtension->CursorSize = 5; /* FIXME: value correct?? */ 520 DeviceExtension->CursorVisible = TRUE; 521 522 if (DeviceExtension->FontBitfield) 523 { 524 ExFreePoolWithTag(DeviceExtension->FontBitfield, TAG_BLUE); 525 DeviceExtension->FontBitfield = NULL; 526 } 527 528 /* More initialization */ 529 DeviceExtension->CharAttribute = BACKGROUND_BLUE | FOREGROUND_LIGHTGRAY; 530 DeviceExtension->Mode = ENABLE_PROCESSED_OUTPUT | 531 ENABLE_WRAP_AT_EOL_OUTPUT; 532 DeviceExtension->CodePage = 437; /* Use default codepage */ 533 } 534 535 if (Enable) 536 { 537 ScrAcquireOwnership(DeviceExtension); 538 539 if (FullReset) 540 { 541 /* 542 * Fully reset the screen and all its settings. 543 */ 544 545 /* Unmap any previously mapped video memory */ 546 if (DeviceExtension->VideoMemory) 547 { 548 ASSERT(DeviceExtension->VideoMemorySize != 0); 549 MmUnmapIoSpace(DeviceExtension->VideoMemory, DeviceExtension->VideoMemorySize); 550 } 551 DeviceExtension->VideoMemory = NULL; 552 DeviceExtension->VideoMemorySize = 0; 553 554 /* Free any previously allocated backup screenbuffer */ 555 if (DeviceExtension->ScreenBuffer) 556 { 557 ASSERT(DeviceExtension->ScreenBufferSize != 0); 558 ExFreePoolWithTag(DeviceExtension->ScreenBuffer, TAG_BLUE); 559 } 560 DeviceExtension->ScreenBuffer = NULL; 561 DeviceExtension->ScreenBufferSize = 0; 562 563 /* Get a pointer to the video memory */ 564 DeviceExtension->VideoMemorySize = DeviceExtension->Rows * DeviceExtension->Columns * 2; 565 if (DeviceExtension->VideoMemorySize == 0) 566 return FALSE; // STATUS_INVALID_VIEW_SIZE; STATUS_MAPPED_FILE_SIZE_ZERO; 567 568 /* Map the video memory */ 569 BaseAddress.QuadPart = VIDMEM_BASE; 570 DeviceExtension->VideoMemory = 571 (PUCHAR)MmMapIoSpace(BaseAddress, DeviceExtension->VideoMemorySize, MmNonCached); 572 if (!DeviceExtension->VideoMemory) 573 { 574 DeviceExtension->VideoMemorySize = 0; 575 return FALSE; // STATUS_NONE_MAPPED; STATUS_NOT_MAPPED_VIEW; STATUS_CONFLICTING_ADDRESSES; 576 } 577 578 /* Initialize the backup screenbuffer in non-paged pool (must be accessible at high IRQL) */ 579 DeviceExtension->ScreenBufferSize = DeviceExtension->VideoMemorySize; 580 DeviceExtension->ScreenBuffer = 581 (PUCHAR)ExAllocatePoolWithTag(NonPagedPool, DeviceExtension->ScreenBufferSize, TAG_BLUE); 582 if (!DeviceExtension->ScreenBuffer) 583 { 584 DPRINT1("Could not allocate screenbuffer, ignore...\n"); 585 DeviceExtension->ScreenBufferSize = 0; 586 } 587 588 /* (Re-)initialize INBV */ 589 ScrInbvInitialize(); 590 } 591 else 592 { 593 /* 594 * Restore the previously disabled screen. 595 */ 596 597 /* Restore the snapshot of the video memory from the backup screenbuffer */ 598 if (DeviceExtension->ScreenBuffer) 599 { 600 ASSERT(DeviceExtension->VideoMemory); 601 ASSERT(DeviceExtension->ScreenBuffer); 602 ASSERT(DeviceExtension->ScreenBufferSize != 0); 603 ASSERT(DeviceExtension->VideoMemorySize == DeviceExtension->ScreenBufferSize); 604 605 RtlCopyMemory(DeviceExtension->VideoMemory, 606 DeviceExtension->ScreenBuffer, 607 DeviceExtension->VideoMemorySize); 608 } 609 610 /* Restore the cursor state */ 611 ScrSetCursor(DeviceExtension); 612 ScrSetCursorShape(DeviceExtension); 613 } 614 DeviceExtension->Enabled = TRUE; 615 } 616 else 617 { 618 DeviceExtension->Enabled = FALSE; 619 if (FullReset) 620 { 621 /* 622 * Fully disable the screen and reset all its settings. 623 */ 624 625 /* Clean INBV up */ 626 ScrInbvCleanup(); 627 628 /* Unmap any previously mapped video memory */ 629 if (DeviceExtension->VideoMemory) 630 { 631 ASSERT(DeviceExtension->VideoMemorySize != 0); 632 MmUnmapIoSpace(DeviceExtension->VideoMemory, DeviceExtension->VideoMemorySize); 633 } 634 DeviceExtension->VideoMemory = NULL; 635 DeviceExtension->VideoMemorySize = 0; 636 637 /* Free any previously allocated backup screenbuffer */ 638 if (DeviceExtension->ScreenBuffer) 639 { 640 ASSERT(DeviceExtension->ScreenBufferSize != 0); 641 ExFreePoolWithTag(DeviceExtension->ScreenBuffer, TAG_BLUE); 642 } 643 DeviceExtension->ScreenBuffer = NULL; 644 DeviceExtension->ScreenBufferSize = 0; 645 646 /* Store dummy values */ 647 DeviceExtension->Columns = 1; 648 DeviceExtension->Rows = 1; 649 DeviceExtension->ScanLines = 1; 650 } 651 else 652 { 653 /* 654 * Partially disable the screen such that it can be restored later. 655 */ 656 657 /* Take a snapshot of the video memory into the backup screenbuffer */ 658 if (DeviceExtension->ScreenBuffer) 659 { 660 ASSERT(DeviceExtension->VideoMemory); 661 ASSERT(DeviceExtension->ScreenBuffer); 662 ASSERT(DeviceExtension->ScreenBufferSize != 0); 663 ASSERT(DeviceExtension->VideoMemorySize == DeviceExtension->ScreenBufferSize); 664 665 RtlCopyMemory(DeviceExtension->ScreenBuffer, 666 DeviceExtension->VideoMemory, 667 DeviceExtension->VideoMemorySize); 668 } 669 } 670 } 671 672 return TRUE; // STATUS_SUCCESS; 673 } 674 675 static DRIVER_DISPATCH ScrCreateClose; 676 static NTSTATUS 677 NTAPI 678 ScrCreateClose( 679 _In_ PDEVICE_OBJECT DeviceObject, 680 _In_ PIRP Irp) 681 { 682 PIO_STACK_LOCATION stk = IoGetCurrentIrpStackLocation(Irp); 683 684 UNREFERENCED_PARAMETER(DeviceObject); 685 686 if (stk->MajorFunction == IRP_MJ_CREATE) 687 Irp->IoStatus.Information = FILE_OPENED; 688 // else: IRP_MJ_CLOSE 689 690 Irp->IoStatus.Status = STATUS_SUCCESS; 691 IoCompleteRequest(Irp, IO_NO_INCREMENT); 692 return STATUS_SUCCESS; 693 } 694 695 static DRIVER_DISPATCH ScrWrite; 696 static NTSTATUS 697 NTAPI 698 ScrWrite( 699 _In_ PDEVICE_OBJECT DeviceObject, 700 _In_ PIRP Irp) 701 { 702 NTSTATUS Status; 703 PIO_STACK_LOCATION stk = IoGetCurrentIrpStackLocation(Irp); 704 PDEVICE_EXTENSION DeviceExtension = DeviceObject->DeviceExtension; 705 PCHAR pch = Irp->UserBuffer; 706 PUCHAR vidmem; 707 ULONG i; 708 ULONG j, offset; 709 USHORT cursorx, cursory; 710 USHORT rows, columns; 711 BOOLEAN processed = !!(DeviceExtension->Mode & ENABLE_PROCESSED_OUTPUT); 712 713 if (!DeviceExtension->Enabled || !DeviceExtension->VideoMemory) 714 { 715 /* Display is not enabled, we're not allowed to touch it */ 716 Status = STATUS_SUCCESS; 717 718 Irp->IoStatus.Status = Status; 719 IoCompleteRequest(Irp, IO_NO_INCREMENT); 720 721 return Status; 722 } 723 724 vidmem = DeviceExtension->VideoMemory; 725 rows = DeviceExtension->Rows; 726 columns = DeviceExtension->Columns; 727 cursorx = DeviceExtension->CursorX; 728 cursory = DeviceExtension->CursorY; 729 730 if (!processed) 731 { 732 /* Raw output mode */ 733 734 /* Calculate the offset from the cursor position */ 735 offset = cursorx + cursory * columns; 736 737 // FIXME: Does the buffer only contains chars? or chars + attributes? 738 // FIXME2: Fix buffer overflow. 739 RtlCopyMemory(&vidmem[offset * 2], pch, stk->Parameters.Write.Length); 740 offset += (stk->Parameters.Write.Length / 2); 741 742 /* Set the cursor position, clipping it to the screen */ 743 cursorx = (USHORT)(offset % columns); 744 cursory = (USHORT)(offset / columns); 745 // cursorx = min(max(cursorx, 0), columns - 1); 746 cursory = min(max(cursory, 0), rows - 1); 747 } 748 else 749 { 750 /* Cooked output mode */ 751 for (i = 0; i < stk->Parameters.Write.Length; i++, pch++) 752 { 753 switch (*pch) 754 { 755 case '\b': 756 { 757 if (cursorx > 0) 758 { 759 cursorx--; 760 } 761 else if (cursory > 0) 762 { 763 cursory--; 764 cursorx = columns - 1; 765 } 766 offset = cursorx + cursory * columns; 767 vidmem[offset * 2] = ' '; 768 vidmem[offset * 2 + 1] = (char)DeviceExtension->CharAttribute; 769 break; 770 } 771 772 case '\n': 773 cursory++; 774 /* Fall back */ 775 case '\r': 776 cursorx = 0; 777 break; 778 779 case '\t': 780 { 781 offset = TAB_WIDTH - (cursorx % TAB_WIDTH); 782 while (offset--) 783 { 784 vidmem[(cursorx + cursory * columns) * 2] = ' '; 785 cursorx++; 786 if (cursorx >= columns) 787 { 788 cursorx = 0; 789 cursory++; 790 /* We jumped to the next line, stop there */ 791 break; 792 } 793 } 794 break; 795 } 796 797 default: 798 { 799 offset = cursorx + cursory * columns; 800 vidmem[offset * 2] = *pch; 801 vidmem[offset * 2 + 1] = (char)DeviceExtension->CharAttribute; 802 cursorx++; 803 if (cursorx >= columns) 804 { 805 cursorx = 0; 806 cursory++; 807 } 808 break; 809 } 810 } 811 812 /* Scroll up the contents of the screen if we are at the end */ 813 if (cursory >= rows) 814 { 815 PUSHORT LinePtr; 816 817 RtlCopyMemory(vidmem, 818 &vidmem[columns * 2], 819 columns * (rows - 1) * 2); 820 821 LinePtr = (PUSHORT)&vidmem[columns * (rows - 1) * 2]; 822 823 for (j = 0; j < columns; j++) 824 { 825 LinePtr[j] = DeviceExtension->CharAttribute << 8; 826 } 827 cursory = rows - 1; 828 for (j = 0; j < columns; j++) 829 { 830 offset = j + cursory * columns; 831 vidmem[offset * 2] = ' '; 832 vidmem[offset * 2 + 1] = (char)DeviceExtension->CharAttribute; 833 } 834 } 835 } 836 } 837 838 /* Set the cursor position */ 839 ASSERT((0 <= cursorx) && (cursorx < DeviceExtension->Columns)); 840 ASSERT((0 <= cursory) && (cursory < DeviceExtension->Rows)); 841 DeviceExtension->CursorX = cursorx; 842 DeviceExtension->CursorY = cursory; 843 ScrSetCursor(DeviceExtension); 844 845 Status = STATUS_SUCCESS; 846 847 Irp->IoStatus.Status = Status; 848 IoCompleteRequest(Irp, IO_VIDEO_INCREMENT); 849 850 return Status; 851 } 852 853 static DRIVER_DISPATCH ScrIoControl; 854 static NTSTATUS 855 NTAPI 856 ScrIoControl( 857 _In_ PDEVICE_OBJECT DeviceObject, 858 _In_ PIRP Irp) 859 { 860 NTSTATUS Status; 861 PIO_STACK_LOCATION stk = IoGetCurrentIrpStackLocation(Irp); 862 PDEVICE_EXTENSION DeviceExtension = DeviceObject->DeviceExtension; 863 864 switch (stk->Parameters.DeviceIoControl.IoControlCode) 865 { 866 case IOCTL_CONSOLE_RESET_SCREEN: 867 { 868 BOOLEAN Enable; 869 870 /* Validate input buffer */ 871 if (stk->Parameters.DeviceIoControl.InputBufferLength < sizeof(ULONG)) 872 { 873 Status = STATUS_INVALID_PARAMETER; 874 break; 875 } 876 ASSERT(Irp->AssociatedIrp.SystemBuffer); 877 878 Enable = !!*(PULONG)Irp->AssociatedIrp.SystemBuffer; 879 880 /* Fully enable or disable the screen */ 881 Status = (ScrResetScreen(DeviceExtension, TRUE, Enable) 882 ? STATUS_SUCCESS : STATUS_UNSUCCESSFUL); 883 Irp->IoStatus.Information = 0; 884 break; 885 } 886 887 case IOCTL_CONSOLE_GET_SCREEN_BUFFER_INFO: 888 { 889 PCONSOLE_SCREEN_BUFFER_INFO pcsbi; 890 USHORT rows = DeviceExtension->Rows; 891 USHORT columns = DeviceExtension->Columns; 892 893 /* Validate output buffer */ 894 if (stk->Parameters.DeviceIoControl.OutputBufferLength < sizeof(CONSOLE_SCREEN_BUFFER_INFO)) 895 { 896 Status = STATUS_INVALID_PARAMETER; 897 break; 898 } 899 ASSERT(Irp->AssociatedIrp.SystemBuffer); 900 901 pcsbi = (PCONSOLE_SCREEN_BUFFER_INFO)Irp->AssociatedIrp.SystemBuffer; 902 RtlZeroMemory(pcsbi, sizeof(CONSOLE_SCREEN_BUFFER_INFO)); 903 904 pcsbi->dwSize.X = columns; 905 pcsbi->dwSize.Y = rows; 906 907 pcsbi->dwCursorPosition.X = DeviceExtension->CursorX; 908 pcsbi->dwCursorPosition.Y = DeviceExtension->CursorY; 909 910 pcsbi->wAttributes = DeviceExtension->CharAttribute; 911 912 pcsbi->srWindow.Left = 0; 913 pcsbi->srWindow.Right = columns - 1; 914 pcsbi->srWindow.Top = 0; 915 pcsbi->srWindow.Bottom = rows - 1; 916 917 pcsbi->dwMaximumWindowSize.X = columns; 918 pcsbi->dwMaximumWindowSize.Y = rows; 919 920 Irp->IoStatus.Information = sizeof(CONSOLE_SCREEN_BUFFER_INFO); 921 Status = STATUS_SUCCESS; 922 break; 923 } 924 925 case IOCTL_CONSOLE_SET_SCREEN_BUFFER_INFO: 926 { 927 PCONSOLE_SCREEN_BUFFER_INFO pcsbi; 928 929 /* Validate input buffer */ 930 if (stk->Parameters.DeviceIoControl.InputBufferLength < sizeof(CONSOLE_SCREEN_BUFFER_INFO)) 931 { 932 Status = STATUS_INVALID_PARAMETER; 933 break; 934 } 935 ASSERT(Irp->AssociatedIrp.SystemBuffer); 936 937 pcsbi = (PCONSOLE_SCREEN_BUFFER_INFO)Irp->AssociatedIrp.SystemBuffer; 938 939 if ( pcsbi->dwCursorPosition.X < 0 || pcsbi->dwCursorPosition.X >= DeviceExtension->Columns || 940 pcsbi->dwCursorPosition.Y < 0 || pcsbi->dwCursorPosition.Y >= DeviceExtension->Rows ) 941 { 942 Irp->IoStatus.Information = 0; 943 Status = STATUS_INVALID_PARAMETER; 944 break; 945 } 946 947 DeviceExtension->CharAttribute = pcsbi->wAttributes; 948 949 /* Set the cursor position */ 950 ASSERT((0 <= pcsbi->dwCursorPosition.X) && (pcsbi->dwCursorPosition.X < DeviceExtension->Columns)); 951 ASSERT((0 <= pcsbi->dwCursorPosition.Y) && (pcsbi->dwCursorPosition.Y < DeviceExtension->Rows)); 952 DeviceExtension->CursorX = pcsbi->dwCursorPosition.X; 953 DeviceExtension->CursorY = pcsbi->dwCursorPosition.Y; 954 if (DeviceExtension->Enabled) 955 ScrSetCursor(DeviceExtension); 956 957 Irp->IoStatus.Information = 0; 958 Status = STATUS_SUCCESS; 959 break; 960 } 961 962 case IOCTL_CONSOLE_GET_CURSOR_INFO: 963 { 964 PCONSOLE_CURSOR_INFO pcci; 965 966 /* Validate output buffer */ 967 if (stk->Parameters.DeviceIoControl.OutputBufferLength < sizeof(CONSOLE_CURSOR_INFO)) 968 { 969 Status = STATUS_INVALID_PARAMETER; 970 break; 971 } 972 ASSERT(Irp->AssociatedIrp.SystemBuffer); 973 974 pcci = (PCONSOLE_CURSOR_INFO)Irp->AssociatedIrp.SystemBuffer; 975 RtlZeroMemory(pcci, sizeof(CONSOLE_CURSOR_INFO)); 976 977 pcci->dwSize = DeviceExtension->CursorSize; 978 pcci->bVisible = DeviceExtension->CursorVisible; 979 980 Irp->IoStatus.Information = sizeof(CONSOLE_CURSOR_INFO); 981 Status = STATUS_SUCCESS; 982 break; 983 } 984 985 case IOCTL_CONSOLE_SET_CURSOR_INFO: 986 { 987 PCONSOLE_CURSOR_INFO pcci; 988 989 /* Validate input buffer */ 990 if (stk->Parameters.DeviceIoControl.InputBufferLength < sizeof(CONSOLE_CURSOR_INFO)) 991 { 992 Status = STATUS_INVALID_PARAMETER; 993 break; 994 } 995 ASSERT(Irp->AssociatedIrp.SystemBuffer); 996 997 pcci = (PCONSOLE_CURSOR_INFO)Irp->AssociatedIrp.SystemBuffer; 998 999 DeviceExtension->CursorSize = pcci->dwSize; 1000 DeviceExtension->CursorVisible = pcci->bVisible; 1001 if (DeviceExtension->Enabled) 1002 ScrSetCursorShape(DeviceExtension); 1003 1004 Irp->IoStatus.Information = 0; 1005 Status = STATUS_SUCCESS; 1006 break; 1007 } 1008 1009 case IOCTL_CONSOLE_GET_MODE: 1010 { 1011 PCONSOLE_MODE pcm; 1012 1013 /* Validate output buffer */ 1014 if (stk->Parameters.DeviceIoControl.OutputBufferLength < sizeof(CONSOLE_MODE)) 1015 { 1016 Status = STATUS_INVALID_PARAMETER; 1017 break; 1018 } 1019 ASSERT(Irp->AssociatedIrp.SystemBuffer); 1020 1021 pcm = (PCONSOLE_MODE)Irp->AssociatedIrp.SystemBuffer; 1022 RtlZeroMemory(pcm, sizeof(CONSOLE_MODE)); 1023 1024 pcm->dwMode = DeviceExtension->Mode; 1025 1026 Irp->IoStatus.Information = sizeof(CONSOLE_MODE); 1027 Status = STATUS_SUCCESS; 1028 break; 1029 } 1030 1031 case IOCTL_CONSOLE_SET_MODE: 1032 { 1033 PCONSOLE_MODE pcm; 1034 1035 /* Validate input buffer */ 1036 if (stk->Parameters.DeviceIoControl.InputBufferLength < sizeof(CONSOLE_MODE)) 1037 { 1038 Status = STATUS_INVALID_PARAMETER; 1039 break; 1040 } 1041 ASSERT(Irp->AssociatedIrp.SystemBuffer); 1042 1043 pcm = (PCONSOLE_MODE)Irp->AssociatedIrp.SystemBuffer; 1044 DeviceExtension->Mode = pcm->dwMode; 1045 1046 Irp->IoStatus.Information = 0; 1047 Status = STATUS_SUCCESS; 1048 break; 1049 } 1050 1051 case IOCTL_CONSOLE_FILL_OUTPUT_ATTRIBUTE: 1052 { 1053 POUTPUT_ATTRIBUTE Buf; 1054 PUCHAR vidmem; 1055 ULONG offset; 1056 ULONG dwCount; 1057 ULONG nMaxLength; 1058 1059 /* Validate input and output buffers */ 1060 if (stk->Parameters.DeviceIoControl.InputBufferLength < sizeof(OUTPUT_ATTRIBUTE) || 1061 stk->Parameters.DeviceIoControl.OutputBufferLength < sizeof(OUTPUT_ATTRIBUTE)) 1062 { 1063 Status = STATUS_INVALID_PARAMETER; 1064 break; 1065 } 1066 ASSERT(Irp->AssociatedIrp.SystemBuffer); 1067 1068 Buf = (POUTPUT_ATTRIBUTE)Irp->AssociatedIrp.SystemBuffer; 1069 nMaxLength = Buf->nLength; 1070 1071 Buf->dwTransfered = 0; 1072 Irp->IoStatus.Information = sizeof(OUTPUT_ATTRIBUTE); 1073 1074 if ( Buf->dwCoord.X < 0 || Buf->dwCoord.X >= DeviceExtension->Columns || 1075 Buf->dwCoord.Y < 0 || Buf->dwCoord.Y >= DeviceExtension->Rows || 1076 nMaxLength == 0 ) 1077 { 1078 Status = STATUS_SUCCESS; 1079 break; 1080 } 1081 1082 if (DeviceExtension->Enabled && DeviceExtension->VideoMemory) 1083 { 1084 vidmem = DeviceExtension->VideoMemory; 1085 offset = (Buf->dwCoord.X + Buf->dwCoord.Y * DeviceExtension->Columns) * 2 + 1; 1086 1087 nMaxLength = min(nMaxLength, 1088 (DeviceExtension->Rows - Buf->dwCoord.Y) 1089 * DeviceExtension->Columns - Buf->dwCoord.X); 1090 1091 for (dwCount = 0; dwCount < nMaxLength; dwCount++) 1092 { 1093 vidmem[offset + (dwCount * 2)] = (char)Buf->wAttribute; 1094 } 1095 Buf->dwTransfered = dwCount; 1096 } 1097 1098 Status = STATUS_SUCCESS; 1099 break; 1100 } 1101 1102 case IOCTL_CONSOLE_READ_OUTPUT_ATTRIBUTE: 1103 { 1104 POUTPUT_ATTRIBUTE Buf; 1105 PUSHORT pAttr; 1106 PUCHAR vidmem; 1107 ULONG offset; 1108 ULONG dwCount; 1109 ULONG nMaxLength; 1110 1111 /* Validate input buffer */ 1112 if (stk->Parameters.DeviceIoControl.InputBufferLength < sizeof(OUTPUT_ATTRIBUTE)) 1113 { 1114 Status = STATUS_INVALID_PARAMETER; 1115 break; 1116 } 1117 ASSERT(Irp->AssociatedIrp.SystemBuffer); 1118 1119 Buf = (POUTPUT_ATTRIBUTE)Irp->AssociatedIrp.SystemBuffer; 1120 Irp->IoStatus.Information = 0; 1121 1122 /* Validate output buffer */ 1123 if (stk->Parameters.DeviceIoControl.OutputBufferLength == 0) 1124 { 1125 Status = STATUS_SUCCESS; 1126 break; 1127 } 1128 ASSERT(Irp->MdlAddress); 1129 pAttr = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority); 1130 if (pAttr == NULL) 1131 { 1132 Status = STATUS_INSUFFICIENT_RESOURCES; 1133 break; 1134 } 1135 1136 if ( Buf->dwCoord.X < 0 || Buf->dwCoord.X >= DeviceExtension->Columns || 1137 Buf->dwCoord.Y < 0 || Buf->dwCoord.Y >= DeviceExtension->Rows ) 1138 { 1139 Status = STATUS_SUCCESS; 1140 break; 1141 } 1142 1143 nMaxLength = stk->Parameters.DeviceIoControl.OutputBufferLength; 1144 nMaxLength /= sizeof(USHORT); 1145 1146 if (DeviceExtension->Enabled && DeviceExtension->VideoMemory) 1147 { 1148 vidmem = DeviceExtension->VideoMemory; 1149 offset = (Buf->dwCoord.X + Buf->dwCoord.Y * DeviceExtension->Columns) * 2 + 1; 1150 1151 nMaxLength = min(nMaxLength, 1152 (DeviceExtension->Rows - Buf->dwCoord.Y) 1153 * DeviceExtension->Columns - Buf->dwCoord.X); 1154 1155 for (dwCount = 0; dwCount < nMaxLength; dwCount++, pAttr++) 1156 { 1157 *((PCHAR)pAttr) = vidmem[offset + (dwCount * 2)]; 1158 } 1159 Irp->IoStatus.Information = dwCount * sizeof(USHORT); 1160 } 1161 1162 Status = STATUS_SUCCESS; 1163 break; 1164 } 1165 1166 case IOCTL_CONSOLE_WRITE_OUTPUT_ATTRIBUTE: 1167 { 1168 COORD dwCoord; 1169 PCOORD pCoord; 1170 PUSHORT pAttr; 1171 PUCHAR vidmem; 1172 ULONG offset; 1173 ULONG dwCount; 1174 ULONG nMaxLength; 1175 1176 // 1177 // NOTE: For whatever reason no OUTPUT_ATTRIBUTE structure 1178 // is used for this IOCTL. 1179 // 1180 1181 /* Validate output buffer */ 1182 if (stk->Parameters.DeviceIoControl.OutputBufferLength < sizeof(COORD)) 1183 { 1184 Status = STATUS_INVALID_PARAMETER; 1185 break; 1186 } 1187 ASSERT(Irp->MdlAddress); 1188 pCoord = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority); 1189 if (pCoord == NULL) 1190 { 1191 Status = STATUS_INSUFFICIENT_RESOURCES; 1192 break; 1193 } 1194 /* Capture the input info data */ 1195 dwCoord = *pCoord; 1196 1197 nMaxLength = stk->Parameters.DeviceIoControl.OutputBufferLength - sizeof(COORD); 1198 nMaxLength /= sizeof(USHORT); 1199 1200 Irp->IoStatus.Information = 0; 1201 1202 if ( dwCoord.X < 0 || dwCoord.X >= DeviceExtension->Columns || 1203 dwCoord.Y < 0 || dwCoord.Y >= DeviceExtension->Rows || 1204 nMaxLength == 0 ) 1205 { 1206 Status = STATUS_SUCCESS; 1207 break; 1208 } 1209 1210 pAttr = (PUSHORT)(pCoord + 1); 1211 1212 if (DeviceExtension->Enabled && DeviceExtension->VideoMemory) 1213 { 1214 vidmem = DeviceExtension->VideoMemory; 1215 offset = (dwCoord.X + dwCoord.Y * DeviceExtension->Columns) * 2 + 1; 1216 1217 nMaxLength = min(nMaxLength, 1218 (DeviceExtension->Rows - dwCoord.Y) 1219 * DeviceExtension->Columns - dwCoord.X); 1220 1221 for (dwCount = 0; dwCount < nMaxLength; dwCount++, pAttr++) 1222 { 1223 vidmem[offset + (dwCount * 2)] = *((PCHAR)pAttr); 1224 } 1225 Irp->IoStatus.Information = dwCount * sizeof(USHORT); 1226 } 1227 1228 Status = STATUS_SUCCESS; 1229 break; 1230 } 1231 1232 case IOCTL_CONSOLE_SET_TEXT_ATTRIBUTE: 1233 { 1234 /* Validate input buffer */ 1235 if (stk->Parameters.DeviceIoControl.InputBufferLength < sizeof(USHORT)) 1236 { 1237 Status = STATUS_INVALID_PARAMETER; 1238 break; 1239 } 1240 ASSERT(Irp->AssociatedIrp.SystemBuffer); 1241 1242 DeviceExtension->CharAttribute = *(PUSHORT)Irp->AssociatedIrp.SystemBuffer; 1243 1244 Irp->IoStatus.Information = 0; 1245 Status = STATUS_SUCCESS; 1246 break; 1247 } 1248 1249 case IOCTL_CONSOLE_FILL_OUTPUT_CHARACTER: 1250 { 1251 POUTPUT_CHARACTER Buf; 1252 PUCHAR vidmem; 1253 ULONG offset; 1254 ULONG dwCount; 1255 ULONG nMaxLength; 1256 1257 /* Validate input and output buffers */ 1258 if (stk->Parameters.DeviceIoControl.InputBufferLength < sizeof(OUTPUT_CHARACTER) || 1259 stk->Parameters.DeviceIoControl.OutputBufferLength < sizeof(OUTPUT_CHARACTER)) 1260 { 1261 Status = STATUS_INVALID_PARAMETER; 1262 break; 1263 } 1264 ASSERT(Irp->AssociatedIrp.SystemBuffer); 1265 1266 Buf = (POUTPUT_CHARACTER)Irp->AssociatedIrp.SystemBuffer; 1267 nMaxLength = Buf->nLength; 1268 1269 Buf->dwTransfered = 0; 1270 Irp->IoStatus.Information = sizeof(OUTPUT_CHARACTER); 1271 1272 if ( Buf->dwCoord.X < 0 || Buf->dwCoord.X >= DeviceExtension->Columns || 1273 Buf->dwCoord.Y < 0 || Buf->dwCoord.Y >= DeviceExtension->Rows || 1274 nMaxLength == 0 ) 1275 { 1276 Status = STATUS_SUCCESS; 1277 break; 1278 } 1279 1280 if (DeviceExtension->Enabled && DeviceExtension->VideoMemory) 1281 { 1282 vidmem = DeviceExtension->VideoMemory; 1283 offset = (Buf->dwCoord.X + Buf->dwCoord.Y * DeviceExtension->Columns) * 2; 1284 1285 nMaxLength = min(nMaxLength, 1286 (DeviceExtension->Rows - Buf->dwCoord.Y) 1287 * DeviceExtension->Columns - Buf->dwCoord.X); 1288 1289 for (dwCount = 0; dwCount < nMaxLength; dwCount++) 1290 { 1291 vidmem[offset + (dwCount * 2)] = (char)Buf->cCharacter; 1292 } 1293 Buf->dwTransfered = dwCount; 1294 } 1295 1296 Status = STATUS_SUCCESS; 1297 break; 1298 } 1299 1300 case IOCTL_CONSOLE_READ_OUTPUT_CHARACTER: 1301 { 1302 POUTPUT_CHARACTER Buf; 1303 PCHAR pChar; 1304 PUCHAR vidmem; 1305 ULONG offset; 1306 ULONG dwCount; 1307 ULONG nMaxLength; 1308 1309 /* Validate input buffer */ 1310 if (stk->Parameters.DeviceIoControl.InputBufferLength < sizeof(OUTPUT_CHARACTER)) 1311 { 1312 Status = STATUS_INVALID_PARAMETER; 1313 break; 1314 } 1315 ASSERT(Irp->AssociatedIrp.SystemBuffer); 1316 1317 Buf = (POUTPUT_CHARACTER)Irp->AssociatedIrp.SystemBuffer; 1318 Irp->IoStatus.Information = 0; 1319 1320 /* Validate output buffer */ 1321 if (stk->Parameters.DeviceIoControl.OutputBufferLength == 0) 1322 { 1323 Status = STATUS_SUCCESS; 1324 break; 1325 } 1326 ASSERT(Irp->MdlAddress); 1327 pChar = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority); 1328 if (pChar == NULL) 1329 { 1330 Status = STATUS_INSUFFICIENT_RESOURCES; 1331 break; 1332 } 1333 1334 if ( Buf->dwCoord.X < 0 || Buf->dwCoord.X >= DeviceExtension->Columns || 1335 Buf->dwCoord.Y < 0 || Buf->dwCoord.Y >= DeviceExtension->Rows ) 1336 { 1337 Status = STATUS_SUCCESS; 1338 break; 1339 } 1340 1341 nMaxLength = stk->Parameters.DeviceIoControl.OutputBufferLength; 1342 1343 if (DeviceExtension->Enabled && DeviceExtension->VideoMemory) 1344 { 1345 vidmem = DeviceExtension->VideoMemory; 1346 offset = (Buf->dwCoord.X + Buf->dwCoord.Y * DeviceExtension->Columns) * 2; 1347 1348 nMaxLength = min(nMaxLength, 1349 (DeviceExtension->Rows - Buf->dwCoord.Y) 1350 * DeviceExtension->Columns - Buf->dwCoord.X); 1351 1352 for (dwCount = 0; dwCount < nMaxLength; dwCount++, pChar++) 1353 { 1354 *pChar = vidmem[offset + (dwCount * 2)]; 1355 } 1356 Irp->IoStatus.Information = dwCount * sizeof(CHAR); 1357 } 1358 1359 Status = STATUS_SUCCESS; 1360 break; 1361 } 1362 1363 case IOCTL_CONSOLE_WRITE_OUTPUT_CHARACTER: 1364 { 1365 COORD dwCoord; 1366 PCOORD pCoord; 1367 PCHAR pChar; 1368 PUCHAR vidmem; 1369 ULONG offset; 1370 ULONG dwCount; 1371 ULONG nMaxLength; 1372 1373 // 1374 // NOTE: For whatever reason no OUTPUT_CHARACTER structure 1375 // is used for this IOCTL. 1376 // 1377 1378 /* Validate output buffer */ 1379 if (stk->Parameters.DeviceIoControl.OutputBufferLength < sizeof(COORD)) 1380 { 1381 Status = STATUS_INVALID_PARAMETER; 1382 break; 1383 } 1384 ASSERT(Irp->MdlAddress); 1385 pCoord = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority); 1386 if (pCoord == NULL) 1387 { 1388 Status = STATUS_INSUFFICIENT_RESOURCES; 1389 break; 1390 } 1391 /* Capture the input info data */ 1392 dwCoord = *pCoord; 1393 1394 nMaxLength = stk->Parameters.DeviceIoControl.OutputBufferLength - sizeof(COORD); 1395 Irp->IoStatus.Information = 0; 1396 1397 if ( dwCoord.X < 0 || dwCoord.X >= DeviceExtension->Columns || 1398 dwCoord.Y < 0 || dwCoord.Y >= DeviceExtension->Rows || 1399 nMaxLength == 0 ) 1400 { 1401 Status = STATUS_SUCCESS; 1402 break; 1403 } 1404 1405 pChar = (PCHAR)(pCoord + 1); 1406 1407 if (DeviceExtension->Enabled && DeviceExtension->VideoMemory) 1408 { 1409 vidmem = DeviceExtension->VideoMemory; 1410 offset = (dwCoord.X + dwCoord.Y * DeviceExtension->Columns) * 2; 1411 1412 nMaxLength = min(nMaxLength, 1413 (DeviceExtension->Rows - dwCoord.Y) 1414 * DeviceExtension->Columns - dwCoord.X); 1415 1416 for (dwCount = 0; dwCount < nMaxLength; dwCount++, pChar++) 1417 { 1418 vidmem[offset + (dwCount * 2)] = *pChar; 1419 } 1420 Irp->IoStatus.Information = dwCount * sizeof(CHAR); 1421 } 1422 1423 Status = STATUS_SUCCESS; 1424 break; 1425 } 1426 1427 case IOCTL_CONSOLE_DRAW: 1428 { 1429 CONSOLE_DRAW ConsoleDraw; 1430 PCONSOLE_DRAW pConsoleDraw; 1431 PUCHAR Src, Dest; 1432 UINT32 SrcDelta, DestDelta, i; 1433 1434 /* Validate output buffer */ 1435 if (stk->Parameters.DeviceIoControl.OutputBufferLength < sizeof(CONSOLE_DRAW)) 1436 { 1437 Status = STATUS_INVALID_PARAMETER; 1438 break; 1439 } 1440 ASSERT(Irp->MdlAddress); 1441 pConsoleDraw = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority); 1442 if (pConsoleDraw == NULL) 1443 { 1444 Status = STATUS_INSUFFICIENT_RESOURCES; 1445 break; 1446 } 1447 /* Capture the input info data */ 1448 ConsoleDraw = *pConsoleDraw; 1449 1450 /* Check whether we have the size for the header plus the data area */ 1451 if ((stk->Parameters.DeviceIoControl.OutputBufferLength - sizeof(CONSOLE_DRAW)) / 2 1452 < ((ULONG)ConsoleDraw.SizeX * (ULONG)ConsoleDraw.SizeY)) 1453 { 1454 Status = STATUS_INVALID_BUFFER_SIZE; 1455 break; 1456 } 1457 1458 Irp->IoStatus.Information = 0; 1459 1460 /* Set the cursor position, clipping it to the screen */ 1461 DeviceExtension->CursorX = min(max(ConsoleDraw.CursorX, 0), DeviceExtension->Columns - 1); 1462 DeviceExtension->CursorY = min(max(ConsoleDraw.CursorY, 0), DeviceExtension->Rows - 1); 1463 if (DeviceExtension->Enabled) 1464 ScrSetCursor(DeviceExtension); 1465 1466 // TODO: For the moment if the ConsoleDraw rectangle has borders 1467 // out of the screen-buffer we just bail out. Would it be better 1468 // to actually clip the rectangle within its borders instead? 1469 if ( ConsoleDraw.X < 0 || ConsoleDraw.X >= DeviceExtension->Columns || 1470 ConsoleDraw.Y < 0 || ConsoleDraw.Y >= DeviceExtension->Rows ) 1471 { 1472 Status = STATUS_SUCCESS; 1473 break; 1474 } 1475 if ( ConsoleDraw.SizeX > DeviceExtension->Columns - ConsoleDraw.X || 1476 ConsoleDraw.SizeY > DeviceExtension->Rows - ConsoleDraw.Y ) 1477 { 1478 Status = STATUS_SUCCESS; 1479 break; 1480 } 1481 1482 if (DeviceExtension->Enabled && DeviceExtension->VideoMemory) 1483 { 1484 Src = (PUCHAR)(pConsoleDraw + 1); 1485 SrcDelta = ConsoleDraw.SizeX * 2; 1486 Dest = DeviceExtension->VideoMemory + 1487 (ConsoleDraw.X + ConsoleDraw.Y * DeviceExtension->Columns) * 2; 1488 DestDelta = DeviceExtension->Columns * 2; 1489 /* 2 == sizeof(CHAR) + sizeof(BYTE) */ 1490 1491 /* Copy each line */ 1492 for (i = 0; i < ConsoleDraw.SizeY; i++) 1493 { 1494 RtlCopyMemory(Dest, Src, SrcDelta); 1495 Src += SrcDelta; 1496 Dest += DestDelta; 1497 } 1498 } 1499 1500 Status = STATUS_SUCCESS; 1501 break; 1502 } 1503 1504 case IOCTL_CONSOLE_LOADFONT: 1505 { 1506 /* Validate input buffer */ 1507 if (stk->Parameters.DeviceIoControl.InputBufferLength < sizeof(ULONG)) 1508 { 1509 Status = STATUS_INVALID_PARAMETER; 1510 break; 1511 } 1512 ASSERT(Irp->AssociatedIrp.SystemBuffer); 1513 1514 if (DeviceExtension->FontBitfield) 1515 { 1516 ExFreePoolWithTag(DeviceExtension->FontBitfield, TAG_BLUE); 1517 DeviceExtension->FontBitfield = NULL; 1518 } 1519 DeviceExtension->CodePage = *(PULONG)Irp->AssociatedIrp.SystemBuffer; 1520 1521 /* Upload a font for the codepage if needed */ 1522 if (DeviceExtension->Enabled && DeviceExtension->VideoMemory) 1523 ScrLoadFontTable(DeviceExtension->CodePage); 1524 1525 Irp->IoStatus.Information = 0; 1526 Status = STATUS_SUCCESS; 1527 break; 1528 } 1529 1530 case IOCTL_CONSOLE_SETFONT: 1531 { 1532 /* Validate input buffer */ 1533 if (stk->Parameters.DeviceIoControl.InputBufferLength < 256 * 8) 1534 { 1535 Status = STATUS_INVALID_PARAMETER; 1536 break; 1537 } 1538 ASSERT(Irp->AssociatedIrp.SystemBuffer); 1539 1540 DeviceExtension->CodePage = 0; 1541 if (DeviceExtension->FontBitfield) 1542 ExFreePoolWithTag(DeviceExtension->FontBitfield, TAG_BLUE); 1543 DeviceExtension->FontBitfield = ExAllocatePoolWithTag(NonPagedPool, 256 * 8, TAG_BLUE); 1544 if (!DeviceExtension->FontBitfield) 1545 { 1546 Status = STATUS_NO_MEMORY; 1547 break; 1548 } 1549 RtlCopyMemory(DeviceExtension->FontBitfield, Irp->AssociatedIrp.SystemBuffer, 256 * 8); 1550 1551 /* Upload the font if needed */ 1552 if (DeviceExtension->Enabled && DeviceExtension->VideoMemory) 1553 ScrSetFont(DeviceExtension->FontBitfield); 1554 1555 Irp->IoStatus.Information = 0; 1556 Status = STATUS_SUCCESS; 1557 break; 1558 } 1559 1560 default: 1561 Status = STATUS_NOT_IMPLEMENTED; 1562 } 1563 1564 Irp->IoStatus.Status = Status; 1565 IoCompleteRequest(Irp, IO_VIDEO_INCREMENT); 1566 1567 return Status; 1568 } 1569 1570 static DRIVER_DISPATCH ScrDispatch; 1571 static NTSTATUS 1572 NTAPI 1573 ScrDispatch( 1574 _In_ PDEVICE_OBJECT DeviceObject, 1575 _In_ PIRP Irp) 1576 { 1577 PIO_STACK_LOCATION stk = IoGetCurrentIrpStackLocation(Irp); 1578 1579 UNREFERENCED_PARAMETER(DeviceObject); 1580 1581 DPRINT1("ScrDispatch(0x%p): stk->MajorFunction = %lu UNIMPLEMENTED\n", 1582 DeviceObject, stk->MajorFunction); 1583 1584 Irp->IoStatus.Status = STATUS_NOT_IMPLEMENTED; 1585 IoCompleteRequest(Irp, IO_NO_INCREMENT); 1586 return STATUS_NOT_IMPLEMENTED; 1587 } 1588 1589 /* 1590 * Module entry point 1591 */ 1592 NTSTATUS 1593 NTAPI 1594 DriverEntry( 1595 _In_ PDRIVER_OBJECT DriverObject, 1596 _In_ PUNICODE_STRING RegistryPath) 1597 { 1598 NTSTATUS Status; 1599 PDEVICE_OBJECT DeviceObject; 1600 UNICODE_STRING DeviceName = RTL_CONSTANT_STRING(L"\\Device\\BlueScreen"); 1601 UNICODE_STRING SymlinkName = RTL_CONSTANT_STRING(L"\\??\\BlueScreen"); 1602 1603 DPRINT("Screen Driver 0.0.6\n"); 1604 1605 DriverObject->MajorFunction[IRP_MJ_CREATE] = ScrCreateClose; 1606 DriverObject->MajorFunction[IRP_MJ_CLOSE] = ScrCreateClose; 1607 DriverObject->MajorFunction[IRP_MJ_READ] = ScrDispatch; 1608 DriverObject->MajorFunction[IRP_MJ_WRITE] = ScrWrite; 1609 DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = ScrIoControl; 1610 1611 Status = IoCreateDevice(DriverObject, 1612 sizeof(DEVICE_EXTENSION), 1613 &DeviceName, 1614 FILE_DEVICE_SCREEN, 1615 FILE_DEVICE_SECURE_OPEN, 1616 TRUE, 1617 &DeviceObject); 1618 if (!NT_SUCCESS(Status)) 1619 { 1620 return Status; 1621 } 1622 1623 Status = IoCreateSymbolicLink(&SymlinkName, &DeviceName); 1624 if (NT_SUCCESS(Status)) 1625 { 1626 /* By default disable the screen (but don't touch INBV: ResetDisplayParametersDeviceExtension is still NULL) */ 1627 ScrResetScreen(DeviceObject->DeviceExtension, TRUE, FALSE); 1628 /* Now set ResetDisplayParametersDeviceExtension to enable synchronizing with INBV */ 1629 ResetDisplayParametersDeviceExtension = DeviceObject->DeviceExtension; 1630 DeviceObject->Flags &= ~DO_DEVICE_INITIALIZING; 1631 } 1632 else 1633 { 1634 IoDeleteDevice(DeviceObject); 1635 } 1636 return Status; 1637 } 1638 1639 /* EOF */ 1640