Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2877 lines
74 KiB

  1. /*++
  2. Copyright (c) 1990-1998 Microsoft Corporation
  3. Module Name:
  4. hdlsterm.c
  5. Abstract:
  6. This file implements functions for dealing with a terminal attached.
  7. Author:
  8. Sean Selitrennikoff (v-seans) Oct, 1999
  9. Environment:
  10. kernel mode
  11. Revision History:
  12. --*/
  13. #include "exp.h"
  14. #pragma hdrstop
  15. #include <hdlsblk.h>
  16. #include <hdlsterm.h>
  17. #include <inbv.h>
  18. //
  19. // Defines for headless
  20. //
  21. //
  22. #define HEADLESS_OOM_STRING L"Entry could not be recorded due to lack of memory.\n"
  23. #define HEADLESS_LOG_NUMBER_OF_ENTRIES 256
  24. #define HEADLESS_TMP_BUFFER_SIZE 80
  25. //
  26. // Note: HdlspAddLogEntry() allocates a buffer off the stack of this size,
  27. // so keep this number small. Anything longer than 80 is probably useless, as
  28. // a VT100 can only handle 80 characters across.
  29. // Do not make this any shorter than the string for HEADLESS_LOG_LOADING_FILENAME
  30. //
  31. #define HDLSP_LOG_MAX_STRING_LENGTH 80
  32. #define HEADLESS_ACQUIRE_SPIN_LOCK() \
  33. if (!HeadlessGlobals->InBugCheck) { \
  34. KeAcquireSpinLock(&(HeadlessGlobals->SpinLock), &OldIrql); \
  35. } else { \
  36. OldIrql = (KIRQL)-1; \
  37. }
  38. #define HEADLESS_RELEASE_SPIN_LOCK() \
  39. if (OldIrql != (KIRQL)-1) { \
  40. KeReleaseSpinLock(&(HeadlessGlobals->SpinLock), OldIrql); \
  41. } else { \
  42. ASSERT(HeadlessGlobals->InBugCheck); \
  43. }
  44. #define COM1_PORT 0x03f8
  45. #define COM2_PORT 0x02f8
  46. //
  47. // This table provides a quick lookup conversion between ASCII values
  48. // that fall between 128 and 255, and their UNICODE counterpart.
  49. //
  50. // Note that ASCII values between 0 and 127 are equvilent to their
  51. // unicode counter parts, so no lookups would be required.
  52. //
  53. // Therefore when using this table, remove the high bit from the ASCII
  54. // value and use the resulting value as an offset into this array. For
  55. // example, 0x80 ->(remove the high bit) 00 -> 0x00C7.
  56. //
  57. USHORT PcAnsiToUnicode[0xFF] = {
  58. 0x00C7,
  59. 0x00FC,
  60. 0x00E9,
  61. 0x00E2,
  62. 0x00E4,
  63. 0x00E0,
  64. 0x00E5,
  65. 0x0087,
  66. 0x00EA,
  67. 0x00EB,
  68. 0x00E8,
  69. 0x00EF,
  70. 0x00EE,
  71. 0x00EC,
  72. 0x00C4,
  73. 0x00C5,
  74. 0x00C9,
  75. 0x00E6,
  76. 0x00C6,
  77. 0x00F4,
  78. 0x00F6,
  79. 0x00F2,
  80. 0x00FB,
  81. 0x00F9,
  82. 0x00FF,
  83. 0x00D6,
  84. 0x00DC,
  85. 0x00A2,
  86. 0x00A3,
  87. 0x00A5,
  88. 0x20A7,
  89. 0x0192,
  90. 0x00E1,
  91. 0x00ED,
  92. 0x00F3,
  93. 0x00FA,
  94. 0x00F1,
  95. 0x00D1,
  96. 0x00AA,
  97. 0x00BA,
  98. 0x00BF,
  99. 0x2310,
  100. 0x00AC,
  101. 0x00BD,
  102. 0x00BC,
  103. 0x00A1,
  104. 0x00AB,
  105. 0x00BB,
  106. 0x2591,
  107. 0x2592,
  108. 0x2593,
  109. 0x2502,
  110. 0x2524,
  111. 0x2561,
  112. 0x2562,
  113. 0x2556,
  114. 0x2555,
  115. 0x2563,
  116. 0x2551,
  117. 0x2557,
  118. 0x255D,
  119. 0x255C,
  120. 0x255B,
  121. 0x2510,
  122. 0x2514,
  123. 0x2534,
  124. 0x252C,
  125. 0x251C,
  126. 0x2500,
  127. 0x253C,
  128. 0x255E,
  129. 0x255F,
  130. 0x255A,
  131. 0x2554,
  132. 0x2569,
  133. 0x2566,
  134. 0x2560,
  135. 0x2550,
  136. 0x256C,
  137. 0x2567,
  138. 0x2568,
  139. 0x2564,
  140. 0x2565,
  141. 0x2559,
  142. 0x2558,
  143. 0x2552,
  144. 0x2553,
  145. 0x256B,
  146. 0x256A,
  147. 0x2518,
  148. 0x250C,
  149. 0x2588,
  150. 0x2584,
  151. 0x258C,
  152. 0x2590,
  153. 0x2580,
  154. 0x03B1,
  155. 0x00DF,
  156. 0x0393,
  157. 0x03C0,
  158. 0x03A3,
  159. 0x03C3,
  160. 0x00B5,
  161. 0x03C4,
  162. 0x03A6,
  163. 0x0398,
  164. 0x03A9,
  165. 0x03B4,
  166. 0x221E,
  167. 0x03C6,
  168. 0x03B5,
  169. 0x2229,
  170. 0x2261,
  171. 0x00B1,
  172. 0x2265,
  173. 0x2264,
  174. 0x2320,
  175. 0x2321,
  176. 0x00F7,
  177. 0x2248,
  178. 0x00B0,
  179. 0x2219,
  180. 0x00B7,
  181. 0x221A,
  182. 0x207F,
  183. 0x00B2,
  184. 0x25A0,
  185. 0x00A0
  186. };
  187. //
  188. // Log entry structure
  189. //
  190. typedef struct _HEADLESS_LOG_ENTRY {
  191. SYSTEM_TIMEOFDAY_INFORMATION TimeOfEntry;
  192. PWCHAR String;
  193. } HEADLESS_LOG_ENTRY, *PHEADLESS_LOG_ENTRY;
  194. // Blue Screen Data Structure
  195. //
  196. typedef struct _HEADLESS_BLUE_SCREEN_DATA {
  197. PUCHAR Property;
  198. PUCHAR XMLData;
  199. struct _HEADLESS_BLUE_SCREEN_DATA *Next;
  200. }HEADLESS_BLUE_SCREEN_DATA, * PHEADLESS_BLUE_SCREEN_DATA;
  201. //
  202. // Global variables headless component uses
  203. //
  204. typedef struct _HEADLESS_GLOBALS {
  205. //
  206. // Global spin lock for accessing headless internal routines.
  207. //
  208. KSPIN_LOCK SpinLock;
  209. //
  210. // Handle for when routines are locked down into memory.
  211. //
  212. HANDLE PageLockHandle;
  213. //
  214. // List of log entries.
  215. //
  216. PHEADLESS_LOG_ENTRY LogEntries;
  217. //
  218. // Global temp buffer, not to be held across lock release/acquires.
  219. //
  220. PUCHAR TmpBuffer;
  221. //
  222. // Current user input line
  223. //
  224. PUCHAR InputBuffer;
  225. //
  226. // Blue Screen Data
  227. //
  228. PHEADLESS_BLUE_SCREEN_DATA BlueScreenData;
  229. //
  230. // Flags and parameters for determining headless state
  231. //
  232. union {
  233. struct {
  234. ULONG TerminalEnabled : 1;
  235. ULONG InBugCheck : 1;
  236. ULONG NewLogEntryAdded : 1;
  237. ULONG UsedBiosSettings : 1;
  238. ULONG InputProcessing : 1;
  239. ULONG InputLineDone : 1;
  240. ULONG ProcessingCmd : 1;
  241. ULONG TerminalParity : 1;
  242. ULONG TerminalStopBits : 1;
  243. ULONG TerminalPortNumber : 3;
  244. ULONG IsNonLegacyDevice : 1;
  245. };
  246. ULONG AllFlags;
  247. };
  248. //
  249. // Port settings
  250. //
  251. ULONG TerminalBaudRate;
  252. ULONG TerminalPort;
  253. PUCHAR TerminalPortAddress;
  254. LARGE_INTEGER DelayTime; // in 100ns units
  255. ULONG MicroSecondsDelayTime;
  256. UCHAR TerminalType; // What kind of terminal do we think
  257. // we're talking to?
  258. // 0 = VT100
  259. // 1 = VT100+
  260. // 2 = VT-UTF8
  261. // 3 = PC ANSI
  262. // 4-255 = reserved
  263. //
  264. // Current location in Input buffer;
  265. //
  266. SIZE_T InputBufferIndex;
  267. //
  268. // Logging Indexes.
  269. //
  270. USHORT LogEntryLast;
  271. USHORT LogEntryStart;
  272. //
  273. // Machine's GUID.
  274. //
  275. GUID SystemGUID;
  276. BOOLEAN IsMMIODevice; // Is UART in SysIO or MMIO space?
  277. //
  278. // if this is TRUE, then the last character was a CR.
  279. // if this is TRUE and the current character is a LF,
  280. // then we filter the LF.
  281. //
  282. BOOLEAN IsLastCharCR;
  283. } HEADLESS_GLOBALS, *PHEADLESS_GLOBALS;
  284. //
  285. // The one and only resident global variable
  286. //
  287. PHEADLESS_GLOBALS HeadlessGlobals = NULL;
  288. //
  289. // Forward declarations.
  290. //
  291. NTSTATUS
  292. HdlspDispatch(
  293. IN HEADLESS_CMD Command,
  294. IN PVOID InputBuffer OPTIONAL,
  295. IN SIZE_T InputBufferSize OPTIONAL,
  296. OUT PVOID OutputBuffer OPTIONAL,
  297. OUT PSIZE_T OutputBufferSize OPTIONAL
  298. );
  299. NTSTATUS
  300. HdlspEnableTerminal(
  301. BOOLEAN bEnable
  302. );
  303. VOID
  304. HdlspPutString(
  305. PUCHAR String
  306. );
  307. VOID
  308. HdlspPutData(
  309. PUCHAR InputBuffer,
  310. SIZE_T InputBufferLength
  311. );
  312. BOOLEAN
  313. HdlspGetLine(
  314. PUCHAR InputBuffer,
  315. SIZE_T InputBufferLength
  316. );
  317. VOID
  318. HdlspBugCheckProcessing(
  319. VOID
  320. );
  321. VOID
  322. HdlspProcessDumpCommand(
  323. IN BOOLEAN Paging
  324. );
  325. VOID
  326. HdlspPutMore(
  327. OUT PBOOLEAN Stop
  328. );
  329. VOID
  330. HdlspAddLogEntry(
  331. IN PWCHAR String
  332. );
  333. NTSTATUS
  334. HdlspSetBlueScreenInformation(
  335. IN PHEADLESS_CMD_SET_BLUE_SCREEN_DATA pData,
  336. IN SIZE_T cData
  337. );
  338. VOID
  339. HdlspSendBlueScreenInfo(
  340. ULONG BugcheckCode
  341. );
  342. VOID
  343. HdlspKernelAddLogEntry(
  344. IN ULONG StringCode,
  345. IN PUNICODE_STRING DriverName OPTIONAL
  346. );
  347. VOID
  348. HdlspSendStringAtBaud(
  349. IN PUCHAR String
  350. );
  351. #if defined(ALLOC_PRAGMA)
  352. #pragma alloc_text(INIT, HeadlessInit)
  353. #pragma alloc_text(PAGE, HeadlessTerminalAddResources)
  354. #pragma alloc_text(PAGEHDLS, HdlspDispatch)
  355. #pragma alloc_text(PAGEHDLS, HdlspEnableTerminal)
  356. #pragma alloc_text(PAGEHDLS, HdlspPutString)
  357. #pragma alloc_text(PAGEHDLS, HdlspPutData)
  358. #pragma alloc_text(PAGEHDLS, HdlspGetLine)
  359. #pragma alloc_text(PAGEHDLS, HdlspBugCheckProcessing)
  360. #pragma alloc_text(PAGEHDLS, HdlspProcessDumpCommand)
  361. #pragma alloc_text(PAGEHDLS, HdlspPutMore)
  362. #pragma alloc_text(PAGEHDLS, HdlspAddLogEntry)
  363. #pragma alloc_text(PAGEHDLS, HdlspSetBlueScreenInformation)
  364. #pragma alloc_text(PAGEHDLS, HdlspSendBlueScreenInfo)
  365. #pragma alloc_text(PAGEHDLS, HdlspKernelAddLogEntry)
  366. #pragma alloc_text(PAGEHDLS, HdlspSendStringAtBaud)
  367. #endif
  368. VOID
  369. HeadlessInit(
  370. PLOADER_PARAMETER_BLOCK LoaderBlock
  371. )
  372. /*++
  373. Routine Description:
  374. This routine sets up all the information for supporting a headless terminal. It
  375. does not initialize the terminal.
  376. Arguments:
  377. HeadlessLoaderBlock - The loader block passed in from the loader.
  378. Environment:
  379. Only to be called at INIT time.
  380. --*/
  381. {
  382. PHEADLESS_LOADER_BLOCK HeadlessLoaderBlock;
  383. PHEADLESS_GLOBALS GlobalBlock;
  384. ULONG TmpUlong;
  385. if (LoaderBlock->Extension->HeadlessLoaderBlock == NULL) {
  386. return;
  387. }
  388. HeadlessLoaderBlock = LoaderBlock->Extension->HeadlessLoaderBlock;
  389. if ((HeadlessLoaderBlock->PortNumber <= 4) || (BOOLEAN)(HeadlessLoaderBlock->UsedBiosSettings)) {
  390. //
  391. // Allocate space for the global variables we will use.
  392. //
  393. GlobalBlock = ExAllocatePoolWithTag(NonPagedPool,
  394. sizeof(HEADLESS_GLOBALS),
  395. ((ULONG)'sldH')
  396. );
  397. if (GlobalBlock == NULL) {
  398. return;
  399. }
  400. //
  401. // Start everything at zero, and then init the rest by hand.
  402. //
  403. RtlZeroMemory(GlobalBlock, sizeof(HEADLESS_GLOBALS));
  404. KeInitializeSpinLock(&(GlobalBlock->SpinLock));
  405. //
  406. // Copy stuff from loader block
  407. //
  408. GlobalBlock->TerminalPortNumber = HeadlessLoaderBlock->PortNumber;
  409. GlobalBlock->TerminalPortAddress = HeadlessLoaderBlock->PortAddress;
  410. GlobalBlock->TerminalBaudRate = HeadlessLoaderBlock->BaudRate;
  411. GlobalBlock->TerminalParity = (BOOLEAN)(HeadlessLoaderBlock->Parity);
  412. GlobalBlock->TerminalStopBits = HeadlessLoaderBlock->StopBits;
  413. GlobalBlock->UsedBiosSettings = (BOOLEAN)(HeadlessLoaderBlock->UsedBiosSettings);
  414. GlobalBlock->IsMMIODevice = (BOOLEAN)(HeadlessLoaderBlock->IsMMIODevice);
  415. GlobalBlock->IsLastCharCR = FALSE;
  416. GlobalBlock->TerminalType = (UCHAR)(HeadlessLoaderBlock->TerminalType);
  417. RtlCopyMemory( &GlobalBlock->SystemGUID,
  418. &HeadlessLoaderBlock->SystemGUID,
  419. sizeof(GUID) );
  420. //
  421. // We need to determine if this is a non-legacy device that we're
  422. // speaking through. This can happen in several different ways,
  423. // including a PCI device placing a UART in System I/O space (which
  424. // wouldn't qualify as being "non-legacy"), or even a NON-PCI
  425. // device placing a UART up in MMIO (which again wouldn't qualify).
  426. //
  427. // Therefore, if the address is outside of System I/O, *or* if it's
  428. // sitting on a PCI device, then set the IsNonLegacyDevice entry.
  429. //
  430. if( GlobalBlock->IsMMIODevice ) {
  431. GlobalBlock->IsNonLegacyDevice = TRUE;
  432. }
  433. //
  434. // If we're speaking through a PCI device, we need to secure it. We'll
  435. // use the debugger APIs to make sure the device is understood and that it
  436. // doesn't get moved.
  437. //
  438. if( (HeadlessLoaderBlock->PciDeviceId != (USHORT)0xFFFF) &&
  439. (HeadlessLoaderBlock->PciDeviceId != 0) &&
  440. (HeadlessLoaderBlock->PciVendorId != (USHORT)0xFFFF) &&
  441. (HeadlessLoaderBlock->PciVendorId != 0) ) {
  442. //
  443. // The loader thinks he spoke through a PCI device. Remember
  444. // that it's non-legacy.
  445. //
  446. GlobalBlock->IsNonLegacyDevice = TRUE;
  447. //
  448. // Tell everyone else in the system to leave this device alone.
  449. // before we do that, the user may actually want PnP to enumerate the
  450. // device and possibly apply power management to it. They can indicate
  451. // this by setting bit 0 of PciFlags.
  452. //
  453. if( !(HeadlessLoaderBlock->PciFlags & 0x1) ) {
  454. DEBUG_DEVICE_DESCRIPTOR DebugDeviceDescriptor;
  455. RtlZeroMemory( &DebugDeviceDescriptor,
  456. sizeof(DEBUG_DEVICE_DESCRIPTOR) );
  457. //
  458. // We're required to understand exactly what this structure looks like
  459. // because we need to set every value to (-1), then fill in only the
  460. // fields that we explicitly know about.
  461. //
  462. DebugDeviceDescriptor.DeviceID = HeadlessLoaderBlock->PciDeviceId;
  463. DebugDeviceDescriptor.VendorID = HeadlessLoaderBlock->PciVendorId;
  464. DebugDeviceDescriptor.Bus = HeadlessLoaderBlock->PciBusNumber;
  465. DebugDeviceDescriptor.Slot = HeadlessLoaderBlock->PciSlotNumber;
  466. //
  467. // Now fill in the rest with (-1).
  468. //
  469. DebugDeviceDescriptor.BaseClass = 0xFF;
  470. DebugDeviceDescriptor.SubClass = 0xFF;
  471. DebugDeviceDescriptor.ProgIf = 0xFF;
  472. //
  473. // Do it.
  474. //
  475. KdSetupPciDeviceForDebugging( LoaderBlock,
  476. &DebugDeviceDescriptor );
  477. }
  478. }
  479. //
  480. // Allocate space for log entries.
  481. //
  482. GlobalBlock->LogEntries = ExAllocatePoolWithTag(NonPagedPool,
  483. HEADLESS_LOG_NUMBER_OF_ENTRIES *
  484. sizeof(HEADLESS_LOG_ENTRY),
  485. ((ULONG)'sldH')
  486. );
  487. if (GlobalBlock->LogEntries == NULL) {
  488. goto Fail;
  489. }
  490. GlobalBlock->LogEntryLast = (USHORT)-1;
  491. GlobalBlock->LogEntryStart = (USHORT)-1;
  492. //
  493. // Allocate a temporary buffer for general use.
  494. //
  495. GlobalBlock->TmpBuffer = ExAllocatePoolWithTag(NonPagedPool,
  496. HEADLESS_TMP_BUFFER_SIZE,
  497. ((ULONG)'sldH')
  498. );
  499. if (GlobalBlock->TmpBuffer == NULL) {
  500. goto Fail;
  501. }
  502. GlobalBlock->InputBuffer = ExAllocatePoolWithTag(NonPagedPool,
  503. HEADLESS_TMP_BUFFER_SIZE,
  504. ((ULONG)'sldH')
  505. );
  506. if (GlobalBlock->InputBuffer == NULL) {
  507. goto Fail;
  508. }
  509. GlobalBlock->PageLockHandle = MmLockPagableCodeSection((PVOID)(ULONG_PTR)HdlspDispatch);
  510. if (GlobalBlock->PageLockHandle == NULL) {
  511. goto Fail;
  512. }
  513. //
  514. // Figure to delay time between bytes to satify the baud rate given.
  515. //
  516. if (GlobalBlock->TerminalBaudRate == 9600) {
  517. TmpUlong = GlobalBlock->TerminalBaudRate;
  518. //
  519. // Convert to chars per second.
  520. //
  521. TmpUlong = TmpUlong / 10; // 10 bits per character (8-1-1) is the max.
  522. GlobalBlock->MicroSecondsDelayTime = ((1000000 / TmpUlong) * 10) / 8; // We will send at 80% speed to be sure.
  523. GlobalBlock->DelayTime.HighPart = -1;
  524. GlobalBlock->DelayTime.LowPart = -10 * GlobalBlock->MicroSecondsDelayTime; // relative time
  525. }
  526. HeadlessGlobals = GlobalBlock;
  527. }
  528. return;
  529. Fail:
  530. if (GlobalBlock->LogEntries != NULL) {
  531. ExFreePool(GlobalBlock->LogEntries);
  532. }
  533. if (GlobalBlock->TmpBuffer != NULL) {
  534. ExFreePool(GlobalBlock->TmpBuffer);
  535. }
  536. if (GlobalBlock->InputBuffer != NULL) {
  537. ExFreePool(GlobalBlock->InputBuffer);
  538. }
  539. ExFreePool(GlobalBlock);
  540. }
  541. NTSTATUS
  542. HeadlessDispatch(
  543. IN HEADLESS_CMD Command,
  544. IN PVOID InputBuffer OPTIONAL,
  545. IN SIZE_T InputBufferSize OPTIONAL,
  546. OUT PVOID OutputBuffer OPTIONAL,
  547. OUT PSIZE_T OutputBufferSize OPTIONAL
  548. )
  549. /*++
  550. Routine Description:
  551. This routine is the main entry point for all headless interaction with clients.
  552. Arguments:
  553. Command - The command to execute.
  554. InputBuffer - An optionally supplied buffer containing input parameters.
  555. InputBufferSize - Size of the supplied input buffer.
  556. OutputBuffer - An optionally supplied buffer where to place output parameters.
  557. OutputBufferSize - Size of the supplied output buffer, if the buffer is too small
  558. then STATUS_BUFFER_TOO_SMALL is returned and this parameter contains the total
  559. bytes necessary to complete the operation.
  560. Environment:
  561. If headless is enabled, it will acquire spin locks, so call from DPC level or
  562. less, only from kernel mode.
  563. --*/
  564. {
  565. //
  566. // If headless is not enabled on this machine, then some commands need special
  567. // processing, and all other we fool by saying that it succeeded.
  568. //
  569. // If for some reason we were unable to lock the headless component down into
  570. // memory when we initialized, treat this as the terminal not being connected.
  571. //
  572. if ((HeadlessGlobals == NULL) || (HeadlessGlobals->PageLockHandle == NULL)) {
  573. if (Command == HeadlessCmdEnableTerminal) {
  574. return STATUS_UNSUCCESSFUL;
  575. }
  576. //
  577. // The following command all have responses, so we must fill in the
  578. // correct response for when headless is not enabled.
  579. //
  580. if ((Command == HeadlessCmdQueryInformation) ||
  581. (Command == HeadlessCmdGetByte) ||
  582. (Command == HeadlessCmdGetLine) ||
  583. (Command == HeadlessCmdCheckForReboot) ||
  584. (Command == HeadlessCmdTerminalPoll)) {
  585. if ((OutputBuffer == NULL) || (OutputBufferSize == NULL)) {
  586. return STATUS_INVALID_PARAMETER;
  587. }
  588. //
  589. // All structures are designed such that a 0 or FALSE is the correct
  590. // response when headless is not present.
  591. //
  592. RtlZeroMemory(OutputBuffer, *OutputBufferSize);
  593. }
  594. return STATUS_SUCCESS;
  595. }
  596. return HdlspDispatch(Command,
  597. InputBuffer,
  598. InputBufferSize,
  599. OutputBuffer,
  600. OutputBufferSize
  601. );
  602. }
  603. NTSTATUS
  604. HdlspDispatch(
  605. IN HEADLESS_CMD Command,
  606. IN PVOID InputBuffer OPTIONAL,
  607. IN SIZE_T InputBufferSize OPTIONAL,
  608. OUT PVOID OutputBuffer OPTIONAL,
  609. OUT PSIZE_T OutputBufferSize OPTIONAL
  610. )
  611. /*++
  612. Routine Description:
  613. This routine is the pageable version of the dispatch routine.
  614. In general this routine is not intended to be used by more than one thread at
  615. a time. There are two exceptions, see below, but otherwise any second command
  616. that is submitted is rejected.
  617. There are only a couple of things that allowed to be called in parallel:
  618. AddLogEntry can be called when another command is being processed.
  619. StartBugCheck and BugCheckProcessing can as well.
  620. AddLogEntry is synchronized with all the other commands. It atomically adds
  621. the entry while holding the spin lock. Thus, all other command should try and
  622. hold the spin lock when manipulating global variables.
  623. The BugCheck routines do not use any spinlocking - an unfortunate side effect
  624. of that is that since another thread may still be executing and in this code,
  625. terminal I/O is indeterminable during this time. We cannot wait for the other
  626. thread to exit, as it may be that thread itself has already been stopped. Thus,
  627. in the case of a bugcheck, this is unsolvable. However, since bugchecks should
  628. never happen - having the possibility of a small overlap is acceptable, since
  629. the other thread either exits or is stopped, I/O will happen correctly with the
  630. terminal. This may require the user to press ENTER a couple of times, but that
  631. is acceptable in a bugcheck situation.
  632. Arguments:
  633. Command - The command to execute.
  634. InputBuffer - An optionally supplied buffer containing input parameters.
  635. InputBufferSize - Size of the supplied input buffer.
  636. OutputBuffer - An optionally supplied buffer where to place output parameters.
  637. OutputBufferSize - Size of the supplied output buffer, if the buffer is too small
  638. then STATUS_BUFFER_TOO_SMALL is returned and this parameter contains the total
  639. bytes necessary to complete the operation.
  640. Environment:
  641. Only called from HeadlessDispatch, which guarantees it is paged in and locked down.
  642. --*/
  643. {
  644. NTSTATUS Status = STATUS_SUCCESS;
  645. PUCHAR Tmp;
  646. UCHAR LocalBuffer[HEADLESS_TMP_BUFFER_SIZE];
  647. PHEADLESS_RSP_QUERY_INFO Response;
  648. KIRQL OldIrql;
  649. ASSERT(HeadlessGlobals != NULL);
  650. ASSERT(HeadlessGlobals->PageLockHandle != NULL);
  651. if ((Command != HeadlessCmdAddLogEntry) &&
  652. (Command != HeadlessCmdStartBugCheck) &&
  653. (Command != HeadlessCmdSendBlueScreenData) &&
  654. (Command != HeadlessCmdDoBugCheckProcessing)) {
  655. HEADLESS_ACQUIRE_SPIN_LOCK();
  656. if (HeadlessGlobals->ProcessingCmd) {
  657. HEADLESS_RELEASE_SPIN_LOCK();
  658. return STATUS_UNSUCCESSFUL;
  659. }
  660. HeadlessGlobals->ProcessingCmd = TRUE;
  661. HEADLESS_RELEASE_SPIN_LOCK();
  662. }
  663. //
  664. // Verify parameters for each command and then call the appropriate subroutine
  665. // to process it.
  666. //
  667. switch (Command) {
  668. //
  669. // Enable terminal
  670. //
  671. case HeadlessCmdEnableTerminal:
  672. if ((InputBuffer == NULL) ||
  673. (InputBufferSize != sizeof(HEADLESS_CMD_ENABLE_TERMINAL))) {
  674. Status = STATUS_INVALID_PARAMETER;
  675. goto EndOfFunction;
  676. }
  677. Status = HdlspEnableTerminal(((PHEADLESS_CMD_ENABLE_TERMINAL)InputBuffer)->Enable);
  678. goto EndOfFunction;
  679. //
  680. // Check for reboot string
  681. //
  682. case HeadlessCmdCheckForReboot:
  683. if ((OutputBuffer == NULL) ||
  684. (OutputBufferSize == NULL) ||
  685. (*OutputBufferSize != sizeof(HEADLESS_RSP_REBOOT))) {
  686. Status = STATUS_INVALID_PARAMETER;
  687. goto EndOfFunction;
  688. }
  689. if (HeadlessGlobals->TerminalEnabled) {
  690. if (HdlspGetLine(LocalBuffer, HEADLESS_TMP_BUFFER_SIZE)) {
  691. ((PHEADLESS_RSP_REBOOT)OutputBuffer)->Reboot = (BOOLEAN)
  692. (!strcmp((LPCSTR)LocalBuffer, "reboot") ||
  693. !strcmp((LPCSTR)LocalBuffer, "shutdown"));
  694. }
  695. } else {
  696. ((PHEADLESS_RSP_REBOOT)OutputBuffer)->Reboot = FALSE;
  697. }
  698. Status = STATUS_SUCCESS;
  699. goto EndOfFunction;
  700. //
  701. // Output a string.
  702. //
  703. case HeadlessCmdPutString:
  704. if (InputBuffer == NULL) {
  705. Status = STATUS_INVALID_PARAMETER;
  706. goto EndOfFunction;
  707. }
  708. if (HeadlessGlobals->TerminalEnabled) {
  709. HdlspPutString(&(((PHEADLESS_CMD_PUT_STRING)InputBuffer)->String[0]));
  710. }
  711. Status = STATUS_SUCCESS;
  712. goto EndOfFunction;
  713. //
  714. // Output a data stream.
  715. //
  716. case HeadlessCmdPutData:
  717. if ( (InputBuffer == NULL) ||
  718. (InputBufferSize == 0) ) {
  719. Status = STATUS_INVALID_PARAMETER;
  720. goto EndOfFunction;
  721. }
  722. if (HeadlessGlobals->TerminalEnabled) {
  723. HdlspPutData(&(((PHEADLESS_CMD_PUT_STRING)InputBuffer)->String[0]),
  724. InputBufferSize);
  725. }
  726. Status = STATUS_SUCCESS;
  727. goto EndOfFunction;
  728. //
  729. // Poll for input
  730. //
  731. case HeadlessCmdTerminalPoll:
  732. if ((OutputBuffer == NULL) ||
  733. (OutputBufferSize == NULL) ||
  734. (*OutputBufferSize != sizeof(HEADLESS_RSP_POLL))) {
  735. Status = STATUS_INVALID_PARAMETER;
  736. goto EndOfFunction;
  737. }
  738. if (HeadlessGlobals->TerminalEnabled) {
  739. ((PHEADLESS_RSP_POLL)OutputBuffer)->QueuedInput = InbvPortPollOnly(HeadlessGlobals->TerminalPort);
  740. } else {
  741. ((PHEADLESS_RSP_POLL)OutputBuffer)->QueuedInput = FALSE;
  742. }
  743. Status = STATUS_SUCCESS;
  744. goto EndOfFunction;
  745. //
  746. // Get a single byte of input
  747. //
  748. case HeadlessCmdGetByte:
  749. if ((OutputBuffer == NULL) ||
  750. (OutputBufferSize == NULL) ||
  751. (*OutputBufferSize != sizeof(HEADLESS_RSP_GET_BYTE))) {
  752. Status = STATUS_INVALID_PARAMETER;
  753. goto EndOfFunction;
  754. }
  755. if (HeadlessGlobals->TerminalEnabled) {
  756. if (InbvPortPollOnly(HeadlessGlobals->TerminalPort)) {
  757. InbvPortGetByte(HeadlessGlobals->TerminalPort,
  758. &(((PHEADLESS_RSP_GET_BYTE)OutputBuffer)->Value)
  759. );
  760. } else {
  761. ((PHEADLESS_RSP_GET_BYTE)OutputBuffer)->Value = 0;
  762. }
  763. } else {
  764. ((PHEADLESS_RSP_GET_BYTE)OutputBuffer)->Value = 0;
  765. }
  766. Status = STATUS_SUCCESS;
  767. goto EndOfFunction;
  768. //
  769. // Get an entire line of input, if available.
  770. //
  771. case HeadlessCmdGetLine:
  772. if ((OutputBuffer == NULL) ||
  773. (OutputBufferSize == NULL) ||
  774. (*OutputBufferSize < sizeof(HEADLESS_RSP_GET_LINE))) {
  775. Status = STATUS_INVALID_PARAMETER;
  776. goto EndOfFunction;
  777. }
  778. if (HeadlessGlobals->TerminalEnabled) {
  779. ((PHEADLESS_RSP_GET_LINE)OutputBuffer)->LineComplete =
  780. HdlspGetLine(&(((PHEADLESS_RSP_GET_LINE)OutputBuffer)->Buffer[0]),
  781. *OutputBufferSize -
  782. sizeof(HEADLESS_RSP_GET_LINE) +
  783. sizeof(UCHAR)
  784. );
  785. } else {
  786. ((PHEADLESS_RSP_GET_LINE)OutputBuffer)->LineComplete = FALSE;
  787. }
  788. Status = STATUS_SUCCESS;
  789. goto EndOfFunction;
  790. //
  791. // Let the kernel know to convert to bug check processing mode.
  792. //
  793. case HeadlessCmdStartBugCheck:
  794. HeadlessGlobals->InBugCheck = TRUE;
  795. Status = STATUS_SUCCESS;
  796. goto EndOfFunction;
  797. //
  798. // Process user I/O during a bugcheck
  799. //
  800. case HeadlessCmdDoBugCheckProcessing:
  801. if (HeadlessGlobals->TerminalEnabled) {
  802. //
  803. // NOTE: No spin lock here because we are in bugcheck.
  804. //
  805. HdlspBugCheckProcessing();
  806. }
  807. Status = STATUS_SUCCESS;
  808. goto EndOfFunction;
  809. //
  810. // Process query information command
  811. //
  812. case HeadlessCmdQueryInformation:
  813. if ((OutputBuffer == NULL) ||
  814. (OutputBufferSize == NULL) ||
  815. (*OutputBufferSize < sizeof(HEADLESS_RSP_QUERY_INFO))) {
  816. Status = STATUS_INVALID_PARAMETER;
  817. goto EndOfFunction;
  818. }
  819. Response = (PHEADLESS_RSP_QUERY_INFO)OutputBuffer;
  820. Response->PortType = HeadlessSerialPort;
  821. Response->Serial.TerminalAttached = TRUE;
  822. Response->Serial.UsedBiosSettings = (BOOLEAN)(HeadlessGlobals->UsedBiosSettings);
  823. Response->Serial.TerminalBaudRate = HeadlessGlobals->TerminalBaudRate;
  824. if( (HeadlessGlobals->TerminalPortNumber >= 1) || (BOOLEAN)(HeadlessGlobals->UsedBiosSettings) ) {
  825. Response->Serial.TerminalPort = HeadlessGlobals->TerminalPortNumber;
  826. Response->Serial.TerminalPortBaseAddress = HeadlessGlobals->TerminalPortAddress;
  827. Response->Serial.TerminalType = HeadlessGlobals->TerminalType;
  828. } else {
  829. Response->Serial.TerminalPort = SerialPortUndefined;
  830. Response->Serial.TerminalPortBaseAddress = 0;
  831. Response->Serial.TerminalType = HeadlessGlobals->TerminalType;
  832. }
  833. Status = STATUS_SUCCESS;
  834. goto EndOfFunction;
  835. //
  836. // Process add log entry command
  837. //
  838. case HeadlessCmdAddLogEntry:
  839. if (InputBuffer == NULL) {
  840. Status = STATUS_INVALID_PARAMETER;
  841. goto EndOfFunction;
  842. }
  843. ASSERT(KeIsExecutingDpc() == FALSE);
  844. HdlspAddLogEntry(&(((PHEADLESS_CMD_ADD_LOG_ENTRY)InputBuffer)->String[0]));
  845. Status = STATUS_SUCCESS;
  846. goto EndOfFunction;
  847. //
  848. // Print log entries
  849. //
  850. case HeadlessCmdDisplayLog:
  851. if ((InputBuffer == NULL) ||
  852. (InputBufferSize != sizeof(HEADLESS_CMD_DISPLAY_LOG))) {
  853. Status = STATUS_INVALID_PARAMETER;
  854. goto EndOfFunction;
  855. }
  856. HdlspProcessDumpCommand(((PHEADLESS_CMD_DISPLAY_LOG)InputBuffer)->Paging);
  857. Status = STATUS_SUCCESS;
  858. goto EndOfFunction;
  859. //
  860. // Various output commands
  861. //
  862. case HeadlessCmdClearDisplay:
  863. case HeadlessCmdClearToEndOfDisplay:
  864. case HeadlessCmdClearToEndOfLine:
  865. case HeadlessCmdDisplayAttributesOff:
  866. case HeadlessCmdDisplayInverseVideo:
  867. case HeadlessCmdSetColor:
  868. case HeadlessCmdPositionCursor:
  869. if (HeadlessGlobals->TerminalEnabled) {
  870. switch (Command) {
  871. case HeadlessCmdClearDisplay:
  872. Tmp = (PUCHAR)"\033[2J";
  873. break;
  874. case HeadlessCmdClearToEndOfDisplay:
  875. Tmp = (PUCHAR)"\033[0J";
  876. break;
  877. case HeadlessCmdClearToEndOfLine:
  878. Tmp = (PUCHAR)"\033[0K";
  879. break;
  880. case HeadlessCmdDisplayAttributesOff:
  881. Tmp = (PUCHAR)"\033[0m";
  882. break;
  883. case HeadlessCmdDisplayInverseVideo:
  884. Tmp = (PUCHAR)"\033[7m";
  885. break;
  886. case HeadlessCmdSetColor:
  887. if ((InputBuffer == NULL) ||
  888. (InputBufferSize != sizeof(HEADLESS_CMD_SET_COLOR))) {
  889. Status = STATUS_INVALID_PARAMETER;
  890. goto EndOfFunction;
  891. }
  892. sprintf((LPSTR)LocalBuffer,
  893. "\033[%d;%dm",
  894. ((PHEADLESS_CMD_SET_COLOR)InputBuffer)->BkgColor,
  895. ((PHEADLESS_CMD_SET_COLOR)InputBuffer)->FgColor
  896. );
  897. Tmp = &(LocalBuffer[0]);
  898. break;
  899. case HeadlessCmdPositionCursor:
  900. if ((InputBuffer == NULL) ||
  901. (InputBufferSize != sizeof(HEADLESS_CMD_POSITION_CURSOR))) {
  902. Status = STATUS_INVALID_PARAMETER;
  903. goto EndOfFunction;
  904. }
  905. sprintf((LPSTR)LocalBuffer,
  906. "\033[%d;%dH",
  907. ((PHEADLESS_CMD_POSITION_CURSOR)InputBuffer)->Y + 1,
  908. ((PHEADLESS_CMD_POSITION_CURSOR)InputBuffer)->X + 1
  909. );
  910. Tmp = &(LocalBuffer[0]);
  911. break;
  912. default:
  913. //
  914. // should never get here...
  915. //
  916. ASSERT(0);
  917. Status = STATUS_INVALID_PARAMETER;
  918. goto EndOfFunction;
  919. }
  920. HdlspSendStringAtBaud(Tmp);
  921. }
  922. Status = STATUS_SUCCESS;
  923. goto EndOfFunction;
  924. case HeadlessCmdSetBlueScreenData:
  925. if (InputBuffer == NULL) {
  926. return STATUS_INVALID_PARAMETER;
  927. }
  928. Status = HdlspSetBlueScreenInformation(InputBuffer, InputBufferSize);
  929. goto EndOfFunction;
  930. case HeadlessCmdSendBlueScreenData:
  931. if (HeadlessGlobals->TerminalEnabled && HeadlessGlobals->InBugCheck) {
  932. if ((InputBuffer == NULL) ||
  933. (InputBufferSize != sizeof(HEADLESS_CMD_SEND_BLUE_SCREEN_DATA))) {
  934. ASSERT(0);
  935. return STATUS_INVALID_PARAMETER;
  936. }
  937. HdlspSendBlueScreenInfo(((PHEADLESS_CMD_SEND_BLUE_SCREEN_DATA)InputBuffer)->BugcheckCode);
  938. HdlspSendStringAtBaud((PUCHAR)"\n\r!SAC>");
  939. }
  940. goto EndOfFunction;
  941. case HeadlessCmdQueryGUID:
  942. if( (OutputBuffer == NULL) ||
  943. (OutputBufferSize == NULL) ||
  944. (*OutputBufferSize < sizeof(GUID)) ) {
  945. Status = STATUS_INVALID_PARAMETER;
  946. goto EndOfFunction;
  947. }
  948. RtlCopyMemory( OutputBuffer,
  949. &HeadlessGlobals->SystemGUID,
  950. sizeof(GUID) );
  951. Status = STATUS_SUCCESS;
  952. goto EndOfFunction;
  953. default:
  954. Status = STATUS_INVALID_PARAMETER;
  955. goto EndOfFunction;
  956. }
  957. EndOfFunction:
  958. if ((Command != HeadlessCmdAddLogEntry) &&
  959. (Command != HeadlessCmdStartBugCheck) &&
  960. (Command != HeadlessCmdSendBlueScreenData) &&
  961. (Command != HeadlessCmdDoBugCheckProcessing)) {
  962. ASSERT(HeadlessGlobals->ProcessingCmd);
  963. HeadlessGlobals->ProcessingCmd = FALSE;
  964. }
  965. return Status;
  966. }
  967. NTSTATUS
  968. HdlspEnableTerminal(
  969. BOOLEAN bEnable
  970. )
  971. /*++
  972. Routine Description:
  973. This routine attempts to initialize the terminal, if there is one attached, or
  974. disconnect the terminal.
  975. Note: Assumes it is called with the global spin lock held!
  976. Arguments:
  977. bEnable - If TRUE, we will allow Inbv calls to display,
  978. otherwise we will not.
  979. Returns:
  980. STATUS_SUCCESS if successful, else STATUS_UNSUCCESSFUL.
  981. Environment:
  982. Only called from HdlspDispatch, which guarantees it is paged in and locked down.
  983. --*/
  984. {
  985. if ((bEnable == TRUE) && !HeadlessGlobals->TerminalEnabled) {
  986. HeadlessGlobals->TerminalEnabled = InbvPortInitialize(
  987. HeadlessGlobals->TerminalBaudRate,
  988. HeadlessGlobals->TerminalPortNumber,
  989. HeadlessGlobals->TerminalPortAddress,
  990. &(HeadlessGlobals->TerminalPort),
  991. HeadlessGlobals->IsMMIODevice
  992. );
  993. if (!HeadlessGlobals->TerminalEnabled) {
  994. return STATUS_UNSUCCESSFUL;
  995. }
  996. } else if (bEnable == FALSE) {
  997. InbvPortTerminate(HeadlessGlobals->TerminalPort);
  998. HeadlessGlobals->TerminalPort = 0;
  999. HeadlessGlobals->TerminalEnabled = FALSE;
  1000. }
  1001. //
  1002. // We know we want the FIFO on while using the headless port,
  1003. // but we don't know if we should leave the FIFO on
  1004. // after we disable the headless port. Hence, we turn off
  1005. // the FIFO when we disable the headless port.
  1006. //
  1007. // turn ON the term port FIFO if we enable headless
  1008. // turn OFF the term port FIFO if we disable headless
  1009. //
  1010. InbvPortEnableFifo(
  1011. HeadlessGlobals->TerminalPort,
  1012. bEnable
  1013. );
  1014. return STATUS_SUCCESS;
  1015. }
  1016. VOID
  1017. UTF8Encode(
  1018. USHORT InputValue,
  1019. PUCHAR UTF8Encoding
  1020. )
  1021. /*++
  1022. Routine Description:
  1023. Generates the UTF8 translation for a 16-bit value.
  1024. Arguments:
  1025. InputValue - 16-bit value to be encoded.
  1026. UTF8Encoding - receives the UTF8-encoding of the 16-bit value
  1027. Return Value:
  1028. NONE.
  1029. --*/
  1030. {
  1031. //
  1032. // convert into UTF8 for actual transmission
  1033. //
  1034. // UTF-8 encodes 2-byte Unicode characters as follows:
  1035. // If the first nine bits are zero (00000000 0xxxxxxx), encode it as one byte 0xxxxxxx
  1036. // If the first five bits are zero (00000yyy yyxxxxxx), encode it as two bytes 110yyyyy 10xxxxxx
  1037. // Otherwise (zzzzyyyy yyxxxxxx), encode it as three bytes 1110zzzz 10yyyyyy 10xxxxxx
  1038. //
  1039. if( (InputValue & 0xFF80) == 0 ) {
  1040. //
  1041. // if the top 9 bits are zero, then just
  1042. // encode as 1 byte. (ASCII passes through unchanged).
  1043. //
  1044. UTF8Encoding[2] = (UCHAR)(InputValue & 0xFF);
  1045. } else if( (InputValue & 0xF700) == 0 ) {
  1046. //
  1047. // if the top 5 bits are zero, then encode as 2 bytes
  1048. //
  1049. UTF8Encoding[2] = (UCHAR)(InputValue & 0x3F) | 0x80;
  1050. UTF8Encoding[1] = (UCHAR)((InputValue >> 6) & 0x1F) | 0xC0;
  1051. } else {
  1052. //
  1053. // encode as 3 bytes
  1054. //
  1055. UTF8Encoding[2] = (UCHAR)(InputValue & 0x3F) | 0x80;
  1056. UTF8Encoding[1] = (UCHAR)((InputValue >> 6) & 0x3F) | 0x80;
  1057. UTF8Encoding[0] = (UCHAR)((InputValue >> 12) & 0xF) | 0xE0;
  1058. }
  1059. }
  1060. VOID
  1061. HdlspPutString(
  1062. PUCHAR String
  1063. )
  1064. /*++
  1065. Routine Description:
  1066. This routine writes a string out to the terminal.
  1067. Note: the routine assumes it is called with the global spin lock held.
  1068. Arguments:
  1069. String - NULL terminated string to write.
  1070. Returns:
  1071. None.
  1072. Environment:
  1073. Only called from HdlspDispatch, which guarantees it is paged in and locked down.
  1074. --*/
  1075. {
  1076. PUCHAR Src, Dest;
  1077. UCHAR Char = 0;
  1078. //
  1079. // We need to worry about sending a vt100 characters not in the standard
  1080. // ASCII set, so we copy over only ASCII characters into a new buffer and
  1081. // then send that one to the terminal.
  1082. //
  1083. Src = String;
  1084. Dest = &(HeadlessGlobals->TmpBuffer[0]);
  1085. while (*Src != '\0') {
  1086. if (Dest >= &(HeadlessGlobals->TmpBuffer[HEADLESS_TMP_BUFFER_SIZE - 1])) {
  1087. HeadlessGlobals->TmpBuffer[HEADLESS_TMP_BUFFER_SIZE - 1] = '\0';
  1088. HdlspSendStringAtBaud(HeadlessGlobals->TmpBuffer);
  1089. Dest = &(HeadlessGlobals->TmpBuffer[0]);
  1090. } else {
  1091. Char = *Src;
  1092. //
  1093. // filter some characters that aren't printable in VT100
  1094. // into substitute characters which are printable
  1095. //
  1096. if (Char & 0x80) {
  1097. switch (Char) {
  1098. case 0xB0: // Light shaded block
  1099. case 0xB3: // Light vertical
  1100. case 0xBA: // Double vertical line
  1101. Char = '|';
  1102. break;
  1103. case 0xB1: // Middle shaded block
  1104. case 0xDC: // Lower half block
  1105. case 0xDD: // Right half block
  1106. case 0xDE: // Left half block
  1107. case 0xDF: // Upper half block
  1108. Char = '%';
  1109. break;
  1110. case 0xB2: // Dark shaded block
  1111. case 0xDB: // Full block
  1112. Char = '#';
  1113. break;
  1114. case 0xA9: // Reversed NOT sign
  1115. case 0xAA: // NOT sign
  1116. case 0xBB: // '�'
  1117. case 0xBC: // '�'
  1118. case 0xBF: // '�'
  1119. case 0xC0: // '�'
  1120. case 0xC8: // '�'
  1121. case 0xC9: // '�'
  1122. case 0xD9: // '�'
  1123. case 0xDA: // '�'
  1124. Char = '+';
  1125. break;
  1126. case 0xC4: // '�'
  1127. Char = '-';
  1128. break;
  1129. case 0xCD: // '�'
  1130. Char = '=';
  1131. break;
  1132. }
  1133. }
  1134. //
  1135. // If the high-bit is still set, and we're here, then we are going to
  1136. // spew UTF8-encoded data (assuming our terminal type says it's okay).
  1137. //
  1138. if( (Char & 0x80) ) {
  1139. UCHAR UTF8Encoding[3];
  1140. ULONG i;
  1141. //
  1142. // Lookup the Unicode equivilent of this 8-bit ANSI value.
  1143. //
  1144. UTF8Encode( PcAnsiToUnicode[(Char & 0x7F)],
  1145. UTF8Encoding );
  1146. for( i = 0; i < 3; i++ ) {
  1147. if( UTF8Encoding[i] != 0 ) {
  1148. *Dest = UTF8Encoding[i];
  1149. Dest++;
  1150. }
  1151. }
  1152. } else {
  1153. //
  1154. // He's 7-bit ASCII. Put it in the Destination buffer
  1155. // and move on.
  1156. //
  1157. *Dest = Char;
  1158. Dest++;
  1159. }
  1160. Src++;
  1161. }
  1162. }
  1163. *Dest = '\0';
  1164. HdlspSendStringAtBaud(HeadlessGlobals->TmpBuffer);
  1165. }
  1166. VOID
  1167. HdlspPutData(
  1168. PUCHAR InputBuffer,
  1169. SIZE_T InputBufferSize
  1170. )
  1171. /*++
  1172. Routine Description:
  1173. This routine writes an array of UCHARs out to the terminal.
  1174. Note: the routine assumes it is called with the global spin lock held.
  1175. Arguments:
  1176. InputBuffer - Array of characters to write.
  1177. InputBufferSize - Number of characters to write.
  1178. Returns:
  1179. None.
  1180. Environment:
  1181. Only called from HdlspDispatch, which guarantees it is paged in and locked down.
  1182. --*/
  1183. {
  1184. KIRQL CurrentIrql;
  1185. ULONG i;
  1186. //
  1187. // Get the data into our own buffer.
  1188. //
  1189. if( InputBufferSize > HEADLESS_TMP_BUFFER_SIZE ) {
  1190. InputBufferSize = HEADLESS_TMP_BUFFER_SIZE;
  1191. }
  1192. RtlCopyMemory( &(HeadlessGlobals->TmpBuffer[0]),
  1193. InputBuffer,
  1194. InputBufferSize );
  1195. //
  1196. // Write the data out to the headless port.
  1197. // NOTE: this code is very similar to HdlspSendStringAtBaud, so
  1198. // be careful about modifying one without the other.
  1199. //
  1200. //
  1201. // If we are in the worker thread, up the timer resolution so we can output
  1202. // the string at the appropriate baud rate.
  1203. //
  1204. CurrentIrql = KeGetCurrentIrql();
  1205. if (CurrentIrql < DISPATCH_LEVEL) {
  1206. ExSetTimerResolution(-1 * HeadlessGlobals->DelayTime.LowPart, TRUE);
  1207. }
  1208. for (i = 0; i < InputBufferSize; i++) {
  1209. InbvPortPutByte(HeadlessGlobals->TerminalPort, HeadlessGlobals->TmpBuffer[i]);
  1210. if( HeadlessGlobals->TerminalBaudRate == 9600 ) {
  1211. if (CurrentIrql < DISPATCH_LEVEL) {
  1212. KeDelayExecutionThread(KernelMode, FALSE, &(HeadlessGlobals->DelayTime));
  1213. } else {
  1214. KeStallExecutionProcessor(HeadlessGlobals->MicroSecondsDelayTime);
  1215. }
  1216. }
  1217. }
  1218. //
  1219. // If we are in the worker thread, reset the timer resolution.
  1220. //
  1221. if (CurrentIrql < DISPATCH_LEVEL) {
  1222. ExSetTimerResolution(0, FALSE);
  1223. }
  1224. }
  1225. BOOLEAN
  1226. HdlspGetLine(
  1227. PUCHAR InputBuffer,
  1228. SIZE_T InputBufferLength
  1229. )
  1230. /*++
  1231. Routine Description:
  1232. This function fills the given buffer with an input line, once the user has
  1233. pressed return. Until then it will return FALSE. It strips of leading and
  1234. trailing whitespace.
  1235. Arguments:
  1236. InputBuffer - Place to store the terminal input line.
  1237. InputBufferLength - Length, in bytes, of InputBuffer.
  1238. Return Value:
  1239. TRUE if InputBuffer is filled, else FALSE.
  1240. Environment:
  1241. Only called from HdlspDispatch, which guarantees it is paged in and locked down.
  1242. --*/
  1243. {
  1244. UCHAR NewByte;
  1245. SIZE_T i;
  1246. KIRQL OldIrql;
  1247. BOOLEAN CheckForLF;
  1248. CheckForLF = FALSE;
  1249. HEADLESS_ACQUIRE_SPIN_LOCK();
  1250. if (HeadlessGlobals->InputProcessing) {
  1251. HEADLESS_RELEASE_SPIN_LOCK();
  1252. return FALSE;
  1253. }
  1254. HeadlessGlobals->InputProcessing = TRUE;
  1255. HEADLESS_RELEASE_SPIN_LOCK();
  1256. //
  1257. // Check if we already have a line to be returned (could happen if
  1258. // InputBuffer is/was too small to contain the whole line)
  1259. //
  1260. if (HeadlessGlobals->InputLineDone) {
  1261. goto ReturnInputLine;
  1262. }
  1263. GetByte:
  1264. if (!InbvPortPollOnly(HeadlessGlobals->TerminalPort) ||
  1265. !InbvPortGetByte(HeadlessGlobals->TerminalPort, &NewByte)) {
  1266. NewByte = 0;
  1267. }
  1268. //
  1269. // If no waiting input, leave
  1270. //
  1271. if (NewByte == 0) {
  1272. HeadlessGlobals->InputProcessing = FALSE;
  1273. return FALSE;
  1274. }
  1275. //
  1276. // Store input character in our buffer
  1277. //
  1278. HeadlessGlobals->InputBuffer[HeadlessGlobals->InputBufferIndex] = NewByte;
  1279. //
  1280. // filter out the LF if we JUST received a CR
  1281. //
  1282. if (HeadlessGlobals->IsLastCharCR) {
  1283. //
  1284. // if this is a LF, then ignore it and go get the next character.
  1285. // if this is NOT an LF, then there is nothing to do
  1286. //
  1287. if (NewByte == 0x0A) {
  1288. HeadlessGlobals->IsLastCharCR = FALSE;
  1289. goto GetByte;
  1290. }
  1291. }
  1292. //
  1293. // if this is a CR, then remember it
  1294. //
  1295. HeadlessGlobals->IsLastCharCR = (NewByte == 0x0D) ? TRUE : FALSE;
  1296. //
  1297. // If this is a return, then we are done and need to return the line
  1298. //
  1299. if ((NewByte == (UCHAR)'\n') || (NewByte == (UCHAR)'\r')) {
  1300. HdlspSendStringAtBaud((PUCHAR)"\r\n");
  1301. HeadlessGlobals->InputBuffer[HeadlessGlobals->InputBufferIndex] = '\0';
  1302. HeadlessGlobals->InputBufferIndex++;
  1303. goto StripWhitespaceAndReturnLine;
  1304. }
  1305. //
  1306. // If this is a backspace or delete, then we need to do that.
  1307. //
  1308. if ((NewByte == 0x8) || (NewByte == 0x7F)) { // backspace (^H) or delete
  1309. if (HeadlessGlobals->InputBufferIndex > 0) {
  1310. HdlspSendStringAtBaud((PUCHAR)"\010 \010");
  1311. HeadlessGlobals->InputBufferIndex--;
  1312. }
  1313. } else if (NewByte == 0x3) { // Control-C
  1314. //
  1315. // Terminate the string and return it.
  1316. //
  1317. HeadlessGlobals->InputBufferIndex++;
  1318. HeadlessGlobals->InputBuffer[HeadlessGlobals->InputBufferIndex] = '\0';
  1319. HeadlessGlobals->InputBufferIndex++;
  1320. goto StripWhitespaceAndReturnLine;
  1321. } else if ((NewByte == 0x9) || (NewByte == 0x1B)) { // Tab or Esc
  1322. //
  1323. // Ignore tabs and escapes
  1324. //
  1325. HdlspSendStringAtBaud((PUCHAR)"\007");
  1326. HeadlessGlobals->InputProcessing = FALSE;
  1327. return FALSE;
  1328. } else if (HeadlessGlobals->InputBufferIndex == HEADLESS_TMP_BUFFER_SIZE - 2) {
  1329. //
  1330. // We are at the end of the buffer - remove the last character from
  1331. // the terminal screen and replace it with this one.
  1332. //
  1333. sprintf((LPSTR)HeadlessGlobals->TmpBuffer, "\010%c", NewByte);
  1334. HdlspSendStringAtBaud(HeadlessGlobals->TmpBuffer);
  1335. } else {
  1336. //
  1337. // Echo the character to the screen
  1338. //
  1339. sprintf((LPSTR)HeadlessGlobals->TmpBuffer, "%c", NewByte);
  1340. HdlspSendStringAtBaud(HeadlessGlobals->TmpBuffer);
  1341. HeadlessGlobals->InputBufferIndex++;
  1342. }
  1343. goto GetByte;
  1344. StripWhitespaceAndReturnLine:
  1345. //
  1346. // Before returning the input line, strip off all leading and trailing blanks
  1347. //
  1348. ASSERT(HeadlessGlobals->InputBufferIndex > 0);
  1349. i = HeadlessGlobals->InputBufferIndex - 1;
  1350. while ((i != 0) &&
  1351. ((HeadlessGlobals->InputBuffer[i] == '\0') ||
  1352. (HeadlessGlobals->InputBuffer[i] == ' ') ||
  1353. (HeadlessGlobals->InputBuffer[i] == '\t'))) {
  1354. i--;
  1355. }
  1356. if (HeadlessGlobals->InputBuffer[i] != '\0') {
  1357. HeadlessGlobals->InputBuffer[i + 1] = '\0';
  1358. }
  1359. i = 0;
  1360. while ((HeadlessGlobals->InputBuffer[i] != '\0') &&
  1361. ((HeadlessGlobals->InputBuffer[i] == '\t') ||
  1362. (HeadlessGlobals->InputBuffer[i] == ' '))) {
  1363. i++;
  1364. }
  1365. if (i != 0) {
  1366. strcpy(
  1367. (LPSTR)&(HeadlessGlobals->InputBuffer[0]),
  1368. (LPSTR)&(HeadlessGlobals->InputBuffer[i]));
  1369. }
  1370. ReturnInputLine:
  1371. //
  1372. // Return the line.
  1373. //
  1374. if (InputBufferLength >= HeadlessGlobals->InputBufferIndex) {
  1375. RtlCopyMemory(InputBuffer, HeadlessGlobals->InputBuffer, HeadlessGlobals->InputBufferIndex);
  1376. HeadlessGlobals->InputBufferIndex = 0;
  1377. HeadlessGlobals->InputLineDone = FALSE;
  1378. } else {
  1379. RtlCopyMemory(InputBuffer, HeadlessGlobals->InputBuffer, InputBufferLength);
  1380. RtlCopyBytes(HeadlessGlobals->InputBuffer,
  1381. &(HeadlessGlobals->InputBuffer[InputBufferLength]),
  1382. HeadlessGlobals->InputBufferIndex - InputBufferLength
  1383. );
  1384. HeadlessGlobals->InputLineDone = TRUE;
  1385. HeadlessGlobals->InputBufferIndex -= InputBufferLength;
  1386. }
  1387. HeadlessGlobals->InputProcessing = FALSE;
  1388. return TRUE;
  1389. }
  1390. NTSTATUS
  1391. HeadlessTerminalAddResources(
  1392. PCM_RESOURCE_LIST Resources,
  1393. ULONG ResourceListSize,
  1394. BOOLEAN TranslatedList,
  1395. PCM_RESOURCE_LIST *NewList,
  1396. PULONG NewListSize
  1397. )
  1398. /*++
  1399. Routine Description:
  1400. This function adds any resources that the terminal needs to the list of resources
  1401. given, reallocating to a new block if necessary.
  1402. Arguments:
  1403. Resources - The current resource list.
  1404. ResourceListSize - Length, in bytes, of the list.
  1405. TranslatedList - Is this a translated list or not.
  1406. NewList - A pointer to an allocated new list, if headless adds something, otherwise
  1407. it will return NULL, indicating no new resources were added.
  1408. NewListSize - Returns the length, in bytes, of the returned list.
  1409. Return Value:
  1410. STATUS_SUCCESS if successful, else STATUS_INSUFFICIENT_RESOURCES.
  1411. --*/
  1412. {
  1413. PCM_FULL_RESOURCE_DESCRIPTOR NewDescriptor;
  1414. PHYSICAL_ADDRESS Address;
  1415. PHYSICAL_ADDRESS TranslatedAddress;
  1416. ULONG AddressSpace;
  1417. if (HeadlessGlobals == NULL) {
  1418. *NewList = NULL;
  1419. *NewListSize = 0;
  1420. return STATUS_SUCCESS;
  1421. }
  1422. if( HeadlessGlobals->IsNonLegacyDevice ) {
  1423. *NewList = NULL;
  1424. *NewListSize = 0;
  1425. return STATUS_SUCCESS;
  1426. }
  1427. //
  1428. // Allocate space for a new list.
  1429. //
  1430. *NewListSize = ResourceListSize + sizeof(CM_FULL_RESOURCE_DESCRIPTOR);
  1431. *NewList = (PCM_RESOURCE_LIST)ExAllocatePool(PagedPool, *NewListSize);
  1432. if (*NewList == NULL) {
  1433. *NewListSize = 0;
  1434. return STATUS_INSUFFICIENT_RESOURCES;
  1435. }
  1436. //
  1437. // Copy old list into the new buffer
  1438. //
  1439. RtlCopyMemory(*NewList, Resources, ResourceListSize);
  1440. Address.QuadPart = PtrToUlong(HeadlessGlobals->TerminalPortAddress);
  1441. //
  1442. // If this port information is supposed to be translated, do it.
  1443. //
  1444. if (TranslatedList) {
  1445. AddressSpace = 1; // Address space port.
  1446. HalTranslateBusAddress(Internal, // device bus or internal
  1447. 0, // bus number
  1448. Address, // source address
  1449. &AddressSpace, // address space
  1450. &TranslatedAddress // translated address
  1451. );
  1452. } else {
  1453. TranslatedAddress = Address;
  1454. }
  1455. //
  1456. // Add our stuff to the end.
  1457. //
  1458. (*NewList)->Count++;
  1459. NewDescriptor = (PCM_FULL_RESOURCE_DESCRIPTOR)(((PUCHAR)(*NewList)) + ResourceListSize);
  1460. NewDescriptor->BusNumber = 0;
  1461. NewDescriptor->InterfaceType = Isa;
  1462. NewDescriptor->PartialResourceList.Count = 1;
  1463. NewDescriptor->PartialResourceList.Revision = 0;
  1464. NewDescriptor->PartialResourceList.Version = 0;
  1465. NewDescriptor->PartialResourceList.PartialDescriptors[0].Type = CmResourceTypePort;
  1466. NewDescriptor->PartialResourceList.PartialDescriptors[0].ShareDisposition =
  1467. CmResourceShareDriverExclusive;
  1468. NewDescriptor->PartialResourceList.PartialDescriptors[0].Flags = CM_RESOURCE_PORT_IO;
  1469. NewDescriptor->PartialResourceList.PartialDescriptors[0].u.Port.Start =
  1470. TranslatedAddress;
  1471. NewDescriptor->PartialResourceList.PartialDescriptors[0].u.Port.Length = 0x8;
  1472. return STATUS_SUCCESS;
  1473. }
  1474. VOID
  1475. HdlspBugCheckProcessing(
  1476. VOID
  1477. )
  1478. /*++
  1479. Routine Description:
  1480. This function is used to prompt and display information to the user via the
  1481. terminal. The system is assumed to be singly threaded and at a raised IRQL state.
  1482. NOTE: This is pre-emptive to the system, so no locking required.
  1483. Arguments:
  1484. None.
  1485. Return Value:
  1486. None.
  1487. Environment:
  1488. ONLY IN BUGCHECK!
  1489. --*/
  1490. {
  1491. UCHAR InputBuffer[HEADLESS_TMP_BUFFER_SIZE];
  1492. ULONG i;
  1493. ASSERT(HeadlessGlobals->InBugCheck);
  1494. //
  1495. // Check for characters
  1496. //
  1497. if (HdlspGetLine(InputBuffer, HEADLESS_TMP_BUFFER_SIZE)) {
  1498. //
  1499. // Process the line
  1500. //
  1501. if ((_stricmp((LPCSTR)InputBuffer, "?") == 0) ||
  1502. (_stricmp((LPCSTR)InputBuffer, "help") == 0)) {
  1503. HdlspSendStringAtBaud((PUCHAR)"\r\n");
  1504. HdlspSendStringAtBaud((PUCHAR)"d Display all log entries, paging is on.\r\n");
  1505. HdlspSendStringAtBaud((PUCHAR)"help Display this list.\r\n");
  1506. HdlspSendStringAtBaud((PUCHAR)"restart Restart the system immediately.\r\n");
  1507. HdlspSendStringAtBaud((PUCHAR)"? Display this list.\r\n");
  1508. HdlspSendStringAtBaud((PUCHAR)"\r\n");
  1509. } else if (_stricmp((LPCSTR)InputBuffer, "d") == 0) {
  1510. HdlspProcessDumpCommand(TRUE);
  1511. } else if (_stricmp((LPCSTR)InputBuffer, "restart") == 0) {
  1512. InbvSolidColorFill(0,0,639,479,0); // make the screen black
  1513. for (i =0; i<10; i++) { // pause long enough for things to get out serial port
  1514. KeStallExecutionProcessor(100000);
  1515. }
  1516. HalReturnToFirmware(HalRebootRoutine);
  1517. } else {
  1518. HdlspSendStringAtBaud((PUCHAR)"Type ? or Help for a list of commands.\r\n");
  1519. }
  1520. //
  1521. // Put a new command prompt
  1522. //
  1523. HdlspSendStringAtBaud((PUCHAR)"\n\r!SAC>");
  1524. }
  1525. }
  1526. VOID
  1527. HdlspProcessDumpCommand(
  1528. IN BOOLEAN Paging
  1529. )
  1530. /*++
  1531. Routine Description:
  1532. This function is used to display all current log entries.
  1533. Arguments:
  1534. Paging - Should this do paging or not.
  1535. Return Value:
  1536. None.
  1537. Environment:
  1538. May only be called from a raised IRQL if a StartBugCheck command has been issued.
  1539. --*/
  1540. {
  1541. PHEADLESS_LOG_ENTRY LogEntry;
  1542. ULONG LogEntryIndex;
  1543. TIME_FIELDS TimeFields;
  1544. UNICODE_STRING UnicodeString;
  1545. ANSI_STRING AnsiString;
  1546. ULONG LineNumber;
  1547. BOOLEAN Stop;
  1548. KIRQL OldIrql;
  1549. HEADLESS_ACQUIRE_SPIN_LOCK();
  1550. if (HeadlessGlobals->LogEntryStart == (USHORT)-1) {
  1551. HEADLESS_RELEASE_SPIN_LOCK();
  1552. return;
  1553. }
  1554. HeadlessGlobals->NewLogEntryAdded = FALSE;
  1555. AnsiString.Length = 0;
  1556. AnsiString.MaximumLength = HEADLESS_TMP_BUFFER_SIZE;
  1557. AnsiString.Buffer = (PCHAR)HeadlessGlobals->TmpBuffer;
  1558. LogEntryIndex = HeadlessGlobals->LogEntryStart;
  1559. LineNumber = 0;
  1560. while (TRUE) {
  1561. LogEntry = &(HeadlessGlobals->LogEntries[LogEntryIndex]);
  1562. //
  1563. // Print the log entry out to the terminal.
  1564. //
  1565. HEADLESS_RELEASE_SPIN_LOCK();
  1566. RtlTimeToTimeFields(&(LogEntry->TimeOfEntry.CurrentTime), &TimeFields);
  1567. sprintf((LPSTR)HeadlessGlobals->TmpBuffer,
  1568. "%02d:%02d:%02d.%03d : ",
  1569. TimeFields.Hour,
  1570. TimeFields.Minute,
  1571. TimeFields.Second,
  1572. TimeFields.Milliseconds
  1573. );
  1574. HdlspPutString(HeadlessGlobals->TmpBuffer);
  1575. if (wcslen(LogEntry->String) >= HEADLESS_TMP_BUFFER_SIZE - 1) {
  1576. LogEntry->String[HEADLESS_TMP_BUFFER_SIZE - 1] = UNICODE_NULL;
  1577. }
  1578. RtlInitUnicodeString(&UnicodeString, LogEntry->String);
  1579. RtlUnicodeStringToAnsiString(&AnsiString, &UnicodeString, FALSE);
  1580. HEADLESS_ACQUIRE_SPIN_LOCK();
  1581. if (HeadlessGlobals->NewLogEntryAdded) {
  1582. //
  1583. // Inform user and quite current output
  1584. //
  1585. HdlspPutString((PUCHAR)"New log entries have been added during dump, command aborted.\r\n");
  1586. HEADLESS_RELEASE_SPIN_LOCK();
  1587. return;
  1588. }
  1589. HdlspPutString(HeadlessGlobals->TmpBuffer);
  1590. HdlspPutString((PUCHAR)"\r\n");
  1591. LineNumber++;
  1592. //
  1593. // if last item, exit loop.
  1594. //
  1595. if (LogEntryIndex == HeadlessGlobals->LogEntryLast) {
  1596. HEADLESS_RELEASE_SPIN_LOCK();
  1597. return;
  1598. }
  1599. //
  1600. // If screen is full, pause for paging.
  1601. //
  1602. if (Paging && (LineNumber > 20)) {
  1603. HEADLESS_RELEASE_SPIN_LOCK();
  1604. HdlspPutMore(&Stop);
  1605. HEADLESS_ACQUIRE_SPIN_LOCK();
  1606. if (Stop) {
  1607. HdlspPutString((PUCHAR)"\r\n");
  1608. HEADLESS_RELEASE_SPIN_LOCK();
  1609. return;
  1610. }
  1611. if (HeadlessGlobals->NewLogEntryAdded) {
  1612. //
  1613. // Inform user and quite current output
  1614. //
  1615. HdlspPutString((PUCHAR)"New log entries have been added while waiting, command aborted.\r\n");
  1616. HEADLESS_RELEASE_SPIN_LOCK();
  1617. return;
  1618. }
  1619. LineNumber = 0;
  1620. }
  1621. //
  1622. // Next entry please
  1623. //
  1624. LogEntryIndex++;
  1625. LogEntryIndex %= HEADLESS_LOG_NUMBER_OF_ENTRIES;
  1626. }
  1627. }
  1628. VOID
  1629. HdlspPutMore(
  1630. OUT PBOOLEAN Stop
  1631. )
  1632. /*++
  1633. Routine Description:
  1634. This function is used to display a paging prompt.
  1635. Arguments:
  1636. Stop - Returns TRUE if Control-C was pressed, else FALSE.
  1637. Return Value:
  1638. Stop - Returns TRUE if Control-C was pressed, else FALSE.
  1639. --*/
  1640. {
  1641. UCHAR Buffer[10];
  1642. LARGE_INTEGER WaitTime;
  1643. WaitTime.QuadPart = Int32x32To64((LONG)100, -1000); // 100ms from now.
  1644. HdlspPutString((PUCHAR)"----Press <Enter> for more----");
  1645. while (!HdlspGetLine(Buffer, 10)) {
  1646. if (!HeadlessGlobals->InBugCheck) {
  1647. KeDelayExecutionThread(KernelMode, FALSE, &WaitTime);
  1648. }
  1649. }
  1650. if (Buffer[0] == 0x3) { // Control-C
  1651. *Stop = TRUE;
  1652. } else {
  1653. *Stop = FALSE;
  1654. }
  1655. //
  1656. // Drain any remaining buffered input
  1657. //
  1658. while (HdlspGetLine(Buffer, 10)) {
  1659. }
  1660. }
  1661. VOID
  1662. HdlspAddLogEntry(
  1663. PWCHAR String
  1664. )
  1665. /*++
  1666. Routine Description:
  1667. This function is used to add a string to the internal log buffer.
  1668. Arguments:
  1669. String - The string to add.
  1670. Return Value:
  1671. None.
  1672. Environment:
  1673. Only called from HdlspDispatch, which guarantees it is paged in and locked down.
  1674. --*/
  1675. {
  1676. SIZE_T StringSize;
  1677. PWCHAR OldString = NULL;
  1678. PWCHAR NewString;
  1679. SYSTEM_TIMEOFDAY_INFORMATION TimeOfEntry;
  1680. NTSTATUS Status;
  1681. KIRQL OldIrql;
  1682. StringSize = (wcslen(String) * sizeof(WCHAR)) + sizeof(UNICODE_NULL);
  1683. //
  1684. // Guard against ZwQuery..() call being paged out.
  1685. //
  1686. if (KeGetCurrentIrql() >= DISPATCH_LEVEL) {
  1687. ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL);
  1688. return;
  1689. }
  1690. //
  1691. // Get the time so we can log it.
  1692. //
  1693. Status = ZwQuerySystemInformation(SystemTimeOfDayInformation,
  1694. &TimeOfEntry,
  1695. sizeof(TimeOfEntry),
  1696. NULL
  1697. );
  1698. if (!NT_SUCCESS(Status)) {
  1699. RtlZeroMemory(&TimeOfEntry, sizeof(TimeOfEntry));
  1700. }
  1701. //
  1702. // Allocate a string for the log entry.
  1703. //
  1704. NewString = ExAllocatePoolWithTag(NonPagedPool, StringSize, ((ULONG)'sldH'));
  1705. if (NewString != NULL) {
  1706. RtlCopyMemory(NewString, String, StringSize);
  1707. }
  1708. HEADLESS_ACQUIRE_SPIN_LOCK();
  1709. HeadlessGlobals->NewLogEntryAdded = TRUE;
  1710. //
  1711. // Get the entry to use.
  1712. //
  1713. HeadlessGlobals->LogEntryLast++;
  1714. HeadlessGlobals->LogEntryLast %= HEADLESS_LOG_NUMBER_OF_ENTRIES;
  1715. //
  1716. // See if we have to move the start entry index
  1717. //
  1718. if (HeadlessGlobals->LogEntryLast == HeadlessGlobals->LogEntryStart) {
  1719. //
  1720. // Store away the old string so we can free it later.
  1721. //
  1722. if (wcscmp(HeadlessGlobals->LogEntries[HeadlessGlobals->LogEntryStart].String,
  1723. HEADLESS_OOM_STRING) != 0) {
  1724. OldString = HeadlessGlobals->LogEntries[HeadlessGlobals->LogEntryStart].String;
  1725. }
  1726. HeadlessGlobals->LogEntryStart++;;
  1727. HeadlessGlobals->LogEntryStart %= HEADLESS_LOG_NUMBER_OF_ENTRIES;
  1728. } else if (HeadlessGlobals->LogEntryStart == (USHORT)-1) {
  1729. HeadlessGlobals->LogEntryStart = 0;
  1730. }
  1731. //
  1732. // Fill in the entry part
  1733. //
  1734. RtlCopyMemory(&(HeadlessGlobals->LogEntries[HeadlessGlobals->LogEntryLast].TimeOfEntry),
  1735. &(TimeOfEntry),
  1736. sizeof(TimeOfEntry)
  1737. );
  1738. //
  1739. // Set the entry pointer
  1740. //
  1741. if (NewString == NULL) {
  1742. HeadlessGlobals->LogEntries[HeadlessGlobals->LogEntryLast].String = HEADLESS_OOM_STRING;
  1743. } else {
  1744. HeadlessGlobals->LogEntries[HeadlessGlobals->LogEntryLast].String = NewString;
  1745. }
  1746. HEADLESS_RELEASE_SPIN_LOCK();
  1747. if (OldString != NULL) {
  1748. ExFreePool(OldString);
  1749. }
  1750. }
  1751. NTSTATUS
  1752. HdlspSetBlueScreenInformation(
  1753. IN PHEADLESS_CMD_SET_BLUE_SCREEN_DATA pData,
  1754. IN SIZE_T cData
  1755. )
  1756. /*++
  1757. Routine Description:
  1758. This routines allows components to set bugcheck information about the headless
  1759. terminal.
  1760. Arguments:
  1761. pData - A pointer to the data, value pair to store.
  1762. cData - Length, in bytes, of pData.
  1763. Return Value:
  1764. Status of the operation - STATUS_SUCCESS, STATUS_NO_MEMORY e.g.
  1765. Environment:
  1766. HdlspDispatch guarantess only one person to enter this procedure.
  1767. This is the only procedure modifying the HeadlessGlobals->BlueScreenData
  1768. However, bugcheck processing uses this information to send it across the
  1769. blue screen at dispatch level. No hand shaking required except ensuring that
  1770. changes to the list are done such that once bugcheck processing starts, the list
  1771. is unchanged. May cause a memory leak in a bugcheck situation, but in essence
  1772. that is better than an access violation, and acceptable since the machine is stopping.
  1773. --*/
  1774. {
  1775. PHEADLESS_BLUE_SCREEN_DATA HeadlessProp,Prev;
  1776. NTSTATUS Status;
  1777. PUCHAR pVal,pOldVal;
  1778. PUCHAR pNewVal;
  1779. SIZE_T len;
  1780. ASSERT(FIELD_OFFSET(HEADLESS_CMD_SET_BLUE_SCREEN_DATA,Data) == sizeof(ULONG));
  1781. if (HeadlessGlobals->InBugCheck) {
  1782. return STATUS_UNSUCCESSFUL;
  1783. }
  1784. if ((pData == NULL) ||
  1785. (pData->ValueIndex < 2) || // There must be at least two \0 characters in the pair.
  1786. (pData->ValueIndex >= (cData - sizeof(HEADLESS_CMD_SET_BLUE_SCREEN_DATA)) / sizeof (UCHAR)) ||
  1787. (pData->Data[pData->ValueIndex-1] != '\0') ||
  1788. (pData->Data[(cData - sizeof(HEADLESS_CMD_SET_BLUE_SCREEN_DATA))/sizeof(UCHAR)] != '\0' )) {
  1789. return STATUS_INVALID_PARAMETER;
  1790. }
  1791. Status = STATUS_SUCCESS;
  1792. //
  1793. // Manipulation of this linked list is done only by this single entrant
  1794. // function.
  1795. //
  1796. HeadlessProp = Prev = HeadlessGlobals->BlueScreenData;
  1797. while (HeadlessProp) {
  1798. if (strcmp((LPCSTR)HeadlessProp->Property, (LPCSTR)pData->Data) == 0) {
  1799. break;
  1800. }
  1801. Prev = HeadlessProp;
  1802. HeadlessProp = HeadlessProp->Next;
  1803. }
  1804. pVal = (PUCHAR)&((pData->Data)[pData->ValueIndex]);
  1805. len = strlen((LPCSTR)pVal);
  1806. if (HeadlessProp != NULL) {
  1807. //
  1808. // The property exists. So replace it.
  1809. //
  1810. if (len) {
  1811. //
  1812. // need to replace old string.
  1813. //
  1814. pNewVal = (PUCHAR)ExAllocatePoolWithTag(NonPagedPool,
  1815. len+1,
  1816. ((ULONG)'sldH')
  1817. );
  1818. if (pNewVal) {
  1819. strcpy( (LPSTR)pNewVal, (LPCSTR)pVal );
  1820. pOldVal = HeadlessProp->XMLData;
  1821. HeadlessProp->XMLData = pNewVal;
  1822. if (HeadlessGlobals->InBugCheck == FALSE) {
  1823. ExFreePool(pOldVal);
  1824. }
  1825. } else {
  1826. Status = STATUS_NO_MEMORY;
  1827. }
  1828. } else {
  1829. //
  1830. // We want to delete it, hence we passed an empty string
  1831. //
  1832. Prev->Next = HeadlessProp->Next;
  1833. if (HeadlessGlobals->BlueScreenData == HeadlessProp) {
  1834. HeadlessGlobals->BlueScreenData = Prev->Next;
  1835. }
  1836. if (HeadlessGlobals->InBugCheck == FALSE) {
  1837. ExFreePool ( HeadlessProp->XMLData );
  1838. ExFreePool ( HeadlessProp->Property );
  1839. ExFreePool ( HeadlessProp );
  1840. }
  1841. }
  1842. } else {
  1843. //
  1844. // Create a new Property-XMLValue Pair
  1845. //
  1846. if (len) { // Must be a non-empty string
  1847. HeadlessProp = (PHEADLESS_BLUE_SCREEN_DATA)ExAllocatePoolWithTag(NonPagedPool,
  1848. sizeof(HEADLESS_BLUE_SCREEN_DATA),
  1849. ((ULONG) 'sldH' )
  1850. );
  1851. if (HeadlessProp) {
  1852. HeadlessProp->XMLData = (PUCHAR)ExAllocatePoolWithTag(NonPagedPool,
  1853. len+1,
  1854. ((ULONG)'sldH')
  1855. );
  1856. if (HeadlessProp->XMLData) {
  1857. strcpy((LPSTR)HeadlessProp->XMLData,(LPCSTR)pVal);
  1858. pVal = pData->Data;
  1859. len = strlen ((LPCSTR)pVal);
  1860. if (len) {
  1861. HeadlessProp->Property = (PUCHAR)ExAllocatePoolWithTag(NonPagedPool,
  1862. len+1,
  1863. ((ULONG)'sldH')
  1864. );
  1865. if (HeadlessProp->Property) {
  1866. strcpy((LPSTR)HeadlessProp->Property,(LPCSTR) pVal);
  1867. HeadlessProp->Next = HeadlessGlobals->BlueScreenData;
  1868. HeadlessGlobals->BlueScreenData = HeadlessProp;
  1869. } else {
  1870. Status = STATUS_NO_MEMORY;
  1871. ExFreePool(HeadlessProp->XMLData);
  1872. ExFreePool ( HeadlessProp );
  1873. }
  1874. } else { // empty property string ( will never come here )
  1875. Status = STATUS_INVALID_PARAMETER;
  1876. ExFreePool(HeadlessProp->XMLData);
  1877. ExFreePool(HeadlessProp);
  1878. }
  1879. } else {
  1880. Status = STATUS_NO_MEMORY;
  1881. ExFreePool(HeadlessProp);
  1882. }
  1883. }
  1884. } else {// empty value string.
  1885. Status = STATUS_INVALID_PARAMETER;
  1886. }
  1887. }
  1888. return Status;
  1889. }
  1890. VOID
  1891. HdlspSendBlueScreenInfo(
  1892. ULONG BugcheckCode
  1893. )
  1894. /*++
  1895. Routine Description:
  1896. This routines dumps all the current blue screen data to the terminal.
  1897. Arguments:
  1898. BugcheckCode - the NT defined bug check code.
  1899. Return Value:
  1900. None.
  1901. Environment:
  1902. Only called once in a bugcheck.
  1903. --*/
  1904. {
  1905. PHEADLESS_BLUE_SCREEN_DATA pData;
  1906. UCHAR Temp[160];
  1907. ASSERT(HeadlessGlobals->InBugCheck);
  1908. HdlspSendStringAtBaud((PUCHAR)"\007\007\007<?xml>\007<BP>");
  1909. HdlspSendStringAtBaud((PUCHAR)"\r\n<INSTANCE CLASSNAME=\"BLUESCREEN\">");
  1910. sprintf((LPSTR)Temp,"\r\n<PROPERTY NAME=\"STOPCODE\" TYPE=\"string\"><VALUE>\"0x%0X\"</VALUE></PROPERTY>",BugcheckCode);
  1911. HdlspSendStringAtBaud(Temp);
  1912. pData = HeadlessGlobals->BlueScreenData;
  1913. while (pData) {
  1914. HdlspSendStringAtBaud(pData->XMLData);
  1915. pData = pData->Next;
  1916. }
  1917. HdlspSendStringAtBaud((PUCHAR)"\r\n</INSTANCE>\r\n</BP>\007");
  1918. }
  1919. VOID
  1920. HeadlessKernelAddLogEntry(
  1921. IN ULONG StringCode,
  1922. IN PUNICODE_STRING DriverName OPTIONAL
  1923. )
  1924. /*++
  1925. Routine Description:
  1926. This routine adds a string to the headless log if possible.
  1927. Parameters:
  1928. StringCode - The string to add.
  1929. DriverName - An optional parameter that some string codes require.
  1930. Return Value:
  1931. None.
  1932. --*/
  1933. {
  1934. //
  1935. // If headless not enabled, just exit now.
  1936. //
  1937. if ((HeadlessGlobals == NULL) || (HeadlessGlobals->PageLockHandle == NULL)) {
  1938. return;
  1939. }
  1940. //
  1941. // Call the paged version of this routine. Note: it will not be paged here,
  1942. // as the handle is non-NULL.
  1943. //
  1944. HdlspKernelAddLogEntry(StringCode, DriverName);
  1945. }
  1946. VOID
  1947. HdlspKernelAddLogEntry(
  1948. IN ULONG StringCode,
  1949. IN PUNICODE_STRING DriverName OPTIONAL
  1950. )
  1951. /*++
  1952. Routine Description:
  1953. This routine adds a string to the headless log if possible.
  1954. Parameters:
  1955. StringCode - The string to add.
  1956. DriverName - An optional parameter that some string codes require.
  1957. Return Value:
  1958. None.
  1959. --*/
  1960. {
  1961. PHEADLESS_CMD_ADD_LOG_ENTRY HeadlessLogEntry;
  1962. UCHAR LocalBuffer[sizeof(HEADLESS_CMD_ADD_LOG_ENTRY) +
  1963. (HDLSP_LOG_MAX_STRING_LENGTH * sizeof(WCHAR))];
  1964. SIZE_T Index;
  1965. SIZE_T StringLength;
  1966. PWCHAR String;
  1967. HeadlessLogEntry = (PHEADLESS_CMD_ADD_LOG_ENTRY)LocalBuffer;
  1968. //
  1969. // Get the string associated with this string code.
  1970. //
  1971. switch (StringCode) {
  1972. case HEADLESS_LOG_LOADING_FILENAME:
  1973. String = L"KRNL: Loading ";
  1974. break;
  1975. case HEADLESS_LOG_LOAD_SUCCESSFUL:
  1976. String = L"KRNL: Load succeeded.";
  1977. break;
  1978. case HEADLESS_LOG_LOAD_FAILED:
  1979. String = L"KRNL: Load failed.";
  1980. break;
  1981. case HEADLESS_LOG_EVENT_CREATE_FAILED:
  1982. String = L"KRNL: Failed to create event.";
  1983. break;
  1984. case HEADLESS_LOG_OBJECT_TYPE_CREATE_FAILED:
  1985. String = L"KRNL: Failed to create object types.";
  1986. break;
  1987. case HEADLESS_LOG_ROOT_DIR_CREATE_FAILED:
  1988. String = L"KRNL: Failed to create root directories.";
  1989. break;
  1990. case HEADLESS_LOG_PNP_PHASE0_INIT_FAILED:
  1991. String = L"KRNL: Failed to initialize (phase 0) plug and play services.";
  1992. break;
  1993. case HEADLESS_LOG_PNP_PHASE1_INIT_FAILED:
  1994. String = L"KRNL: Failed to initialize (phase 1) plug and play services.";
  1995. break;
  1996. case HEADLESS_LOG_BOOT_DRIVERS_INIT_FAILED:
  1997. String = L"KRNL: Failed to initialize boot drivers.";
  1998. break;
  1999. case HEADLESS_LOG_LOCATE_SYSTEM_DLL_FAILED:
  2000. String = L"KRNL: Failed to locate system dll.";
  2001. break;
  2002. case HEADLESS_LOG_SYSTEM_DRIVERS_INIT_FAILED:
  2003. String = L"KRNL: Failed to initialize system drivers.";
  2004. break;
  2005. case HEADLESS_LOG_ASSIGN_SYSTEM_ROOT_FAILED:
  2006. String = L"KRNL: Failed to reassign system root.";
  2007. break;
  2008. case HEADLESS_LOG_PROTECT_SYSTEM_ROOT_FAILED:
  2009. String = L"KRNL: Failed to protect system partition.";
  2010. break;
  2011. case HEADLESS_LOG_UNICODE_TO_ANSI_FAILED:
  2012. String = L"KRNL: Failed to UnicodeToAnsi system root.";
  2013. break;
  2014. case HEADLESS_LOG_ANSI_TO_UNICODE_FAILED:
  2015. String = L"KRNL: Failed to AnsiToUnicode system root.";
  2016. break;
  2017. case HEADLESS_LOG_FIND_GROUPS_FAILED:
  2018. String = L"KRNL: Failed to find any groups.";
  2019. break;
  2020. case HEADLESS_LOG_WAIT_BOOT_DEVICES_DELETE_FAILED:
  2021. String = L"KRNL: Failed waiting for boot devices to delete.";
  2022. break;
  2023. case HEADLESS_LOG_WAIT_BOOT_DEVICES_START_FAILED:
  2024. String = L"KRNL: Failed waiting for boot devices to start.";
  2025. break;
  2026. case HEADLESS_LOG_WAIT_BOOT_DEVICES_REINIT_FAILED:
  2027. String = L"KRNL: Failed waiting for boot devices to reinit.";
  2028. break;
  2029. case HEADLESS_LOG_MARK_BOOT_PARTITION_FAILED:
  2030. String = L"KRNL: Failed marking boot partition.";
  2031. break;
  2032. default:
  2033. ASSERT(0);
  2034. String = NULL;
  2035. }
  2036. if (String != NULL) {
  2037. //
  2038. // Start by copying in the given string.
  2039. //
  2040. wcscpy(&(HeadlessLogEntry->String[0]), String);
  2041. } else {
  2042. HeadlessLogEntry->String[0] = UNICODE_NULL;
  2043. }
  2044. //
  2045. // If this is the loading_filename command, then we need to append the
  2046. // name to the end.
  2047. //
  2048. if ((StringCode == HEADLESS_LOG_LOADING_FILENAME) && (DriverName != NULL)) {
  2049. ASSERT(String != NULL);
  2050. StringLength = wcslen(String);
  2051. //
  2052. // Only copy as many bytes as we have room for.
  2053. //
  2054. if (DriverName->Length >= (HDLSP_LOG_MAX_STRING_LENGTH - StringLength)) {
  2055. Index = (HDLSP_LOG_MAX_STRING_LENGTH - StringLength - 1);
  2056. } else {
  2057. Index = DriverName->Length / sizeof(WCHAR);
  2058. }
  2059. //
  2060. // Copy in this many bytes.
  2061. //
  2062. RtlCopyBytes(&(HeadlessLogEntry->String[StringLength]),
  2063. DriverName->Buffer,
  2064. Index * sizeof(WCHAR)
  2065. );
  2066. if (DriverName->Buffer[(DriverName->Length / sizeof(WCHAR)) - 1] != UNICODE_NULL) {
  2067. HeadlessLogEntry->String[StringLength + Index] = UNICODE_NULL;
  2068. }
  2069. }
  2070. //
  2071. // Log it.
  2072. //
  2073. HdlspDispatch(HeadlessCmdAddLogEntry,
  2074. HeadlessLogEntry,
  2075. sizeof(HEADLESS_CMD_ADD_LOG_ENTRY) +
  2076. (wcslen(&(HeadlessLogEntry->String[0])) * sizeof(WCHAR)),
  2077. NULL,
  2078. NULL
  2079. );
  2080. }
  2081. VOID
  2082. HdlspSendStringAtBaud(
  2083. IN PUCHAR String
  2084. )
  2085. /*++
  2086. Routine Description:
  2087. This routine outputs a string one character at a time to the terminal, fitting the
  2088. baud rate specified for the connection.
  2089. Parameters:
  2090. String - The string to send.
  2091. Return Value:
  2092. None.
  2093. --*/
  2094. {
  2095. PUCHAR Dest;
  2096. KIRQL CurrentIrql;
  2097. CurrentIrql = KeGetCurrentIrql();
  2098. //
  2099. // If we are in the worker thread, up the timer resolution so we can output
  2100. // the string at the appropriate baud rate.
  2101. //
  2102. if (CurrentIrql < DISPATCH_LEVEL) {
  2103. ExSetTimerResolution(-1 * HeadlessGlobals->DelayTime.LowPart, TRUE);
  2104. }
  2105. for (Dest = String; *Dest != '\0'; Dest++) {
  2106. InbvPortPutByte(HeadlessGlobals->TerminalPort, *Dest);
  2107. if( HeadlessGlobals->TerminalBaudRate == 9600 ) {
  2108. if (CurrentIrql < DISPATCH_LEVEL) {
  2109. KeDelayExecutionThread(KernelMode, FALSE, &(HeadlessGlobals->DelayTime));
  2110. } else {
  2111. KeStallExecutionProcessor(HeadlessGlobals->MicroSecondsDelayTime);
  2112. }
  2113. }
  2114. }
  2115. //
  2116. // If we are in the worker thread, reset the timer resolution.
  2117. //
  2118. if (CurrentIrql < DISPATCH_LEVEL) {
  2119. ExSetTimerResolution(0, FALSE);
  2120. }
  2121. }