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.

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