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.

650 lines
16 KiB

  1. /*++
  2. Copyright (c) 1997-2000 Microsoft Corporation
  3. Module Name:
  4. device.c
  5. Abstract:
  6. This module contains functions associated with enumerating
  7. ordinary (PCI Header type 0) devices.
  8. Author:
  9. Peter Johnston (peterj) 09-Mar-1997
  10. Revision History:
  11. --*/
  12. #include "pcip.h"
  13. #ifdef ALLOC_PRAGMA
  14. #pragma alloc_text(PAGE, Device_MassageHeaderForLimitsDetermination)
  15. #pragma alloc_text(PAGE, Device_RestoreCurrent)
  16. #pragma alloc_text(PAGE, Device_SaveLimits)
  17. #pragma alloc_text(PAGE, Device_SaveCurrentSettings)
  18. #pragma alloc_text(PAGE, Device_GetAdditionalResourceDescriptors)
  19. #endif
  20. VOID
  21. Device_MassageHeaderForLimitsDetermination(
  22. IN PPCI_CONFIGURABLE_OBJECT This
  23. )
  24. /*++
  25. Description:
  26. The limits for a device are determined by writing ones to the
  27. Base Address Registers (BARs) and examining what the hardware does
  28. to them. For example, if a device requires 256 bytes of space,
  29. writing 0xffffffff to the BAR which configures this requirement
  30. tells the device to begin decoding its 256 bytes at that address.
  31. Clearly this is impossible, at most one byte can be configured
  32. at that address. The hardware will lower the address by clearing
  33. the least significant bits until the range requirement can be met.
  34. In this case, we would get 0xffffff00 back from the register when
  35. it is next read.
  36. Arguments:
  37. This - Pointer to a PCI driver "configurable" object. This
  38. object contains configuration data for the function
  39. currently being configured.
  40. Return Value:
  41. The Working configuration has been modified so that all range
  42. fields have been set to their maximum possible values.
  43. The Current configuration has been modified such that writing it
  44. to the hardware will restore the hardware to it's current (disabled)
  45. state.
  46. --*/
  47. {
  48. ULONG index;
  49. index = 0;
  50. //
  51. // PCI IDE controllers operating in legacy mode implement
  52. // the first 4 BARs but don't actually use them,... nor are
  53. // they initialized correctly, and sometimes, nothing will
  54. // change if we change them,... but we can't read them to determine
  55. // if they are implemented or not,... so,...
  56. //
  57. if (PCI_IS_LEGACY_IDE_CONTROLLER(This->PdoExtension)) {
  58. //
  59. // If both interfaces are in native mode and the BARs behave
  60. // normally. If both are in legacy mode then we should skip
  61. // the first 4 BARs. Any other combination is meaningless so
  62. // we skip the BARs and let PCIIDE take the system out when
  63. // its turn comes.
  64. //
  65. index = 4;
  66. }
  67. do {
  68. This->Working->u.type0.BaseAddresses[index] = 0xffffffff;
  69. index++;
  70. } while (index < PCI_TYPE0_ADDRESSES);
  71. //
  72. // Set the ROM to its maximum as well,... and disable it.
  73. //
  74. This->Working->u.type0.ROMBaseAddress =
  75. 0xffffffff & PCI_ADDRESS_ROM_ADDRESS_MASK;
  76. return;
  77. }
  78. VOID
  79. Device_RestoreCurrent(
  80. IN PPCI_CONFIGURABLE_OBJECT This
  81. )
  82. /*++
  83. Description:
  84. Restore any type specific fields in the original copy of config
  85. space. In the case of type 0 devices, there are none.
  86. Arguments:
  87. This - Pointer to a PCI driver "configurable" object. This
  88. object contains configuration data for the function
  89. currently being configured.
  90. Return Value:
  91. None.
  92. --*/
  93. {
  94. return;
  95. }
  96. VOID
  97. Device_SaveLimits(
  98. IN PPCI_CONFIGURABLE_OBJECT This
  99. )
  100. /*++
  101. Description:
  102. Fill in the Limit structure with a IO_RESOURCE_REQUIREMENT
  103. for each implemented BAR.
  104. Arguments:
  105. This - Pointer to a PCI driver "configurable" object. This
  106. object contains configuration data for the function
  107. currently being configured.
  108. Return Value:
  109. None.
  110. --*/
  111. {
  112. ULONG index;
  113. PIO_RESOURCE_DESCRIPTOR descriptor;
  114. PULONG bar = This->Working->u.type0.BaseAddresses;
  115. //
  116. // PCI IDE controllers operating in legacy mode implement
  117. // the first 4 BARs but don't actually use them,... nor are
  118. // they initialized correctly, and sometimes, nothing will
  119. // change if we change them,... but we can't read them to determine
  120. // if they are implemented or not,... so,...
  121. //
  122. if (PCI_IS_LEGACY_IDE_CONTROLLER(This->PdoExtension)) {
  123. //
  124. // If both interfaces are in native mode and the BARs behave
  125. // normally. If both are in legacy mode then we should skip
  126. // the first 4 BARs. Any other combination is meaningless so
  127. // we skip the BARs and let PCIIDE take the system out when
  128. // its turn comes.
  129. //
  130. //
  131. // Set the limit to zero in the first 4 bars so we will
  132. // 'detect' the bars as unimplemented.
  133. //
  134. for (index = 0; index < 4; index++) {
  135. bar[index] = 0;
  136. }
  137. }
  138. #if defined(PCI_S3_HACKS)
  139. //
  140. // Check for S3 868 and 968. These cards report memory
  141. // requirements of 32MB but decode 64MB. Gross huh?
  142. //
  143. #if defined(PCIIDE_HACKS)
  144. //
  145. // Ok, it looks gross but turning the above and below into
  146. // an elseif seems marginally more efficient. plj.
  147. //
  148. else
  149. #endif
  150. if (This->PdoExtension->VendorId == 0x5333) {
  151. USHORT deviceId = This->PdoExtension->DeviceId;
  152. if ((deviceId == 0x88f0) || (deviceId == 0x8880)) {
  153. for (index = 0; index < PCI_TYPE0_ADDRESSES; index++) {
  154. //
  155. // Check for memory requirement of 32MB and
  156. // change it to 64MB.
  157. //
  158. if (bar[index] == 0xfe000000) {
  159. bar[index] = 0xfc000000;
  160. PciDebugPrint(
  161. PciDbgObnoxious,
  162. "PCI - Adjusted broken S3 requirement from 32MB to 64MB\n"
  163. );
  164. }
  165. }
  166. }
  167. }
  168. #endif
  169. #if defined(PCI_CIRRUS_54XX_HACK)
  170. //
  171. // This device reports an IO requirement of 0x400 ports, in
  172. // the second BAR. What it really wants is access to the VGA
  173. // registers (3b0 thru 3bb and 3c0 thru 3df). It will actually
  174. // allow them to move but (a) the driver doesn't understand this
  175. // and the device no longer sees the VGA registers, ie vga.sys
  176. // won't work any more, (b) if the device is under a bridge and
  177. // the ISA bit is set, we can't satisfy the requirement,.......
  178. // however, if we left it where it was, it will work under the
  179. // bridge as long as the bridge has the VGA bit set.
  180. //
  181. // Basically, Cirrus tried to make the VGA registers moveable
  182. // which is a noble thing, unfortunately the implementation
  183. // requires a bunch of software knowledge that across all the
  184. // drivers involved, we just don't have.
  185. //
  186. // Solution? Delete the requirement.
  187. //
  188. if ((This->PdoExtension->VendorId == 0x1013) &&
  189. (This->PdoExtension->DeviceId == 0x00a0)) {
  190. //
  191. // If the second requirement is IO for length 0x400,
  192. // currently unassigned, don't report it at all.
  193. //
  194. if ((bar[1] & 0xffff) == 0x0000fc01) {
  195. //
  196. // Only do this if the device does not have a valid
  197. // current setting in this BAR.
  198. //
  199. if (This->Current->u.type0.BaseAddresses[1] == 1) {
  200. bar[1] = 0;
  201. #if DBG
  202. PciDebugPrint(
  203. PciDbgObnoxious,
  204. "PCI - Ignored Cirrus GD54xx broken IO requirement (400 ports)\n"
  205. );
  206. } else {
  207. PciDebugPrint(
  208. PciDbgInformative,
  209. "PCI - Cirrus GD54xx 400 port IO requirement has a valid setting (%08x)\n",
  210. This->Current->u.type0.BaseAddresses[1]
  211. );
  212. #endif
  213. }
  214. #if DBG
  215. } else {
  216. //
  217. // The device doesn't look like we expected it to, complain.
  218. // (unless 0 in which case we assume cirrus already fixed it).
  219. //
  220. if (bar[1] != 0) {
  221. PciDebugPrint(
  222. PciDbgInformative,
  223. "PCI - Warning Cirrus Adapter 101300a0 has unexpected resource requirement (%08x)\n",
  224. bar[1]
  225. );
  226. }
  227. #endif
  228. }
  229. }
  230. #endif
  231. descriptor = This->PdoExtension->Resources->Limit;
  232. //
  233. // Create an IO_RESOURCE_DESCRIPTOR for each implemented
  234. // resource supported by this function.
  235. //
  236. for (index = 0; index < PCI_TYPE0_ADDRESSES; index++) {
  237. if (PciCreateIoDescriptorFromBarLimit(descriptor, bar, FALSE)) {
  238. //
  239. // This base address register is 64 bit, skip one.
  240. //
  241. ASSERT((index+1) < PCI_TYPE0_ADDRESSES);
  242. index++;
  243. bar++;
  244. //
  245. // Null descriptor in place holder.
  246. //
  247. descriptor++;
  248. descriptor->Type = CmResourceTypeNull;
  249. }
  250. descriptor++;
  251. bar++;
  252. }
  253. //
  254. // Do the same for the ROM
  255. //
  256. PciCreateIoDescriptorFromBarLimit(descriptor,
  257. &This->Working->u.type0.ROMBaseAddress,
  258. TRUE);
  259. }
  260. VOID
  261. Device_SaveCurrentSettings(
  262. IN PPCI_CONFIGURABLE_OBJECT This
  263. )
  264. /*++
  265. Description:
  266. Fill in the Current array in the PDO extension with the current
  267. settings for each implemented BAR.
  268. Arguments:
  269. This - Pointer to a PCI driver "configurable" object. This
  270. object contains configuration data for the function
  271. currently being configured.
  272. Return Value:
  273. None.
  274. --*/
  275. {
  276. ULONG index;
  277. PCM_PARTIAL_RESOURCE_DESCRIPTOR partial;
  278. PIO_RESOURCE_DESCRIPTOR ioResourceDescriptor;
  279. PULONG baseAddress = This->Current->u.type0.BaseAddresses;
  280. ULONG bar;
  281. ULONG addressMask;
  282. BOOLEAN nonZeroBars = FALSE;
  283. partial = This->PdoExtension->Resources->Current;
  284. ioResourceDescriptor = This->PdoExtension->Resources->Limit;
  285. //
  286. // Create an CM_PARTIAL_RESOURCE_DESCRIPTOR for each implemented
  287. // resource supported by this function.
  288. //
  289. // Note: SaveLimits must have been called before SaveCurrentSettings
  290. // so we can tell which BARs are implemented.
  291. //
  292. // Note: The following loop runs one extra time to get the ROM.
  293. //
  294. for (index = 0;
  295. index <= PCI_TYPE0_ADDRESSES;
  296. index++, partial++, ioResourceDescriptor++) {
  297. partial->Type = ioResourceDescriptor->Type;
  298. bar = *baseAddress++;
  299. //
  300. // If this BAR is not implemented, no further processing for
  301. // this partial descriptor.
  302. //
  303. if (partial->Type == CmResourceTypeNull) {
  304. continue;
  305. }
  306. //
  307. // Copy the length from the limits descriptor, then we
  308. // actually need to do a little processing to figure out
  309. // the current limits.
  310. //
  311. partial->Flags = ioResourceDescriptor->Flags;
  312. partial->ShareDisposition = ioResourceDescriptor->ShareDisposition;
  313. partial->u.Generic.Length = ioResourceDescriptor->u.Generic.Length;
  314. partial->u.Generic.Start.HighPart = 0;
  315. if (index == PCI_TYPE0_ADDRESSES) {
  316. bar = This->Current->u.type0.ROMBaseAddress;
  317. addressMask = PCI_ADDRESS_ROM_ADDRESS_MASK;
  318. //
  319. // If the ROM Enabled bit is clear, don't record
  320. // a current setting for this ROM BAR.
  321. //
  322. if ((bar & PCI_ROMADDRESS_ENABLED) == 0) {
  323. partial->Type = CmResourceTypeNull;
  324. continue;
  325. }
  326. } else if (bar & PCI_ADDRESS_IO_SPACE) {
  327. ASSERT(partial->Type == CmResourceTypePort);
  328. addressMask = PCI_ADDRESS_IO_ADDRESS_MASK;
  329. } else {
  330. ASSERT(partial->Type == CmResourceTypeMemory);
  331. addressMask = PCI_ADDRESS_MEMORY_ADDRESS_MASK;
  332. if ((bar & PCI_ADDRESS_MEMORY_TYPE_MASK) == PCI_TYPE_64BIT) {
  333. //
  334. // This is a 64 bit PCI device. Get the high 32 bits
  335. // from the next BAR.
  336. //
  337. partial->u.Generic.Start.HighPart = *baseAddress;
  338. } else if ((bar & PCI_ADDRESS_MEMORY_TYPE_MASK) == PCI_TYPE_20BIT) {
  339. //
  340. // This device must locate below 1MB, the BAR shouldn't
  341. // have any top bits set but this isn't clear from the
  342. // spec. Enforce it by clearing the top bits.
  343. //
  344. addressMask &= 0x000fffff;
  345. }
  346. }
  347. partial->u.Generic.Start.LowPart = bar & addressMask;
  348. if (partial->u.Generic.Start.QuadPart == 0) {
  349. //
  350. // No current setting if the value is current setting
  351. // is 0.
  352. //
  353. partial->Type = CmResourceTypeNull;
  354. continue;
  355. }
  356. nonZeroBars = TRUE;
  357. }
  358. //
  359. // Save type 0 specific data in the PDO.
  360. //
  361. This->PdoExtension->SubsystemVendorId =
  362. This->Current->u.type0.SubVendorID;
  363. This->PdoExtension->SubsystemId =
  364. This->Current->u.type0.SubSystemID;
  365. }
  366. VOID
  367. Device_ChangeResourceSettings(
  368. IN PPCI_PDO_EXTENSION PdoExtension,
  369. IN PPCI_COMMON_CONFIG CommonConfig
  370. )
  371. /*++
  372. Description:
  373. Reconfigure each BAR using the settings from the Current array
  374. in the PDO extension. All we actually do here is write the new
  375. settings into the memory pointed to by CommonConfig, the actual
  376. write to the hardware is done elsewhere.
  377. Note: Possibly not all BARs will be changed, at least one has
  378. changed or this routine would not have been called.
  379. Arguments:
  380. PdoExtension A pointer to the PDO extension for this device.
  381. CurrentConfig A pointer to the current contents of PCI config
  382. space.
  383. Return Value:
  384. None.
  385. --*/
  386. {
  387. ULONG index;
  388. PCM_PARTIAL_RESOURCE_DESCRIPTOR partial;
  389. PULONG baseAddress;
  390. ULONG bar;
  391. ULONG lowPart;
  392. if (PdoExtension->Resources == NULL) {
  393. //
  394. // Nothing to play with.
  395. //
  396. return;
  397. }
  398. partial = PdoExtension->Resources->Current;
  399. baseAddress = CommonConfig->u.type0.BaseAddresses;
  400. for (index = 0;
  401. index <= PCI_TYPE0_ADDRESSES;
  402. index++, partial++, baseAddress++) {
  403. //
  404. // If this BAR is not implemented, no further processing for
  405. // this partial descriptor.
  406. //
  407. if (partial->Type == CmResourceTypeNull) {
  408. continue;
  409. }
  410. lowPart = partial->u.Generic.Start.LowPart;
  411. bar = *baseAddress;
  412. if (index == PCI_TYPE0_ADDRESSES) {
  413. ASSERT(partial->Type == CmResourceTypeMemory);
  414. bar = CommonConfig->u.type0.ROMBaseAddress;
  415. bar &= ~PCI_ADDRESS_ROM_ADDRESS_MASK;
  416. bar |= (lowPart & PCI_ADDRESS_ROM_ADDRESS_MASK);
  417. CommonConfig->u.type0.ROMBaseAddress = bar;
  418. } else if (bar & PCI_ADDRESS_IO_SPACE) {
  419. ASSERT(partial->Type == CmResourceTypePort);
  420. *baseAddress = lowPart;
  421. } else {
  422. ASSERT(partial->Type == CmResourceTypeMemory);
  423. *baseAddress = lowPart;
  424. if ((bar & PCI_ADDRESS_MEMORY_TYPE_MASK) == PCI_TYPE_64BIT) {
  425. //
  426. // This is a 64 bit address. Need to set the upper
  427. // 32 bits in the next BAR.
  428. //
  429. baseAddress++;
  430. *baseAddress = partial->u.Generic.Start.HighPart;
  431. //
  432. // We need to skip the next partial entry and descrement
  433. // the loop count because we consumed a BAR here.
  434. //
  435. index++;
  436. partial++;
  437. #if DBG
  438. } else if ((bar & PCI_ADDRESS_MEMORY_TYPE_MASK) == PCI_TYPE_20BIT) {
  439. //
  440. // This device must locate below 1MB, make sure we're
  441. // configuring it that way.
  442. //
  443. ASSERT((lowPart & 0xfff00000) == 0);
  444. #endif
  445. }
  446. }
  447. }
  448. }
  449. VOID
  450. Device_GetAdditionalResourceDescriptors(
  451. IN PPCI_PDO_EXTENSION PdoExtension,
  452. IN PPCI_COMMON_CONFIG CommonConfig,
  453. IN PIO_RESOURCE_DESCRIPTOR Resource
  454. )
  455. {
  456. //
  457. // Type 0 (devices) don't require resources not adequately
  458. // described in the BARs.
  459. //
  460. return;
  461. }
  462. NTSTATUS
  463. Device_ResetDevice(
  464. IN PPCI_PDO_EXTENSION PdoExtension,
  465. IN PPCI_COMMON_CONFIG CommonConfig
  466. )
  467. {
  468. return STATUS_SUCCESS;
  469. }