/*++ Copyright (c) 1990-1998 Microsoft Corporation Module Name: bootvid.c Abstract: This file implements the interface between the kernel, and the graphical boot driver. Author: Erick Smith (ericks) Feb. 3, 1998 Environment: kernel mode Revision History: --*/ #include "ntos.h" #include "ntimage.h" #include #include #include #include #include #include "stdlib.h" #include "stdio.h" #include #include #include #include #include #include #include "anim.h" ULONG InbvTerminalBkgdColor = HEADLESS_TERM_DEFAULT_BKGD_COLOR; ULONG InbvTerminalTextColor = HEADLESS_TERM_DEFAULT_TEXT_COLOR; PUCHAR FindBitmapResource( IN PLOADER_PARAMETER_BLOCK LoaderBlock, IN ULONG_PTR ResourceIdentifier ); #if defined(ALLOC_PRAGMA) #pragma alloc_text(INIT,InbvIndicateProgress) #pragma alloc_text(INIT,InbvDriverInitialize) #pragma alloc_text(INIT,FindBitmapResource) #endif // // System global variable // BOOLEAN InbvBootDriverInstalled = FALSE; BOOLEAN InbvDisplayDebugStrings = FALSE; INBV_DISPLAY_STATE InbvDisplayState = INBV_DISPLAY_STATE_OWNED; KSPIN_LOCK BootDriverLock; KIRQL InbvOldIrql; INBV_RESET_DISPLAY_PARAMETERS InbvResetDisplayParameters = NULL; INBV_DISPLAY_STRING_FILTER InbvDisplayFilter = NULL; #define MAX_RESOURCES 16 ULONG ResourceCount = 0; PUCHAR ResourceList[MAX_RESOURCES]; ULONG ProgressBarLeft; ULONG ProgressBarTop; BOOLEAN ShowProgressBar = TRUE; struct _InbvProgressState { ULONG Floor; ULONG Ceiling; ULONG Bias; } InbvProgressState; #ifdef ALLOC_DATA_PRAGMA #pragma data_seg("INITDATA") #endif struct _BT_PROGRESS_INDICATOR { ULONG Count; ULONG Expected; ULONG Percentage; } InbvProgressIndicator = { 0, 25, 0 }; #ifdef ALLOC_DATA_PRAGMA #pragma data_seg() #endif VOID InbvAcquireLock( VOID ) /*++ Routine Description: This is an internal function used to grab the boot driver lock. This ensures that only one thread will enter the driver code at a time. Notes: You must call ReleaseLock for each call to AcquireLock. --*/ { KIRQL Irql; KIRQL LocalIrql; LocalIrql = KeGetCurrentIrql(); if (LocalIrql <= DISPATCH_LEVEL) { while (!KeTestSpinLock(&BootDriverLock)) ; KeRaiseIrql(DISPATCH_LEVEL, &Irql); LocalIrql = Irql; } KiAcquireSpinLock(&BootDriverLock); InbvOldIrql = LocalIrql; } VOID InbvReleaseLock( VOID ) /*++ Routine Description: This routine releases the boot driver lock. --*/ { KIRQL OldIrql = InbvOldIrql; KiReleaseSpinLock(&BootDriverLock); if (OldIrql <= DISPATCH_LEVEL) { KeLowerIrql(OldIrql); } } BOOLEAN InbvTestLock( VOID ) /*++ Routine Description: This routine allows you to try to acquire the display lock. If it can't get the lock right away, it returns failure. Returns: TRUE - If you aqcuired the lock. FALSE - If another thread is currently using the boot driver. Notes: You must call InbvReleaseLock if this function returns TRUE! --*/ { KIRQL Irql; if (KeTryToAcquireSpinLock(&BootDriverLock, &Irql)) { InbvOldIrql = Irql; return TRUE; } else { return FALSE; } } VOID InbvEnableBootDriver( BOOLEAN bEnable ) /*++ Routine Description: This routine allows the kernel to control whether Inbv calls make it through to the boot driver, and when they don't. Arguments: bEnable - If TRUE, we will allow Inbv calls to display, otherwise we will not. --*/ { if (InbvBootDriverInstalled) { if (InbvDisplayState < INBV_DISPLAY_STATE_LOST) { // // We can only wait for our lock, and execute our clean up code // if the driver is installed. // InbvAcquireLock(); if (InbvDisplayState == INBV_DISPLAY_STATE_OWNED) { VidCleanUp(); } InbvDisplayState = (bEnable ? INBV_DISPLAY_STATE_OWNED : INBV_DISPLAY_STATE_DISABLED); InbvReleaseLock(); } } else { // // This allow us to set display state before boot driver starts. // InbvDisplayState = (bEnable ? INBV_DISPLAY_STATE_OWNED : INBV_DISPLAY_STATE_DISABLED); } } BOOLEAN InbvEnableDisplayString( BOOLEAN bEnable ) /*++ Routine Description: This routine allows the kernel to control when HalDisplayString calls make it through to the boot driver, and when they don't. Arguments: bEnable - If TRUE, we will allow HalDisplayString calls to display, otherwise we will not. Returns: TRUE - If display string were currently being dumped. FALSE - otherwise. --*/ { BOOLEAN PrevValue = InbvDisplayDebugStrings; InbvDisplayDebugStrings = bEnable; return PrevValue; } BOOLEAN InbvIsBootDriverInstalled( VOID ) /*++ Routine Description: This routine allows a component to determine if the gui boot driver is in use. --*/ { return InbvBootDriverInstalled; } BOOLEAN InbvResetDisplay( ) /*++ Routine Description: This routine will reset the display from text mode to a supported graphics mode. Notes: This routine expects the display to be in text mode when called. --*/ { if (InbvBootDriverInstalled && (InbvDisplayState == INBV_DISPLAY_STATE_OWNED)) { VidResetDisplay(TRUE); return TRUE; } else { return FALSE; } } VOID InbvScreenToBufferBlt( PUCHAR Buffer, ULONG x, ULONG y, ULONG width, ULONG height, ULONG lDelta ) /*++ Routine Description: This routine allows for copying portions of video memory into system memory. Arguments: Buffer - Location in which to place the video image. x, y - X and Y coordinates of top-left corner of image. width, height - The width and height of the image in pixels. lDelta - width of the buffer in bytes Notes: This routine does not automatically acquire the device lock, so the caller must call InbvAquireLock or InbvTestLock to acquire the device lock. --*/ { if (InbvBootDriverInstalled && (InbvDisplayState == INBV_DISPLAY_STATE_OWNED)) { VidScreenToBufferBlt(Buffer, x, y, width, height, lDelta); } } VOID InbvBufferToScreenBlt( PUCHAR Buffer, ULONG x, ULONG y, ULONG width, ULONG height, ULONG lDelta ) /*++ Routine Description: This routine allows for copying previously saved portions of video memory back to the screen. Arguments: Buffer - Location in which to place the video image. x, y - X and Y coordinates of top-left corner of image. width, height - The width and height of the image in pixels. lDelta - width of the buffer in bytes Notes: This routine does not automatically acquire the device lock, so the caller must call InbvAquireLock or InbvTestLock to acquire the device lock. --*/ { if (InbvBootDriverInstalled && (InbvDisplayState == INBV_DISPLAY_STATE_OWNED)) { VidBufferToScreenBlt(Buffer, x, y, width, height, lDelta); } } VOID InbvBitBlt( PUCHAR Buffer, ULONG x, ULONG y ) /*++ Routine Description: This routine blts the bitmap described in 'Buffer' to the location x and y on the screen. Arguments: Buffer - points to a bitmap (in the same format as stored on disk). x, y - the upper left corner at which the bitmap will be drawn. --*/ { if (InbvBootDriverInstalled && (InbvDisplayState == INBV_DISPLAY_STATE_OWNED)) { InbvAcquireLock(); VidBitBlt(Buffer, x, y); InbvReleaseLock(); } } VOID InbvSolidColorFill( ULONG x1, ULONG y1, ULONG x2, ULONG y2, ULONG color ) /*++ Routine Description: This routine fills a rectangular portion of the screen with a given color. --*/ { ULONG x, y; HEADLESS_CMD_SET_COLOR HeadlessCmd; if (InbvDisplayState == INBV_DISPLAY_STATE_OWNED) { InbvAcquireLock(); if (InbvBootDriverInstalled) { VidSolidColorFill(x1, y1, x2, y2, color); } // // Now fill in the area on the terminal // InbvTerminalBkgdColor = HEADLESS_TERM_DEFAULT_BKGD_COLOR; HeadlessCmd.FgColor = InbvTerminalTextColor; HeadlessCmd.BkgColor = InbvTerminalBkgdColor; HeadlessDispatch(HeadlessCmdSetColor, &HeadlessCmd, sizeof(HEADLESS_CMD_SET_COLOR), NULL, NULL ); // // All block fills come in as if on VGA (640x480). The terminal is only 24x80 // so just assume it is full screen reset for now. This works because the only // thing enables terminal output is KeBugCheckEx(), which does a full screen fill. // HeadlessDispatch(HeadlessCmdClearDisplay, NULL, 0, NULL, NULL); InbvReleaseLock(); } } ULONG InbvSetTextColor( ULONG Color ) /*++ Routine Description: Sets the text color used when dislaying text. Arguments: Color - the new text color. Returns: The previous text color. --*/ { HEADLESS_CMD_SET_COLOR HeadlessCmd; InbvTerminalTextColor = HEADLESS_TERM_DEFAULT_TEXT_COLOR; HeadlessCmd.FgColor = InbvTerminalTextColor; HeadlessCmd.BkgColor = InbvTerminalBkgdColor; HeadlessDispatch(HeadlessCmdSetColor, &HeadlessCmd, sizeof(HEADLESS_CMD_SET_COLOR), NULL, NULL ); return VidSetTextColor(Color); } VOID InbvInstallDisplayStringFilter( INBV_DISPLAY_STRING_FILTER DisplayFilter ) /*++ --*/ { InbvDisplayFilter = DisplayFilter; } BOOLEAN InbvDisplayString( PUCHAR Str ) /*++ Routine Description: This routine displays a string on the screen. Arguments: Str - The string to be displayed. --*/ { PUCHAR *String = &Str; if (InbvDisplayState == INBV_DISPLAY_STATE_OWNED) { if (InbvDisplayDebugStrings) { if (InbvDisplayFilter) { InbvDisplayFilter(String); } InbvAcquireLock(); if (InbvBootDriverInstalled) { VidDisplayString(*String); } // // Since the command structure is exactly a string, we can do this. The // ASSERT() will catch if this ever changes. If it does change, then // we will need to allocate a structure, or have one pre-allocated, for // filling in and copying over the string. // ASSERT(FIELD_OFFSET(HEADLESS_CMD_PUT_STRING, String) == 0); HeadlessDispatch(HeadlessCmdPutString, *String, strlen(*String) + sizeof(UCHAR), NULL, NULL ); InbvReleaseLock(); } return TRUE; } else { return FALSE; } } #define PROGRESS_BAR_TICK_WIDTH 9 #define PROGRESS_BAR_TICK_HEIGHT 8 #define PROGRESS_BAR_TICKS 18 #define PROGRESS_BAR_COLOR 11 VOID InbvSetProgressBarCoordinates( ULONG x, ULONG y ) /*++ Routine Description: This routine sets the upper left coordinate of the progress bar. Arguments: x, y - upper left coordinate of progress bar. --*/ { ProgressBarLeft = x; ProgressBarTop = y; ShowProgressBar = TRUE; } VOID InbvUpdateProgressBar( ULONG Percentage ) /*++ Routine Description: This routine is called by the system during startup to update the status bar displayed on the gui boot screen. --*/ { int i, Ticks; if (ShowProgressBar && InbvBootDriverInstalled && (InbvDisplayState == INBV_DISPLAY_STATE_OWNED)) { // // Draw the ticks for the current percentage // // // The following calculations are biased by 100 do that // InbvProgressState.Bias can be expressed as an integer fraction. // Ticks = Percentage * InbvProgressState.Bias; Ticks += InbvProgressState.Floor; Ticks *= PROGRESS_BAR_TICKS; Ticks /= 10000; for (i=0; i 99) { Percentage = 99; } // // See if the progress bar should be updated. // if (Percentage != InbvProgressIndicator.Percentage) { InbvProgressIndicator.Percentage = Percentage; InbvUpdateProgressBar(Percentage); } } PUCHAR FindBitmapResource( IN PLOADER_PARAMETER_BLOCK LoaderBlock, IN ULONG_PTR ResourceIdentifier ) /*++ Routine Description: Gets a pointer to the bitmap image compiled into this binary, if one exists. Arguments: LoaderBlock - Used in obtaining the bitmap resource ResourceIdentifier - Identifier for the resource to return the address for Return Value: Pointer to bitmap resource, if successful. NULL otherwise. --*/ { NTSTATUS Status; PLIST_ENTRY Entry; PKLDR_DATA_TABLE_ENTRY DataTableEntry; ULONG_PTR ResourceIdPath[3]; PIMAGE_RESOURCE_DATA_ENTRY ResourceDataEntry; PUCHAR Bitmap; UNICODE_STRING KernelString1; UNICODE_STRING KernelString2; RtlInitUnicodeString(&KernelString1, L"NTOSKRNL.EXE"); RtlInitUnicodeString(&KernelString2, L"NTKRNLMP.EXE"); // // Find our loader block entry // Entry = LoaderBlock->LoadOrderListHead.Flink; while (Entry != &LoaderBlock->LoadOrderListHead) { // // Get the address of the data table entry for this component. // DataTableEntry = CONTAINING_RECORD(Entry, KLDR_DATA_TABLE_ENTRY, InLoadOrderLinks); // // Case-insensitive comparison with "NTOSKRNL.EXE" and "NTKRNLMP.EXE" // if (RtlEqualUnicodeString(&DataTableEntry->BaseDllName, &KernelString1, TRUE) == TRUE) { break; } if (RtlEqualUnicodeString(&DataTableEntry->BaseDllName, &KernelString2, TRUE) == TRUE) { break; } Entry = Entry->Flink; } // // If we couldn't find ntoskrnl in the loader list, give up // if (Entry == &LoaderBlock->LoadOrderListHead) { return NULL; } ResourceIdPath[0] = 2; // RT_BITMAP = 2 ResourceIdPath[1] = ResourceIdentifier; ResourceIdPath[2] = 0; // ?? Status = LdrFindResource_U( DataTableEntry->DllBase, ResourceIdPath, 3, (VOID *) &ResourceDataEntry ); if (!NT_SUCCESS(Status)) { return NULL; } Status = LdrAccessResource( DataTableEntry->DllBase, ResourceDataEntry, &Bitmap, NULL ); if (!NT_SUCCESS(Status)) { return NULL; } return Bitmap; } PUCHAR InbvGetResourceAddress( IN ULONG ResourceNumber ) /*++ Routine Description: This routine returns the cached resources address for a given resource. --*/ { if (ResourceNumber <= ResourceCount) { return ResourceList[ResourceNumber-1]; } else { return NULL; } } BOOLEAN InbvDriverInitialize( IN PLOADER_PARAMETER_BLOCK LoaderBlock, ULONG Count ) /*++ Routine Description: This routine will call into the graphical boot driver and give the driver a chance to initialize. At this point, the boot driver should determine whether it can run on the hardware in the machine. --*/ { ULONG i; ULONG_PTR p; PCHAR Options; BOOLEAN DispModeChange = FALSE; // // Only do this once. // if (InbvBootDriverInstalled == TRUE) { return TRUE; } KeInitializeSpinLock(&BootDriverLock); if (InbvDisplayState == INBV_DISPLAY_STATE_OWNED) { Options = LoaderBlock->LoadOptions ? _strupr(LoaderBlock->LoadOptions) : NULL; if (Options) { DispModeChange = (BOOLEAN)(strstr(Options, "BOOTLOGO") == NULL); } else { DispModeChange = TRUE; } } InbvBootDriverInstalled = VidInitialize(DispModeChange); if (InbvBootDriverInstalled == FALSE) { return FALSE; } ResourceCount = Count; for (i=1; i<=Count; i++) { p = (ULONG_PTR) i; ResourceList[i-1] = FindBitmapResource(LoaderBlock, p); } // // Set prograss bar to full range. // InbvSetProgressBarSubset(0, 100); return InbvBootDriverInstalled; } VOID InbvNotifyDisplayOwnershipLost( INBV_RESET_DISPLAY_PARAMETERS ResetDisplayParameters ) /*++ Routine Description: This routine is called by the hal when the hal looses display ownership. At this point win32k.sys has taken over. --*/ { if (InbvBootDriverInstalled) { // // We can only wait for our lock, and execute our clean up code // if the driver is installed and we still own the display. // InbvAcquireLock(); if (InbvDisplayState != INBV_DISPLAY_STATE_LOST) { VidCleanUp(); } InbvDisplayState = INBV_DISPLAY_STATE_LOST; InbvResetDisplayParameters = ResetDisplayParameters; InbvReleaseLock(); } else { InbvDisplayState = INBV_DISPLAY_STATE_LOST; InbvResetDisplayParameters = ResetDisplayParameters; } } VOID InbvAcquireDisplayOwnership( VOID ) /*++ Routine Description: Allows the kernel to reaquire ownership of the display. --*/ { if (InbvResetDisplayParameters && (InbvDisplayState == INBV_DISPLAY_STATE_LOST)) { InbvResetDisplayParameters(80,50); } InbvDisplayState = INBV_DISPLAY_STATE_OWNED; } VOID InbvSetDisplayOwnership( BOOLEAN DisplayOwned ) /*++ Routine Description: This routine allows the kernel to set a display state. This is useful after a hibernate. At this point win32k will reacquire display ownership but will not tell us. Arguments: Whether the display is owned or not. --*/ { if (DisplayOwned) { InbvDisplayState = INBV_DISPLAY_STATE_OWNED; } else { InbvDisplayState = INBV_DISPLAY_STATE_LOST; } } BOOLEAN InbvCheckDisplayOwnership( VOID ) /*++ Routine Description: Indicates whether the Hal owns the display. --*/ { return (InbvDisplayState != INBV_DISPLAY_STATE_LOST); } INBV_DISPLAY_STATE InbvGetDisplayState( VOID ) /*++ Routine Description: Indicates whether the Hal owns the display. --*/ { return InbvDisplayState; } VOID InbvSetScrollRegion( ULONG x1, ULONG y1, ULONG x2, ULONG y2 ) /*++ Routine Description: Control what portions of the screen are used for text. Arguments: Lines - number of lines of text. --*/ { VidSetScrollRegion(x1, y1, x2, y2); }