xref: /reactos/drivers/setup/blue/blue.c (revision 682f85ad)
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