|
|
/*++
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 <zwapi.h>
#include <ntdddisk.h>
#include <setupblk.h>
#include <fsrtl.h>
#include <ntverp.h>
#include "stdlib.h"
#include "stdio.h"
#include <string.h>
#include <safeboot.h>
#include <inbv.h>
#include <bootvid.h>
#include <hdlsblk.h>
#include <hdlsterm.h>
#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<Ticks; i++) { InbvAcquireLock(); VidSolidColorFill(ProgressBarLeft + (i * PROGRESS_BAR_TICK_WIDTH), ProgressBarTop, ProgressBarLeft + ((i + 1) * PROGRESS_BAR_TICK_WIDTH) - 2, ProgressBarTop + PROGRESS_BAR_TICK_HEIGHT - 1, PROGRESS_BAR_COLOR); InbvReleaseLock(); }
} }
VOID InbvSetProgressBarSubset( ULONG Floor, ULONG Ceiling )
/*++
Routine Description:
Sets floor and ceiling for subsequent calls to InbvUpdateProgressBar. While a floor and ceiling are in effect, a caller's 100% is a percentage of this range. If floor and ceiling are zero, the entire range is used.
Arguments:
Floor Lower limit of the subset. Ceiling Upper limit of the subset.
Return Value:
None.
--*/
{ ASSERT(Floor < Ceiling); ASSERT(Ceiling <= 100);
InbvProgressState.Floor = Floor * 100; InbvProgressState.Ceiling = Ceiling * 100; InbvProgressState.Bias = (Ceiling - Floor); }
VOID InbvIndicateProgress( VOID )
/*++
Routine Description:
This routine is called to indicate that progress is being made. The number of calls is counted and compared to the expected number of calls, the boot progress bar is updated apropriately.
Arguments:
None.
Return Value:
None.
--*/
{ ULONG Percentage;
InbvProgressIndicator.Count++;
//
// Calculate how far along we think we are.
//
Percentage = (InbvProgressIndicator.Count * 100) / InbvProgressIndicator.Expected;
//
// The Expected number of calls can vary from boot to boot
// but should remain relatively constant. Allow for the
// possibility we were called more than we expected to be.
// (The progress bar simply stalls at this point).
//
if (Percentage > 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); }
|