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.

720 lines
19 KiB

  1. /*++
  2. Copyright (c) 2000 Microsoft Corporation
  3. Module Name:
  4. chiphacks.c
  5. Abstract:
  6. Implements utilities for finding and hacking
  7. various chipsets
  8. Author:
  9. Jake Oshins (jakeo) 10/02/2000
  10. Environment:
  11. Kernel mode only.
  12. Revision History:
  13. --*/
  14. #include "chiphacks.h"
  15. #include "stdio.h"
  16. #ifdef ALLOC_PRAGMA
  17. #pragma alloc_text(PAGE, HalpGetChipHacks)
  18. #pragma alloc_text(PAGE, HalpDoesChipNeedHack)
  19. #pragma alloc_text(PAGE, HalpSetAcpiIrqHack)
  20. #pragma alloc_text(PAGELK, HalpClearSlpSmiStsInICH)
  21. #endif
  22. NTSTATUS
  23. HalpGetChipHacks(
  24. IN USHORT VendorId,
  25. IN USHORT DeviceId,
  26. IN ULONG Ssid OPTIONAL,
  27. OUT ULONG *HackFlags
  28. )
  29. /*++
  30. Routine Description:
  31. This routine looks under HKLM\System\CurrentControlSet\Control\HAL
  32. to see if there is an entry for the PCI device being
  33. described. If so, it returns a set of associated flags.
  34. Arguments:
  35. VendorId - PCI Vendor ID of chip
  36. DeviceId - PCI Device ID of chip
  37. Ssid - PCI subsystem ID of chip, if applicable
  38. HackFlags - value read from registry
  39. --*/
  40. {
  41. OBJECT_ATTRIBUTES ObjectAttributes;
  42. UNICODE_STRING UnicodeString;
  43. STRING AString;
  44. NTSTATUS Status;
  45. HANDLE BaseHandle = NULL;
  46. HANDLE Handle = NULL;
  47. ULONG disposition;
  48. ULONG Length;
  49. CHAR buffer[20] = {0};
  50. struct {
  51. KEY_VALUE_PARTIAL_INFORMATION Inf;
  52. UCHAR Data[3];
  53. } PartialInformation;
  54. PAGED_CODE();
  55. //
  56. // Open current control set
  57. //
  58. RtlInitUnicodeString (&UnicodeString,
  59. L"\\REGISTRY\\MACHINE\\SYSTEM\\CURRENTCONTROLSET\\Control");
  60. InitializeObjectAttributes(&ObjectAttributes,
  61. &UnicodeString,
  62. OBJ_CASE_INSENSITIVE,
  63. NULL,
  64. (PSECURITY_DESCRIPTOR) NULL);
  65. Status = ZwOpenKey (&BaseHandle,
  66. KEY_READ,
  67. &ObjectAttributes);
  68. if (!NT_SUCCESS(Status)) {
  69. return STATUS_UNSUCCESSFUL;
  70. }
  71. // Get the right key
  72. RtlInitUnicodeString (&UnicodeString,
  73. L"HAL");
  74. InitializeObjectAttributes(&ObjectAttributes,
  75. &UnicodeString,
  76. OBJ_CASE_INSENSITIVE,
  77. BaseHandle,
  78. (PSECURITY_DESCRIPTOR) NULL);
  79. Status = ZwCreateKey (&Handle,
  80. KEY_READ,
  81. &ObjectAttributes,
  82. 0,
  83. (PUNICODE_STRING) NULL,
  84. REG_OPTION_NON_VOLATILE,
  85. &disposition);
  86. if(!NT_SUCCESS(Status)) {
  87. goto GetChipHacksCleanup;
  88. }
  89. //
  90. // Look in the registry to see if the registry
  91. // contains an entry for this chip. The first
  92. // step is to build a string that defines the chip.
  93. //
  94. if (Ssid) {
  95. sprintf(buffer, "%04x%04x%08x",
  96. VendorId,
  97. DeviceId,
  98. Ssid);
  99. } else {
  100. sprintf(buffer, "%04x%04x",
  101. VendorId,
  102. DeviceId);
  103. }
  104. RtlInitAnsiString(&AString, buffer);
  105. RtlUpperString(&AString, &AString);
  106. Status = STATUS_NOT_FOUND;
  107. if (NT_SUCCESS(RtlAnsiStringToUnicodeString(&UnicodeString,
  108. &AString,
  109. TRUE))) {
  110. Status = ZwQueryValueKey (Handle,
  111. &UnicodeString,
  112. KeyValuePartialInformation,
  113. &PartialInformation,
  114. sizeof (PartialInformation),
  115. &Length);
  116. if (NT_SUCCESS(Status)) {
  117. //
  118. // We found a value in the registry
  119. // that corresponds with the chip
  120. // we just ran across.
  121. //
  122. *HackFlags = *((PULONG)(PartialInformation.Inf.Data));
  123. }
  124. RtlFreeUnicodeString(&UnicodeString);
  125. }
  126. GetChipHacksCleanup:
  127. if (Handle) ZwClose (Handle);
  128. if (BaseHandle) ZwClose (BaseHandle);
  129. return Status;
  130. }
  131. BOOLEAN
  132. HalpDoesChipNeedHack(
  133. IN USHORT VendorId,
  134. IN USHORT DeviceId,
  135. IN ULONG Ssid OPTIONAL,
  136. IN ULONG HackFlags
  137. )
  138. /*++
  139. Routine Description:
  140. This routine is a wrapper for HalpGetChipHacks.
  141. Arguments:
  142. VendorId - PCI Vendor ID of chip
  143. DeviceId - PCI Device ID of chip
  144. Ssid - PCI subsystem ID of chip, if applicable
  145. HackFlags - value to compare with registry
  146. --*/
  147. {
  148. ULONG flagsFromRegistry;
  149. NTSTATUS status;
  150. PAGED_CODE();
  151. status = HalpGetChipHacks(VendorId,
  152. DeviceId,
  153. Ssid,
  154. &flagsFromRegistry);
  155. if (NT_SUCCESS(status)) {
  156. if (HackFlags & flagsFromRegistry) {
  157. return TRUE;
  158. }
  159. }
  160. return FALSE;
  161. }
  162. VOID
  163. HalpStopOhciInterrupt(
  164. ULONG BusNumber,
  165. PCI_SLOT_NUMBER SlotNumber
  166. )
  167. /*++
  168. Routine Description:
  169. This routine shuts off the interrupt from an OHCI
  170. USB controller. This may be necessary because
  171. a BIOS may enable the PCI interrupt from a USB controller
  172. in order to do "legacy USB support" where it translates
  173. USB keyboard and mouse traffic into something that DOS
  174. can use. (Our loader and all of Win9x approximate DOS.)
  175. Arguments:
  176. BusNumber - Bus number of OHCI controller
  177. SlotNumber - Slot number of OHCI controller
  178. Note:
  179. This routine also may need to be called at raised IRQL
  180. when returning from hibernation.
  181. --*/
  182. {
  183. //
  184. // 7.1.2 HcControl Register
  185. //
  186. #define HcCtrl_InterruptRouting 0x00000100L
  187. //
  188. // 7.1.3 HcCommandStatus Register
  189. //
  190. #define HcCmd_OwnershipChangeRequest 0x00000008L
  191. //
  192. // 7.1.4 HcInterrruptStatus Register
  193. // 7.1.5 HcInterruptEnable Register
  194. // 7.1.6 HcInterruptDisable Register
  195. //
  196. #define HcInt_SchedulingOverrun 0x00000001L
  197. #define HcInt_WritebackDoneHead 0x00000002L
  198. #define HcInt_StartOfFrame 0x00000004L
  199. #define HcInt_ResumeDetected 0x00000008L
  200. #define HcInt_UnrecoverableError 0x00000010L
  201. #define HcInt_FrameNumberOverflow 0x00000020L
  202. #define HcInt_RootHubStatusChange 0x00000040L
  203. #define HcInt_OwnershipChange 0x40000000L
  204. #define HcInt_MasterInterruptEnable 0x80000000L
  205. //
  206. // Host Controler Hardware Registers as accessed in memory
  207. //
  208. struct {
  209. // 0 0x00 - 0,4,8,c
  210. ULONG HcRevision;
  211. ULONG HcControl;
  212. ULONG HcCommandStatus;
  213. ULONG HcInterruptStatus; // use HcInt flags below
  214. // 1 0x10
  215. ULONG HcInterruptEnable; // use HcInt flags below
  216. ULONG HcInterruptDisable; // use HcInt flags below
  217. } volatile *ohci;
  218. PCI_COMMON_CONFIG PciHeader;
  219. PHYSICAL_ADDRESS BarAddr;
  220. HalGetBusData (
  221. PCIConfiguration,
  222. BusNumber,
  223. SlotNumber.u.AsULONG,
  224. &PciHeader,
  225. PCI_COMMON_HDR_LENGTH
  226. );
  227. if (PciHeader.Command & PCI_ENABLE_MEMORY_SPACE) {
  228. //
  229. // The controller is enabled.
  230. //
  231. BarAddr.HighPart = 0;
  232. BarAddr.LowPart = (PciHeader.u.type0.BaseAddresses[0] & PCI_ADDRESS_MEMORY_ADDRESS_MASK);
  233. if (BarAddr.LowPart != 0) {
  234. //
  235. // The BAR is populated. So map an address for it.
  236. //
  237. ohci = HalpMapPhysicalMemory64(BarAddr, 2);
  238. //
  239. // Set the interrupt disable bit, but disable SMM control of the
  240. // host controller first.
  241. //
  242. if (ohci) {
  243. if (ohci->HcControl & HcCtrl_InterruptRouting) {
  244. if ((ohci->HcControl == HcCtrl_InterruptRouting) &&
  245. (ohci->HcInterruptEnable == 0)) {
  246. // Major assumption: If HcCtrl_InterruptRouting is
  247. // set but no other bits in HcControl are set, i.e.
  248. // HCFS==UsbReset, and no interrupts are enabled, then
  249. // assume that the BIOS is not actually using the host
  250. // controller. In this case just clear the erroneously
  251. // set HcCtrl_InterruptRouting.
  252. //
  253. ohci->HcControl = 0; // Clear HcCtrl_InterruptRouting
  254. } else {
  255. ULONG msCount;
  256. //
  257. // A SMM driver does own the HC, it will take some time
  258. // to get the SMM driver to relinquish control of the
  259. // HC. We will ping the SMM driver, and then wait
  260. // repeatedly until the SMM driver has relinquished
  261. // control of the HC.
  262. //
  263. // Disable the root hub status change to prevent an
  264. // unhandled interrupt from being asserted after
  265. // handoff. (Not clear what platforms really require
  266. // this...)
  267. //
  268. ohci->HcInterruptDisable = HcInt_RootHubStatusChange;
  269. // The HcInt_MasterInterruptEnable and HcInt_OwnershipChange
  270. // bits should already be set, but make sure they are.
  271. //
  272. ohci->HcInterruptEnable = HcInt_MasterInterruptEnable |
  273. HcInt_OwnershipChange;
  274. // Ping the SMM driver to relinquish control of the HC.
  275. //
  276. ohci->HcCommandStatus = HcCmd_OwnershipChangeRequest;
  277. // Wait 500ms for the SMM driver to relinquish control.
  278. //
  279. for (msCount = 0; msCount < 500; msCount++) {
  280. KeStallExecutionProcessor(1000);
  281. if (!(ohci->HcControl & HcCtrl_InterruptRouting)) {
  282. // SMM driver has relinquished control.
  283. break;
  284. }
  285. }
  286. }
  287. }
  288. ohci->HcInterruptDisable = HcInt_MasterInterruptEnable;
  289. //
  290. // Unmap the virtual address.
  291. //
  292. HalpUnmapVirtualAddress((PVOID)ohci, 2);
  293. }
  294. }
  295. }
  296. }
  297. VOID
  298. HalpStopUhciInterrupt(
  299. ULONG BusNumber,
  300. PCI_SLOT_NUMBER SlotNumber,
  301. BOOLEAN ResetHostController
  302. )
  303. /*++
  304. Routine Description:
  305. This routine shuts off the interrupt from an UHCI
  306. USB controller. This may be necessary because
  307. a BIOS may enable the PCI interrupt from a USB controller
  308. in order to do "legacy USB support" where it translates
  309. USB keyboard and mouse traffic into something that DOS
  310. can use. (Our loader and all of Win9x approximate DOS.)
  311. Arguments:
  312. BusNumber - Bus number of UHCI controller
  313. SlotNumber - Slot number of UHCI controller
  314. Note:
  315. This routine also may need to be called at raised IRQL
  316. when returning from hibernation.
  317. --*/
  318. {
  319. ULONG Usb = 0;
  320. USHORT cmd;
  321. PCI_COMMON_CONFIG PciHeader;
  322. if (ResetHostController) {
  323. //
  324. // Clear out the host controller legacy support register
  325. // prior to handing the USB to the USB driver, because we
  326. // don't want any SMIs being generated.
  327. //
  328. Usb = 0x0000;
  329. HalSetBusDataByOffset (
  330. PCIConfiguration,
  331. BusNumber,
  332. SlotNumber.u.AsULONG,
  333. &Usb,
  334. 0xc0,
  335. sizeof(ULONG)
  336. );
  337. //
  338. // Put the USB controller into reset, as it may share it's
  339. // PIRQD line with another USB controller on the chipset.
  340. // This is not a problem unless the bios is running in legacy
  341. // mode and causing interrupts. In this case, the minute PIRQD
  342. // gets flipped by one usbuhci controller, the other could
  343. // start generating unhandled interrupts and hang the system.
  344. // This is the case with the ICH2 chipset.
  345. //
  346. HalGetBusData (
  347. PCIConfiguration,
  348. BusNumber,
  349. SlotNumber.u.AsULONG,
  350. &PciHeader,
  351. PCI_COMMON_HDR_LENGTH
  352. );
  353. if (PciHeader.Command & PCI_ENABLE_IO_SPACE) {
  354. //
  355. // The controller is enabled.
  356. //
  357. Usb = (PciHeader.u.type0.BaseAddresses[4] & PCI_ADDRESS_IO_ADDRESS_MASK);
  358. if (Usb != 0 && Usb < 0x0000ffff) {
  359. // Valid I/O address.
  360. //
  361. // If we are returning from suspend, don't put the controller
  362. // into reset.
  363. //
  364. cmd = READ_PORT_USHORT(UlongToPtr(Usb));
  365. if (!(cmd & 0x0008)) {
  366. //
  367. // Put the controller in reset. Usbuhci will take it out of reset
  368. // when it grabs it.
  369. //
  370. cmd = 0x0004;
  371. WRITE_PORT_USHORT(UlongToPtr(Usb), cmd);
  372. //
  373. // Wait 10ms and then take the controller out of reset.
  374. //
  375. KeStallExecutionProcessor(10000);
  376. cmd &= 0x0000;
  377. WRITE_PORT_USHORT(UlongToPtr(Usb), cmd);
  378. }
  379. }
  380. }
  381. } else {
  382. //
  383. // Shut off the interrupt for the USB controller, as it
  384. // is very frequently the reason that the machine freezes
  385. // during boot. Anding the register with ~0xbf00 clears bit
  386. // 13, PIRQ Enable, which is the whole point. The rest of
  387. // the bits just avoid writing registers that are "write
  388. // one to clear."
  389. //
  390. HalGetBusDataByOffset (
  391. PCIConfiguration,
  392. BusNumber,
  393. SlotNumber.u.AsULONG,
  394. &Usb,
  395. 0xc0,
  396. sizeof(ULONG)
  397. );
  398. Usb &= ~0xbf00;
  399. HalSetBusDataByOffset (
  400. PCIConfiguration,
  401. BusNumber,
  402. SlotNumber.u.AsULONG,
  403. &Usb,
  404. 0xc0,
  405. sizeof(ULONG)
  406. );
  407. }
  408. }
  409. VOID
  410. HalpWhackICHUsbSmi(
  411. ULONG BusNumber,
  412. PCI_SLOT_NUMBER SlotNumber
  413. )
  414. {
  415. ULONG PmBase = 0;
  416. ULONG SmiEn;
  417. //
  418. // ICH (and the like) have the PM_BASE register in
  419. // config space at offset 0x40.
  420. //
  421. HalGetBusDataByOffset (
  422. PCIConfiguration,
  423. BusNumber,
  424. SlotNumber.u.AsULONG,
  425. &PmBase,
  426. 0x40,
  427. 4);
  428. if (!PmBase) {
  429. return;
  430. }
  431. PmBase &= ~PCI_ADDRESS_IO_SPACE;
  432. //
  433. // At PM_BASE + 0x30 in I/O space, we have the SMI_EN
  434. // register.
  435. //
  436. SmiEn = READ_PORT_ULONG((PULONG)(((PUCHAR)PmBase) + 0x30));
  437. //
  438. // Clear bit 3, LEGACY_USB_EN.
  439. //
  440. SmiEn &= ~8;
  441. WRITE_PORT_ULONG((PULONG)(((PUCHAR)PmBase) + 0x30), SmiEn);
  442. return;
  443. }
  444. VOID
  445. HalpSetAcpiIrqHack(
  446. ULONG Value
  447. )
  448. /*++
  449. Routine Description:
  450. This routine sets the registry key that causes the
  451. ACPI driver to attempt to put all PCI interrupts
  452. on a single IRQ. While putting this hack here may
  453. seem strange, the hack has to be applied before
  454. an INFs are processed. And so much of the chip
  455. recognizing code already exists here, duplicating
  456. it in the ACPI driver would bloat the code and cause
  457. us to do another PCI bus scan and registry search
  458. during boot.
  459. Arguments:
  460. Value - This goes in the ACPI\Parameters\IRQDistribution
  461. key.
  462. --*/
  463. {
  464. OBJECT_ATTRIBUTES ObjectAttributes;
  465. UNICODE_STRING UnicodeString;
  466. HANDLE BaseHandle = NULL;
  467. NTSTATUS status;
  468. PAGED_CODE();
  469. RtlInitUnicodeString (&UnicodeString,
  470. L"\\REGISTRY\\MACHINE\\SYSTEM\\CURRENTCONTROLSET\\Services\\ACPI\\Parameters");
  471. InitializeObjectAttributes(&ObjectAttributes,
  472. &UnicodeString,
  473. OBJ_CASE_INSENSITIVE,
  474. NULL,
  475. (PSECURITY_DESCRIPTOR) NULL);
  476. status = ZwCreateKey (&BaseHandle,
  477. KEY_WRITE,
  478. &ObjectAttributes,
  479. 0,
  480. (PUNICODE_STRING) NULL,
  481. REG_OPTION_NON_VOLATILE,
  482. NULL);
  483. if (!NT_SUCCESS(status)) {
  484. return;
  485. }
  486. RtlInitUnicodeString (&UnicodeString,
  487. L"IRQDistribution");
  488. status = ZwSetValueKey (BaseHandle,
  489. &UnicodeString,
  490. 0,
  491. REG_DWORD,
  492. &Value,
  493. sizeof(ULONG));
  494. ASSERT(NT_SUCCESS(status));
  495. ZwClose(BaseHandle);
  496. return;
  497. }
  498. VOID
  499. HalpClearSlpSmiStsInICH(
  500. VOID
  501. )
  502. {
  503. PPCI_COMMON_CONFIG PciHeader;
  504. UCHAR buffer[0x44] = {0};
  505. ULONG PmBase;
  506. UCHAR SmiSts, SmiEn;
  507. PciHeader = (PPCI_COMMON_CONFIG)&buffer;
  508. //
  509. // ASUS has a BIOS bug that will leave the
  510. // SLP_SMI_STS bit set even when the SLP_SMI_EN
  511. // bit is clear. The BIOS will furthermore
  512. // shut the machine down on the next SMI when
  513. // this occurs.
  514. //
  515. //
  516. // Check for ICH.
  517. //
  518. HalGetBusDataByOffset (
  519. PCIConfiguration,
  520. 0,
  521. 0x1f,
  522. PciHeader,
  523. 0,
  524. 0x44);
  525. if ((PciHeader->VendorID == 0x8086) &&
  526. (PciHeader->BaseClass == PCI_CLASS_BRIDGE_DEV) &&
  527. (PciHeader->SubClass == PCI_SUBCLASS_BR_ISA)) {
  528. //
  529. // This is an ICH. Offset 0x40 will have an I/O BAR
  530. // which is the PM_BASE register.
  531. //
  532. PmBase = *(PULONG)PciHeader->DeviceSpecific;
  533. PmBase &= ~PCI_ADDRESS_IO_SPACE;
  534. SmiEn = READ_PORT_UCHAR(((PUCHAR)PmBase) + 0x30);
  535. if (!(SmiEn & 0x10)) {
  536. //
  537. // The SLP_SMI_EN bit in the SMI_EN register was
  538. // clear.
  539. //
  540. SmiSts = READ_PORT_UCHAR(((PUCHAR)PmBase) + 0x34);
  541. if (SmiSts & 0x10) {
  542. //
  543. // But the SLP_SMI_STS bit was set, implying
  544. // that the ASUS BIOS is about to keel over.
  545. // Clear the bit.
  546. //
  547. WRITE_PORT_UCHAR(((PUCHAR)PmBase) + 0x34, 0x10);
  548. }
  549. }
  550. }
  551. }