[CPORTLIB/KDBG/FREELDR]: Fix CpGetByte and its callers -- it takes a 4th parameter for polling.

[NTOSKRNL]: Implement InbvPortPollOnly and InbvPortGetByte.
[NTOSKRNL]: Implement HeadlessCmdClearDisplay and HeadlessCmdGetByte.
[SACDRV]: Implement the TimerDpcRoutine which calls HeadlessCmdGetByte. We now consume characters in the EMS port.
[SACDRV]: Implement ConMgrSerialPortConsumer. We now do full VT-100 input parsing. DEL, BS, TAB, SPACE all work. Pressing ENTER results in an ASSERT in ConMgrProcessLine as expected.

svn path=/trunk/; revision=59772
This commit is contained in:
Alex Ionescu 2013-08-18 17:47:19 +00:00
parent d914dce9a0
commit a93ca08cdc
14 changed files with 496 additions and 89 deletions

View file

@ -100,7 +100,7 @@ BOOLEAN Rs232PortGetByte(PUCHAR ByteReceived)
if (PortInitialized == FALSE)
return FALSE;
return (CpGetByte(&Rs232ComPort, ByteReceived, TRUE) == CP_GET_SUCCESS);
return (CpGetByte(&Rs232ComPort, ByteReceived, TRUE, FALSE) == CP_GET_SUCCESS);
}
/*

View file

@ -143,7 +143,7 @@ BOOLEAN
WinLdrPortGetByte(IN ULONG PortId,
OUT PUCHAR Data)
{
return CpGetByte(&Port[PortId], Data, TRUE) == CP_GET_SUCCESS;
return CpGetByte(&Port[PortId], Data, TRUE, FALSE) == CP_GET_SUCCESS;
}
BOOLEAN
@ -151,7 +151,7 @@ WinLdrPortPollOnly(IN ULONG PortId)
{
UCHAR Dummy;
return CpGetByte(&Port[PortId], &Dummy, FALSE) == CP_GET_SUCCESS;
return CpGetByte(&Port[PortId], &Dummy, FALSE, TRUE) == CP_GET_SUCCESS;
}
VOID

View file

@ -27,77 +27,11 @@ PSAC_CHANNEL SacChannel;
ULONG ExecutePostConsumerCommand;
PSAC_CHANNEL ExecutePostConsumerCommandData;
BOOLEAN InputInEscape, InputInEscTab, ConMgrLastCharWasCR;
CHAR InputBuffer[80];
/* FUNCTIONS *****************************************************************/
VOID
NTAPI
ConMgrSerialPortConsumer(VOID)
{
NTSTATUS Status;
CHAR Char;
SAC_DBG(0x2000, "SAC TimerDpcRoutine: Entering.\n"); //bug
/* Acquire the manager lock and make sure a channel is selected */
SacAcquireMutexLock();
ASSERT(CurrentChannel);
/* Read whatever came off the serial port */
for (Status = SerialBufferGetChar(&Char);
NT_SUCCESS(Status);
Status = SerialBufferGetChar(&Char))
{
/* If nothing came through, bail out */
if (Status == STATUS_NO_DATA_DETECTED) break;
}
/* We're done, release the lock */
SacReleaseMutexLock();
SAC_DBG(0x2000, "SAC TimerDpcRoutine: Exiting.\n"); //bug
}
VOID
NTAPI
ConMgrWorkerProcessEvents(IN PSAC_DEVICE_EXTENSION DeviceExtension)
{
SAC_DBG(SAC_DBG_ENTRY_EXIT, "SAC WorkerProcessEvents: Entering.\n");
/* Enter the main loop */
while (TRUE)
{
/* Wait for something to do */
KeWaitForSingleObject(&DeviceExtension->Event,
Executive,
KernelMode,
FALSE,
NULL);
/* Consume data off the serial port */
ConMgrSerialPortConsumer();
switch (ExecutePostConsumerCommand)
{
case 1:
/* A reboot was sent, do it */
DoRebootCommand(FALSE);
break;
case 2:
/* A close was sent, do it */
ChanMgrCloseChannel(ExecutePostConsumerCommandData);
ChanMgrReleaseChannel(ExecutePostConsumerCommandData);
break;
case 3:
/* A shutdown was sent, do it */
DoRebootCommand(TRUE);
break;
}
/* Clear the serial port consumer state */
ExecutePostConsumerCommand = 0;
ExecutePostConsumerCommandData = NULL;
}
}
VOID
NTAPI
SacPutString(IN PWCHAR String)
@ -512,6 +446,319 @@ ConMgrChannelOWrite(IN PSAC_CHANNEL Channel,
return Status;
}
VOID
NTAPI
ConMgrProcessInputLine(VOID)
{
ASSERT(FALSE);
}
#define Nothing 0
VOID
NTAPI
ConMgrSerialPortConsumer(VOID)
{
NTSTATUS Status;
CHAR Char, LastChar;
CHAR WriteBuffer[2], ReadBuffer[2];
ULONG ReadBufferSize, i;
WCHAR StringBuffer[2];
SAC_DBG(SAC_DBG_MACHINE, "SAC TimerDpcRoutine: Entering.\n"); //bug
/* Acquire the manager lock and make sure a channel is selected */
SacAcquireMutexLock();
ASSERT(CurrentChannel);
/* Read whatever came off the serial port */
for (Status = SerialBufferGetChar(&Char);
NT_SUCCESS(Status);
Status = SerialBufferGetChar(&Char))
{
/* If nothing came through, bail out */
if (Status == STATUS_NO_DATA_DETECTED) break;
/* Check if ESC was pressed */
if (Char == '\x1B')
{
/* Was it already pressed? */
if (!InputInEscape)
{
/* First time ESC is pressed! Remember and reset TAB state */
InputInEscTab = FALSE;
InputInEscape = TRUE;
continue;
}
}
else if (Char == '\t')
{
/* TAB was pressed, is it following ESC (VT-100 sequence)? */
if (InputInEscape)
{
/* Yes! This must be the only ESC-TAB we see in once moment */
ASSERT(InputInEscTab == FALSE);
/* No longer treat us as being in ESC */
InputInEscape = FALSE;
/* ESC-TAB is the sequence for changing channels */
Status = ConMgrAdvanceCurrentChannel();
if (!NT_SUCCESS(Status)) break;
/* Remember ESC-TAB was pressed */
InputInEscTab = TRUE;
continue;
}
}
else if ((Char == '0') && (InputInEscTab))
{
/* It this ESC-TAB-0? */
ASSERT(InputInEscape == FALSE);
InputInEscTab = FALSE;
/* If writes are already enabled, don't do this */
if (!CurrentChannel->WriteEnabled)
{
/* Reset the channel, this is our special sequence */
Status = ConMgrResetCurrentChannel(FALSE);
if (!NT_SUCCESS(Status)) break;
}
continue;
}
else
{
/* This is ESC-TAB-something else */
InputInEscTab = FALSE;
/* If writes are already enabled, don't do this */
if (!CurrentChannel->WriteEnabled)
{
/* Display the current channel */
InputInEscape = FALSE;
Status = ConMgrDisplayCurrentChannel();
if (!NT_SUCCESS(Status)) break;
continue;
}
}
/* Check if an ESC-sequence was being typed into a command channel */
if ((InputInEscape) && (CurrentChannel != SacChannel))
{
/* Store the ESC in the current channel buffer */
WriteBuffer[0] = '\x1B';
ChannelIWrite(CurrentChannel, WriteBuffer, sizeof(CHAR));
}
/* Check if we are no longer pressing ESC and exit the mode if so */
if (Char != '\x1B') InputInEscape = FALSE;
/* Whatever was typed in, save it int eh current channel */
ChannelIWrite(CurrentChannel, &Char, sizeof(Char));
/* If this is a command channel, we're done, nothing to process */
if (CurrentChannel != SacChannel) continue;
/* Check for line feed right after a carriage return */
if ((ConMgrLastCharWasCR) && (Char == '\n'))
{
/* Ignore the line feed, but clear the carriage return */
ChannelIReadLast(CurrentChannel);
ConMgrLastCharWasCR = 0;
continue;
}
/* Check if the user did a carriage return */
ConMgrLastCharWasCR = (Char == '\n');
/* If the user did an "ENTER", we need to run the command */
if ((Char == '\n') || (Char == '\r'))
{
/* Echo back to the terminal */
SacPutString(L"\r\n");
DoLineParsing:
/* Inhibit the character (either CR or LF) */
ChannelIReadLast(CurrentChannel);
/* NULL-terminate the channel's input buffer */
WriteBuffer[0] = ANSI_NULL;
ChannelIWrite(CurrentChannel, WriteBuffer, sizeof(CHAR));
/* Loop over every last character */
do
{
/* Read every character in the channel, and strip whitespace */
LastChar = ChannelIReadLast(CurrentChannel);
WriteBuffer[0] = LastChar;
} while ((!(LastChar) ||
(LastChar == ' ') ||
(LastChar == '\t')) &&
(ChannelIBufferLength(CurrentChannel)));
/* Write back into the channel the last character */
ChannelIWrite(CurrentChannel, WriteBuffer, sizeof(CHAR));
/* NULL-terminate the input buffer */
WriteBuffer[0] = ANSI_NULL;
ChannelIWrite(CurrentChannel, WriteBuffer, sizeof(WCHAR));
/* Now loop over every first character */
do
{
/* Read every character in the channel, and strip whitespace */
ChannelIRead(CurrentChannel,
ReadBuffer,
sizeof(ReadBuffer),
&ReadBufferSize);
WriteBuffer[0] = ReadBuffer[0];
} while ((ReadBufferSize) &&
((ReadBuffer[0] != ' ') || (ReadBuffer[0] != '\t')));
/* We read one more than we should, so treat that as our first one */
InputBuffer[0] = ReadBuffer[0];
i = 1;
/* And now loop reading all the others */
do
{
/* Read each character -- there should be max 80 */
ChannelIRead(CurrentChannel,
ReadBuffer,
sizeof(ReadBuffer),
&ReadBufferSize);
ASSERT(i < SAC_VTUTF8_COL_WIDTH);
InputBuffer[i++] = ReadBuffer[0];
} while (ReadBufferSize);
/* Now go over the entire input stream */
for (i = 0; InputBuffer[i]; i++)
{
/* Again it should be less than 80 characters */
ASSERT(i < SAC_VTUTF8_COL_WIDTH);
/* And upcase each character */
Char = InputBuffer[i];
if ((Char >= 'A') && (Char <= 'Z')) InputBuffer[i] = Char + ' ';
}
/* Ok, at this point, no pending command should exist */
ASSERT(ExecutePostConsumerCommand == Nothing);
/* Go and process the input, then show the prompt again */
ConMgrProcessInputLine();
SacPutSimpleMessage(SAC_PROMPT);
/* If the user typed a valid command, get out of here */
if (ExecutePostConsumerCommand != Nothing) break;
/* Keep going */
continue;
}
/* Check if the user typed backspace or delete */
if ((Char == '\b') || (Char == '\x7F'))
{
/* Omit the last character, which should be the DEL/BS itself */
if (ChannelIBufferLength(CurrentChannel))
{
ChannelIReadLast(CurrentChannel);
}
/* Omit the before-last character, which is the one to delete */
if (ChannelIBufferLength(CurrentChannel))
{
/* Also send two backspaces back to the console */
SacPutString(L"\b \b");
ChannelIReadLast(CurrentChannel);
}
/* Keep going */
continue;
}
/* If the user pressed CTRL-C at this point, treat it like ENTER */
if (Char == '\x03') goto DoLineParsing;
/* Check if the user pressed TAB */
if (Char == '\t')
{
/* Omit it, send a BELL, and keep going. We ignore TABs */
ChannelIReadLast(CurrentChannel);
SacPutString(L"\a");
continue;
}
/* Check if the user is getting close to the end of the screen */
if (ChannelIBufferLength(CurrentChannel) == (SAC_VTUTF8_COL_WIDTH - 2))
{
/* Delete the last character, replacing it with this one instead */
swprintf(StringBuffer, L"\b%c", Char);
SacPutString(StringBuffer);
/* Omit the last two characters from the buffer */
ChannelIReadLast(CurrentChannel);
ChannelIReadLast(CurrentChannel);
/* NULL-terminate it */
WriteBuffer[0] = Char;
ChannelIWrite(CurrentChannel, WriteBuffer, sizeof(CHAR));
continue;
}
/* Nothing of interest happened, just write the character back */
swprintf(StringBuffer, L"%c", Char);
SacPutString(StringBuffer);
}
/* We're done, release the lock */
SacReleaseMutexLock();
SAC_DBG(SAC_DBG_MACHINE, "SAC TimerDpcRoutine: Exiting.\n"); //bug
}
VOID
NTAPI
ConMgrWorkerProcessEvents(IN PSAC_DEVICE_EXTENSION DeviceExtension)
{
SAC_DBG(SAC_DBG_ENTRY_EXIT, "SAC WorkerProcessEvents: Entering.\n");
/* Enter the main loop */
while (TRUE)
{
/* Wait for something to do */
KeWaitForSingleObject(&DeviceExtension->Event,
Executive,
KernelMode,
FALSE,
NULL);
/* Consume data off the serial port */
ConMgrSerialPortConsumer();
switch (ExecutePostConsumerCommand)
{
case 1:
/* A reboot was sent, do it */
DoRebootCommand(FALSE);
break;
case 2:
/* A close was sent, do it */
ChanMgrCloseChannel(ExecutePostConsumerCommandData);
ChanMgrReleaseChannel(ExecutePostConsumerCommandData);
break;
case 3:
/* A shutdown was sent, do it */
DoRebootCommand(TRUE);
break;
}
/* Clear the serial port consumer state */
ExecutePostConsumerCommand = 0;
ExecutePostConsumerCommandData = NULL;
}
}
NTSTATUS
NTAPI
ConMgrGetChannelCloseMessage(IN PSAC_CHANNEL Channel,
@ -522,13 +769,6 @@ ConMgrGetChannelCloseMessage(IN PSAC_CHANNEL Channel,
return STATUS_NOT_IMPLEMENTED;
}
VOID
NTAPI
ConMgrProcessInputLine(VOID)
{
ASSERT(FALSE);
}
NTSTATUS
NTAPI
ConMgrHandleEvent(IN ULONG EventCode,

View file

@ -238,7 +238,7 @@ InitializeDeviceData(IN PDEVICE_OBJECT DeviceObject)
KeInitializeTimer(&DeviceExtension->Timer);
KeInitializeDpc(&DeviceExtension->Dpc, TimerDpcRoutine, DeviceExtension);
KeInitializeSpinLock(&DeviceExtension->Lock);
KeInitializeEvent(&DeviceExtension->Event, SynchronizationEvent, 0);
KeInitializeEvent(&DeviceExtension->Event, SynchronizationEvent, FALSE);
InitializeListHead(&DeviceExtension->List);
/* Attempt to enable HDL support */
@ -317,8 +317,8 @@ InitializeDeviceData(IN PDEVICE_OBJECT DeviceObject)
DeviceExtension->PriorityFail = TRUE;
/* Initialize rundown and wait for the thread to do it */
KeInitializeEvent(&DeviceExtension->RundownEvent, SynchronizationEvent, 0);
KeSetEvent(&DeviceExtension->Event, DeviceExtension->PriorityBoost, 0);
KeInitializeEvent(&DeviceExtension->RundownEvent, SynchronizationEvent, FALSE);
KeSetEvent(&DeviceExtension->Event, DeviceExtension->PriorityBoost, FALSE);
Status = KeWaitForSingleObject(&DeviceExtension->RundownEvent,
Executive,
KernelMode,

View file

@ -12,6 +12,8 @@
/* GLOBALS *******************************************************************/
LONG TimerDpcCount;
/* FUNCTIONS *****************************************************************/
NTSTATUS
@ -61,7 +63,48 @@ TimerDpcRoutine(IN PKDPC Dpc,
IN PVOID SystemArgument1,
IN PVOID SystemArgument2)
{
HEADLESS_RSP_GET_BYTE ByteValue;
ULONG ValueSize;
BOOLEAN GotChar;
NTSTATUS Status;
PSAC_DEVICE_EXTENSION SacExtension;
/* Update our counter */
_InterlockedExchangeAdd(&TimerDpcCount, 1);
/* Set defaults and loop for new characters */
GotChar = FALSE;
ValueSize = sizeof(ByteValue);
do
{
/* Ask the kernel for a byte */
Status = HeadlessDispatch(HeadlessCmdGetByte,
NULL,
0,
&ByteValue,
&ValueSize);
/* Break out if there's nothing interesting */
if (!NT_SUCCESS(Status)) break;
if (!ByteValue.Value) break;
/* Update the serial port buffer */
SerialPortBuffer[SerialPortProducerIndex] = ByteValue.Value;
GotChar = TRUE;
/* Update the index, let it roll-over if needed */
_InterlockedExchange(&SerialPortProducerIndex,
(SerialPortProducerIndex + 1) &
(SAC_SERIAL_PORT_BUFFER_SIZE - 1));
} while (ByteValue.Value);
/* Did we get anything */
if (GotChar)
{
/* Signal the worker thread that there is work to do */
SacExtension = DeferredContext;
KeSetEvent(&SacExtension->Event, SacExtension->PriorityBoost, FALSE);
}
}
VOID

View file

@ -156,6 +156,7 @@
#define SAC_MAX_CHANNELS 10
#define SAC_SERIAL_PORT_BUFFER_SIZE 1024 // 1KB
#define SAC_MAX_MESSAGES 200
#define SAC_VTUTF8_COL_WIDTH 80
//
// Channel flags
@ -766,6 +767,35 @@ ChannelDestroy(
IN PSAC_CHANNEL Channel
);
NTSTATUS
NTAPI
ChannelIWrite(
IN PSAC_CHANNEL Channel,
IN PCHAR Buffer,
IN ULONG BufferSize
);
UCHAR
NTAPI
ChannelIReadLast(
IN PSAC_CHANNEL Channel
);
ULONG
NTAPI
ChannelIBufferLength(
IN PSAC_CHANNEL Channel
);
NTSTATUS
NTAPI
ChannelIRead(
IN PSAC_CHANNEL Channel,
IN PCHAR Buffer,
IN ULONG BufferSize,
IN OUT PULONG ResultBufferSize
);
//
// RAW Channel Table
//
@ -901,6 +931,7 @@ extern PSAC_MESSAGE_ENTRY GlobalMessageTable;
extern KMUTEX CurrentChannelLock;
extern LONG CurrentChannelRefCount;
extern PCHAR SerialPortBuffer;
extern LONG SerialPortConsumerIndex, SerialPortProducerIndex;
extern PCHAR Utf8ConversionBuffer;
extern ULONG Utf8ConversionBufferSize;

View file

@ -14,7 +14,9 @@
PCHAR Utf8ConversionBuffer;
ULONG Utf8ConversionBufferSize = PAGE_SIZE;
PSAC_MACHINE_INFO MachineInformation;
PVOID RequestSacCmdEventObjectBody;
PKEVENT RequestSacCmdEventWaitObjectBody;
PVOID RequestSacCmdSuccessEventObjectBody;

View file

@ -71,7 +71,8 @@ NTAPI
CpGetByte(
IN PCPPORT Port,
OUT PUCHAR Byte,
IN BOOLEAN Wait
IN BOOLEAN Wait,
IN BOOLEAN Poll
);
VOID

View file

@ -255,7 +255,8 @@ USHORT
NTAPI
CpGetByte(IN PCPPORT Port,
OUT PUCHAR Byte,
IN BOOLEAN Wait)
IN BOOLEAN Wait,
IN BOOLEAN Poll)
{
UCHAR Lsr;
ULONG LimitCount = Wait ? TIMEOUT_COUNT : 1;
@ -277,6 +278,9 @@ CpGetByte(IN PCPPORT Port,
return CP_GET_ERROR;
}
/* If only polling was requested by caller, return now */
if (Poll) return CP_GET_SUCCESS;
/* Otherwise read the byte and return it */
*Byte = READ_PORT_UCHAR(Port->Address + RECEIVE_BUFFER_REGISTER);

View file

@ -262,6 +262,7 @@ HdlspDispatch(IN HEADLESS_CMD Command,
PHEADLESS_RSP_QUERY_INFO HeadlessInfo;
PHEADLESS_CMD_PUT_STRING PutString;
PHEADLESS_CMD_ENABLE_TERMINAL EnableTerminal;
PHEADLESS_RSP_GET_BYTE GetByte;
NTSTATUS Status = STATUS_NOT_IMPLEMENTED;
ASSERT(HeadlessGlobals != NULL);
// ASSERT(HeadlessGlobals->PageLockHandle != NULL);
@ -328,8 +329,18 @@ HdlspDispatch(IN HEADLESS_CMD Command,
Status = STATUS_SUCCESS;
break;
case HeadlessCmdClearDisplay:
break;
case HeadlessCmdClearDisplay:
/* Send the VT100 claer screen command if the terminal is enabled */
if (HeadlessGlobals->TerminalEnabled)
{
HdlspSendStringAtBaud((PUCHAR)"\033[2J");
}
/* Return success either way */
Status = STATUS_SUCCESS;
break;
case HeadlessCmdClearToEndOfDisplay:
break;
case HeadlessCmdClearToEndOfLine:
@ -344,8 +355,46 @@ HdlspDispatch(IN HEADLESS_CMD Command,
break;
case HeadlessCmdTerminalPoll:
break;
case HeadlessCmdGetByte:
break;
case HeadlessCmdGetByte:
/* Make sure the caller passed valid data */
if (!(OutputBuffer) ||
!(OutputBufferSize) ||
(*OutputBufferSize < sizeof(*GetByte)))
{
DPRINT1("Invalid buffer\n");
Status = STATUS_INVALID_PARAMETER;
break;
}
/* Make sure the terminal is enabled */
GetByte = OutputBuffer;
if (HeadlessGlobals->TerminalEnabled)
{
/* Poll if something is on the wire */
if (InbvPortPollOnly(HeadlessGlobals->TerminalPort))
{
/* If so, read it */
InbvPortGetByte(HeadlessGlobals->TerminalPort,
&GetByte->Value);
}
else
{
/* Nothing is there, return 0 */
GetByte->Value = 0;
}
}
else
{
/* Otherwise return nothing */
GetByte->Value = 0;
}
/* Return success either way */
Status = STATUS_SUCCESS;
break;
case HeadlessCmdGetLine:
break;
case HeadlessCmdStartBugCheck:

View file

@ -23,6 +23,25 @@ CPPORT Port[4] =
/* FUNCTIONS *****************************************************************/
BOOLEAN
NTAPI
InbvPortPollOnly(IN ULONG PortId)
{
UCHAR Dummy;
/* Poll a byte from the port */
return CpGetByte(&Port[PortId], &Dummy, FALSE, TRUE) == CP_GET_SUCCESS;
}
BOOLEAN
NTAPI
InbvPortGetByte(IN ULONG PortId,
OUT PUCHAR Char)
{
/* Read a byte from the port */
return CpGetByte(&Port[PortId], Char, TRUE, FALSE) == CP_GET_SUCCESS;
}
VOID
NTAPI
InbvPortEnableFifo(IN ULONG PortId,

View file

@ -192,6 +192,11 @@ typedef struct _HEADLESS_CMD_PUT_STRING
UCHAR String[1];
} HEADLESS_CMD_PUT_STRING, *PHEADLESS_CMD_PUT_STRING;
typedef struct _HEADLESS_RSP_GET_BYTE
{
UCHAR Value;
} HEADLESS_RSP_GET_BYTE, *PHEADLESS_RSP_GET_BYTE;
NTSTATUS
NTAPI
HeadlessDispatch(

View file

@ -107,4 +107,17 @@ InbvPortInitialize(
IN BOOLEAN IsMMIODevice
);
BOOLEAN
NTAPI
InbvPortPollOnly(
IN ULONG PortId
);
BOOLEAN
NTAPI
InbvPortGetByte(
IN ULONG PortId,
OUT PUCHAR Char
);
extern BOOLEAN InbvBootDriverInstalled;

View file

@ -136,7 +136,7 @@ KdPortGetByteEx(
IN PCPPORT PortInformation,
OUT PUCHAR ByteReceived)
{
return (CpGetByte(PortInformation, ByteReceived, FALSE) == CP_GET_SUCCESS);
return (CpGetByte(PortInformation, ByteReceived, FALSE, TRUE) == CP_GET_SUCCESS);
}
VOID