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.

906 lines
15 KiB

  1. /*++
  2. Copyright (c) 2000 Microsoft Corporation
  3. Module Name:
  4. ixcmos.c
  5. Abstract:
  6. Procedures necessary to access CMOS/ECMOS information.
  7. Author:
  8. David Risner (o-ncrdr) 20 Apr 1992
  9. Revision History:
  10. Landy Wang (corollary!landy) 04 Dec 1992
  11. - Move much code from ixclock.asm to here so different HALs
  12. can reuse the common functionality.
  13. Forrest Foltz (forrestf) 24-Oct-2000
  14. Ported ixcmos.asm to ixcmos.c
  15. --*/
  16. #include "halcmn.h"
  17. ULONG HalpHardwareLockFlags;
  18. //
  19. // Module-specific types
  20. //
  21. typedef UCHAR (*READ_CMOS_CHAR)(ULONG Address);
  22. typedef VOID (*WRITE_CMOS_CHAR)(ULONG Address, UCHAR Data);
  23. typedef struct _CMOS_BUS_PARAMETERS {
  24. ULONG MaximumAddress;
  25. READ_CMOS_CHAR ReadFunction;
  26. WRITE_CMOS_CHAR WriteFunction;
  27. } CMOS_BUS_PARAMETERS, *PCMOS_BUS_PARAMETERS;
  28. //
  29. // External data
  30. //
  31. extern KSPIN_LOCK HalpSystemHardwareLock;
  32. //
  33. // Local prototypes
  34. //
  35. UCHAR
  36. HalpCmosReadByte(
  37. ULONG Address
  38. );
  39. VOID
  40. HalpCmosWriteByte(
  41. ULONG Address,
  42. UCHAR Data
  43. );
  44. UCHAR
  45. HalpECmosReadByte(
  46. ULONG Address
  47. );
  48. VOID
  49. HalpECmosWriteByte(
  50. ULONG Address,
  51. UCHAR Data
  52. );
  53. UCHAR
  54. HalpGetCmosCenturyByte (
  55. VOID
  56. );
  57. ULONG
  58. HalpGetSetCmosData (
  59. IN ULONG SourceLocation,
  60. IN ULONG SourceAddress,
  61. IN PVOID ReturnBuffer,
  62. IN ULONG ByteCount,
  63. IN BOOLEAN Write
  64. );
  65. VOID
  66. HalpSetCmosCenturyByte (
  67. UCHAR Century
  68. );
  69. //
  70. // Local data
  71. //
  72. //
  73. // Describes each of the CMOS types
  74. //
  75. CMOS_BUS_PARAMETERS HalpCmosBusParameterTable[] = {
  76. { 0xFF, HalpCmosReadByte, HalpCmosWriteByte },
  77. { 0xFFFF, HalpECmosReadByte, HalpECmosWriteByte }
  78. };
  79. //
  80. // Contains the offset to the CMOS century information
  81. //
  82. ULONG HalpCmosCenturyOffset;
  83. //
  84. // HalpRebootNow is a reboot vector. Set in an MP system to cause any
  85. // processors that might be looping in HalpAcquireCmosSpinLock to transfer
  86. // control to the vector in HalpRebootNow.
  87. //
  88. VOID (*HalpRebootNow)(VOID);
  89. ULONG
  90. HalpGetCmosData (
  91. IN ULONG SourceLocation,
  92. IN ULONG SourceAddress,
  93. IN PVOID ReturnBuffer,
  94. IN ULONG ByteCount
  95. )
  96. /*++
  97. Routine Description:
  98. This routine reads the requested number of bytes from CMOS/ECMOS and
  99. stores the data read into the supplied buffer in system memory. If
  100. the requested data amount exceeds the allowable extent of the source
  101. location, the return data is truncated.
  102. Arguments:
  103. SourceLocation - where data is to be read from CMOS or ECMOS
  104. 0 - CMOS, 1 - ECMOS
  105. SourceAddress - address in CMOS/ECMOS where data is to be transferred
  106. ReturnBuffer - address in system memory for data to transfer
  107. ByteCount - number of bytes to be read
  108. Returns:
  109. Number of byte actually read.
  110. --*/
  111. {
  112. return HalpGetSetCmosData(SourceLocation,
  113. SourceAddress,
  114. ReturnBuffer,
  115. ByteCount,
  116. FALSE);
  117. }
  118. ULONG
  119. HalpSetCmosData (
  120. IN ULONG SourceLocation,
  121. IN ULONG SourceAddress,
  122. IN PVOID ReturnBuffer,
  123. IN ULONG ByteCount
  124. )
  125. /*++
  126. Routine Description:
  127. This routine writes the requested number of bytes to CMOS/ECMOS.
  128. Arguments:
  129. SourceLocation - where data is to be written from CMOS or ECMOS
  130. 0 - CMOS, 1 - ECMOS
  131. SourceAddress - address in CMOS/ECMOS where data is to be transferred
  132. ReturnBuffer - address in system memory for data to transfer
  133. ByteCount - number of bytes to be written
  134. Returns:
  135. Number of byte actually read.
  136. --*/
  137. {
  138. return HalpGetSetCmosData(SourceLocation,
  139. SourceAddress,
  140. ReturnBuffer,
  141. ByteCount,
  142. TRUE);
  143. }
  144. ULONG
  145. HalpGetSetCmosData (
  146. IN ULONG SourceLocation,
  147. IN ULONG RangeStart,
  148. IN PVOID Buffer,
  149. IN ULONG ByteCount,
  150. IN BOOLEAN Write
  151. )
  152. /*++
  153. Routine Description:
  154. This routine reads the requested number of bytes from CMOS/ECMOS and
  155. stores the data read into the supplied buffer in system memory. If
  156. the requested data amount exceeds the allowable extent of the source
  157. location, the return data is truncated.
  158. Arguments:
  159. SourceLocation - where data is to be read from CMOS or ECMOS
  160. 0 - CMOS, 1 - ECMOS
  161. RangeStart - address in CMOS/ECMOS where data is to be transferred
  162. Buffer - address in system memory for data to transfer
  163. ByteCount - number of bytes to be transferred
  164. Write - Indicates whether the operation is a read or a write
  165. Returns:
  166. Number of byte actually transferred
  167. --*/
  168. {
  169. ULONG address;
  170. PCHAR buffer;
  171. ULONG last;
  172. PCMOS_BUS_PARAMETERS cmosParameters;
  173. //
  174. // Validate the "bus type" and get a pointer to the parameters
  175. // for the corresponding CMOS "bus".
  176. //
  177. if (SourceLocation != 0 && SourceLocation != 1) {
  178. return 0;
  179. }
  180. cmosParameters = &HalpCmosBusParameterTable[SourceLocation];
  181. //
  182. // Limit the range of bytes to that which the cmos bus can accomodate.
  183. //
  184. address = RangeStart;
  185. buffer = Buffer;
  186. last = address + ByteCount - 1;
  187. if (last > cmosParameters->MaximumAddress) {
  188. last = cmosParameters->MaximumAddress;
  189. }
  190. //
  191. // Take the cmos spin lock, perform the transfer, and release the lock.
  192. //
  193. HalpAcquireCmosSpinLock();
  194. while (address <= last) {
  195. if (Write == FALSE) {
  196. *buffer = cmosParameters->ReadFunction(address);
  197. } else {
  198. cmosParameters->WriteFunction(address,*buffer);
  199. }
  200. address += 1;
  201. buffer += 1;
  202. }
  203. HalpReleaseCmosSpinLock();
  204. //
  205. // Calculate and return the number of bytes trasferred.
  206. //
  207. return last - RangeStart;
  208. }
  209. UCHAR
  210. HalpCmosReadByte(
  211. ULONG Address
  212. )
  213. /*++
  214. Routine Description:
  215. This routine reads a single byte from cmos.
  216. Arguments:
  217. Address - The CMOS address from which to retrieve the byte.
  218. Returns:
  219. The byte that was read.
  220. --*/
  221. {
  222. return CMOS_READ((UCHAR)Address);
  223. }
  224. VOID
  225. HalpCmosWriteByte(
  226. ULONG Address,
  227. UCHAR Data
  228. )
  229. /*++
  230. Routine Description:
  231. This routine writes a single byte to cmos.
  232. Arguments:
  233. Address - The CMOS address at which to write the byte
  234. Data - The byte to write
  235. Returns:
  236. Nothing
  237. --*/
  238. {
  239. CMOS_WRITE((UCHAR)Address,Data);
  240. }
  241. UCHAR
  242. HalpECmosReadByte(
  243. ULONG Address
  244. )
  245. /*++
  246. Routine Description:
  247. This routine reads a single byte from extended cmos (ECMOS).
  248. Arguments:
  249. Address - The CMOS address from which to retrieve the byte.
  250. Returns:
  251. The byte that was read.
  252. --*/
  253. {
  254. UCHAR data;
  255. WRITE_PORT_USHORT_PAIR (ECMOS_ADDRESS_PORT_LSB,
  256. ECMOS_ADDRESS_PORT_MSB,
  257. (USHORT)Address);
  258. IO_DELAY();
  259. data = READ_PORT_UCHAR(ECMOS_DATA_PORT);
  260. IO_DELAY();
  261. return data;
  262. }
  263. VOID
  264. HalpECmosWriteByte(
  265. ULONG Address,
  266. UCHAR Data
  267. )
  268. /*++
  269. Routine Description:
  270. This routine writes a single byte to extended cmos (ECMOS).
  271. Arguments:
  272. Address - The CMOS address at which to write the byte
  273. Data - The byte to write
  274. Returns:
  275. Nothing
  276. --*/
  277. {
  278. WRITE_PORT_USHORT_PAIR (ECMOS_ADDRESS_PORT_LSB,
  279. ECMOS_ADDRESS_PORT_MSB,
  280. (USHORT)Address);
  281. IO_DELAY();
  282. WRITE_PORT_UCHAR(ECMOS_DATA_PORT,Data);
  283. IO_DELAY();
  284. }
  285. VOID
  286. HalpReadCmosTime(
  287. PTIME_FIELDS TimeFields
  288. )
  289. /*++
  290. Routine Description:
  291. This routine reads current time from CMOS memory and stores it
  292. in the TIME_FIELDS structure passed in by caller.
  293. Arguments:
  294. TimeFields - A pointer to the TIME_FIELDS structure.
  295. Return Value:
  296. None.
  297. --*/
  298. {
  299. USHORT year;
  300. HalpAcquireCmosSpinLockAndWait();
  301. //
  302. // The RTC is only accurate to within one second. So add a
  303. // half a second so that we are closer, on average, to the right
  304. // answer.
  305. //
  306. TimeFields->Milliseconds = 500;
  307. TimeFields->Second = CMOS_READ_BCD(RTC_OFFSET_SECOND);
  308. TimeFields->Minute = CMOS_READ_BCD(RTC_OFFSET_MINUTE);
  309. TimeFields->Hour = CMOS_READ_BCD(RTC_OFFSET_HOUR);
  310. TimeFields->Weekday = CMOS_READ_BCD(RTC_OFFSET_DAY_OF_WEEK);
  311. TimeFields->Day = CMOS_READ_BCD(RTC_OFFSET_DATE_OF_MONTH);
  312. year = BCD_TO_BIN(HalpGetCmosCenturyByte());
  313. year = year * 100 + CMOS_READ_BCD(RTC_OFFSET_YEAR);
  314. if (year >= 1900 && year < 1920) {
  315. //
  316. // Compensate for the century field
  317. //
  318. year += 100;
  319. }
  320. TimeFields->Year = year;
  321. HalpReleaseCmosSpinLock();
  322. }
  323. VOID
  324. HalpWriteCmosTime (
  325. PTIME_FIELDS TimeFields
  326. )
  327. /*++
  328. Routine Description:
  329. This routine writes current time from TIME_FILEDS structure
  330. to CMOS memory.
  331. Arguments:
  332. TimeFields - A pointer to the TIME_FIELDS structure.
  333. Return Value:
  334. None.
  335. --*/
  336. {
  337. ULONG year;
  338. HalpAcquireCmosSpinLockAndWait();
  339. CMOS_WRITE_BCD(RTC_OFFSET_SECOND,(UCHAR)TimeFields->Second);
  340. CMOS_WRITE_BCD(RTC_OFFSET_MINUTE,(UCHAR)TimeFields->Minute);
  341. CMOS_WRITE_BCD(RTC_OFFSET_HOUR,(UCHAR)TimeFields->Hour);
  342. CMOS_WRITE_BCD(RTC_OFFSET_DAY_OF_WEEK,(UCHAR)TimeFields->Weekday);
  343. CMOS_WRITE_BCD(RTC_OFFSET_DATE_OF_MONTH,(UCHAR)TimeFields->Month);
  344. year = TimeFields->Year;
  345. if (year > 9999) {
  346. year = 9999;
  347. }
  348. HalpSetCmosCenturyByte(BIN_TO_BCD((UCHAR)(year / 100)));
  349. CMOS_WRITE_BCD(RTC_OFFSET_YEAR,(UCHAR)(year % 100));
  350. HalpReleaseCmosSpinLock();
  351. }
  352. VOID
  353. HalpAcquireCmosSpinLockAndWait (
  354. VOID
  355. )
  356. /*++
  357. Routine Description:
  358. This routine acquires the CMOS spinlock, then waits for the CMOS
  359. BUSY flag to be clear.
  360. Arguments:
  361. None.
  362. Return Value:
  363. None.
  364. --*/
  365. {
  366. ULONG count;
  367. ULONG value;
  368. //
  369. // Acquire the cmos spinlock and wait until it is not busy. While
  370. // waiting, periodically release and re-acquire the spinlock.
  371. //
  372. HalpAcquireCmosSpinLock();
  373. count = 0;
  374. while (TRUE) {
  375. value = CMOS_READ(CMOS_STATUS_A);
  376. if ((value & CMOS_STATUS_BUSY) == 0) {
  377. return;
  378. }
  379. count += 1;
  380. if (count == 100) {
  381. count = 0;
  382. HalpReleaseCmosSpinLock();
  383. HalpAcquireCmosSpinLock();
  384. }
  385. }
  386. }
  387. VOID
  388. HalpReleaseCmosSpinLock (
  389. VOID
  390. )
  391. /*++
  392. Routine Description:
  393. This routine acquires the spin lock used to protect access to various
  394. pieces of hardware.
  395. Arguments:
  396. None
  397. Returns:
  398. Nothing
  399. --*/
  400. {
  401. ULONG flags;
  402. flags = HalpHardwareLockFlags;
  403. KeReleaseSpinLockFromDpcLevel(&HalpSystemHardwareLock);
  404. HalpRestoreInterrupts(flags);
  405. }
  406. VOID
  407. HalpAcquireCmosSpinLock (
  408. VOID
  409. )
  410. /*++
  411. Routine Description:
  412. This routine acquires the spin lock used to protect access to various
  413. pieces of hardware.
  414. Arguments:
  415. None
  416. Returns:
  417. Nothing
  418. --*/
  419. {
  420. BOOLEAN acquired;
  421. ULONG flags;
  422. KIRQL oldIrql;
  423. #if defined(NT_UP)
  424. HalpHardwareLockFlags = HalpDisableInterrupts();
  425. #else
  426. while (TRUE) {
  427. flags = HalpDisableInterrupts();
  428. acquired = KeTryToAcquireSpinLockAtDpcLevel(&HalpSystemHardwareLock);
  429. if (acquired != FALSE) {
  430. break;
  431. }
  432. HalpRestoreInterrupts(flags);
  433. while (KeTestSpinLock(&HalpSystemHardwareLock) == FALSE) {
  434. if (HalpRebootNow != NULL) {
  435. HalpRebootNow();
  436. }
  437. PAUSE_PROCESSOR;
  438. }
  439. }
  440. HalpHardwareLockFlags = flags;
  441. #endif
  442. }
  443. VOID
  444. HalpAcquireSystemHardwareSpinLock (
  445. VOID
  446. )
  447. /*++
  448. Routine Description:
  449. This routine acquires the spin lock used to protect access to various
  450. pieces of hardware. It is a synonym of HalpAcquireCmosSpinLock().
  451. Arguments:
  452. None
  453. Returns:
  454. Nothing
  455. --*/
  456. {
  457. HalpAcquireCmosSpinLock();
  458. }
  459. VOID
  460. HalpReleaseSystemHardwareSpinLock (
  461. VOID
  462. )
  463. /*++
  464. Routine Description:
  465. This routine releases the spin lock used to protect access to various
  466. pieces of hardware. It is a synonym of HalpReleaseCmosSpinLock().
  467. Arguments:
  468. None
  469. Returns:
  470. Nothing
  471. --*/
  472. {
  473. HalpReleaseCmosSpinLock();
  474. }
  475. UCHAR
  476. HalpGetCmosCenturyByte (
  477. VOID
  478. )
  479. /*++
  480. Routine Description:
  481. This routine retrieves the century byte from the CMOS.
  482. N.B. The cmos spinlock must be acquired before calling this function.
  483. Arguments:
  484. None
  485. Returns:
  486. The century byte.
  487. --*/
  488. {
  489. UCHAR value;
  490. UCHAR oldStatus;
  491. UCHAR centuryByte;
  492. //
  493. // Make sure the century offset is initialized
  494. //
  495. ASSERT(HalpCmosCenturyOffset != 0);
  496. if ((HalpCmosCenturyOffset & CMOS_BANK_1) != 0) {
  497. //
  498. // Perform a bank 1 read
  499. //
  500. oldStatus = CMOS_READ(CMOS_STATUS_A);
  501. value = oldStatus | CMOS_STATUS_BANK1;
  502. CMOS_WRITE(CMOS_STATUS_A,value);
  503. centuryByte = CMOS_READ((UCHAR)HalpCmosCenturyOffset);
  504. CMOS_WRITE(CMOS_STATUS_A,oldStatus);
  505. } else {
  506. centuryByte = CMOS_READ((UCHAR)HalpCmosCenturyOffset);
  507. }
  508. return centuryByte;
  509. }
  510. VOID
  511. HalpSetCmosCenturyByte (
  512. UCHAR Century
  513. )
  514. /*++
  515. Routine Description:
  516. This routine sets the century byte in the CMOS.
  517. N.B. The cmos spinlock must be acquired before calling this function.
  518. Arguments:
  519. Century - The century byte to set
  520. Returns:
  521. Nothing
  522. --*/
  523. {
  524. UCHAR value;
  525. UCHAR oldStatus;
  526. //
  527. // Make sure the century offset is initialized
  528. //
  529. ASSERT(HalpCmosCenturyOffset != 0);
  530. if ((HalpCmosCenturyOffset & CMOS_BANK_1) != 0) {
  531. //
  532. // Perform a bank 1 write
  533. //
  534. oldStatus = CMOS_READ(CMOS_STATUS_A);
  535. value = oldStatus | CMOS_STATUS_BANK1;
  536. CMOS_WRITE(CMOS_STATUS_A,value);
  537. CMOS_WRITE((UCHAR)HalpCmosCenturyOffset,Century);
  538. CMOS_WRITE(CMOS_STATUS_A,oldStatus);
  539. } else {
  540. CMOS_WRITE((UCHAR)HalpCmosCenturyOffset,Century);
  541. }
  542. }
  543. VOID
  544. HalpFlushTLB (
  545. VOID
  546. )
  547. /*++
  548. Routine Description:
  549. Flushes the current TLB.
  550. Arguments:
  551. None.
  552. Return Value:
  553. None.
  554. --*/
  555. {
  556. ULONG flags;
  557. PKPCR pcr;
  558. PKPRCB prcb;
  559. ULONG64 cr3;
  560. ULONG64 cr4;
  561. ULONG64 oldCr4;
  562. flags = HalpDisableInterrupts();
  563. cr3 = ReadCR3();
  564. pcr = KeGetPcr();
  565. prcb = pcr->CurrentPrcb;
  566. //
  567. // Note: the original code (ixcmos.asm) had differing behavior based
  568. // on whether this was CPU 0. That behavior is mimicked here.
  569. // It would be good to find out why this is done.
  570. //
  571. if (prcb->Number == 0) {
  572. WriteCR3(cr3);
  573. } else {
  574. cr4 = ReadCR4();
  575. WriteCR4(cr4 & ~CR4_PGE);
  576. WriteCR3(cr3);
  577. WriteCR4(cr4);
  578. }
  579. HalpRestoreInterrupts(flags);
  580. }
  581. VOID
  582. HalpCpuID (
  583. IN ULONG Function,
  584. OUT PULONG Eax,
  585. OUT PULONG Ebx,
  586. OUT PULONG Ecx,
  587. OUT PULONG Edx
  588. )
  589. /*++
  590. Routine Description:
  591. This function executes a cpu id and returns the result as found in
  592. registers eax, ebx, ecx and edx.
  593. Arguments:
  594. Function - supplies the CPUID function to execute.
  595. Eax - supplies a pointer to the storage to contain the contents of eax.
  596. Eax - supplies a pointer to the storage to contain the contents of ebx.
  597. Eax - supplies a pointer to the storage to contain the contents of ecx.
  598. Eax - supplies a pointer to the storage to contain the contents of edx.
  599. Return Value:
  600. None.
  601. --*/
  602. {
  603. CPU_INFO cpuInfo;
  604. KiCpuId (Function,&cpuInfo);
  605. *Eax = cpuInfo.Eax;
  606. *Ebx = cpuInfo.Ebx;
  607. *Ecx = cpuInfo.Ecx;
  608. *Edx = cpuInfo.Edx;
  609. }