2 * PROJECT: ReactOS Kernel
3 * LICENSE: BSD - See COPYING.ARM in the top level directory
4 * PURPOSE: Boot Video Driver support
5 * COPYRIGHT: Copyright 2007 Alex Ionescu (alex.ionescu@reactos.org)
6 * Copyright 2010 Aleksey Bragin (aleksey@reactos.org)
7 * Copyright 2015-2022 Hermès Bélusca-Maïto
10 /* INCLUDES ******************************************************************/
13 #include "inbv/logo.h"
15 /* GLOBALS *******************************************************************/
18 * Enable this define if you want Inbv to use coloured headless mode.
20 // #define INBV_HEADLESS_COLORS
22 typedef struct _INBV_PROGRESS_STATE
27 } INBV_PROGRESS_STATE
;
29 typedef struct _BT_PROGRESS_INDICATOR
34 } BT_PROGRESS_INDICATOR
, *PBT_PROGRESS_INDICATOR
;
36 static KSPIN_LOCK BootDriverLock
;
37 static KIRQL InbvOldIrql
;
38 static INBV_DISPLAY_STATE InbvDisplayState
= INBV_DISPLAY_STATE_DISABLED
;
39 BOOLEAN InbvBootDriverInstalled
= FALSE
;
40 static INBV_RESET_DISPLAY_PARAMETERS InbvResetDisplayParameters
= NULL
;
42 static BOOLEAN InbvDisplayDebugStrings
= FALSE
;
43 static INBV_DISPLAY_STRING_FILTER InbvDisplayFilter
= NULL
;
45 ULONG ProgressBarLeft
= 0, ProgressBarTop
= 0;
46 BOOLEAN ShowProgressBar
= FALSE
;
47 static INBV_PROGRESS_STATE InbvProgressState
;
48 static BT_PROGRESS_INDICATOR InbvProgressIndicator
= {0, 25, 0};
50 static ULONG ResourceCount
= 0;
51 static PUCHAR ResourceList
[1 + IDB_MAX_RESOURCES
]; // First entry == NULL, followed by 'ResourceCount' entries.
55 * Headless terminal text colors
58 #ifdef INBV_HEADLESS_COLORS
60 // Conversion table CGA to ANSI color index
61 static const UCHAR CGA_TO_ANSI_COLOR_TABLE
[16] =
79 67 // Bright Grey (White)
82 #define CGA_TO_ANSI_COLOR(CgaColor) \
83 CGA_TO_ANSI_COLOR_TABLE[CgaColor & 0x0F]
87 // Default colors: text in white, background in black
88 static ULONG InbvTerminalTextColor
= 37;
89 static ULONG InbvTerminalBkgdColor
= 40;
92 /* FUNCTIONS *****************************************************************/
98 _In_ PLOADER_PARAMETER_BLOCK LoaderBlock
,
99 _In_ ULONG ResourceId
)
101 UNICODE_STRING UpString
= RTL_CONSTANT_STRING(L
"ntoskrnl.exe");
102 UNICODE_STRING MpString
= RTL_CONSTANT_STRING(L
"ntkrnlmp.exe");
103 PLIST_ENTRY NextEntry
, ListHead
;
104 PLDR_DATA_TABLE_ENTRY LdrEntry
;
105 PIMAGE_RESOURCE_DATA_ENTRY ResourceDataEntry
;
106 LDR_RESOURCE_INFO ResourceInfo
;
110 /* Loop the driver list */
111 ListHead
= &LoaderBlock
->LoadOrderListHead
;
112 for (NextEntry
= ListHead
->Flink
;
113 NextEntry
!= ListHead
;
114 NextEntry
= NextEntry
->Flink
)
117 LdrEntry
= CONTAINING_RECORD(NextEntry
,
118 LDR_DATA_TABLE_ENTRY
,
121 /* Check for a match */
122 if (RtlEqualUnicodeString(&LdrEntry
->BaseDllName
, &UpString
, TRUE
) ||
123 RtlEqualUnicodeString(&LdrEntry
->BaseDllName
, &MpString
, TRUE
))
130 /* Check if we found it */
131 if (NextEntry
!= ListHead
)
133 /* Try to find the resource */
134 ResourceInfo
.Type
= 2; // RT_BITMAP;
135 ResourceInfo
.Name
= ResourceId
;
136 ResourceInfo
.Language
= 0;
137 Status
= LdrFindResource_U(LdrEntry
->DllBase
,
141 if (NT_SUCCESS(Status
))
143 /* Access the resource */
145 Status
= LdrAccessResource(LdrEntry
->DllBase
,
149 if ((Data
) && (ResourceId
< 3))
151 KiBugCheckData
[4] ^= RtlComputeCrc32(0, Data
, Size
);
153 if (!NT_SUCCESS(Status
)) Data
= NULL
;
157 /* Return the pointer */
163 InbvGetResourceAddress(
164 _In_ ULONG ResourceNumber
)
166 /* Validate the resource number */
167 if (ResourceNumber
> ResourceCount
) return NULL
;
169 /* Return the address */
170 return ResourceList
[ResourceNumber
];
176 InbvDriverInitialize(
177 _In_ PLOADER_PARAMETER_BLOCK LoaderBlock
,
181 BOOLEAN ResetMode
= FALSE
; // By default do not reset the video mode
184 /* Quit if we're already installed */
185 if (InbvBootDriverInstalled
) return TRUE
;
187 /* Initialize the lock and check the current display state */
188 KeInitializeSpinLock(&BootDriverLock
);
189 if (InbvDisplayState
== INBV_DISPLAY_STATE_OWNED
)
191 /* Reset the video mode in case we do not have a custom boot logo */
192 CommandLine
= (LoaderBlock
->LoadOptions
? _strupr(LoaderBlock
->LoadOptions
) : NULL
);
193 ResetMode
= (CommandLine
== NULL
) || (strstr(CommandLine
, "BOOTLOGO") == NULL
);
196 /* Initialize the video */
197 InbvBootDriverInstalled
= VidInitialize(ResetMode
);
198 if (InbvBootDriverInstalled
)
200 /* Find bitmap resources in the kernel */
201 ResourceCount
= min(Count
, RTL_NUMBER_OF(ResourceList
) - 1);
202 for (i
= 1; i
<= ResourceCount
; i
++)
205 ResourceList
[i
] = FindBitmapResource(LoaderBlock
, i
);
208 /* Set the progress bar ranges */
209 InbvSetProgressBarSubset(0, 100);
211 // BootAnimInitialize(LoaderBlock, Count);
214 /* Return install state */
215 return InbvBootDriverInstalled
;
220 InbvAcquireLock(VOID
)
224 /* Check if we're at dispatch level or lower */
225 OldIrql
= KeGetCurrentIrql();
226 if (OldIrql
<= DISPATCH_LEVEL
)
228 /* Loop until the lock is free */
229 while (!KeTestSpinLock(&BootDriverLock
));
231 /* Raise IRQL to dispatch level */
232 KeRaiseIrql(DISPATCH_LEVEL
, &OldIrql
);
235 /* Acquire the lock */
236 KiAcquireSpinLock(&BootDriverLock
);
237 InbvOldIrql
= OldIrql
;
242 InbvReleaseLock(VOID
)
246 /* Capture the old IRQL */
247 OldIrql
= InbvOldIrql
;
249 /* Release the driver lock */
250 KiReleaseSpinLock(&BootDriverLock
);
252 /* If we were at dispatch level or lower, restore the old IRQL */
253 if (InbvOldIrql
<= DISPATCH_LEVEL
) KeLowerIrql(OldIrql
);
258 InbvEnableBootDriver(
261 /* Check if we're installed */
262 if (InbvBootDriverInstalled
)
264 /* Check for lost state */
265 if (InbvDisplayState
>= INBV_DISPLAY_STATE_LOST
) return;
267 /* Acquire the lock */
270 /* Cleanup the screen if we own it */
271 if (InbvDisplayState
== INBV_DISPLAY_STATE_OWNED
) VidCleanUp();
273 /* Set the new display state */
274 InbvDisplayState
= Enable
? INBV_DISPLAY_STATE_OWNED
:
275 INBV_DISPLAY_STATE_DISABLED
;
277 /* Release the lock */
282 /* Set the new display state */
283 InbvDisplayState
= Enable
? INBV_DISPLAY_STATE_OWNED
:
284 INBV_DISPLAY_STATE_DISABLED
;
290 InbvAcquireDisplayOwnership(VOID
)
292 /* Check if we have a callback and we're just acquiring it now */
293 if ((InbvResetDisplayParameters
) &&
294 (InbvDisplayState
== INBV_DISPLAY_STATE_LOST
))
296 /* Call the callback */
297 InbvResetDisplayParameters(80, 50);
300 /* Acquire the display */
301 InbvDisplayState
= INBV_DISPLAY_STATE_OWNED
;
306 InbvSetDisplayOwnership(
307 _In_ BOOLEAN DisplayOwned
)
309 /* Set the new display state */
310 InbvDisplayState
= DisplayOwned
? INBV_DISPLAY_STATE_OWNED
:
311 INBV_DISPLAY_STATE_LOST
;
316 InbvCheckDisplayOwnership(VOID
)
318 /* Return if we own it or not */
319 return InbvDisplayState
!= INBV_DISPLAY_STATE_LOST
;
324 InbvGetDisplayState(VOID
)
326 /* Return the actual state */
327 return InbvDisplayState
;
335 /* Make sure we own the display */
336 if (InbvDisplayState
== INBV_DISPLAY_STATE_OWNED
)
338 /* If we're not allowed, return success anyway */
339 if (!InbvDisplayDebugStrings
) return TRUE
;
341 /* Check if a filter is installed */
342 if (InbvDisplayFilter
) InbvDisplayFilter(&String
);
344 /* Acquire the lock */
347 /* Make sure we're installed and display the string */
348 if (InbvBootDriverInstalled
) VidDisplayString((PUCHAR
)String
);
350 /* Print the string on the EMS port */
351 HeadlessDispatch(HeadlessCmdPutString
,
353 strlen(String
) + sizeof(ANSI_NULL
),
357 /* Release the lock */
364 /* We don't own it, fail */
370 InbvEnableDisplayString(
375 /* Get the old setting */
376 OldSetting
= InbvDisplayDebugStrings
;
379 InbvDisplayDebugStrings
= Enable
;
381 /* Return the old setting */
387 InbvInstallDisplayStringFilter(
388 _In_ INBV_DISPLAY_STRING_FILTER DisplayFilter
)
390 /* Save the filter */
391 InbvDisplayFilter
= DisplayFilter
;
396 InbvIsBootDriverInstalled(VOID
)
398 /* Return driver state */
399 return InbvBootDriverInstalled
;
404 InbvNotifyDisplayOwnershipLost(
405 _In_ INBV_RESET_DISPLAY_PARAMETERS Callback
)
407 /* Check if we're installed */
408 if (InbvBootDriverInstalled
)
410 /* Acquire the lock and cleanup if we own the screen */
412 if (InbvDisplayState
!= INBV_DISPLAY_STATE_LOST
) VidCleanUp();
414 /* Set the reset callback and display state */
415 InbvResetDisplayParameters
= Callback
;
416 InbvDisplayState
= INBV_DISPLAY_STATE_LOST
;
418 /* Release the lock */
423 /* Set the reset callback and display state */
424 InbvResetDisplayParameters
= Callback
;
425 InbvDisplayState
= INBV_DISPLAY_STATE_LOST
;
431 InbvResetDisplay(VOID
)
433 /* Check if we're installed and we own it */
434 if (InbvBootDriverInstalled
&&
435 (InbvDisplayState
== INBV_DISPLAY_STATE_OWNED
))
438 VidResetDisplay(TRUE
);
442 /* Nothing to reset */
454 /* Just call bootvid */
455 VidSetScrollRegion(Left
, Top
, Right
, Bottom
);
463 HEADLESS_CMD_SET_COLOR HeadlessSetColor
;
465 /* Set color for EMS port */
466 #ifdef INBV_HEADLESS_COLORS
467 InbvTerminalTextColor
= 30 + CGA_TO_ANSI_COLOR(Color
);
469 InbvTerminalTextColor
= 37;
471 HeadlessSetColor
.TextColor
= InbvTerminalTextColor
;
472 HeadlessSetColor
.BkgdColor
= InbvTerminalBkgdColor
;
473 HeadlessDispatch(HeadlessCmdSetColor
,
475 sizeof(HeadlessSetColor
),
479 /* Update the text color */
480 VidSetTextColor(Color
);
492 HEADLESS_CMD_SET_COLOR HeadlessSetColor
;
494 /* Make sure we own it */
495 if (InbvDisplayState
== INBV_DISPLAY_STATE_OWNED
)
497 /* Acquire the lock */
500 /* Check if we're installed */
501 if (InbvBootDriverInstalled
)
504 VidSolidColorFill(Left
, Top
, Right
, Bottom
, (UCHAR
)Color
);
507 /* Set color for EMS port and clear display */
508 #ifdef INBV_HEADLESS_COLORS
509 InbvTerminalBkgdColor
= 40 + CGA_TO_ANSI_COLOR(Color
);
511 InbvTerminalBkgdColor
= 40;
513 HeadlessSetColor
.TextColor
= InbvTerminalTextColor
;
514 HeadlessSetColor
.BkgdColor
= InbvTerminalBkgdColor
;
515 HeadlessDispatch(HeadlessCmdSetColor
,
517 sizeof(HeadlessSetColor
),
520 HeadlessDispatch(HeadlessCmdClearDisplay
,
524 /* Release the lock */
536 /* Check if we're installed and we own it */
537 if (InbvBootDriverInstalled
&&
538 (InbvDisplayState
== INBV_DISPLAY_STATE_OWNED
))
540 /* Acquire the lock */
544 VidBitBlt(Buffer
, X
, Y
);
546 /* Release the lock */
553 InbvBufferToScreenBlt(
561 /* Check if we're installed and we own it */
562 if (InbvBootDriverInstalled
&&
563 (InbvDisplayState
== INBV_DISPLAY_STATE_OWNED
))
566 VidBufferToScreenBlt(Buffer
, X
, Y
, Width
, Height
, Delta
);
572 InbvScreenToBufferBlt(
580 /* Check if we're installed and we own it */
581 if (InbvBootDriverInstalled
&&
582 (InbvDisplayState
== INBV_DISPLAY_STATE_OWNED
))
585 VidScreenToBufferBlt(Buffer
, X
, Y
, Width
, Height
, Delta
);
591 * Sets the screen coordinates of the loading progress bar and enable it.
595 * The left/top coordinates.
601 InbvSetProgressBarCoordinates(
605 /* Update the coordinates */
606 ProgressBarLeft
= Left
;
607 ProgressBarTop
= Top
;
609 /* Enable the progress bar */
610 ShowProgressBar
= TRUE
;
615 * Gives some progress feedback, without specifying any explicit number
616 * of progress steps or percentage.
617 * The corresponding percentage is derived from the progress indicator's
618 * current count, capped to the number of expected calls to be made to
619 * this function (default: 25, see @b InbvProgressIndicator.Expected).
626 InbvIndicateProgress(VOID
)
630 /* Increase progress */
631 InbvProgressIndicator
.Count
++;
633 /* Compute the new percentage - Don't go over 100% */
634 Percentage
= 100 * InbvProgressIndicator
.Count
/
635 InbvProgressIndicator
.Expected
;
636 Percentage
= min(Percentage
, 99);
638 if (Percentage
!= InbvProgressIndicator
.Percentage
)
640 /* Percentage has changed, update the progress bar */
641 InbvProgressIndicator
.Percentage
= Percentage
;
642 InbvUpdateProgressBar(Percentage
);
648 * Specifies a progress percentage sub-range.
649 * Further calls to InbvIndicateProgress() or InbvUpdateProgressBar()
650 * will update the progress percentage relative to this sub-range.
651 * In particular, the percentage provided to InbvUpdateProgressBar()
652 * is relative to this sub-range.
655 * The lower bound percentage of the sub-range (default: 0).
658 * The upper bound percentage of the sub-range (default: 100).
664 InbvSetProgressBarSubset(
669 ASSERT(Floor
< Ceiling
);
670 ASSERT(Ceiling
<= 100);
672 /* Update the progress bar state */
673 InbvProgressState
.Floor
= Floor
* 100;
674 InbvProgressState
.Ceiling
= Ceiling
* 100;
675 InbvProgressState
.Bias
= Ceiling
- Floor
;
680 * Updates the progress bar percentage, relative to the current
681 * percentage sub-range previously set by InbvSetProgressBarSubset().
683 * @param[in] Percentage
684 * The progress percentage, relative to the current sub-range.
690 InbvUpdateProgressBar(
691 _In_ ULONG Percentage
)
695 /* Make sure the progress bar is enabled, that we own and are installed */
696 if (ShowProgressBar
&&
697 InbvBootDriverInstalled
&&
698 (InbvDisplayState
== INBV_DISPLAY_STATE_OWNED
))
700 /* Compute the total progress and tick the progress bar */
701 TotalProgress
= InbvProgressState
.Floor
+ (Percentage
* InbvProgressState
.Bias
);
702 // TotalProgress /= (100 * 100);
704 BootAnimTickProgressBar(TotalProgress
);
710 NtDisplayString(IN PUNICODE_STRING DisplayString
)
713 UNICODE_STRING CapturedString
;
714 OEM_STRING OemString
;
716 KPROCESSOR_MODE PreviousMode
;
720 PreviousMode
= ExGetPreviousMode();
722 /* We require the TCB privilege */
723 if (!SeSinglePrivilegeCheck(SeTcbPrivilege
, PreviousMode
))
724 return STATUS_PRIVILEGE_NOT_HELD
;
726 /* Capture the string */
727 Status
= ProbeAndCaptureUnicodeString(&CapturedString
, PreviousMode
, DisplayString
);
728 if (!NT_SUCCESS(Status
))
731 /* Do not display the string if it is empty */
732 if (CapturedString
.Length
== 0 || CapturedString
.Buffer
== NULL
)
734 Status
= STATUS_SUCCESS
;
739 * Convert the string since INBV understands only ANSI/OEM. Allocate the
740 * string buffer in non-paged pool because INBV passes it down to BOOTVID.
741 * We cannot perform the allocation using RtlUnicodeStringToOemString()
742 * since its allocator uses PagedPool.
744 OemLength
= RtlUnicodeStringToOemSize(&CapturedString
);
745 if (OemLength
> MAXUSHORT
)
747 Status
= STATUS_BUFFER_OVERFLOW
;
750 RtlInitEmptyAnsiString((PANSI_STRING
)&OemString
, NULL
, (USHORT
)OemLength
);
751 OemString
.Buffer
= ExAllocatePoolWithTag(NonPagedPool
, OemLength
, TAG_OSTR
);
752 if (OemString
.Buffer
== NULL
)
754 Status
= STATUS_NO_MEMORY
;
757 Status
= RtlUnicodeStringToOemString(&OemString
, &CapturedString
, FALSE
);
758 if (!NT_SUCCESS(Status
))
760 ExFreePoolWithTag(OemString
.Buffer
, TAG_OSTR
);
764 /* Display the string */
765 InbvDisplayString(OemString
.Buffer
);
767 /* Free the string buffer */
768 ExFreePoolWithTag(OemString
.Buffer
, TAG_OSTR
);
770 Status
= STATUS_SUCCESS
;
773 /* Free the captured string */
774 ReleaseCapturedUnicodeString(&CapturedString
, PreviousMode
);