2840 lines
73 KiB
C
2840 lines
73 KiB
C
/*++
|
||
|
||
Copyright (c) 1990-1998 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
hdlsterm.c
|
||
|
||
Abstract:
|
||
|
||
This file implements functions for dealing with a terminal attached.
|
||
|
||
Author:
|
||
|
||
Sean Selitrennikoff (v-seans) Oct, 1999
|
||
|
||
Environment:
|
||
|
||
kernel mode
|
||
|
||
Revision History:
|
||
|
||
--*/
|
||
|
||
#include "exp.h"
|
||
#pragma hdrstop
|
||
|
||
#include <hdlsblk.h>
|
||
#include <hdlsterm.h>
|
||
#include <inbv.h>
|
||
|
||
|
||
//
|
||
// Defines for headless
|
||
//
|
||
//
|
||
#define HEADLESS_OOM_STRING L"Entry could not be recorded due to lack of memory.\n"
|
||
#define HEADLESS_LOG_NUMBER_OF_ENTRIES 256
|
||
#define HEADLESS_TMP_BUFFER_SIZE 80
|
||
|
||
|
||
//
|
||
// Note: HdlspAddLogEntry() allocates a buffer off the stack of this size,
|
||
// so keep this number small. Anything longer than 80 is probably useless, as
|
||
// a VT100 can only handle 80 characters across.
|
||
// Do not make this any shorter than the string for HEADLESS_LOG_LOADING_FILENAME
|
||
//
|
||
#define HDLSP_LOG_MAX_STRING_LENGTH 80
|
||
|
||
|
||
#define HEADLESS_ACQUIRE_SPIN_LOCK() \
|
||
if (!HeadlessGlobals->InBugCheck) { \
|
||
KeAcquireSpinLock(&(HeadlessGlobals->SpinLock), &OldIrql); \
|
||
} else { \
|
||
OldIrql = (KIRQL)-1; \
|
||
}
|
||
|
||
#define HEADLESS_RELEASE_SPIN_LOCK() \
|
||
if (OldIrql != (KIRQL)-1) { \
|
||
KeReleaseSpinLock(&(HeadlessGlobals->SpinLock), OldIrql); \
|
||
} else { \
|
||
ASSERT(HeadlessGlobals->InBugCheck); \
|
||
}
|
||
|
||
#define COM1_PORT 0x03f8
|
||
#define COM2_PORT 0x02f8
|
||
|
||
|
||
//
|
||
// This table provides a quick lookup conversion between ASCII values
|
||
// that fall between 128 and 255, and their UNICODE counterpart.
|
||
//
|
||
// Note that ASCII values between 0 and 127 are equvilent to their
|
||
// unicode counter parts, so no lookups would be required.
|
||
//
|
||
// Therefore when using this table, remove the high bit from the ASCII
|
||
// value and use the resulting value as an offset into this array. For
|
||
// example, 0x80 ->(remove the high bit) 00 -> 0x00C7.
|
||
//
|
||
USHORT PcAnsiToUnicode[0xFF] = {
|
||
0x00C7,
|
||
0x00FC,
|
||
0x00E9,
|
||
0x00E2,
|
||
0x00E4,
|
||
0x00E0,
|
||
0x00E5,
|
||
0x0087,
|
||
0x00EA,
|
||
0x00EB,
|
||
0x00E8,
|
||
0x00EF,
|
||
0x00EE,
|
||
0x00EC,
|
||
0x00C4,
|
||
0x00C5,
|
||
0x00C9,
|
||
0x00E6,
|
||
0x00C6,
|
||
0x00F4,
|
||
0x00F6,
|
||
0x00F2,
|
||
0x00FB,
|
||
0x00F9,
|
||
0x00FF,
|
||
0x00D6,
|
||
0x00DC,
|
||
0x00A2,
|
||
0x00A3,
|
||
0x00A5,
|
||
0x20A7,
|
||
0x0192,
|
||
0x00E1,
|
||
0x00ED,
|
||
0x00F3,
|
||
0x00FA,
|
||
0x00F1,
|
||
0x00D1,
|
||
0x00AA,
|
||
0x00BA,
|
||
0x00BF,
|
||
0x2310,
|
||
0x00AC,
|
||
0x00BD,
|
||
0x00BC,
|
||
0x00A1,
|
||
0x00AB,
|
||
0x00BB,
|
||
0x2591,
|
||
0x2592,
|
||
0x2593,
|
||
0x2502,
|
||
0x2524,
|
||
0x2561,
|
||
0x2562,
|
||
0x2556,
|
||
0x2555,
|
||
0x2563,
|
||
0x2551,
|
||
0x2557,
|
||
0x255D,
|
||
0x255C,
|
||
0x255B,
|
||
0x2510,
|
||
0x2514,
|
||
0x2534,
|
||
0x252C,
|
||
0x251C,
|
||
0x2500,
|
||
0x253C,
|
||
0x255E,
|
||
0x255F,
|
||
0x255A,
|
||
0x2554,
|
||
0x2569,
|
||
0x2566,
|
||
0x2560,
|
||
0x2550,
|
||
0x256C,
|
||
0x2567,
|
||
0x2568,
|
||
0x2564,
|
||
0x2565,
|
||
0x2559,
|
||
0x2558,
|
||
0x2552,
|
||
0x2553,
|
||
0x256B,
|
||
0x256A,
|
||
0x2518,
|
||
0x250C,
|
||
0x2588,
|
||
0x2584,
|
||
0x258C,
|
||
0x2590,
|
||
0x2580,
|
||
0x03B1,
|
||
0x00DF,
|
||
0x0393,
|
||
0x03C0,
|
||
0x03A3,
|
||
0x03C3,
|
||
0x00B5,
|
||
0x03C4,
|
||
0x03A6,
|
||
0x0398,
|
||
0x03A9,
|
||
0x03B4,
|
||
0x221E,
|
||
0x03C6,
|
||
0x03B5,
|
||
0x2229,
|
||
0x2261,
|
||
0x00B1,
|
||
0x2265,
|
||
0x2264,
|
||
0x2320,
|
||
0x2321,
|
||
0x00F7,
|
||
0x2248,
|
||
0x00B0,
|
||
0x2219,
|
||
0x00B7,
|
||
0x221A,
|
||
0x207F,
|
||
0x00B2,
|
||
0x25A0,
|
||
0x00A0
|
||
};
|
||
|
||
|
||
|
||
|
||
|
||
//
|
||
// Log entry structure
|
||
//
|
||
typedef struct _HEADLESS_LOG_ENTRY {
|
||
SYSTEM_TIMEOFDAY_INFORMATION TimeOfEntry;
|
||
PWCHAR String;
|
||
} HEADLESS_LOG_ENTRY, *PHEADLESS_LOG_ENTRY;
|
||
|
||
// Blue Screen Data Structure
|
||
//
|
||
typedef struct _HEADLESS_BLUE_SCREEN_DATA {
|
||
PUCHAR Property;
|
||
PUCHAR XMLData;
|
||
struct _HEADLESS_BLUE_SCREEN_DATA *Next;
|
||
}HEADLESS_BLUE_SCREEN_DATA, * PHEADLESS_BLUE_SCREEN_DATA;
|
||
|
||
//
|
||
// Global variables headless component uses
|
||
//
|
||
typedef struct _HEADLESS_GLOBALS {
|
||
|
||
//
|
||
// Global spin lock for accessing headless internal routines.
|
||
//
|
||
KSPIN_LOCK SpinLock;
|
||
|
||
//
|
||
// Handle for when routines are locked down into memory.
|
||
//
|
||
HANDLE PageLockHandle;
|
||
|
||
//
|
||
// List of log entries.
|
||
//
|
||
PHEADLESS_LOG_ENTRY LogEntries;
|
||
|
||
//
|
||
// Global temp buffer, not to be held across lock release/acquires.
|
||
//
|
||
PUCHAR TmpBuffer;
|
||
|
||
//
|
||
// Current user input line
|
||
//
|
||
PUCHAR InputBuffer;
|
||
|
||
//
|
||
// Blue Screen Data
|
||
//
|
||
PHEADLESS_BLUE_SCREEN_DATA BlueScreenData;
|
||
|
||
//
|
||
// Flags and parameters for determining headless state
|
||
//
|
||
union {
|
||
struct {
|
||
ULONG TerminalEnabled : 1;
|
||
ULONG InBugCheck : 1;
|
||
ULONG NewLogEntryAdded : 1;
|
||
ULONG UsedBiosSettings : 1;
|
||
ULONG InputProcessing : 1;
|
||
ULONG InputLineDone : 1;
|
||
ULONG ProcessingCmd : 1;
|
||
ULONG TerminalParity : 1;
|
||
ULONG TerminalStopBits : 1;
|
||
ULONG TerminalPortNumber : 3;
|
||
ULONG IsNonLegacyDevice : 1;
|
||
};
|
||
ULONG AllFlags;
|
||
};
|
||
|
||
//
|
||
// Port settings
|
||
//
|
||
ULONG TerminalBaudRate;
|
||
ULONG TerminalPort;
|
||
PUCHAR TerminalPortAddress;
|
||
LARGE_INTEGER DelayTime; // in 100ns units
|
||
ULONG MicroSecondsDelayTime;
|
||
UCHAR TerminalType; // What kind of terminal do we think
|
||
// we're talking to?
|
||
// 0 = VT100
|
||
// 1 = VT100+
|
||
// 2 = VT-UTF8
|
||
// 3 = PC ANSI
|
||
// 4-255 = reserved
|
||
|
||
|
||
//
|
||
// Current location in Input buffer;
|
||
//
|
||
SIZE_T InputBufferIndex;
|
||
|
||
//
|
||
// Logging Indexes.
|
||
//
|
||
USHORT LogEntryLast;
|
||
USHORT LogEntryStart;
|
||
|
||
//
|
||
// Machine's GUID.
|
||
//
|
||
GUID SystemGUID;
|
||
|
||
BOOLEAN IsMMIODevice; // Is UART in SysIO or MMIO space?
|
||
|
||
//
|
||
// if this is TRUE, then the last character was a CR.
|
||
// if this is TRUE and the current character is a LF,
|
||
// then we filter the LF.
|
||
//
|
||
BOOLEAN IsLastCharCR;
|
||
|
||
} HEADLESS_GLOBALS, *PHEADLESS_GLOBALS;
|
||
|
||
|
||
//
|
||
// The one and only resident global variable
|
||
//
|
||
PHEADLESS_GLOBALS HeadlessGlobals = NULL;
|
||
|
||
|
||
//
|
||
// Forward declarations.
|
||
//
|
||
NTSTATUS
|
||
HdlspDispatch(
|
||
IN HEADLESS_CMD Command,
|
||
IN PVOID InputBuffer OPTIONAL,
|
||
IN SIZE_T InputBufferSize OPTIONAL,
|
||
OUT PVOID OutputBuffer OPTIONAL,
|
||
OUT PSIZE_T OutputBufferSize OPTIONAL
|
||
);
|
||
|
||
NTSTATUS
|
||
HdlspEnableTerminal(
|
||
BOOLEAN bEnable
|
||
);
|
||
|
||
VOID
|
||
HdlspPutString(
|
||
PUCHAR String
|
||
);
|
||
|
||
VOID
|
||
HdlspPutData(
|
||
PUCHAR InputBuffer,
|
||
SIZE_T InputBufferLength
|
||
);
|
||
|
||
BOOLEAN
|
||
HdlspGetLine(
|
||
PUCHAR InputBuffer,
|
||
SIZE_T InputBufferLength
|
||
);
|
||
|
||
VOID
|
||
HdlspBugCheckProcessing(
|
||
VOID
|
||
);
|
||
|
||
VOID
|
||
HdlspProcessDumpCommand(
|
||
IN BOOLEAN Paging
|
||
);
|
||
|
||
VOID
|
||
HdlspPutMore(
|
||
OUT PBOOLEAN Stop
|
||
);
|
||
|
||
VOID
|
||
HdlspAddLogEntry(
|
||
IN PWCHAR String
|
||
);
|
||
|
||
NTSTATUS
|
||
HdlspSetBlueScreenInformation(
|
||
IN PHEADLESS_CMD_SET_BLUE_SCREEN_DATA pData,
|
||
IN SIZE_T cData
|
||
);
|
||
|
||
VOID
|
||
HdlspSendBlueScreenInfo(
|
||
ULONG BugcheckCode
|
||
);
|
||
|
||
VOID
|
||
HdlspKernelAddLogEntry(
|
||
IN ULONG StringCode,
|
||
IN PUNICODE_STRING DriverName OPTIONAL
|
||
);
|
||
|
||
VOID
|
||
HdlspSendStringAtBaud(
|
||
IN PUCHAR String
|
||
);
|
||
|
||
#if defined(ALLOC_PRAGMA)
|
||
#pragma alloc_text(INIT, HeadlessInit)
|
||
#pragma alloc_text(PAGE, HeadlessTerminalAddResources)
|
||
#pragma alloc_text(PAGEHDLS, HdlspDispatch)
|
||
#pragma alloc_text(PAGEHDLS, HdlspEnableTerminal)
|
||
#pragma alloc_text(PAGEHDLS, HdlspPutString)
|
||
#pragma alloc_text(PAGEHDLS, HdlspPutData)
|
||
#pragma alloc_text(PAGEHDLS, HdlspGetLine)
|
||
#pragma alloc_text(PAGEHDLS, HdlspBugCheckProcessing)
|
||
#pragma alloc_text(PAGEHDLS, HdlspProcessDumpCommand)
|
||
#pragma alloc_text(PAGEHDLS, HdlspPutMore)
|
||
#pragma alloc_text(PAGEHDLS, HdlspAddLogEntry)
|
||
#pragma alloc_text(PAGEHDLS, HdlspSetBlueScreenInformation)
|
||
#pragma alloc_text(PAGEHDLS, HdlspSendBlueScreenInfo)
|
||
#pragma alloc_text(PAGEHDLS, HdlspKernelAddLogEntry)
|
||
#pragma alloc_text(PAGEHDLS, HdlspSendStringAtBaud)
|
||
#endif
|
||
|
||
|
||
|
||
VOID
|
||
HeadlessInit(
|
||
PLOADER_PARAMETER_BLOCK LoaderBlock
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine sets up all the information for supporting a headless terminal. It
|
||
does not initialize the terminal.
|
||
|
||
Arguments:
|
||
|
||
|
||
HeadlessLoaderBlock - The loader block passed in from the loader.
|
||
|
||
Environment:
|
||
|
||
Only to be called at INIT time.
|
||
|
||
--*/
|
||
{
|
||
PHEADLESS_LOADER_BLOCK HeadlessLoaderBlock;
|
||
PHEADLESS_GLOBALS GlobalBlock;
|
||
ULONG TmpUlong;
|
||
|
||
|
||
if (LoaderBlock->Extension->HeadlessLoaderBlock == NULL) {
|
||
return;
|
||
}
|
||
|
||
|
||
HeadlessLoaderBlock = LoaderBlock->Extension->HeadlessLoaderBlock;
|
||
|
||
|
||
if ((HeadlessLoaderBlock->PortNumber <= 4) || (BOOLEAN)(HeadlessLoaderBlock->UsedBiosSettings)) {
|
||
|
||
//
|
||
// Allocate space for the global variables we will use.
|
||
//
|
||
GlobalBlock = ExAllocatePoolWithTag(NonPagedPool,
|
||
sizeof(HEADLESS_GLOBALS),
|
||
((ULONG)'sldH')
|
||
);
|
||
|
||
if (GlobalBlock == NULL) {
|
||
|
||
return;
|
||
}
|
||
|
||
//
|
||
// Start everything at zero, and then init the rest by hand.
|
||
//
|
||
RtlZeroMemory(GlobalBlock, sizeof(HEADLESS_GLOBALS));
|
||
|
||
|
||
KeInitializeSpinLock(&(GlobalBlock->SpinLock));
|
||
|
||
//
|
||
// Copy stuff from loader block
|
||
//
|
||
GlobalBlock->TerminalPortNumber = HeadlessLoaderBlock->PortNumber;
|
||
GlobalBlock->TerminalPortAddress = HeadlessLoaderBlock->PortAddress;
|
||
GlobalBlock->TerminalBaudRate = HeadlessLoaderBlock->BaudRate;
|
||
GlobalBlock->TerminalParity = (BOOLEAN)(HeadlessLoaderBlock->Parity);
|
||
GlobalBlock->TerminalStopBits = HeadlessLoaderBlock->StopBits;
|
||
GlobalBlock->UsedBiosSettings = (BOOLEAN)(HeadlessLoaderBlock->UsedBiosSettings);
|
||
GlobalBlock->IsMMIODevice = (BOOLEAN)(HeadlessLoaderBlock->IsMMIODevice);
|
||
GlobalBlock->IsLastCharCR = FALSE;
|
||
GlobalBlock->TerminalType = (UCHAR)(HeadlessLoaderBlock->TerminalType);
|
||
|
||
RtlCopyMemory( &GlobalBlock->SystemGUID,
|
||
&HeadlessLoaderBlock->SystemGUID,
|
||
sizeof(GUID) );
|
||
|
||
|
||
//
|
||
// We need to determine if this is a non-legacy device that we're
|
||
// speaking through. This can happen in several different ways,
|
||
// including a PCI device placing a UART in System I/O space (which
|
||
// wouldn't qualify as being "non-legacy"), or even a NON-PCI
|
||
// device placing a UART up in MMIO (which again wouldn't qualify).
|
||
//
|
||
// Therefore, if the address is outside of System I/O, *or* if it's
|
||
// sitting on a PCI device, then set the IsNonLegacyDevice entry.
|
||
//
|
||
if( GlobalBlock->IsMMIODevice ) {
|
||
GlobalBlock->IsNonLegacyDevice = TRUE;
|
||
}
|
||
|
||
|
||
//
|
||
// If we're speaking through a PCI device, we need to secure it. We'll
|
||
// use the debugger APIs to make sure the device is understood and that it
|
||
// doesn't get moved.
|
||
//
|
||
if( (HeadlessLoaderBlock->PciDeviceId != (USHORT)0xFFFF) &&
|
||
(HeadlessLoaderBlock->PciDeviceId != 0) &&
|
||
(HeadlessLoaderBlock->PciVendorId != (USHORT)0xFFFF) &&
|
||
(HeadlessLoaderBlock->PciVendorId != 0) ) {
|
||
|
||
//
|
||
// The loader thinks he spoke through a PCI device. Remember
|
||
// that it's non-legacy.
|
||
//
|
||
GlobalBlock->IsNonLegacyDevice = TRUE;
|
||
|
||
//
|
||
// Tell everyone else in the system to leave this device alone.
|
||
// before we do that, the user may actually want PnP to enumerate the
|
||
// device and possibly apply power management to it. They can indicate
|
||
// this by setting bit 0 of PciFlags.
|
||
//
|
||
if( !(HeadlessLoaderBlock->PciFlags & 0x1) ) {
|
||
|
||
DEBUG_DEVICE_DESCRIPTOR DebugDeviceDescriptor;
|
||
|
||
RtlZeroMemory( &DebugDeviceDescriptor,
|
||
sizeof(DEBUG_DEVICE_DESCRIPTOR) );
|
||
|
||
//
|
||
// We're required to understand exactly what this structure looks like
|
||
// because we need to set every value to (-1), then fill in only the
|
||
// fields that we explicitly know about.
|
||
//
|
||
DebugDeviceDescriptor.DeviceID = HeadlessLoaderBlock->PciDeviceId;
|
||
DebugDeviceDescriptor.VendorID = HeadlessLoaderBlock->PciVendorId;
|
||
DebugDeviceDescriptor.Bus = HeadlessLoaderBlock->PciBusNumber;
|
||
DebugDeviceDescriptor.Slot = HeadlessLoaderBlock->PciSlotNumber;
|
||
|
||
//
|
||
// Now fill in the rest with (-1).
|
||
//
|
||
DebugDeviceDescriptor.BaseClass = 0xFF;
|
||
DebugDeviceDescriptor.SubClass = 0xFF;
|
||
DebugDeviceDescriptor.ProgIf = 0xFF;
|
||
|
||
|
||
//
|
||
// Do it.
|
||
//
|
||
KdSetupPciDeviceForDebugging( LoaderBlock,
|
||
&DebugDeviceDescriptor );
|
||
}
|
||
}
|
||
|
||
|
||
|
||
//
|
||
// Allocate space for log entries.
|
||
//
|
||
GlobalBlock->LogEntries = ExAllocatePoolWithTag(NonPagedPool,
|
||
HEADLESS_LOG_NUMBER_OF_ENTRIES *
|
||
sizeof(HEADLESS_LOG_ENTRY),
|
||
((ULONG)'sldH')
|
||
);
|
||
|
||
if (GlobalBlock->LogEntries == NULL) {
|
||
|
||
goto Fail;
|
||
}
|
||
|
||
GlobalBlock->LogEntryLast = (USHORT)-1;
|
||
GlobalBlock->LogEntryStart = (USHORT)-1;
|
||
|
||
|
||
//
|
||
// Allocate a temporary buffer for general use.
|
||
//
|
||
GlobalBlock->TmpBuffer = ExAllocatePoolWithTag(NonPagedPool,
|
||
HEADLESS_TMP_BUFFER_SIZE,
|
||
((ULONG)'sldH')
|
||
);
|
||
|
||
if (GlobalBlock->TmpBuffer == NULL) {
|
||
|
||
goto Fail;
|
||
}
|
||
|
||
GlobalBlock->InputBuffer = ExAllocatePoolWithTag(NonPagedPool,
|
||
HEADLESS_TMP_BUFFER_SIZE,
|
||
((ULONG)'sldH')
|
||
);
|
||
|
||
if (GlobalBlock->InputBuffer == NULL) {
|
||
|
||
goto Fail;
|
||
}
|
||
|
||
GlobalBlock->PageLockHandle = MmLockPagableCodeSection((PVOID)(ULONG_PTR)HdlspDispatch);
|
||
|
||
if (GlobalBlock->PageLockHandle == NULL) {
|
||
|
||
goto Fail;
|
||
}
|
||
|
||
//
|
||
// Figure to delay time between bytes to satify the baud rate given.
|
||
//
|
||
if (GlobalBlock->TerminalBaudRate == 9600) {
|
||
|
||
TmpUlong = GlobalBlock->TerminalBaudRate;
|
||
|
||
//
|
||
// Convert to chars per second.
|
||
//
|
||
TmpUlong = TmpUlong / 10; // 10 bits per character (8-1-1) is the max.
|
||
|
||
GlobalBlock->MicroSecondsDelayTime = ((1000000 / TmpUlong) * 10) / 8; // We will send at 80% speed to be sure.
|
||
GlobalBlock->DelayTime.HighPart = -1;
|
||
GlobalBlock->DelayTime.LowPart = -10 * GlobalBlock->MicroSecondsDelayTime; // relative time
|
||
}
|
||
|
||
HeadlessGlobals = GlobalBlock;
|
||
|
||
|
||
//
|
||
// If all went well, go ahead and initialize the headless port.
|
||
// Do this here so we have it ready to go in case of an early
|
||
// bugcheck. In that case, we will have the port ready to accept
|
||
// traffic without having to initialize it while we're trying to
|
||
// bugcheck the machine.
|
||
//
|
||
HdlspEnableTerminal(TRUE);
|
||
|
||
}
|
||
|
||
return;
|
||
|
||
Fail:
|
||
|
||
if (GlobalBlock->LogEntries != NULL) {
|
||
ExFreePool(GlobalBlock->LogEntries);
|
||
}
|
||
|
||
if (GlobalBlock->TmpBuffer != NULL) {
|
||
ExFreePool(GlobalBlock->TmpBuffer);
|
||
}
|
||
|
||
if (GlobalBlock->InputBuffer != NULL) {
|
||
ExFreePool(GlobalBlock->InputBuffer);
|
||
}
|
||
|
||
ExFreePool(GlobalBlock);
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
HeadlessDispatch(
|
||
IN HEADLESS_CMD Command,
|
||
IN PVOID InputBuffer OPTIONAL,
|
||
IN SIZE_T InputBufferSize OPTIONAL,
|
||
OUT PVOID OutputBuffer OPTIONAL,
|
||
OUT PSIZE_T OutputBufferSize OPTIONAL
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is the main entry point for all headless interaction with clients.
|
||
|
||
Arguments:
|
||
|
||
Command - The command to execute.
|
||
|
||
InputBuffer - An optionally supplied buffer containing input parameters.
|
||
|
||
InputBufferSize - Size of the supplied input buffer.
|
||
|
||
OutputBuffer - An optionally supplied buffer where to place output parameters.
|
||
|
||
OutputBufferSize - Size of the supplied output buffer, if the buffer is too small
|
||
then STATUS_BUFFER_TOO_SMALL is returned and this parameter contains the total
|
||
bytes necessary to complete the operation.
|
||
|
||
Environment:
|
||
|
||
If headless is enabled, it will acquire spin locks, so call from DPC level or
|
||
less, only from kernel mode.
|
||
|
||
--*/
|
||
{
|
||
//
|
||
// If headless is not enabled on this machine, then some commands need special
|
||
// processing, and all other we fool by saying that it succeeded.
|
||
//
|
||
// If for some reason we were unable to lock the headless component down into
|
||
// memory when we initialized, treat this as the terminal not being connected.
|
||
//
|
||
if ((HeadlessGlobals == NULL) || (HeadlessGlobals->PageLockHandle == NULL)) {
|
||
|
||
if (Command == HeadlessCmdEnableTerminal) {
|
||
return STATUS_UNSUCCESSFUL;
|
||
}
|
||
|
||
//
|
||
// The following command all have responses, so we must fill in the
|
||
// correct response for when headless is not enabled.
|
||
//
|
||
if ((Command == HeadlessCmdQueryInformation) ||
|
||
(Command == HeadlessCmdGetByte) ||
|
||
(Command == HeadlessCmdGetLine) ||
|
||
(Command == HeadlessCmdCheckForReboot) ||
|
||
(Command == HeadlessCmdTerminalPoll)) {
|
||
|
||
if ((OutputBuffer == NULL) || (OutputBufferSize == NULL)) {
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
//
|
||
// All structures are designed such that a 0 or FALSE is the correct
|
||
// response when headless is not present.
|
||
//
|
||
RtlZeroMemory(OutputBuffer, *OutputBufferSize);
|
||
}
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
return HdlspDispatch(Command,
|
||
InputBuffer,
|
||
InputBufferSize,
|
||
OutputBuffer,
|
||
OutputBufferSize
|
||
);
|
||
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
HdlspDispatch(
|
||
IN HEADLESS_CMD Command,
|
||
IN PVOID InputBuffer OPTIONAL,
|
||
IN SIZE_T InputBufferSize OPTIONAL,
|
||
OUT PVOID OutputBuffer OPTIONAL,
|
||
OUT PSIZE_T OutputBufferSize OPTIONAL
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is the pageable version of the dispatch routine.
|
||
|
||
In general this routine is not intended to be used by more than one thread at
|
||
a time. There are two exceptions, see below, but otherwise any second command
|
||
that is submitted is rejected.
|
||
|
||
There are only a couple of things that allowed to be called in parallel:
|
||
AddLogEntry can be called when another command is being processed.
|
||
StartBugCheck and BugCheckProcessing can as well.
|
||
|
||
AddLogEntry is synchronized with all the other commands. It atomically adds
|
||
the entry while holding the spin lock. Thus, all other command should try and
|
||
hold the spin lock when manipulating global variables.
|
||
|
||
The BugCheck routines do not use any spinlocking - an unfortunate side effect
|
||
of that is that since another thread may still be executing and in this code,
|
||
terminal I/O is indeterminable during this time. We cannot wait for the other
|
||
thread to exit, as it may be that thread itself has already been stopped. Thus,
|
||
in the case of a bugcheck, this is unsolvable. However, since bugchecks should
|
||
never happen - having the possibility of a small overlap is acceptable, since
|
||
the other thread either exits or is stopped, I/O will happen correctly with the
|
||
terminal. This may require the user to press ENTER a couple of times, but that
|
||
is acceptable in a bugcheck situation.
|
||
|
||
Arguments:
|
||
|
||
Command - The command to execute.
|
||
|
||
InputBuffer - An optionally supplied buffer containing input parameters.
|
||
|
||
InputBufferSize - Size of the supplied input buffer.
|
||
|
||
OutputBuffer - An optionally supplied buffer where to place output parameters.
|
||
|
||
OutputBufferSize - Size of the supplied output buffer, if the buffer is too small
|
||
then STATUS_BUFFER_TOO_SMALL is returned and this parameter contains the total
|
||
bytes necessary to complete the operation.
|
||
|
||
Environment:
|
||
|
||
Only called from HeadlessDispatch, which guarantees it is paged in and locked down.
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS Status = STATUS_SUCCESS;
|
||
PUCHAR Tmp;
|
||
UCHAR LocalBuffer[HEADLESS_TMP_BUFFER_SIZE];
|
||
PHEADLESS_RSP_QUERY_INFO Response;
|
||
KIRQL OldIrql;
|
||
|
||
ASSERT(HeadlessGlobals != NULL);
|
||
ASSERT(HeadlessGlobals->PageLockHandle != NULL);
|
||
|
||
|
||
if ((Command != HeadlessCmdAddLogEntry) &&
|
||
(Command != HeadlessCmdStartBugCheck) &&
|
||
(Command != HeadlessCmdSendBlueScreenData) &&
|
||
(Command != HeadlessCmdDoBugCheckProcessing)) {
|
||
|
||
HEADLESS_ACQUIRE_SPIN_LOCK();
|
||
|
||
if (HeadlessGlobals->ProcessingCmd) {
|
||
|
||
HEADLESS_RELEASE_SPIN_LOCK();
|
||
return STATUS_UNSUCCESSFUL;
|
||
}
|
||
|
||
HeadlessGlobals->ProcessingCmd = TRUE;
|
||
|
||
HEADLESS_RELEASE_SPIN_LOCK();
|
||
}
|
||
|
||
//
|
||
// Verify parameters for each command and then call the appropriate subroutine
|
||
// to process it.
|
||
//
|
||
switch (Command) {
|
||
|
||
//
|
||
// Enable terminal
|
||
//
|
||
case HeadlessCmdEnableTerminal:
|
||
|
||
if ((InputBuffer == NULL) ||
|
||
(InputBufferSize != sizeof(HEADLESS_CMD_ENABLE_TERMINAL))) {
|
||
Status = STATUS_INVALID_PARAMETER;
|
||
goto EndOfFunction;
|
||
}
|
||
|
||
Status = HdlspEnableTerminal(((PHEADLESS_CMD_ENABLE_TERMINAL)InputBuffer)->Enable);
|
||
goto EndOfFunction;
|
||
|
||
|
||
//
|
||
// Check for reboot string
|
||
//
|
||
case HeadlessCmdCheckForReboot:
|
||
|
||
if ((OutputBuffer == NULL) ||
|
||
(OutputBufferSize == NULL) ||
|
||
(*OutputBufferSize != sizeof(HEADLESS_RSP_REBOOT))) {
|
||
Status = STATUS_INVALID_PARAMETER;
|
||
goto EndOfFunction;
|
||
}
|
||
|
||
if (HeadlessGlobals->TerminalEnabled) {
|
||
|
||
if (HdlspGetLine(LocalBuffer, HEADLESS_TMP_BUFFER_SIZE)) {
|
||
|
||
((PHEADLESS_RSP_REBOOT)OutputBuffer)->Reboot = (BOOLEAN)
|
||
(!strcmp((LPCSTR)LocalBuffer, "reboot") ||
|
||
!strcmp((LPCSTR)LocalBuffer, "shutdown"));
|
||
|
||
}
|
||
|
||
} else {
|
||
|
||
((PHEADLESS_RSP_REBOOT)OutputBuffer)->Reboot = FALSE;
|
||
|
||
}
|
||
|
||
Status = STATUS_SUCCESS;
|
||
goto EndOfFunction;
|
||
|
||
|
||
|
||
//
|
||
// Output a string.
|
||
//
|
||
case HeadlessCmdPutString:
|
||
|
||
if (InputBuffer == NULL) {
|
||
Status = STATUS_INVALID_PARAMETER;
|
||
goto EndOfFunction;
|
||
}
|
||
|
||
if (HeadlessGlobals->TerminalEnabled) {
|
||
|
||
HdlspPutString(&(((PHEADLESS_CMD_PUT_STRING)InputBuffer)->String[0]));
|
||
|
||
}
|
||
Status = STATUS_SUCCESS;
|
||
goto EndOfFunction;
|
||
|
||
|
||
//
|
||
// Output a data stream.
|
||
//
|
||
case HeadlessCmdPutData:
|
||
|
||
if ( (InputBuffer == NULL) ||
|
||
(InputBufferSize == 0) ) {
|
||
Status = STATUS_INVALID_PARAMETER;
|
||
goto EndOfFunction;
|
||
}
|
||
|
||
if (HeadlessGlobals->TerminalEnabled) {
|
||
|
||
HdlspPutData(&(((PHEADLESS_CMD_PUT_STRING)InputBuffer)->String[0]),
|
||
InputBufferSize);
|
||
|
||
}
|
||
Status = STATUS_SUCCESS;
|
||
goto EndOfFunction;
|
||
|
||
|
||
//
|
||
// Poll for input
|
||
//
|
||
case HeadlessCmdTerminalPoll:
|
||
|
||
if ((OutputBuffer == NULL) ||
|
||
(OutputBufferSize == NULL) ||
|
||
(*OutputBufferSize != sizeof(HEADLESS_RSP_POLL))) {
|
||
Status = STATUS_INVALID_PARAMETER;
|
||
goto EndOfFunction;
|
||
}
|
||
|
||
if (HeadlessGlobals->TerminalEnabled) {
|
||
|
||
((PHEADLESS_RSP_POLL)OutputBuffer)->QueuedInput = InbvPortPollOnly(HeadlessGlobals->TerminalPort);
|
||
|
||
} else {
|
||
|
||
((PHEADLESS_RSP_POLL)OutputBuffer)->QueuedInput = FALSE;
|
||
|
||
}
|
||
|
||
Status = STATUS_SUCCESS;
|
||
goto EndOfFunction;
|
||
|
||
|
||
//
|
||
// Get a single byte of input
|
||
//
|
||
case HeadlessCmdGetByte:
|
||
|
||
if ((OutputBuffer == NULL) ||
|
||
(OutputBufferSize == NULL) ||
|
||
(*OutputBufferSize != sizeof(HEADLESS_RSP_GET_BYTE))) {
|
||
Status = STATUS_INVALID_PARAMETER;
|
||
goto EndOfFunction;
|
||
}
|
||
|
||
if (HeadlessGlobals->TerminalEnabled) {
|
||
|
||
if (InbvPortPollOnly(HeadlessGlobals->TerminalPort)) {
|
||
InbvPortGetByte(HeadlessGlobals->TerminalPort,
|
||
&(((PHEADLESS_RSP_GET_BYTE)OutputBuffer)->Value)
|
||
);
|
||
} else {
|
||
((PHEADLESS_RSP_GET_BYTE)OutputBuffer)->Value = 0;
|
||
}
|
||
|
||
} else {
|
||
|
||
((PHEADLESS_RSP_GET_BYTE)OutputBuffer)->Value = 0;
|
||
|
||
}
|
||
|
||
Status = STATUS_SUCCESS;
|
||
goto EndOfFunction;
|
||
|
||
|
||
//
|
||
// Get an entire line of input, if available.
|
||
//
|
||
case HeadlessCmdGetLine:
|
||
|
||
if ((OutputBuffer == NULL) ||
|
||
(OutputBufferSize == NULL) ||
|
||
(*OutputBufferSize < sizeof(HEADLESS_RSP_GET_LINE))) {
|
||
Status = STATUS_INVALID_PARAMETER;
|
||
goto EndOfFunction;
|
||
}
|
||
|
||
if (HeadlessGlobals->TerminalEnabled) {
|
||
|
||
((PHEADLESS_RSP_GET_LINE)OutputBuffer)->LineComplete =
|
||
HdlspGetLine(&(((PHEADLESS_RSP_GET_LINE)OutputBuffer)->Buffer[0]),
|
||
*OutputBufferSize -
|
||
sizeof(HEADLESS_RSP_GET_LINE) +
|
||
sizeof(UCHAR)
|
||
);
|
||
|
||
} else {
|
||
|
||
((PHEADLESS_RSP_GET_LINE)OutputBuffer)->LineComplete = FALSE;
|
||
|
||
}
|
||
|
||
Status = STATUS_SUCCESS;
|
||
goto EndOfFunction;
|
||
|
||
|
||
//
|
||
// Let the kernel know to convert to bug check processing mode.
|
||
//
|
||
case HeadlessCmdStartBugCheck:
|
||
|
||
HeadlessGlobals->InBugCheck = TRUE;
|
||
Status = STATUS_SUCCESS;
|
||
|
||
goto EndOfFunction;
|
||
|
||
|
||
|
||
//
|
||
// Process user I/O during a bugcheck
|
||
//
|
||
case HeadlessCmdDoBugCheckProcessing:
|
||
|
||
if (HeadlessGlobals->TerminalEnabled) {
|
||
|
||
//
|
||
// NOTE: No spin lock here because we are in bugcheck.
|
||
//
|
||
HdlspBugCheckProcessing();
|
||
|
||
}
|
||
|
||
Status = STATUS_SUCCESS;
|
||
goto EndOfFunction;
|
||
|
||
|
||
//
|
||
// Process query information command
|
||
//
|
||
case HeadlessCmdQueryInformation:
|
||
|
||
if ((OutputBuffer == NULL) ||
|
||
(OutputBufferSize == NULL) ||
|
||
(*OutputBufferSize < sizeof(HEADLESS_RSP_QUERY_INFO))) {
|
||
Status = STATUS_INVALID_PARAMETER;
|
||
goto EndOfFunction;
|
||
}
|
||
|
||
Response = (PHEADLESS_RSP_QUERY_INFO)OutputBuffer;
|
||
|
||
Response->PortType = HeadlessSerialPort;
|
||
Response->Serial.TerminalAttached = TRUE;
|
||
Response->Serial.UsedBiosSettings = (BOOLEAN)(HeadlessGlobals->UsedBiosSettings);
|
||
Response->Serial.TerminalBaudRate = HeadlessGlobals->TerminalBaudRate;
|
||
|
||
if( (HeadlessGlobals->TerminalPortNumber >= 1) || (BOOLEAN)(HeadlessGlobals->UsedBiosSettings) ) {
|
||
|
||
Response->Serial.TerminalPort = HeadlessGlobals->TerminalPortNumber;
|
||
Response->Serial.TerminalPortBaseAddress = HeadlessGlobals->TerminalPortAddress;
|
||
Response->Serial.TerminalType = HeadlessGlobals->TerminalType;
|
||
|
||
} else {
|
||
|
||
Response->Serial.TerminalPort = SerialPortUndefined;
|
||
Response->Serial.TerminalPortBaseAddress = 0;
|
||
Response->Serial.TerminalType = HeadlessGlobals->TerminalType;
|
||
|
||
}
|
||
Status = STATUS_SUCCESS;
|
||
goto EndOfFunction;
|
||
|
||
|
||
//
|
||
// Process add log entry command
|
||
//
|
||
case HeadlessCmdAddLogEntry:
|
||
|
||
if (InputBuffer == NULL) {
|
||
Status = STATUS_INVALID_PARAMETER;
|
||
goto EndOfFunction;
|
||
}
|
||
|
||
ASSERT(KeIsExecutingDpc() == FALSE);
|
||
|
||
HdlspAddLogEntry(&(((PHEADLESS_CMD_ADD_LOG_ENTRY)InputBuffer)->String[0]));
|
||
Status = STATUS_SUCCESS;
|
||
goto EndOfFunction;
|
||
|
||
|
||
//
|
||
// Print log entries
|
||
//
|
||
case HeadlessCmdDisplayLog:
|
||
|
||
if ((InputBuffer == NULL) ||
|
||
(InputBufferSize != sizeof(HEADLESS_CMD_DISPLAY_LOG))) {
|
||
Status = STATUS_INVALID_PARAMETER;
|
||
goto EndOfFunction;
|
||
}
|
||
|
||
HdlspProcessDumpCommand(((PHEADLESS_CMD_DISPLAY_LOG)InputBuffer)->Paging);
|
||
Status = STATUS_SUCCESS;
|
||
goto EndOfFunction;
|
||
|
||
//
|
||
// Various output commands
|
||
//
|
||
case HeadlessCmdClearDisplay:
|
||
case HeadlessCmdClearToEndOfDisplay:
|
||
case HeadlessCmdClearToEndOfLine:
|
||
case HeadlessCmdDisplayAttributesOff:
|
||
case HeadlessCmdDisplayInverseVideo:
|
||
case HeadlessCmdSetColor:
|
||
case HeadlessCmdPositionCursor:
|
||
|
||
if (HeadlessGlobals->TerminalEnabled) {
|
||
|
||
switch (Command) {
|
||
case HeadlessCmdClearDisplay:
|
||
Tmp = (PUCHAR)"\033[2J";
|
||
break;
|
||
|
||
case HeadlessCmdClearToEndOfDisplay:
|
||
Tmp = (PUCHAR)"\033[0J";
|
||
break;
|
||
|
||
case HeadlessCmdClearToEndOfLine:
|
||
Tmp = (PUCHAR)"\033[0K";
|
||
break;
|
||
|
||
case HeadlessCmdDisplayAttributesOff:
|
||
Tmp = (PUCHAR)"\033[0m";
|
||
break;
|
||
|
||
case HeadlessCmdDisplayInverseVideo:
|
||
Tmp = (PUCHAR)"\033[7m";
|
||
break;
|
||
|
||
case HeadlessCmdSetColor:
|
||
|
||
if ((InputBuffer == NULL) ||
|
||
(InputBufferSize != sizeof(HEADLESS_CMD_SET_COLOR))) {
|
||
Status = STATUS_INVALID_PARAMETER;
|
||
goto EndOfFunction;
|
||
}
|
||
|
||
sprintf((LPSTR)LocalBuffer,
|
||
"\033[%d;%dm",
|
||
((PHEADLESS_CMD_SET_COLOR)InputBuffer)->BkgColor,
|
||
((PHEADLESS_CMD_SET_COLOR)InputBuffer)->FgColor
|
||
);
|
||
|
||
Tmp = &(LocalBuffer[0]);
|
||
break;
|
||
|
||
case HeadlessCmdPositionCursor:
|
||
|
||
if ((InputBuffer == NULL) ||
|
||
(InputBufferSize != sizeof(HEADLESS_CMD_POSITION_CURSOR))) {
|
||
Status = STATUS_INVALID_PARAMETER;
|
||
goto EndOfFunction;
|
||
}
|
||
|
||
sprintf((LPSTR)LocalBuffer,
|
||
"\033[%d;%dH",
|
||
((PHEADLESS_CMD_POSITION_CURSOR)InputBuffer)->Y + 1,
|
||
((PHEADLESS_CMD_POSITION_CURSOR)InputBuffer)->X + 1
|
||
);
|
||
|
||
Tmp = &(LocalBuffer[0]);
|
||
break;
|
||
|
||
|
||
default:
|
||
//
|
||
// should never get here...
|
||
//
|
||
ASSERT(0);
|
||
Status = STATUS_INVALID_PARAMETER;
|
||
goto EndOfFunction;
|
||
|
||
}
|
||
|
||
HdlspSendStringAtBaud(Tmp);
|
||
|
||
}
|
||
Status = STATUS_SUCCESS;
|
||
goto EndOfFunction;
|
||
|
||
case HeadlessCmdSetBlueScreenData:
|
||
|
||
if (InputBuffer == NULL) {
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
Status = HdlspSetBlueScreenInformation(InputBuffer, InputBufferSize);
|
||
goto EndOfFunction;
|
||
|
||
case HeadlessCmdSendBlueScreenData:
|
||
|
||
if (HeadlessGlobals->TerminalEnabled && HeadlessGlobals->InBugCheck) {
|
||
|
||
if ((InputBuffer == NULL) ||
|
||
(InputBufferSize != sizeof(HEADLESS_CMD_SEND_BLUE_SCREEN_DATA))) {
|
||
ASSERT(0);
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
HdlspSendBlueScreenInfo(((PHEADLESS_CMD_SEND_BLUE_SCREEN_DATA)InputBuffer)->BugcheckCode);
|
||
|
||
HdlspSendStringAtBaud((PUCHAR)"\n\r!SAC>");
|
||
|
||
}
|
||
goto EndOfFunction;
|
||
|
||
case HeadlessCmdQueryGUID:
|
||
|
||
if( (OutputBuffer == NULL) ||
|
||
(OutputBufferSize == NULL) ||
|
||
(*OutputBufferSize < sizeof(GUID)) ) {
|
||
|
||
Status = STATUS_INVALID_PARAMETER;
|
||
goto EndOfFunction;
|
||
}
|
||
|
||
RtlCopyMemory( OutputBuffer,
|
||
&HeadlessGlobals->SystemGUID,
|
||
sizeof(GUID) );
|
||
|
||
Status = STATUS_SUCCESS;
|
||
goto EndOfFunction;
|
||
|
||
|
||
default:
|
||
Status = STATUS_INVALID_PARAMETER;
|
||
goto EndOfFunction;
|
||
|
||
}
|
||
|
||
EndOfFunction:
|
||
|
||
if ((Command != HeadlessCmdAddLogEntry) &&
|
||
(Command != HeadlessCmdStartBugCheck) &&
|
||
(Command != HeadlessCmdSendBlueScreenData) &&
|
||
(Command != HeadlessCmdDoBugCheckProcessing)) {
|
||
|
||
ASSERT(HeadlessGlobals->ProcessingCmd);
|
||
|
||
HeadlessGlobals->ProcessingCmd = FALSE;
|
||
}
|
||
|
||
return Status;
|
||
}
|
||
|
||
NTSTATUS
|
||
HdlspEnableTerminal(
|
||
BOOLEAN bEnable
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine attempts to initialize the terminal, if there is one attached, or
|
||
disconnect the terminal.
|
||
|
||
Note: Assumes it is called with the global spin lock held!
|
||
|
||
Arguments:
|
||
|
||
bEnable - If TRUE, we will allow Inbv calls to display,
|
||
otherwise we will not.
|
||
|
||
Returns:
|
||
|
||
STATUS_SUCCESS if successful, else STATUS_UNSUCCESSFUL.
|
||
|
||
Environment:
|
||
|
||
Only called from HdlspDispatch, which guarantees it is paged in and locked down.
|
||
|
||
--*/
|
||
{
|
||
|
||
//
|
||
// Only enable the port if:
|
||
// - they've asked us to
|
||
// - it's not already enabled
|
||
// - we aren't in bugcheck mode and the port is in MMIO space. We
|
||
// need to be careful here because if we are in bugcheck mode, and
|
||
// the port is in mmio space, then InbvPortInitialize() will call
|
||
// off to MmMapIoSpace(), which we can't very well do if we're in
|
||
// the process of bugchecking the machine.
|
||
|
||
if ( (bEnable == TRUE) &&
|
||
(!HeadlessGlobals->TerminalEnabled) &&
|
||
!(HeadlessGlobals->IsMMIODevice && HeadlessGlobals->InBugCheck) ) {
|
||
|
||
HeadlessGlobals->TerminalEnabled = InbvPortInitialize(
|
||
HeadlessGlobals->TerminalBaudRate,
|
||
HeadlessGlobals->TerminalPortNumber,
|
||
HeadlessGlobals->TerminalPortAddress,
|
||
&(HeadlessGlobals->TerminalPort),
|
||
HeadlessGlobals->IsMMIODevice
|
||
);
|
||
|
||
if (!HeadlessGlobals->TerminalEnabled) {
|
||
return STATUS_UNSUCCESSFUL;
|
||
}
|
||
|
||
|
||
//
|
||
// There's likely stale data on the screen from the loader.
|
||
// Let's clear the screen here before the SAC or anyone else
|
||
// has a chance to put up any data they want.
|
||
//
|
||
HdlspSendStringAtBaud((PUCHAR)"\033[2J");
|
||
|
||
|
||
//
|
||
// Let's home the cursor too.
|
||
//
|
||
HdlspSendStringAtBaud((PUCHAR)"\033[H");
|
||
|
||
//
|
||
// We know we want the FIFO on while using the headless port
|
||
//
|
||
InbvPortEnableFifo(
|
||
HeadlessGlobals->TerminalPort,
|
||
bEnable
|
||
);
|
||
|
||
} else if (bEnable == FALSE) {
|
||
|
||
InbvPortTerminate(HeadlessGlobals->TerminalPort);
|
||
|
||
HeadlessGlobals->TerminalPort = 0;
|
||
HeadlessGlobals->TerminalEnabled = FALSE;
|
||
|
||
}
|
||
|
||
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
VOID
|
||
UTF8Encode(
|
||
USHORT InputValue,
|
||
PUCHAR UTF8Encoding
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Generates the UTF8 translation for a 16-bit value.
|
||
|
||
Arguments:
|
||
|
||
InputValue - 16-bit value to be encoded.
|
||
UTF8Encoding - receives the UTF8-encoding of the 16-bit value
|
||
|
||
Return Value:
|
||
|
||
NONE.
|
||
--*/
|
||
{
|
||
|
||
//
|
||
// convert into UTF8 for actual transmission
|
||
//
|
||
// UTF-8 encodes 2-byte Unicode characters as follows:
|
||
// If the first nine bits are zero (00000000 0xxxxxxx), encode it as one byte 0xxxxxxx
|
||
// If the first five bits are zero (00000yyy yyxxxxxx), encode it as two bytes 110yyyyy 10xxxxxx
|
||
// Otherwise (zzzzyyyy yyxxxxxx), encode it as three bytes 1110zzzz 10yyyyyy 10xxxxxx
|
||
//
|
||
if( (InputValue & 0xFF80) == 0 ) {
|
||
//
|
||
// if the top 9 bits are zero, then just
|
||
// encode as 1 byte. (ASCII passes through unchanged).
|
||
//
|
||
UTF8Encoding[2] = (UCHAR)(InputValue & 0xFF);
|
||
} else if( (InputValue & 0xF800) == 0 ) {
|
||
//
|
||
// if the top 5 bits are zero, then encode as 2 bytes
|
||
//
|
||
UTF8Encoding[2] = (UCHAR)(InputValue & 0x3F) | 0x80;
|
||
UTF8Encoding[1] = (UCHAR)((InputValue >> 6) & 0x1F) | 0xC0;
|
||
} else {
|
||
//
|
||
// encode as 3 bytes
|
||
//
|
||
UTF8Encoding[2] = (UCHAR)(InputValue & 0x3F) | 0x80;
|
||
UTF8Encoding[1] = (UCHAR)((InputValue >> 6) & 0x3F) | 0x80;
|
||
UTF8Encoding[0] = (UCHAR)((InputValue >> 12) & 0xF) | 0xE0;
|
||
}
|
||
}
|
||
|
||
VOID
|
||
HdlspPutString(
|
||
PUCHAR String
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine writes a string out to the terminal.
|
||
|
||
Note: the routine assumes it is called with the global spin lock held.
|
||
|
||
Arguments:
|
||
|
||
String - NULL terminated string to write.
|
||
|
||
Returns:
|
||
|
||
None.
|
||
|
||
Environment:
|
||
|
||
Only called from HdlspDispatch, which guarantees it is paged in and locked down.
|
||
|
||
--*/
|
||
{
|
||
PUCHAR Src, Dest;
|
||
UCHAR Char = 0;
|
||
|
||
//
|
||
// We need to worry about sending a vt100 characters not in the standard
|
||
// ASCII set, so we copy over only ASCII characters into a new buffer and
|
||
// then send that one to the terminal.
|
||
//
|
||
Src = String;
|
||
Dest = &(HeadlessGlobals->TmpBuffer[0]);
|
||
|
||
while (*Src != '\0') {
|
||
|
||
if (Dest >= &(HeadlessGlobals->TmpBuffer[HEADLESS_TMP_BUFFER_SIZE - 1])) {
|
||
|
||
HeadlessGlobals->TmpBuffer[HEADLESS_TMP_BUFFER_SIZE - 1] = '\0';
|
||
HdlspSendStringAtBaud(HeadlessGlobals->TmpBuffer);
|
||
Dest = &(HeadlessGlobals->TmpBuffer[0]);
|
||
|
||
} else {
|
||
|
||
Char = *Src;
|
||
|
||
//
|
||
// filter some characters that aren't printable in VT100
|
||
// into substitute characters which are printable
|
||
//
|
||
if (Char & 0x80) {
|
||
|
||
switch (Char) {
|
||
case 0xB0: // Light shaded block
|
||
case 0xB3: // Light vertical
|
||
case 0xBA: // Double vertical line
|
||
Char = '|';
|
||
break;
|
||
case 0xB1: // Middle shaded block
|
||
case 0xDC: // Lower half block
|
||
case 0xDD: // Right half block
|
||
case 0xDE: // Left half block
|
||
case 0xDF: // Upper half block
|
||
Char = '%';
|
||
break;
|
||
case 0xB2: // Dark shaded block
|
||
case 0xDB: // Full block
|
||
Char = '#';
|
||
break;
|
||
case 0xA9: // Reversed NOT sign
|
||
case 0xAA: // NOT sign
|
||
case 0xBB: // '<27>'
|
||
case 0xBC: // '<27>'
|
||
case 0xBF: // '<27>'
|
||
case 0xC0: // '<27>'
|
||
case 0xC8: // '<27>'
|
||
case 0xC9: // '<27>'
|
||
case 0xD9: // '<27>'
|
||
case 0xDA: // '<27>'
|
||
Char = '+';
|
||
break;
|
||
case 0xC4: // '<27>'
|
||
Char = '-';
|
||
break;
|
||
case 0xCD: // '<27>'
|
||
Char = '=';
|
||
break;
|
||
}
|
||
|
||
}
|
||
|
||
|
||
|
||
//
|
||
// If the high-bit is still set, and we're here, then we are going to
|
||
// spew UTF8-encoded data (assuming our terminal type says it's okay).
|
||
//
|
||
if( (Char & 0x80) ) {
|
||
|
||
UCHAR UTF8Encoding[3] = {0};
|
||
ULONG i;
|
||
|
||
//
|
||
// Lookup the Unicode equivilent of this 8-bit ANSI value.
|
||
//
|
||
UTF8Encode( PcAnsiToUnicode[(Char & 0x7F)],
|
||
UTF8Encoding );
|
||
|
||
for( i = 0; i < 3; i++ ) {
|
||
if( UTF8Encoding[i] != 0 ) {
|
||
*Dest = UTF8Encoding[i];
|
||
Dest++;
|
||
}
|
||
}
|
||
|
||
|
||
} else {
|
||
|
||
//
|
||
// He's 7-bit ASCII. Put it in the Destination buffer
|
||
// and move on.
|
||
//
|
||
*Dest = Char;
|
||
Dest++;
|
||
|
||
}
|
||
|
||
Src++;
|
||
|
||
}
|
||
|
||
}
|
||
|
||
*Dest = '\0';
|
||
|
||
HdlspSendStringAtBaud(HeadlessGlobals->TmpBuffer);
|
||
|
||
}
|
||
|
||
VOID
|
||
HdlspPutData(
|
||
PUCHAR InputBuffer,
|
||
SIZE_T InputBufferSize
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine writes an array of UCHARs out to the terminal.
|
||
|
||
Note: the routine assumes it is called with the global spin lock held.
|
||
|
||
Arguments:
|
||
|
||
InputBuffer - Array of characters to write.
|
||
|
||
InputBufferSize - Number of characters to write.
|
||
|
||
Returns:
|
||
|
||
None.
|
||
|
||
Environment:
|
||
|
||
Only called from HdlspDispatch, which guarantees it is paged in and locked down.
|
||
|
||
--*/
|
||
{
|
||
ULONG i;
|
||
|
||
for (i = 0; i < InputBufferSize; i++) {
|
||
|
||
InbvPortPutByte(HeadlessGlobals->TerminalPort, InputBuffer[i]);
|
||
}
|
||
|
||
}
|
||
|
||
BOOLEAN
|
||
HdlspGetLine(
|
||
PUCHAR InputBuffer,
|
||
SIZE_T InputBufferLength
|
||
)
|
||
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function fills the given buffer with an input line, once the user has
|
||
pressed return. Until then it will return FALSE. It strips of leading and
|
||
trailing whitespace.
|
||
|
||
Arguments:
|
||
|
||
InputBuffer - Place to store the terminal input line.
|
||
|
||
InputBufferLength - Length, in bytes, of InputBuffer.
|
||
|
||
Return Value:
|
||
|
||
TRUE if InputBuffer is filled, else FALSE.
|
||
|
||
Environment:
|
||
|
||
Only called from HdlspDispatch, which guarantees it is paged in and locked down.
|
||
|
||
--*/
|
||
|
||
{
|
||
UCHAR NewByte;
|
||
SIZE_T i;
|
||
KIRQL OldIrql;
|
||
BOOLEAN CheckForLF;
|
||
|
||
CheckForLF = FALSE;
|
||
|
||
HEADLESS_ACQUIRE_SPIN_LOCK();
|
||
|
||
if (HeadlessGlobals->InputProcessing) {
|
||
HEADLESS_RELEASE_SPIN_LOCK();
|
||
return FALSE;
|
||
}
|
||
|
||
HeadlessGlobals->InputProcessing = TRUE;
|
||
|
||
HEADLESS_RELEASE_SPIN_LOCK();
|
||
|
||
//
|
||
// Check if we already have a line to be returned (could happen if
|
||
// InputBuffer is/was too small to contain the whole line)
|
||
//
|
||
if (HeadlessGlobals->InputLineDone) {
|
||
goto ReturnInputLine;
|
||
}
|
||
|
||
GetByte:
|
||
|
||
if (!InbvPortPollOnly(HeadlessGlobals->TerminalPort) ||
|
||
!InbvPortGetByte(HeadlessGlobals->TerminalPort, &NewByte)) {
|
||
NewByte = 0;
|
||
}
|
||
|
||
//
|
||
// If no waiting input, leave
|
||
//
|
||
if (NewByte == 0) {
|
||
HeadlessGlobals->InputProcessing = FALSE;
|
||
return FALSE;
|
||
}
|
||
|
||
//
|
||
// Store input character in our buffer
|
||
//
|
||
HeadlessGlobals->InputBuffer[HeadlessGlobals->InputBufferIndex] = NewByte;
|
||
|
||
//
|
||
// filter out the LF if we JUST received a CR
|
||
//
|
||
if (HeadlessGlobals->IsLastCharCR) {
|
||
|
||
//
|
||
// if this is a LF, then ignore it and go get the next character.
|
||
// if this is NOT an LF, then there is nothing to do
|
||
//
|
||
if (NewByte == 0x0A) {
|
||
|
||
HeadlessGlobals->IsLastCharCR = FALSE;
|
||
|
||
goto GetByte;
|
||
|
||
}
|
||
|
||
}
|
||
|
||
//
|
||
// if this is a CR, then remember it
|
||
//
|
||
HeadlessGlobals->IsLastCharCR = (NewByte == 0x0D) ? TRUE : FALSE;
|
||
|
||
//
|
||
// If this is a return, then we are done and need to return the line
|
||
//
|
||
if ((NewByte == (UCHAR)'\n') || (NewByte == (UCHAR)'\r')) {
|
||
HdlspSendStringAtBaud((PUCHAR)"\r\n");
|
||
HeadlessGlobals->InputBuffer[HeadlessGlobals->InputBufferIndex] = '\0';
|
||
HeadlessGlobals->InputBufferIndex++;
|
||
goto StripWhitespaceAndReturnLine;
|
||
}
|
||
|
||
//
|
||
// If this is a backspace or delete, then we need to do that.
|
||
//
|
||
if ((NewByte == 0x8) || (NewByte == 0x7F)) { // backspace (^H) or delete
|
||
|
||
if (HeadlessGlobals->InputBufferIndex > 0) {
|
||
HdlspSendStringAtBaud((PUCHAR)"\010 \010");
|
||
HeadlessGlobals->InputBufferIndex--;
|
||
}
|
||
|
||
} else if (NewByte == 0x3) { // Control-C
|
||
|
||
//
|
||
// Terminate the string and return it.
|
||
//
|
||
HeadlessGlobals->InputBufferIndex++;
|
||
HeadlessGlobals->InputBuffer[HeadlessGlobals->InputBufferIndex] = '\0';
|
||
HeadlessGlobals->InputBufferIndex++;
|
||
goto StripWhitespaceAndReturnLine;
|
||
|
||
} else if ((NewByte == 0x9) || (NewByte == 0x1B)) { // Tab or Esc
|
||
|
||
//
|
||
// Ignore tabs and escapes
|
||
//
|
||
HdlspSendStringAtBaud((PUCHAR)"\007");
|
||
HeadlessGlobals->InputProcessing = FALSE;
|
||
return FALSE;
|
||
|
||
} else if (HeadlessGlobals->InputBufferIndex == HEADLESS_TMP_BUFFER_SIZE - 2) {
|
||
|
||
//
|
||
// We are at the end of the buffer - remove the last character from
|
||
// the terminal screen and replace it with this one.
|
||
//
|
||
sprintf((LPSTR)HeadlessGlobals->TmpBuffer, "\010%c", NewByte);
|
||
HdlspSendStringAtBaud(HeadlessGlobals->TmpBuffer);
|
||
|
||
} else {
|
||
|
||
//
|
||
// Echo the character to the screen
|
||
//
|
||
sprintf((LPSTR)HeadlessGlobals->TmpBuffer, "%c", NewByte);
|
||
HdlspSendStringAtBaud(HeadlessGlobals->TmpBuffer);
|
||
HeadlessGlobals->InputBufferIndex++;
|
||
|
||
}
|
||
|
||
goto GetByte;
|
||
|
||
StripWhitespaceAndReturnLine:
|
||
|
||
//
|
||
// Before returning the input line, strip off all leading and trailing blanks
|
||
//
|
||
ASSERT(HeadlessGlobals->InputBufferIndex > 0);
|
||
|
||
i = HeadlessGlobals->InputBufferIndex - 1;
|
||
|
||
while ((i != 0) &&
|
||
((HeadlessGlobals->InputBuffer[i] == '\0') ||
|
||
(HeadlessGlobals->InputBuffer[i] == ' ') ||
|
||
(HeadlessGlobals->InputBuffer[i] == '\t'))) {
|
||
i--;
|
||
}
|
||
|
||
if (HeadlessGlobals->InputBuffer[i] != '\0') {
|
||
HeadlessGlobals->InputBuffer[i + 1] = '\0';
|
||
}
|
||
|
||
i = 0;
|
||
|
||
while ((HeadlessGlobals->InputBuffer[i] != '\0') &&
|
||
((HeadlessGlobals->InputBuffer[i] == '\t') ||
|
||
(HeadlessGlobals->InputBuffer[i] == ' '))) {
|
||
i++;
|
||
}
|
||
|
||
if (i != 0) {
|
||
strcpy(
|
||
(LPSTR)&(HeadlessGlobals->InputBuffer[0]),
|
||
(LPSTR)&(HeadlessGlobals->InputBuffer[i]));
|
||
}
|
||
|
||
ReturnInputLine:
|
||
|
||
//
|
||
// Return the line.
|
||
//
|
||
|
||
if (InputBufferLength >= HeadlessGlobals->InputBufferIndex) {
|
||
|
||
RtlCopyMemory(InputBuffer, HeadlessGlobals->InputBuffer, HeadlessGlobals->InputBufferIndex);
|
||
HeadlessGlobals->InputBufferIndex = 0;
|
||
HeadlessGlobals->InputLineDone = FALSE;
|
||
|
||
} else {
|
||
|
||
RtlCopyMemory(InputBuffer, HeadlessGlobals->InputBuffer, InputBufferLength);
|
||
RtlCopyBytes(HeadlessGlobals->InputBuffer,
|
||
&(HeadlessGlobals->InputBuffer[InputBufferLength]),
|
||
HeadlessGlobals->InputBufferIndex - InputBufferLength
|
||
);
|
||
HeadlessGlobals->InputLineDone = TRUE;
|
||
HeadlessGlobals->InputBufferIndex -= InputBufferLength;
|
||
|
||
}
|
||
|
||
HeadlessGlobals->InputProcessing = FALSE;
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
NTSTATUS
|
||
HeadlessTerminalAddResources(
|
||
PCM_RESOURCE_LIST Resources,
|
||
ULONG ResourceListSize,
|
||
BOOLEAN TranslatedList,
|
||
PCM_RESOURCE_LIST *NewList,
|
||
PULONG NewListSize
|
||
)
|
||
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function adds any resources that the terminal needs to the list of resources
|
||
given, reallocating to a new block if necessary.
|
||
|
||
Arguments:
|
||
|
||
Resources - The current resource list.
|
||
|
||
ResourceListSize - Length, in bytes, of the list.
|
||
|
||
TranslatedList - Is this a translated list or not.
|
||
|
||
NewList - A pointer to an allocated new list, if headless adds something, otherwise
|
||
it will return NULL, indicating no new resources were added.
|
||
|
||
NewListSize - Returns the length, in bytes, of the returned list.
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS if successful, else STATUS_INSUFFICIENT_RESOURCES.
|
||
|
||
--*/
|
||
{
|
||
PCM_FULL_RESOURCE_DESCRIPTOR NewDescriptor;
|
||
PHYSICAL_ADDRESS Address;
|
||
PHYSICAL_ADDRESS TranslatedAddress;
|
||
ULONG AddressSpace;
|
||
|
||
if (HeadlessGlobals == NULL) {
|
||
*NewList = NULL;
|
||
*NewListSize = 0;
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
if( HeadlessGlobals->IsNonLegacyDevice ) {
|
||
*NewList = NULL;
|
||
*NewListSize = 0;
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
//
|
||
// Allocate space for a new list.
|
||
//
|
||
*NewListSize = ResourceListSize + sizeof(CM_FULL_RESOURCE_DESCRIPTOR);
|
||
|
||
*NewList = (PCM_RESOURCE_LIST)ExAllocatePoolWithTag(PagedPool,
|
||
*NewListSize,
|
||
'sldH');
|
||
|
||
if (*NewList == NULL) {
|
||
*NewListSize = 0;
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
//
|
||
// Copy old list into the new buffer
|
||
//
|
||
RtlCopyMemory(*NewList, Resources, ResourceListSize);
|
||
|
||
Address.QuadPart = (ULONG_PTR)HeadlessGlobals->TerminalPortAddress;
|
||
|
||
//
|
||
// If this port information is supposed to be translated, do it.
|
||
//
|
||
if (TranslatedList) {
|
||
AddressSpace = 1; // Address space port.
|
||
HalTranslateBusAddress(Internal, // device bus or internal
|
||
0, // bus number
|
||
Address, // source address
|
||
&AddressSpace, // address space
|
||
&TranslatedAddress // translated address
|
||
);
|
||
|
||
} else {
|
||
TranslatedAddress = Address;
|
||
}
|
||
|
||
|
||
//
|
||
// Add our stuff to the end.
|
||
//
|
||
(*NewList)->Count++;
|
||
|
||
NewDescriptor = (PCM_FULL_RESOURCE_DESCRIPTOR)(((PUCHAR)(*NewList)) + ResourceListSize);
|
||
|
||
NewDescriptor->BusNumber = 0;
|
||
NewDescriptor->InterfaceType = Isa;
|
||
|
||
NewDescriptor->PartialResourceList.Count = 1;
|
||
NewDescriptor->PartialResourceList.Revision = 0;
|
||
NewDescriptor->PartialResourceList.Version = 0;
|
||
NewDescriptor->PartialResourceList.PartialDescriptors[0].Type = CmResourceTypePort;
|
||
NewDescriptor->PartialResourceList.PartialDescriptors[0].ShareDisposition =
|
||
CmResourceShareDriverExclusive;
|
||
NewDescriptor->PartialResourceList.PartialDescriptors[0].Flags = CM_RESOURCE_PORT_IO;
|
||
NewDescriptor->PartialResourceList.PartialDescriptors[0].u.Port.Start =
|
||
TranslatedAddress;
|
||
NewDescriptor->PartialResourceList.PartialDescriptors[0].u.Port.Length = 0x8;
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
VOID
|
||
HdlspBugCheckProcessing(
|
||
VOID
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function is used to prompt and display information to the user via the
|
||
terminal. The system is assumed to be singly threaded and at a raised IRQL state.
|
||
|
||
NOTE: This is pre-emptive to the system, so no locking required.
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
Environment:
|
||
|
||
ONLY IN BUGCHECK!
|
||
|
||
--*/
|
||
{
|
||
UCHAR InputBuffer[HEADLESS_TMP_BUFFER_SIZE];
|
||
ULONG i;
|
||
|
||
ASSERT(HeadlessGlobals->InBugCheck);
|
||
|
||
//
|
||
// Check for characters
|
||
//
|
||
if (HdlspGetLine(InputBuffer, HEADLESS_TMP_BUFFER_SIZE)) {
|
||
|
||
//
|
||
// Process the line
|
||
//
|
||
if ((_stricmp((LPCSTR)InputBuffer, "?") == 0) ||
|
||
(_stricmp((LPCSTR)InputBuffer, "help") == 0)) {
|
||
|
||
HdlspSendStringAtBaud((PUCHAR)"\r\n");
|
||
HdlspSendStringAtBaud((PUCHAR)"d Display all log entries, paging is on.\r\n");
|
||
HdlspSendStringAtBaud((PUCHAR)"help Display this list.\r\n");
|
||
HdlspSendStringAtBaud((PUCHAR)"restart Restart the system immediately.\r\n");
|
||
HdlspSendStringAtBaud((PUCHAR)"? Display this list.\r\n");
|
||
HdlspSendStringAtBaud((PUCHAR)"\r\n");
|
||
|
||
} else if (_stricmp((LPCSTR)InputBuffer, "d") == 0) {
|
||
|
||
HdlspProcessDumpCommand(TRUE);
|
||
|
||
} else if (_stricmp((LPCSTR)InputBuffer, "restart") == 0) {
|
||
|
||
InbvSolidColorFill(0,0,639,479,0); // make the screen black
|
||
for (i =0; i<10; i++) { // pause long enough for things to get out serial port
|
||
KeStallExecutionProcessor(100000);
|
||
}
|
||
HalReturnToFirmware(HalRebootRoutine);
|
||
|
||
} else {
|
||
HdlspSendStringAtBaud((PUCHAR)"Type ? or Help for a list of commands.\r\n");
|
||
}
|
||
|
||
//
|
||
// Put a new command prompt
|
||
//
|
||
HdlspSendStringAtBaud((PUCHAR)"\n\r!SAC>");
|
||
}
|
||
|
||
}
|
||
|
||
VOID
|
||
HdlspProcessDumpCommand(
|
||
IN BOOLEAN Paging
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function is used to display all current log entries.
|
||
|
||
Arguments:
|
||
|
||
Paging - Should this do paging or not.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
Environment:
|
||
|
||
May only be called from a raised IRQL if a StartBugCheck command has been issued.
|
||
|
||
--*/
|
||
{
|
||
PHEADLESS_LOG_ENTRY LogEntry;
|
||
ULONG LogEntryIndex;
|
||
TIME_FIELDS TimeFields;
|
||
UNICODE_STRING UnicodeString;
|
||
ANSI_STRING AnsiString;
|
||
ULONG LineNumber;
|
||
BOOLEAN Stop;
|
||
KIRQL OldIrql;
|
||
|
||
HEADLESS_ACQUIRE_SPIN_LOCK();
|
||
|
||
if (HeadlessGlobals->LogEntryStart == (USHORT)-1) {
|
||
|
||
HEADLESS_RELEASE_SPIN_LOCK();
|
||
return;
|
||
}
|
||
|
||
HeadlessGlobals->NewLogEntryAdded = FALSE;
|
||
|
||
AnsiString.Length = 0;
|
||
AnsiString.MaximumLength = HEADLESS_TMP_BUFFER_SIZE;
|
||
AnsiString.Buffer = (PCHAR)HeadlessGlobals->TmpBuffer;
|
||
|
||
LogEntryIndex = HeadlessGlobals->LogEntryStart;
|
||
LineNumber = 0;
|
||
|
||
while (TRUE) {
|
||
|
||
LogEntry = &(HeadlessGlobals->LogEntries[LogEntryIndex]);
|
||
|
||
//
|
||
// Print the log entry out to the terminal.
|
||
//
|
||
|
||
HEADLESS_RELEASE_SPIN_LOCK();
|
||
|
||
RtlTimeToTimeFields(&(LogEntry->TimeOfEntry.CurrentTime), &TimeFields);
|
||
|
||
sprintf((LPSTR)HeadlessGlobals->TmpBuffer,
|
||
"%02d:%02d:%02d.%03d : ",
|
||
TimeFields.Hour,
|
||
TimeFields.Minute,
|
||
TimeFields.Second,
|
||
TimeFields.Milliseconds
|
||
);
|
||
|
||
|
||
HdlspPutString(HeadlessGlobals->TmpBuffer);
|
||
|
||
if (wcslen(LogEntry->String) >= HEADLESS_TMP_BUFFER_SIZE - 1) {
|
||
LogEntry->String[HEADLESS_TMP_BUFFER_SIZE - 1] = UNICODE_NULL;
|
||
}
|
||
|
||
RtlInitUnicodeString(&UnicodeString, LogEntry->String);
|
||
RtlUnicodeStringToAnsiString(&AnsiString, &UnicodeString, FALSE);
|
||
|
||
HEADLESS_ACQUIRE_SPIN_LOCK();
|
||
|
||
if (HeadlessGlobals->NewLogEntryAdded) {
|
||
|
||
//
|
||
// Inform user and quite current output
|
||
//
|
||
HdlspPutString((PUCHAR)"New log entries have been added during dump, command aborted.\r\n");
|
||
|
||
HEADLESS_RELEASE_SPIN_LOCK();
|
||
return;
|
||
}
|
||
|
||
HdlspPutString(HeadlessGlobals->TmpBuffer);
|
||
HdlspPutString((PUCHAR)"\r\n");
|
||
LineNumber++;
|
||
|
||
//
|
||
// if last item, exit loop.
|
||
//
|
||
if (LogEntryIndex == HeadlessGlobals->LogEntryLast) {
|
||
HEADLESS_RELEASE_SPIN_LOCK();
|
||
return;
|
||
}
|
||
|
||
//
|
||
// If screen is full, pause for paging.
|
||
//
|
||
if (Paging && (LineNumber > 20)) {
|
||
|
||
HEADLESS_RELEASE_SPIN_LOCK();
|
||
|
||
HdlspPutMore(&Stop);
|
||
|
||
HEADLESS_ACQUIRE_SPIN_LOCK();
|
||
|
||
if (Stop) {
|
||
|
||
HdlspPutString((PUCHAR)"\r\n");
|
||
|
||
HEADLESS_RELEASE_SPIN_LOCK();
|
||
return;
|
||
}
|
||
|
||
if (HeadlessGlobals->NewLogEntryAdded) {
|
||
|
||
//
|
||
// Inform user and quite current output
|
||
//
|
||
HdlspPutString((PUCHAR)"New log entries have been added while waiting, command aborted.\r\n");
|
||
|
||
HEADLESS_RELEASE_SPIN_LOCK();
|
||
return;
|
||
}
|
||
|
||
LineNumber = 0;
|
||
}
|
||
|
||
//
|
||
// Next entry please
|
||
//
|
||
LogEntryIndex++;
|
||
LogEntryIndex %= HEADLESS_LOG_NUMBER_OF_ENTRIES;
|
||
}
|
||
|
||
}
|
||
|
||
VOID
|
||
HdlspPutMore(
|
||
OUT PBOOLEAN Stop
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function is used to display a paging prompt.
|
||
|
||
Arguments:
|
||
|
||
Stop - Returns TRUE if Control-C was pressed, else FALSE.
|
||
|
||
Return Value:
|
||
|
||
Stop - Returns TRUE if Control-C was pressed, else FALSE.
|
||
|
||
--*/
|
||
{
|
||
UCHAR Buffer[10];
|
||
LARGE_INTEGER WaitTime;
|
||
|
||
WaitTime.QuadPart = Int32x32To64((LONG)100, -1000); // 100ms from now.
|
||
|
||
HdlspPutString((PUCHAR)"----Press <Enter> for more----");
|
||
|
||
while (!HdlspGetLine(Buffer, 10)) {
|
||
if (!HeadlessGlobals->InBugCheck) {
|
||
KeDelayExecutionThread(KernelMode, FALSE, &WaitTime);
|
||
}
|
||
}
|
||
if (Buffer[0] == 0x3) { // Control-C
|
||
*Stop = TRUE;
|
||
} else {
|
||
*Stop = FALSE;
|
||
}
|
||
|
||
//
|
||
// Drain any remaining buffered input
|
||
//
|
||
while (HdlspGetLine(Buffer, 10)) {
|
||
}
|
||
}
|
||
|
||
VOID
|
||
HdlspAddLogEntry(
|
||
PWCHAR String
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function is used to add a string to the internal log buffer.
|
||
|
||
Arguments:
|
||
|
||
String - The string to add.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
Environment:
|
||
|
||
Only called from HdlspDispatch, which guarantees it is paged in and locked down.
|
||
|
||
--*/
|
||
{
|
||
SIZE_T StringSize;
|
||
PWCHAR OldString = NULL;
|
||
PWCHAR NewString;
|
||
SYSTEM_TIMEOFDAY_INFORMATION TimeOfEntry;
|
||
NTSTATUS Status;
|
||
KIRQL OldIrql;
|
||
|
||
StringSize = (wcslen(String) * sizeof(WCHAR)) + sizeof(UNICODE_NULL);
|
||
|
||
//
|
||
// Guard against ZwQuery..() call being paged out.
|
||
//
|
||
if (KeGetCurrentIrql() >= DISPATCH_LEVEL) {
|
||
ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL);
|
||
return;
|
||
}
|
||
|
||
//
|
||
// Get the time so we can log it.
|
||
//
|
||
Status = ZwQuerySystemInformation(SystemTimeOfDayInformation,
|
||
&TimeOfEntry,
|
||
sizeof(TimeOfEntry),
|
||
NULL
|
||
);
|
||
|
||
if (!NT_SUCCESS(Status)) {
|
||
|
||
RtlZeroMemory(&TimeOfEntry, sizeof(TimeOfEntry));
|
||
|
||
}
|
||
|
||
//
|
||
// Allocate a string for the log entry.
|
||
//
|
||
NewString = ExAllocatePoolWithTag(NonPagedPool, StringSize, ((ULONG)'sldH'));
|
||
|
||
if (NewString != NULL) {
|
||
RtlCopyMemory(NewString, String, StringSize);
|
||
}
|
||
|
||
HEADLESS_ACQUIRE_SPIN_LOCK();
|
||
|
||
HeadlessGlobals->NewLogEntryAdded = TRUE;
|
||
|
||
//
|
||
// Get the entry to use.
|
||
//
|
||
HeadlessGlobals->LogEntryLast++;
|
||
HeadlessGlobals->LogEntryLast %= HEADLESS_LOG_NUMBER_OF_ENTRIES;
|
||
|
||
//
|
||
// See if we have to move the start entry index
|
||
//
|
||
if (HeadlessGlobals->LogEntryLast == HeadlessGlobals->LogEntryStart) {
|
||
|
||
//
|
||
// Store away the old string so we can free it later.
|
||
//
|
||
if (wcscmp(HeadlessGlobals->LogEntries[HeadlessGlobals->LogEntryStart].String,
|
||
HEADLESS_OOM_STRING) != 0) {
|
||
|
||
OldString = HeadlessGlobals->LogEntries[HeadlessGlobals->LogEntryStart].String;
|
||
}
|
||
|
||
HeadlessGlobals->LogEntryStart++;;
|
||
HeadlessGlobals->LogEntryStart %= HEADLESS_LOG_NUMBER_OF_ENTRIES;
|
||
|
||
} else if (HeadlessGlobals->LogEntryStart == (USHORT)-1) {
|
||
|
||
HeadlessGlobals->LogEntryStart = 0;
|
||
|
||
}
|
||
|
||
|
||
//
|
||
// Fill in the entry part
|
||
//
|
||
RtlCopyMemory(&(HeadlessGlobals->LogEntries[HeadlessGlobals->LogEntryLast].TimeOfEntry),
|
||
&(TimeOfEntry),
|
||
sizeof(TimeOfEntry)
|
||
);
|
||
|
||
//
|
||
// Set the entry pointer
|
||
//
|
||
if (NewString == NULL) {
|
||
HeadlessGlobals->LogEntries[HeadlessGlobals->LogEntryLast].String = HEADLESS_OOM_STRING;
|
||
} else {
|
||
HeadlessGlobals->LogEntries[HeadlessGlobals->LogEntryLast].String = NewString;
|
||
}
|
||
|
||
HEADLESS_RELEASE_SPIN_LOCK();
|
||
|
||
if (OldString != NULL) {
|
||
ExFreePool(OldString);
|
||
}
|
||
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
HdlspSetBlueScreenInformation(
|
||
IN PHEADLESS_CMD_SET_BLUE_SCREEN_DATA pData,
|
||
IN SIZE_T cData
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routines allows components to set bugcheck information about the headless
|
||
terminal.
|
||
|
||
Arguments:
|
||
|
||
pData - A pointer to the data, value pair to store.
|
||
|
||
cData - Length, in bytes, of pData.
|
||
|
||
Return Value:
|
||
|
||
Status of the operation - STATUS_SUCCESS, STATUS_NO_MEMORY e.g.
|
||
|
||
Environment:
|
||
|
||
HdlspDispatch guarantess only one person to enter this procedure.
|
||
|
||
This is the only procedure modifying the HeadlessGlobals->BlueScreenData
|
||
However, bugcheck processing uses this information to send it across the
|
||
blue screen at dispatch level. No hand shaking required except ensuring that
|
||
changes to the list are done such that once bugcheck processing starts, the list
|
||
is unchanged. May cause a memory leak in a bugcheck situation, but in essence
|
||
that is better than an access violation, and acceptable since the machine is stopping.
|
||
|
||
--*/
|
||
{
|
||
|
||
PHEADLESS_BLUE_SCREEN_DATA HeadlessProp,Prev;
|
||
NTSTATUS Status;
|
||
PUCHAR pVal,pOldVal;
|
||
PUCHAR pNewVal;
|
||
SIZE_T len;
|
||
|
||
ASSERT(FIELD_OFFSET(HEADLESS_CMD_SET_BLUE_SCREEN_DATA,Data) == sizeof(ULONG));
|
||
|
||
if (HeadlessGlobals->InBugCheck) {
|
||
return STATUS_UNSUCCESSFUL;
|
||
}
|
||
|
||
if ((pData == NULL) ||
|
||
(pData->ValueIndex < 2) || // There must be at least two \0 characters in the pair.
|
||
(pData->ValueIndex >= (cData - sizeof(HEADLESS_CMD_SET_BLUE_SCREEN_DATA)) / sizeof (UCHAR)) ||
|
||
(pData->Data[pData->ValueIndex-1] != '\0') ||
|
||
(pData->Data[(cData - sizeof(HEADLESS_CMD_SET_BLUE_SCREEN_DATA))/sizeof(UCHAR)] != '\0' )) {
|
||
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
Status = STATUS_SUCCESS;
|
||
|
||
//
|
||
// Manipulation of this linked list is done only by this single entrant
|
||
// function.
|
||
//
|
||
HeadlessProp = Prev = HeadlessGlobals->BlueScreenData;
|
||
|
||
while (HeadlessProp) {
|
||
|
||
if (strcmp((LPCSTR)HeadlessProp->Property, (LPCSTR)pData->Data) == 0) {
|
||
break;
|
||
}
|
||
Prev = HeadlessProp;
|
||
HeadlessProp = HeadlessProp->Next;
|
||
}
|
||
|
||
|
||
pVal = (PUCHAR)&((pData->Data)[pData->ValueIndex]);
|
||
|
||
len = strlen((LPCSTR)pVal);
|
||
|
||
if (HeadlessProp != NULL) {
|
||
|
||
//
|
||
// The property exists. So replace it.
|
||
//
|
||
if (len) {
|
||
|
||
//
|
||
// need to replace old string.
|
||
//
|
||
pNewVal = (PUCHAR)ExAllocatePoolWithTag(NonPagedPool,
|
||
len+1,
|
||
((ULONG)'sldH')
|
||
);
|
||
|
||
if (pNewVal) {
|
||
strcpy( (LPSTR)pNewVal, (LPCSTR)pVal );
|
||
|
||
pOldVal = HeadlessProp->XMLData;
|
||
HeadlessProp->XMLData = pNewVal;
|
||
|
||
if (HeadlessGlobals->InBugCheck == FALSE) {
|
||
ExFreePool(pOldVal);
|
||
}
|
||
|
||
} else {
|
||
Status = STATUS_NO_MEMORY;
|
||
}
|
||
|
||
} else {
|
||
|
||
//
|
||
// We want to delete it, hence we passed an empty string
|
||
//
|
||
Prev->Next = HeadlessProp->Next;
|
||
|
||
if (HeadlessGlobals->BlueScreenData == HeadlessProp) {
|
||
HeadlessGlobals->BlueScreenData = Prev->Next;
|
||
}
|
||
|
||
if (HeadlessGlobals->InBugCheck == FALSE) {
|
||
ExFreePool ( HeadlessProp->XMLData );
|
||
ExFreePool ( HeadlessProp->Property );
|
||
ExFreePool ( HeadlessProp );
|
||
}
|
||
|
||
}
|
||
|
||
} else {
|
||
|
||
//
|
||
// Create a new Property-XMLValue Pair
|
||
//
|
||
if (len) { // Must be a non-empty string
|
||
|
||
HeadlessProp = (PHEADLESS_BLUE_SCREEN_DATA)ExAllocatePoolWithTag(NonPagedPool,
|
||
sizeof(HEADLESS_BLUE_SCREEN_DATA),
|
||
((ULONG) 'sldH' )
|
||
);
|
||
|
||
if (HeadlessProp) {
|
||
|
||
HeadlessProp->XMLData = (PUCHAR)ExAllocatePoolWithTag(NonPagedPool,
|
||
len+1,
|
||
((ULONG)'sldH')
|
||
);
|
||
if (HeadlessProp->XMLData) {
|
||
|
||
strcpy((LPSTR)HeadlessProp->XMLData,(LPCSTR)pVal);
|
||
pVal = pData->Data;
|
||
len = strlen ((LPCSTR)pVal);
|
||
|
||
if (len) {
|
||
|
||
HeadlessProp->Property = (PUCHAR)ExAllocatePoolWithTag(NonPagedPool,
|
||
len+1,
|
||
((ULONG)'sldH')
|
||
);
|
||
|
||
if (HeadlessProp->Property) {
|
||
|
||
strcpy((LPSTR)HeadlessProp->Property,(LPCSTR) pVal);
|
||
HeadlessProp->Next = HeadlessGlobals->BlueScreenData;
|
||
HeadlessGlobals->BlueScreenData = HeadlessProp;
|
||
|
||
} else {
|
||
|
||
Status = STATUS_NO_MEMORY;
|
||
ExFreePool(HeadlessProp->XMLData);
|
||
ExFreePool ( HeadlessProp );
|
||
|
||
}
|
||
|
||
} else { // empty property string ( will never come here )
|
||
|
||
Status = STATUS_INVALID_PARAMETER;
|
||
ExFreePool(HeadlessProp->XMLData);
|
||
ExFreePool(HeadlessProp);
|
||
|
||
}
|
||
|
||
} else {
|
||
|
||
Status = STATUS_NO_MEMORY;
|
||
ExFreePool(HeadlessProp);
|
||
|
||
}
|
||
}
|
||
|
||
} else {// empty value string.
|
||
|
||
Status = STATUS_INVALID_PARAMETER;
|
||
|
||
}
|
||
|
||
}
|
||
|
||
return Status;
|
||
}
|
||
|
||
|
||
VOID
|
||
HdlspSendBlueScreenInfo(
|
||
ULONG BugcheckCode
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routines dumps all the current blue screen data to the terminal.
|
||
|
||
Arguments:
|
||
|
||
BugcheckCode - the NT defined bug check code.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
Environment:
|
||
|
||
Only called once in a bugcheck.
|
||
|
||
--*/
|
||
{
|
||
PHEADLESS_BLUE_SCREEN_DATA pData;
|
||
UCHAR Temp[160];
|
||
|
||
ASSERT(HeadlessGlobals->InBugCheck);
|
||
|
||
HdlspSendStringAtBaud((PUCHAR)"\007\007\007<?xml>\007<BP>");
|
||
|
||
HdlspSendStringAtBaud((PUCHAR)"\r\n<INSTANCE CLASSNAME=\"BLUESCREEN\">");
|
||
|
||
sprintf((LPSTR)Temp,"\r\n<PROPERTY NAME=\"STOPCODE\" TYPE=\"string\"><VALUE>\"0x%0X\"</VALUE></PROPERTY>",BugcheckCode);
|
||
|
||
HdlspSendStringAtBaud(Temp);
|
||
|
||
pData = HeadlessGlobals->BlueScreenData;
|
||
|
||
while (pData) {
|
||
|
||
HdlspSendStringAtBaud(pData->XMLData);
|
||
pData = pData->Next;
|
||
|
||
}
|
||
|
||
HdlspSendStringAtBaud((PUCHAR)"\r\n</INSTANCE>\r\n</BP>\007");
|
||
|
||
}
|
||
|
||
VOID
|
||
HeadlessKernelAddLogEntry(
|
||
IN ULONG StringCode,
|
||
IN PUNICODE_STRING DriverName OPTIONAL
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine adds a string to the headless log if possible.
|
||
|
||
Parameters:
|
||
|
||
StringCode - The string to add.
|
||
|
||
DriverName - An optional parameter that some string codes require.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
//
|
||
// If headless not enabled, just exit now.
|
||
//
|
||
if ((HeadlessGlobals == NULL) || (HeadlessGlobals->PageLockHandle == NULL)) {
|
||
return;
|
||
}
|
||
|
||
//
|
||
// Call the paged version of this routine. Note: it will not be paged here,
|
||
// as the handle is non-NULL.
|
||
//
|
||
HdlspKernelAddLogEntry(StringCode, DriverName);
|
||
}
|
||
|
||
VOID
|
||
HdlspKernelAddLogEntry(
|
||
IN ULONG StringCode,
|
||
IN PUNICODE_STRING DriverName OPTIONAL
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine adds a string to the headless log if possible.
|
||
|
||
Parameters:
|
||
|
||
StringCode - The string to add.
|
||
|
||
DriverName - An optional parameter that some string codes require.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PHEADLESS_CMD_ADD_LOG_ENTRY HeadlessLogEntry;
|
||
UCHAR LocalBuffer[sizeof(HEADLESS_CMD_ADD_LOG_ENTRY) +
|
||
(HDLSP_LOG_MAX_STRING_LENGTH * sizeof(WCHAR))];
|
||
SIZE_T Index;
|
||
SIZE_T StringLength;
|
||
PWCHAR String;
|
||
|
||
|
||
HeadlessLogEntry = (PHEADLESS_CMD_ADD_LOG_ENTRY)LocalBuffer;
|
||
|
||
//
|
||
// Get the string associated with this string code.
|
||
//
|
||
switch (StringCode) {
|
||
case HEADLESS_LOG_LOADING_FILENAME:
|
||
String = L"KRNL: Loading ";
|
||
break;
|
||
|
||
case HEADLESS_LOG_LOAD_SUCCESSFUL:
|
||
String = L"KRNL: Load succeeded.";
|
||
break;
|
||
|
||
case HEADLESS_LOG_LOAD_FAILED:
|
||
String = L"KRNL: Load failed.";
|
||
break;
|
||
|
||
case HEADLESS_LOG_EVENT_CREATE_FAILED:
|
||
String = L"KRNL: Failed to create event.";
|
||
break;
|
||
|
||
case HEADLESS_LOG_OBJECT_TYPE_CREATE_FAILED:
|
||
String = L"KRNL: Failed to create object types.";
|
||
break;
|
||
|
||
case HEADLESS_LOG_ROOT_DIR_CREATE_FAILED:
|
||
String = L"KRNL: Failed to create root directories.";
|
||
break;
|
||
|
||
case HEADLESS_LOG_PNP_PHASE0_INIT_FAILED:
|
||
String = L"KRNL: Failed to initialize (phase 0) plug and play services.";
|
||
break;
|
||
|
||
case HEADLESS_LOG_PNP_PHASE1_INIT_FAILED:
|
||
String = L"KRNL: Failed to initialize (phase 1) plug and play services.";
|
||
break;
|
||
|
||
case HEADLESS_LOG_BOOT_DRIVERS_INIT_FAILED:
|
||
String = L"KRNL: Failed to initialize boot drivers.";
|
||
break;
|
||
|
||
case HEADLESS_LOG_LOCATE_SYSTEM_DLL_FAILED:
|
||
String = L"KRNL: Failed to locate system dll.";
|
||
break;
|
||
|
||
case HEADLESS_LOG_SYSTEM_DRIVERS_INIT_FAILED:
|
||
String = L"KRNL: Failed to initialize system drivers.";
|
||
break;
|
||
|
||
case HEADLESS_LOG_ASSIGN_SYSTEM_ROOT_FAILED:
|
||
String = L"KRNL: Failed to reassign system root.";
|
||
break;
|
||
|
||
case HEADLESS_LOG_PROTECT_SYSTEM_ROOT_FAILED:
|
||
String = L"KRNL: Failed to protect system partition.";
|
||
break;
|
||
|
||
case HEADLESS_LOG_UNICODE_TO_ANSI_FAILED:
|
||
String = L"KRNL: Failed to UnicodeToAnsi system root.";
|
||
break;
|
||
|
||
case HEADLESS_LOG_ANSI_TO_UNICODE_FAILED:
|
||
String = L"KRNL: Failed to AnsiToUnicode system root.";
|
||
break;
|
||
|
||
case HEADLESS_LOG_FIND_GROUPS_FAILED:
|
||
String = L"KRNL: Failed to find any groups.";
|
||
break;
|
||
|
||
case HEADLESS_LOG_WAIT_BOOT_DEVICES_DELETE_FAILED:
|
||
String = L"KRNL: Failed waiting for boot devices to delete.";
|
||
break;
|
||
|
||
case HEADLESS_LOG_WAIT_BOOT_DEVICES_START_FAILED:
|
||
String = L"KRNL: Failed waiting for boot devices to start.";
|
||
break;
|
||
|
||
case HEADLESS_LOG_WAIT_BOOT_DEVICES_REINIT_FAILED:
|
||
String = L"KRNL: Failed waiting for boot devices to reinit.";
|
||
break;
|
||
|
||
case HEADLESS_LOG_MARK_BOOT_PARTITION_FAILED:
|
||
String = L"KRNL: Failed marking boot partition.";
|
||
break;
|
||
|
||
default:
|
||
ASSERT(0);
|
||
String = NULL;
|
||
}
|
||
|
||
if (String != NULL) {
|
||
|
||
//
|
||
// Start by copying in the given string.
|
||
//
|
||
wcscpy(&(HeadlessLogEntry->String[0]), String);
|
||
|
||
} else {
|
||
|
||
HeadlessLogEntry->String[0] = UNICODE_NULL;
|
||
|
||
}
|
||
|
||
//
|
||
// If this is the loading_filename command, then we need to append the
|
||
// name to the end.
|
||
//
|
||
if ((StringCode == HEADLESS_LOG_LOADING_FILENAME) && (DriverName != NULL)) {
|
||
|
||
ASSERT(String != NULL);
|
||
|
||
StringLength = wcslen(String);
|
||
|
||
//
|
||
// Only copy as many bytes as we have room for.
|
||
//
|
||
if ((DriverName->Length / sizeof(WCHAR)) >= (HDLSP_LOG_MAX_STRING_LENGTH - StringLength)) {
|
||
Index = (HDLSP_LOG_MAX_STRING_LENGTH - StringLength - 1);
|
||
} else {
|
||
Index = DriverName->Length / sizeof(WCHAR);
|
||
}
|
||
|
||
//
|
||
// Copy in this many bytes.
|
||
//
|
||
RtlCopyBytes(&(HeadlessLogEntry->String[StringLength]),
|
||
DriverName->Buffer,
|
||
Index * sizeof(WCHAR)
|
||
);
|
||
|
||
if (DriverName->Buffer[(DriverName->Length / sizeof(WCHAR)) - 1] != UNICODE_NULL) {
|
||
HeadlessLogEntry->String[StringLength + Index] = UNICODE_NULL;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Log it.
|
||
//
|
||
HdlspDispatch(HeadlessCmdAddLogEntry,
|
||
HeadlessLogEntry,
|
||
sizeof(HEADLESS_CMD_ADD_LOG_ENTRY) +
|
||
(wcslen(&(HeadlessLogEntry->String[0])) * sizeof(WCHAR)),
|
||
NULL,
|
||
NULL
|
||
);
|
||
}
|
||
|
||
VOID
|
||
HdlspSendStringAtBaud(
|
||
IN PUCHAR String
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine outputs a string one character at a time to the terminal, fitting the
|
||
baud rate specified for the connection.
|
||
|
||
Parameters:
|
||
|
||
String - The string to send.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PUCHAR Dest;
|
||
|
||
for (Dest = String; *Dest != '\0'; Dest++) {
|
||
|
||
InbvPortPutByte(HeadlessGlobals->TerminalPort, *Dest);
|
||
}
|
||
|
||
}
|
||
|