Leaked source code of windows server 2003
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.

1569 lines
36 KiB

  1. /*++
  2. Copyright (c) 1996 Microsoft Corporation
  3. Module Name:
  4. port.c
  5. Abstract:
  6. This modules implements com port code to support reading/writing from com ports.
  7. Author:
  8. Bryan M. Willman (bryanwi) 24-Sep-90
  9. Revision History:
  10. --*/
  11. #include "bldr.h"
  12. #include "string.h"
  13. #include "stdlib.h"
  14. #include "stdio.h"
  15. #include "ntverp.h"
  16. #include "acpitabl.h"
  17. #ifdef _IA64_
  18. #include "bldria64.h"
  19. #endif
  20. //
  21. // Headless Port information.
  22. //
  23. ULONG BlTerminalDeviceId = 0;
  24. BOOLEAN BlTerminalConnected = FALSE;
  25. ULONG BlTerminalDelay = 0;
  26. HEADLESS_LOADER_BLOCK LoaderRedirectionInformation;
  27. //
  28. // Define COM Port registers.
  29. //
  30. #define COM_DAT 0x00
  31. #define COM_IEN 0x01 // interrupt enable register
  32. #define COM_FCR 0x02 // FIFO Control Register
  33. #define COM_LCR 0x03 // line control registers
  34. #define COM_MCR 0x04 // modem control reg
  35. #define COM_LSR 0x05 // line status register
  36. #define COM_MSR 0x06 // modem status register
  37. #define COM_DLL 0x00 // divisor latch least sig
  38. #define COM_DLM 0x01 // divisor latch most sig
  39. #define COM_BI 0x10
  40. #define COM_FE 0x08
  41. #define COM_PE 0x04
  42. #define COM_OE 0x02
  43. #define LC_DLAB 0x80 // divisor latch access bit
  44. #define CLOCK_RATE 0x1C200 // USART clock rate
  45. #define MC_DTRRTS 0x03 // Control bits to assert DTR and RTS
  46. #define MS_DSRCTSCD 0xB0 // Status bits for DSR, CTS and CD
  47. #define MS_CD 0x80
  48. #define COM_OUTRDY 0x20
  49. #define COM_DATRDY 0x01
  50. //
  51. // This bit controls the loopback testing mode of the device. Basically
  52. // the outputs are connected to the inputs (and vice versa).
  53. //
  54. #define SERIAL_MCR_LOOP 0x10
  55. //
  56. // This bit is used for general purpose output.
  57. //
  58. #define SERIAL_MCR_OUT1 0x04
  59. //
  60. // This bit contains the (complemented) state of the clear to send
  61. // (CTS) line.
  62. //
  63. #define SERIAL_MSR_CTS 0x10
  64. //
  65. // This bit contains the (complemented) state of the data set ready
  66. // (DSR) line.
  67. //
  68. #define SERIAL_MSR_DSR 0x20
  69. //
  70. // This bit contains the (complemented) state of the ring indicator
  71. // (RI) line.
  72. //
  73. #define SERIAL_MSR_RI 0x40
  74. //
  75. // This bit contains the (complemented) state of the data carrier detect
  76. // (DCD) line.
  77. //
  78. #define SERIAL_MSR_DCD 0x80
  79. typedef struct _CPPORT {
  80. PUCHAR Address;
  81. ULONG Baud;
  82. USHORT Flags;
  83. } CPPORT, *PCPPORT;
  84. #define PORT_DEFAULTRATE 0x0001 // baud rate not specified, using default
  85. #define PORT_MODEMCONTROL 0x0002 // using modem controls
  86. //
  87. // Define wait timeout value.
  88. //
  89. #define TIMEOUT_COUNT 1024 * 200
  90. extern
  91. VOID
  92. FwStallExecution(
  93. IN ULONG Microseconds
  94. );
  95. //
  96. // Routines for reading/writing bytes out to the UART.
  97. //
  98. UCHAR
  99. (*READ_UCHAR)(
  100. IN PUCHAR Addr
  101. );
  102. VOID
  103. (*WRITE_UCHAR)(
  104. IN PUCHAR Addr,
  105. IN UCHAR Value
  106. );
  107. //
  108. // Define COM Port function prototypes.
  109. //
  110. VOID
  111. CpInitialize (
  112. PCPPORT Port,
  113. PUCHAR Address,
  114. ULONG Rate
  115. );
  116. VOID
  117. CpEnableFifo(
  118. IN PUCHAR Address,
  119. IN BOOLEAN bEnable
  120. );
  121. LOGICAL
  122. CpDoesPortExist(
  123. IN PUCHAR Address
  124. );
  125. UCHAR
  126. CpReadLsr (
  127. IN PCPPORT Port,
  128. IN UCHAR Waiting
  129. );
  130. VOID
  131. CpSetBaud (
  132. PCPPORT Port,
  133. ULONG Rate
  134. );
  135. USHORT
  136. CpGetByte (
  137. PCPPORT Port,
  138. PUCHAR Byte,
  139. BOOLEAN WaitForData,
  140. BOOLEAN PollOnly
  141. );
  142. VOID
  143. CpPutByte (
  144. PCPPORT Port,
  145. UCHAR Byte
  146. );
  147. //
  148. // Define debugger port initial state.
  149. //
  150. CPPORT Port[4] = {
  151. {NULL, 0, PORT_DEFAULTRATE},
  152. {NULL, 0, PORT_DEFAULTRATE},
  153. {NULL, 0, PORT_DEFAULTRATE},
  154. {NULL, 0, PORT_DEFAULTRATE}
  155. };
  156. //
  157. // This is how we find table information from
  158. // the ACPI table index.
  159. //
  160. extern PDESCRIPTION_HEADER
  161. BlFindACPITable(
  162. IN PCHAR TableName,
  163. IN ULONG TableLength
  164. );
  165. //
  166. // We'll use these to fill in some function pointers,
  167. // which in turn will be used to read/write from the
  168. // UART. We can't simply assign the function pointers
  169. // to point to READ_PORT_UCHAR/READ_REGISTER_UCHAR and
  170. // WRITE_PORT_UCHAR/WRITE_REGISTER_UCHAR, because in
  171. // the case of IA64, some of these functions are macros.
  172. //
  173. // To get around this, build these dummy functions that
  174. // will inturn simply call the correct READ/WRITE functions/macros.
  175. //
  176. UCHAR
  177. MY_READ_PORT_UCHAR( IN PUCHAR Addr )
  178. {
  179. return( READ_PORT_UCHAR(Addr) );
  180. }
  181. UCHAR
  182. MY_READ_REGISTER_UCHAR( IN PUCHAR Addr )
  183. {
  184. return( READ_REGISTER_UCHAR(Addr) );
  185. }
  186. VOID
  187. MY_WRITE_PORT_UCHAR( IN PUCHAR Addr, IN UCHAR Value )
  188. {
  189. WRITE_PORT_UCHAR(Addr, Value);
  190. }
  191. VOID
  192. MY_WRITE_REGISTER_UCHAR( IN PUCHAR Addr, IN UCHAR Value )
  193. {
  194. WRITE_REGISTER_UCHAR(Addr, Value);
  195. }
  196. LOGICAL
  197. BlRetrieveBIOSRedirectionInformation(
  198. VOID
  199. )
  200. /*++
  201. Routine Description:
  202. This functions retrieves the COM port information from the ACPI
  203. table.
  204. Arguments:
  205. We'll be filling in the LoaderRedirectionInformation structure.
  206. Returned Value:
  207. TRUE - If a debug port is found.
  208. --*/
  209. {
  210. PSERIAL_PORT_REDIRECTION_TABLE pPortTable = NULL;
  211. PUCHAR CurrentAddress = NULL;
  212. UCHAR Checksum;
  213. ULONG i;
  214. ULONG CheckLength;
  215. pPortTable = (PSERIAL_PORT_REDIRECTION_TABLE)BlFindACPITable( "SPCR",
  216. sizeof(SERIAL_PORT_REDIRECTION_TABLE) );
  217. if( pPortTable ) {
  218. //
  219. // generate a checksum for later validation.
  220. //
  221. CurrentAddress = (PUCHAR)pPortTable;
  222. CheckLength = pPortTable->Header.Length;
  223. Checksum = 0;
  224. for( i = 0; i < CheckLength; i++ ) {
  225. Checksum = Checksum + CurrentAddress[i];
  226. }
  227. if(
  228. // checksum is okay?
  229. (Checksum == 0) &&
  230. // device address defined?
  231. ((UCHAR UNALIGNED *)pPortTable->BaseAddress.Address.LowPart != (UCHAR *)NULL) &&
  232. // he better be in system or memory I/O
  233. // note: 0 - systemI/O
  234. // 1 - memory mapped I/O
  235. ((pPortTable->BaseAddress.AddressSpaceID == 0) ||
  236. (pPortTable->BaseAddress.AddressSpaceID == 1))
  237. ) {
  238. if( pPortTable->BaseAddress.AddressSpaceID == 0 ) {
  239. LoaderRedirectionInformation.IsMMIODevice = TRUE;
  240. } else {
  241. LoaderRedirectionInformation.IsMMIODevice = FALSE;
  242. }
  243. //
  244. // We got the table. Now dig out the information we want.
  245. // See definitiion of SERIAL_PORT_REDIRECTION_TABLE (acpitabl.h)
  246. //
  247. LoaderRedirectionInformation.UsedBiosSettings = TRUE;
  248. LoaderRedirectionInformation.PortNumber = 3;
  249. LoaderRedirectionInformation.PortAddress = (UCHAR UNALIGNED *)(pPortTable->BaseAddress.Address.LowPart);
  250. if( pPortTable->BaudRate == 7 ) {
  251. LoaderRedirectionInformation.BaudRate = BD_115200;
  252. } else if( pPortTable->BaudRate == 6 ) {
  253. LoaderRedirectionInformation.BaudRate = BD_57600;
  254. } else if( pPortTable->BaudRate == 4 ) {
  255. LoaderRedirectionInformation.BaudRate = BD_19200;
  256. } else {
  257. LoaderRedirectionInformation.BaudRate = BD_9600;
  258. }
  259. LoaderRedirectionInformation.Parity = pPortTable->Parity;
  260. LoaderRedirectionInformation.StopBits = pPortTable->StopBits;
  261. LoaderRedirectionInformation.TerminalType = pPortTable->TerminalType;
  262. //
  263. // If this is a new SERIAL_PORT_REDIRECTION_TABLE, then it's got the PCI device
  264. // information.
  265. //
  266. if( pPortTable->Header.Length >= sizeof(SERIAL_PORT_REDIRECTION_TABLE) ) {
  267. LoaderRedirectionInformation.PciDeviceId = (USHORT UNALIGNED)pPortTable->PciDeviceId;
  268. LoaderRedirectionInformation.PciVendorId = (USHORT UNALIGNED)pPortTable->PciVendorId;
  269. LoaderRedirectionInformation.PciBusNumber = (UCHAR)pPortTable->PciBusNumber;
  270. LoaderRedirectionInformation.PciSlotNumber = (UCHAR)pPortTable->PciSlotNumber;
  271. LoaderRedirectionInformation.PciFunctionNumber = (UCHAR)pPortTable->PciFunctionNumber;
  272. LoaderRedirectionInformation.PciFlags = (ULONG UNALIGNED)pPortTable->PciFlags;
  273. } else {
  274. //
  275. // There's no PCI device information in this table.
  276. //
  277. LoaderRedirectionInformation.PciDeviceId = (USHORT)0xFFFF;
  278. LoaderRedirectionInformation.PciVendorId = (USHORT)0xFFFF;
  279. LoaderRedirectionInformation.PciBusNumber = 0;
  280. LoaderRedirectionInformation.PciSlotNumber = 0;
  281. LoaderRedirectionInformation.PciFunctionNumber = 0;
  282. LoaderRedirectionInformation.PciFlags = 0;
  283. }
  284. return TRUE;
  285. }
  286. }
  287. return FALSE;
  288. }
  289. LOGICAL
  290. BlPortInitialize(
  291. IN ULONG BaudRate,
  292. IN ULONG PortNumber,
  293. IN PUCHAR PortAddress OPTIONAL,
  294. IN BOOLEAN ReInitialize,
  295. OUT PULONG BlFileId
  296. )
  297. /*++
  298. Routine Description:
  299. This functions initializes the com port.
  300. Arguments:
  301. BaudRate - Supplies an optional baud rate.
  302. PortNumber - supplies an optinal port number.
  303. ReInitialize - Set to TRUE if we already have this port open, but for some
  304. reason need to completely reset the port. Otw it should be FALSE.
  305. BlFileId - A place to store a fake file Id, if successful.
  306. Returned Value:
  307. TRUE - If a debug port is found, and BlFileId will point to a location within Port[].
  308. --*/
  309. {
  310. //
  311. // Make guesses on any inputs that we didn't get.
  312. //
  313. if( BaudRate == 0 ) {
  314. BaudRate = BD_19200;
  315. }
  316. if( PortNumber == 0 ) {
  317. //
  318. // Try COM2, then COM1
  319. //
  320. if (CpDoesPortExist((PUCHAR)COM2_PORT)) {
  321. PortNumber = 2;
  322. PortAddress = (PUCHAR)COM2_PORT;
  323. } else if (CpDoesPortExist((PUCHAR)COM1_PORT)) {
  324. PortNumber = 1;
  325. PortAddress = (PUCHAR)COM1_PORT;
  326. } else {
  327. return FALSE;
  328. }
  329. }
  330. //
  331. // If the user didn't send us a port address, then
  332. // guess based on the COM port number.
  333. //
  334. if( PortAddress == 0 ) {
  335. switch (PortNumber) {
  336. case 1:
  337. PortAddress = (PUCHAR)COM1_PORT;
  338. break;
  339. case 2:
  340. PortAddress = (PUCHAR)COM2_PORT;
  341. break;
  342. case 3:
  343. PortAddress = (PUCHAR)COM3_PORT;
  344. break;
  345. default:
  346. PortNumber = 4;
  347. PortAddress = (PUCHAR)COM4_PORT;
  348. }
  349. }
  350. //
  351. // we need to handle the case where we're dealing with
  352. // MMIO space (as opposed to System I/O space).
  353. //
  354. if( LoaderRedirectionInformation.IsMMIODevice ) {
  355. PHYSICAL_ADDRESS PhysAddr;
  356. PVOID MyPtr;
  357. PhysAddr.LowPart = PtrToUlong(PortAddress);
  358. PhysAddr.HighPart = 0;
  359. MyPtr = MmMapIoSpace(PhysAddr,(1+COM_MSR),TRUE);
  360. PortAddress = MyPtr;
  361. READ_UCHAR = MY_READ_REGISTER_UCHAR;
  362. WRITE_UCHAR = MY_WRITE_REGISTER_UCHAR;
  363. } else {
  364. // System IO space.
  365. READ_UCHAR = MY_READ_PORT_UCHAR;
  366. WRITE_UCHAR = MY_WRITE_PORT_UCHAR;
  367. }
  368. //
  369. // See if the port even exists...
  370. //
  371. if (!CpDoesPortExist(PortAddress)) {
  372. //
  373. // It's possible that it's a port that's being emulated
  374. // in software. Give them another chance to pass.
  375. //
  376. if (!CpDoesPortExist(PortAddress)) {
  377. return FALSE;
  378. }
  379. }
  380. //
  381. // Check if the port is already in use, and this is a first init.
  382. //
  383. if (!ReInitialize && (Port[PortNumber-1].Address != NULL)) {
  384. return FALSE;
  385. }
  386. //
  387. // Check if someone tries to reinit a port that is not open.
  388. //
  389. if (ReInitialize && (Port[PortNumber-1].Address == NULL)) {
  390. return FALSE;
  391. }
  392. //
  393. // Initialize the specified port.
  394. //
  395. CpInitialize(&(Port[PortNumber-1]),
  396. PortAddress,
  397. BaudRate);
  398. *BlFileId = (PortNumber-1);
  399. return TRUE;
  400. }
  401. VOID
  402. BlLoadGUID(
  403. GUID *pGuid
  404. )
  405. /*++
  406. Routine Description:
  407. Attempt to find the System GUID. If we find it, load it into
  408. pGuid.
  409. Arguments:
  410. pGuid - Variable to receive the detected GUID.
  411. Return Value:
  412. None.
  413. --*/
  414. {
  415. #include <smbios.h>
  416. #include <wmidata.h>
  417. PUCHAR CurrentAddress = NULL;
  418. PUCHAR EndingAddress = NULL;
  419. UCHAR Checksum;
  420. ULONG i;
  421. ULONG CheckLength;
  422. BOOLEAN FoundIt = FALSE;
  423. PSYSID_UUID_ENTRY UuidEntry = NULL;
  424. //
  425. // Make sure we have a place to store the GUID when we find it.
  426. //
  427. if( pGuid == NULL ) {
  428. return;
  429. }
  430. //
  431. // Now search through the SMBIOS tables for the GUID.
  432. //
  433. CurrentAddress = (PUCHAR)SYSID_EPS_SEARCH_START;
  434. EndingAddress = CurrentAddress + SYSID_EPS_SEARCH_SIZE;
  435. while( CurrentAddress < EndingAddress ) {
  436. UuidEntry = (PSYSID_UUID_ENTRY)CurrentAddress;
  437. if( memcmp(UuidEntry->Type, SYSID_TYPE_UUID, 0x6) == 0 ) {
  438. //
  439. // See if the checksum matches too.
  440. //
  441. CheckLength = UuidEntry->Length;
  442. Checksum = 0;
  443. for( i = 0; i < CheckLength; i++ ) {
  444. Checksum = Checksum + CurrentAddress[i];
  445. }
  446. if( Checksum == 0 ) {
  447. FoundIt = TRUE;
  448. RtlCopyMemory( pGuid,
  449. UuidEntry->UUID,
  450. sizeof(GUID) );
  451. break;
  452. }
  453. }
  454. CurrentAddress++;
  455. }
  456. if( !FoundIt ) {
  457. RtlZeroMemory( pGuid,
  458. sizeof(SYSID_UUID) );
  459. }
  460. return;
  461. }
  462. VOID
  463. BlEnableFifo(
  464. IN ULONG DeviceId,
  465. IN BOOLEAN bEnable
  466. )
  467. /*++
  468. Routine Description:
  469. This routine will attempt to enable the FIFO in the 16550 UART.
  470. Note that the behaviour is undefined for the 16450, but practically,
  471. this should have no effect.
  472. Arguments:
  473. DeviceId - Value returned by BlPortInitialize()
  474. bEnable - if TRUE, FIFO is enabled
  475. if FALSE, FIFO is disabled
  476. Return Value:
  477. None
  478. --*/
  479. {
  480. CpEnableFifo(
  481. Port[DeviceId].Address,
  482. bEnable
  483. );
  484. }
  485. VOID
  486. BlInitializeHeadlessPort(
  487. VOID
  488. )
  489. /*++
  490. Routine Description:
  491. Does x86-specific initialization of a dumb terminal connected to a serial port. Currently,
  492. it assumes baud rate and com port are pre-initialized, but this can be changed in the future
  493. by reading the values from boot.ini or someplace.
  494. Arguments:
  495. None.
  496. Return Value:
  497. None.
  498. --*/
  499. {
  500. ULONG i;
  501. PCHAR TmpBuffer;
  502. if( (LoaderRedirectionInformation.PortNumber == 0) &&
  503. !(LoaderRedirectionInformation.PortAddress) ) {
  504. //
  505. // This means that no one has filled in the LoaderRedirectionInformation
  506. // structure, which means that we aren't redirecting right now.
  507. // See if the BIOS was redirecting. If so, pick up those settings
  508. // and use them.
  509. //
  510. BlRetrieveBIOSRedirectionInformation();
  511. }
  512. if( LoaderRedirectionInformation.PortNumber ) {
  513. //
  514. // We really need to make sure there's an address associated with
  515. // this port and not just a port number.
  516. //
  517. if( LoaderRedirectionInformation.PortAddress == NULL ) {
  518. switch( LoaderRedirectionInformation.PortNumber ) {
  519. case 4:
  520. LoaderRedirectionInformation.PortAddress = (PUCHAR)COM4_PORT;
  521. break;
  522. case 3:
  523. LoaderRedirectionInformation.PortAddress = (PUCHAR)COM3_PORT;
  524. break;
  525. case 2:
  526. LoaderRedirectionInformation.PortAddress = (PUCHAR)COM2_PORT;
  527. break;
  528. case 1:
  529. default:
  530. LoaderRedirectionInformation.PortAddress = (PUCHAR)COM1_PORT;
  531. break;
  532. }
  533. }
  534. //
  535. // Either we just created a LoaderRedirectionInformation, or it was
  536. // built before we ever got into this function. Either way, we should
  537. // go try and initialize the port he wants to talk through.
  538. //
  539. BlTerminalConnected = (BOOLEAN)BlPortInitialize(LoaderRedirectionInformation.BaudRate,
  540. LoaderRedirectionInformation.PortNumber,
  541. LoaderRedirectionInformation.PortAddress,
  542. BlTerminalConnected,
  543. &BlTerminalDeviceId);
  544. if (BlIsTerminalConnected()) {
  545. //
  546. // Enable the FIFO on the UART so we reduce the chance of a character
  547. // getting dropped.
  548. //
  549. BlEnableFifo(
  550. BlTerminalDeviceId,
  551. TRUE
  552. );
  553. //
  554. // Go get the machine's GUID.
  555. //
  556. BlLoadGUID( &LoaderRedirectionInformation.SystemGUID );
  557. //
  558. // Figure time to delay based on baudrate. Note: we do this calculation
  559. // to be at 60% of the baud rate because it appears that FwStallExecution
  560. // is extremely inaccurate, and that if we dont go slow enough a lot of
  561. // screen attributes being sent in a row causes a real vt100 to drop
  562. // characters that follows as it repaints/clears/whatever the screen.
  563. //
  564. if( LoaderRedirectionInformation.BaudRate == 0 ) {
  565. LoaderRedirectionInformation.BaudRate = BD_9600;
  566. }
  567. BlTerminalDelay = LoaderRedirectionInformation.BaudRate;
  568. BlTerminalDelay = BlTerminalDelay / 10; // 10 bits per character (8-1-1) is the max.
  569. BlTerminalDelay = ((1000000 / BlTerminalDelay) * 10) / 6; // 60% speed.
  570. //
  571. // Make sure there are no stale attributes on the terminal
  572. // sitting at the other end of our headless port.
  573. //
  574. // <CSI>m (turn attributes off)
  575. TmpBuffer = "\033[m";
  576. for( i = 0; i < strlen(TmpBuffer); i++ ) {
  577. BlPortPutByte( BlTerminalDeviceId, TmpBuffer[i]);
  578. FwStallExecution(BlTerminalDelay);
  579. }
  580. } else {
  581. //
  582. // Make sure we don't have any redirection information
  583. // hanging around if we didn't pass BlIsTerminalConnected()
  584. //
  585. RtlZeroMemory( &LoaderRedirectionInformation, sizeof(HEADLESS_LOADER_BLOCK) );
  586. }
  587. } else {
  588. BlTerminalConnected = FALSE;
  589. }
  590. }
  591. LOGICAL
  592. BlTerminalAttached(
  593. IN ULONG DeviceId
  594. )
  595. /*++
  596. Routine Description:
  597. This routine will attempt to discover if a terminal is attached.
  598. Arguments:
  599. DeviceId - Value returned by BlPortInitialize()
  600. Return Value:
  601. TRUE - Port seems to have something attached.
  602. FALSE - Port doesn't seem to have anything attached.
  603. --*/
  604. {
  605. UCHAR ModemStatus;
  606. BOOLEAN ReturnValue;
  607. //
  608. // Check for a carrier.
  609. //
  610. ModemStatus = READ_UCHAR(Port[DeviceId].Address + COM_MSR);
  611. ReturnValue = ((ModemStatus & MS_DSRCTSCD) == MS_DSRCTSCD);
  612. return ReturnValue;
  613. }
  614. VOID
  615. BlSetHeadlessRestartBlock(
  616. IN PTFTP_RESTART_BLOCK RestartBlock
  617. )
  618. /*++
  619. Routine Description:
  620. This routine will fill in the areas of the restart block that are appropriate
  621. for the headless server effort.
  622. Arguments:
  623. RestartBlock - The magic structure for holding restart information from oschoice
  624. to setupldr.
  625. Return Value:
  626. None.
  627. --*/
  628. {
  629. if( LoaderRedirectionInformation.PortNumber ) {
  630. RestartBlock->HeadlessUsedBiosSettings = (ULONG)LoaderRedirectionInformation.UsedBiosSettings;
  631. RestartBlock->HeadlessPortNumber = (ULONG)LoaderRedirectionInformation.PortNumber;
  632. RestartBlock->HeadlessPortAddress = (PUCHAR)LoaderRedirectionInformation.PortAddress;
  633. RestartBlock->HeadlessBaudRate = (ULONG)LoaderRedirectionInformation.BaudRate;
  634. RestartBlock->HeadlessParity = (ULONG)LoaderRedirectionInformation.Parity;
  635. RestartBlock->HeadlessStopBits = (ULONG)LoaderRedirectionInformation.StopBits;
  636. RestartBlock->HeadlessTerminalType = (ULONG)LoaderRedirectionInformation.TerminalType;
  637. RestartBlock->HeadlessPciDeviceId = LoaderRedirectionInformation.PciDeviceId;
  638. RestartBlock->HeadlessPciVendorId = LoaderRedirectionInformation.PciVendorId;
  639. RestartBlock->HeadlessPciBusNumber = LoaderRedirectionInformation.PciBusNumber;
  640. RestartBlock->HeadlessPciSlotNumber = LoaderRedirectionInformation.PciSlotNumber;
  641. RestartBlock->HeadlessPciFunctionNumber = LoaderRedirectionInformation.PciFunctionNumber;
  642. RestartBlock->HeadlessPciFlags = LoaderRedirectionInformation.PciFlags;
  643. }
  644. }
  645. VOID
  646. BlGetHeadlessRestartBlock(
  647. IN PTFTP_RESTART_BLOCK RestartBlock,
  648. IN BOOLEAN RestartBlockValid
  649. )
  650. /*++
  651. Routine Description:
  652. This routine will get all the information from a restart block
  653. for the headless server effort.
  654. Arguments:
  655. RestartBlock - The magic structure for holding restart information from oschoice
  656. to setupldr.
  657. RestartBlockValid - Is this block valid (full of good info)?
  658. Return Value:
  659. None.
  660. --*/
  661. {
  662. UNREFERENCED_PARAMETER( RestartBlockValid );
  663. LoaderRedirectionInformation.UsedBiosSettings = (BOOLEAN)RestartBlock->HeadlessUsedBiosSettings;
  664. LoaderRedirectionInformation.DataBits = 0;
  665. LoaderRedirectionInformation.StopBits = (UCHAR)RestartBlock->HeadlessStopBits;
  666. LoaderRedirectionInformation.Parity = (BOOLEAN)RestartBlock->HeadlessParity;
  667. LoaderRedirectionInformation.BaudRate = (ULONG)RestartBlock->HeadlessBaudRate;;
  668. LoaderRedirectionInformation.PortNumber = (ULONG)RestartBlock->HeadlessPortNumber;
  669. LoaderRedirectionInformation.PortAddress = (PUCHAR)RestartBlock->HeadlessPortAddress;
  670. LoaderRedirectionInformation.TerminalType = (UCHAR)RestartBlock->HeadlessTerminalType;
  671. LoaderRedirectionInformation.PciDeviceId = (USHORT)RestartBlock->HeadlessPciDeviceId;
  672. LoaderRedirectionInformation.PciVendorId = (USHORT)RestartBlock->HeadlessPciVendorId;
  673. LoaderRedirectionInformation.PciBusNumber = (UCHAR)RestartBlock->HeadlessPciBusNumber;
  674. LoaderRedirectionInformation.PciSlotNumber = (UCHAR)RestartBlock->HeadlessPciSlotNumber;
  675. LoaderRedirectionInformation.PciFunctionNumber = (UCHAR)RestartBlock->HeadlessPciFunctionNumber;
  676. LoaderRedirectionInformation.PciFlags = (ULONG)RestartBlock->HeadlessPciFlags;
  677. }
  678. ULONG
  679. BlPortGetByte (
  680. IN ULONG BlFileId,
  681. OUT PUCHAR Input
  682. )
  683. /*++
  684. Routine Description:
  685. Fetch a byte from the port and return it.
  686. Arguments:
  687. BlFileId - The port to read from.
  688. Input - Returns the data byte.
  689. Return Value:
  690. CP_GET_SUCCESS is returned if a byte is successfully read from the
  691. kernel debugger line.
  692. CP_GET_ERROR is returned if error encountered during reading.
  693. CP_GET_NODATA is returned if timeout.
  694. --*/
  695. {
  696. return CpGetByte(&Port[BlFileId], Input, TRUE, FALSE);
  697. }
  698. VOID
  699. BlPortPutByte (
  700. IN ULONG BlFileId,
  701. IN UCHAR Output
  702. )
  703. /*++
  704. Routine Description:
  705. Write a byte to the port.
  706. Arguments:
  707. BlFileId - The port to write to.
  708. Output - Supplies the output data byte.
  709. Return Value:
  710. None.
  711. --*/
  712. {
  713. CpPutByte(&Port[BlFileId], Output);
  714. }
  715. ULONG
  716. BlPortPollByte (
  717. IN ULONG BlFileId,
  718. OUT PUCHAR Input
  719. )
  720. /*++
  721. Routine Description:
  722. Fetch a byte from the port and return it if one is available.
  723. Arguments:
  724. BlFileId - The port to poll.
  725. Input - Returns the data byte.
  726. Return Value:
  727. CP_GET_SUCCESS is returned if a byte is successfully read.
  728. CP_GET_ERROR is returned if error encountered during reading.
  729. CP_GET_NODATA is returned if timeout.
  730. --*/
  731. {
  732. return CpGetByte(&Port[BlFileId], Input, FALSE, FALSE);
  733. }
  734. ULONG
  735. BlPortPollOnly (
  736. IN ULONG BlFileId
  737. )
  738. /*++
  739. Routine Description:
  740. Check if a byte is available
  741. Arguments:
  742. BlFileId - The port to poll.
  743. Return Value:
  744. CP_GET_SUCCESS is returned if a byte is ready.
  745. CP_GET_ERROR is returned if error encountered.
  746. CP_GET_NODATA is returned if timeout.
  747. --*/
  748. {
  749. UCHAR Input;
  750. return CpGetByte(&Port[BlFileId], &Input, FALSE, TRUE);
  751. }
  752. VOID
  753. CpInitialize (
  754. PCPPORT Port,
  755. PUCHAR Address,
  756. ULONG Rate
  757. )
  758. /*++
  759. Routine Description:
  760. Fill in the com port port object, set the initial baud rate,
  761. turn on the hardware.
  762. Arguments:
  763. Port - address of port object
  764. Address - port address of the com port
  765. (CP_COM1_PORT, CP_COM2_PORT)
  766. Rate - baud rate (CP_BD_150 ... CP_BD_19200)
  767. --*/
  768. {
  769. PUCHAR hwport;
  770. UCHAR mcr, ier;
  771. Port->Address = Address;
  772. Port->Baud = 0;
  773. CpSetBaud(Port, Rate);
  774. //
  775. // Assert DTR, RTS.
  776. //
  777. hwport = Port->Address;
  778. hwport += COM_MCR;
  779. mcr = MC_DTRRTS;
  780. WRITE_UCHAR(hwport, mcr);
  781. hwport = Port->Address;
  782. hwport += COM_IEN;
  783. ier = 0;
  784. WRITE_UCHAR(hwport, ier);
  785. return;
  786. }
  787. VOID
  788. CpEnableFifo(
  789. IN PUCHAR Address,
  790. IN BOOLEAN bEnable
  791. )
  792. /*++
  793. Routine Description:
  794. This routine will attempt to enable the FIFO in the
  795. UART at the address specified. If this is a 16550,
  796. this works. The behaviour on a 16450 is not defined,
  797. but practically, there is no effect.
  798. Arguments:
  799. Address - address of hw port.
  800. bEnable - if TRUE, FIFO is enabled
  801. if FALSE, FIFO is disabled
  802. Return Value:
  803. None
  804. --*/
  805. {
  806. //
  807. // Enable the FIFO in the UART. The behaviour is undefined on the
  808. // 16450, but practically, it should just ignore the command.
  809. //
  810. PUCHAR hwport = Address;
  811. hwport += COM_FCR;
  812. WRITE_UCHAR(hwport, bEnable);
  813. }
  814. LOGICAL
  815. CpDoesPortExist(
  816. IN PUCHAR Address
  817. )
  818. /*++
  819. Routine Description:
  820. This routine will attempt to place the port into its
  821. diagnostic mode. If it does it will twiddle a bit in
  822. the modem control register. If the port exists this
  823. twiddling should show up in the modem status register.
  824. NOTE: This routine must be called before the device is
  825. enabled for interrupts, this includes setting the
  826. output2 bit in the modem control register.
  827. This is blatantly stolen from TonyE's code in ntos\dd\serial\serial.c.
  828. Arguments:
  829. Address - address of hw port.
  830. Return Value:
  831. TRUE - Port exists.
  832. FALSE - Port doesn't exist.
  833. --*/
  834. {
  835. UCHAR OldModemStatus;
  836. UCHAR ModemStatus;
  837. BOOLEAN ReturnValue = TRUE;
  838. //
  839. // Save the old value of the modem control register.
  840. //
  841. OldModemStatus = READ_UCHAR(Address + COM_MCR);
  842. //
  843. // Set the port into diagnostic mode.
  844. //
  845. WRITE_UCHAR(Address + COM_MCR, SERIAL_MCR_LOOP);
  846. //
  847. // Bang on it again to make sure that all the lower bits
  848. // are clear.
  849. //
  850. WRITE_UCHAR(Address + COM_MCR, SERIAL_MCR_LOOP);
  851. //
  852. // Read the modem status register. The high for bits should
  853. // be clear.
  854. //
  855. ModemStatus = READ_UCHAR(Address + COM_MSR);
  856. if (ModemStatus & (SERIAL_MSR_CTS | SERIAL_MSR_DSR |
  857. SERIAL_MSR_RI | SERIAL_MSR_DCD)) {
  858. ReturnValue = FALSE;
  859. goto EndFirstTest;
  860. }
  861. //
  862. // So far so good. Now turn on OUT1 in the modem control register
  863. // and this should turn on ring indicator in the modem status register.
  864. //
  865. WRITE_UCHAR(Address + COM_MCR, (SERIAL_MCR_OUT1 | SERIAL_MCR_LOOP));
  866. ModemStatus = READ_UCHAR(Address + COM_MSR);
  867. if (!(ModemStatus & SERIAL_MSR_RI)) {
  868. ReturnValue = FALSE;
  869. goto EndFirstTest;
  870. }
  871. EndFirstTest:
  872. if( ReturnValue == FALSE ) {
  873. UCHAR OldIEValue = 0, OldLCValue = 0;
  874. USHORT Value1 = 0, Value2 = 0;
  875. UCHAR PreviousLineControl = 0;
  876. //
  877. // We failed the loopback test. Test another way.
  878. //
  879. // Remember the original Interrupt Enable setting and
  880. // Line Control setting.
  881. OldIEValue = READ_UCHAR( Address + COM_IEN );
  882. OldLCValue = READ_UCHAR( Address + COM_LCR );
  883. // Make sure we aren't accessing the divisor latch.
  884. WRITE_UCHAR( Address + COM_LCR, OldLCValue | LC_DLAB );
  885. WRITE_UCHAR( Address + COM_IEN, 0xF );
  886. Value1 = READ_UCHAR( Address + COM_IEN );
  887. Value1 = Value1 << 8;
  888. Value1 |= READ_UCHAR( Address + COM_DAT );
  889. // Now read the divisor latch.
  890. PreviousLineControl = READ_UCHAR( Address + COM_LCR );
  891. WRITE_UCHAR( Address + COM_LCR, (UCHAR)(PreviousLineControl | LC_DLAB) );
  892. Value2 = READ_UCHAR( Address + COM_DLL );
  893. Value2 = Value2 + (READ_UCHAR(Address + COM_DLM) << 8 );
  894. WRITE_UCHAR( Address + COM_LCR, PreviousLineControl );
  895. // Restore original Line Control register and
  896. // Interrupt Enable setting.
  897. WRITE_UCHAR( Address + COM_LCR, OldLCValue );
  898. WRITE_UCHAR( Address + COM_IEN, OldIEValue );
  899. if( Value1 == Value2 ) {
  900. //
  901. // We passed this test. Reset ReturnValue
  902. // appropriately.
  903. //
  904. ReturnValue = TRUE;
  905. }
  906. }
  907. //
  908. // Put the modem control back into a clean state.
  909. //
  910. WRITE_UCHAR(Address + COM_MCR, OldModemStatus);
  911. return ReturnValue;
  912. }
  913. UCHAR
  914. CpReadLsr (
  915. PCPPORT Port,
  916. UCHAR waiting
  917. )
  918. /*++
  919. Routine Description:
  920. Read LSR byte from specified port. If HAL owns port & display
  921. it will also cause a debug status to be kept up to date.
  922. Handles entering & exiting modem control mode for debugger.
  923. Arguments:
  924. Port - Address of CPPORT
  925. Returns:
  926. Byte read from port
  927. --*/
  928. {
  929. static UCHAR ringflag = 0;
  930. UCHAR lsr, msr;
  931. lsr = READ_UCHAR(Port->Address + COM_LSR);
  932. if ((lsr & waiting) == 0) {
  933. msr = READ_UCHAR (Port->Address + COM_MSR);
  934. ringflag |= (msr & SERIAL_MSR_RI) ? 1 : 2;
  935. if (ringflag == 3) {
  936. //
  937. // The ring indicate line has toggled, use modem control from
  938. // now on.
  939. //
  940. Port->Flags |= PORT_MODEMCONTROL;
  941. }
  942. }
  943. return lsr;
  944. }
  945. VOID
  946. CpSetBaud (
  947. PCPPORT Port,
  948. ULONG Rate
  949. )
  950. /*++
  951. Routine Description:
  952. Set the baud rate for the port and record it in the port object.
  953. Arguments:
  954. Port - address of port object
  955. Rate - baud rate (CP_BD_150 ... CP_BD_56000)
  956. --*/
  957. {
  958. ULONG divisorlatch;
  959. PUCHAR hwport;
  960. UCHAR lcr;
  961. //
  962. // compute the divsor
  963. //
  964. divisorlatch = CLOCK_RATE / Rate;
  965. //
  966. // set the divisor latch access bit (DLAB) in the line control reg
  967. //
  968. hwport = Port->Address;
  969. hwport += COM_LCR; // hwport = LCR register
  970. lcr = READ_UCHAR(hwport);
  971. lcr |= LC_DLAB;
  972. WRITE_UCHAR(hwport, lcr);
  973. //
  974. // set the divisor latch value.
  975. //
  976. hwport = Port->Address;
  977. hwport += COM_DLM; // divisor latch msb
  978. WRITE_UCHAR(hwport, (UCHAR)((divisorlatch >> 8) & 0xff));
  979. hwport--; // divisor latch lsb
  980. WRITE_UCHAR(hwport, (UCHAR)(divisorlatch & 0xff));
  981. //
  982. // Set LCR to 3. (3 is a magic number in the original assembler)
  983. //
  984. hwport = Port->Address;
  985. hwport += COM_LCR;
  986. WRITE_UCHAR(hwport, 3);
  987. //
  988. // Remember the baud rate
  989. //
  990. Port->Baud = Rate;
  991. return;
  992. }
  993. USHORT
  994. CpGetByte (
  995. PCPPORT Port,
  996. PUCHAR Byte,
  997. BOOLEAN WaitForByte,
  998. BOOLEAN PollOnly
  999. )
  1000. /*++
  1001. Routine Description:
  1002. Fetch a byte and return it.
  1003. Arguments:
  1004. Port - address of port object that describes hw port
  1005. Byte - address of variable to hold the result
  1006. WaitForByte - flag indicates wait for byte or not.
  1007. PollOnly - flag indicates whether to return immediately, not reading the byte, or not.
  1008. Return Value:
  1009. CP_GET_SUCCESS if data returned, or if data is ready and PollOnly is TRUE.
  1010. CP_GET_NODATA if no data available, but no error.
  1011. CP_GET_ERROR if error (overrun, parity, etc.)
  1012. --*/
  1013. {
  1014. UCHAR lsr;
  1015. ULONG limitcount;
  1016. //
  1017. // Check to make sure the CPPORT we were passed has been initialized.
  1018. // (The only time it won't be initialized is when the kernel debugger
  1019. // is disabled, in which case we just return.)
  1020. //
  1021. if (Port->Address == NULL) {
  1022. return CP_GET_NODATA;
  1023. }
  1024. limitcount = WaitForByte ? TIMEOUT_COUNT : 1;
  1025. while (limitcount != 0) {
  1026. limitcount--;
  1027. lsr = CpReadLsr(Port, COM_DATRDY);
  1028. if ((lsr & COM_DATRDY) == COM_DATRDY) {
  1029. //
  1030. // Check for errors
  1031. //
  1032. //
  1033. // If we get an overrun error, and there is data ready, we should
  1034. // return the data we have, so we ignore overrun errors. Reading
  1035. // the LSR clears this bit, so the first read already cleared the
  1036. // overrun error.
  1037. //
  1038. if (lsr & (COM_FE | COM_PE)) {
  1039. *Byte = 0;
  1040. return CP_GET_ERROR;
  1041. }
  1042. if (PollOnly) {
  1043. return CP_GET_SUCCESS;
  1044. }
  1045. //
  1046. // fetch the byte
  1047. //
  1048. *Byte = READ_UCHAR(Port->Address + COM_DAT);
  1049. if (Port->Flags & PORT_MODEMCONTROL) {
  1050. //
  1051. // Using modem control. If no CD, then skip this byte.
  1052. //
  1053. if ((READ_UCHAR(Port->Address + COM_MSR) & MS_CD) == 0) {
  1054. continue;
  1055. }
  1056. }
  1057. return CP_GET_SUCCESS;
  1058. }
  1059. }
  1060. CpReadLsr(Port, 0);
  1061. return CP_GET_NODATA;
  1062. }
  1063. VOID
  1064. CpPutByte (
  1065. PCPPORT Port,
  1066. UCHAR Byte
  1067. )
  1068. /*++
  1069. Routine Description:
  1070. Write a byte out to the specified com port.
  1071. Arguments:
  1072. Port - Address of CPPORT object
  1073. Byte - data to emit
  1074. --*/
  1075. {
  1076. UCHAR msr, lsr;
  1077. //
  1078. // If modem control, make sure DSR, CTS and CD are all set before
  1079. // sending any data.
  1080. //
  1081. while ((Port->Flags & PORT_MODEMCONTROL) &&
  1082. (msr = READ_UCHAR(Port->Address + COM_MSR) & MS_DSRCTSCD) != MS_DSRCTSCD) {
  1083. //
  1084. // If no CD, and there's a charactor ready, eat it
  1085. //
  1086. lsr = CpReadLsr(Port, 0);
  1087. if ((msr & MS_CD) == 0 && (lsr & COM_DATRDY) == COM_DATRDY) {
  1088. READ_UCHAR(Port->Address + COM_DAT);
  1089. }
  1090. }
  1091. //
  1092. // Wait for port to not be busy
  1093. //
  1094. while (!(CpReadLsr(Port, COM_OUTRDY) & COM_OUTRDY)) ;
  1095. //
  1096. // Send the byte
  1097. //
  1098. WRITE_UCHAR(Port->Address + COM_DAT, Byte);
  1099. return;
  1100. }