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
20 KiB

  1. /*++
  2. Copyright (c) 1996-2000 Microsoft Corporation
  3. Module Name:
  4. config.c
  5. Abstract:
  6. Two kinds of config space access are allowed. One for the config space
  7. associated with a specific PDO and one for a device specified in terms of
  8. a (RootFdo, BusNumber, Slot) tuple.
  9. Author:
  10. Andrew Thornton (andrewth) 27-Aug-1998
  11. Revision History:
  12. --*/
  13. #include "pcip.h"
  14. #define INT_LINE_OFFSET ((ULONG)FIELD_OFFSET(PCI_COMMON_CONFIG,u.type0.InterruptLine))
  15. //
  16. // None of these functions are pageable as they are called to power manage
  17. // devices at high IRQL
  18. //
  19. VOID
  20. PciReadWriteConfigSpace(
  21. IN PPCI_FDO_EXTENSION ParentFdo,
  22. IN PCI_SLOT_NUMBER Slot,
  23. IN PVOID Buffer,
  24. IN ULONG Offset,
  25. IN ULONG Length,
  26. IN BOOLEAN Read
  27. )
  28. /*++
  29. Routine Description:
  30. This is the base routine through which all config space access from the
  31. pci driver go.
  32. Arguments:
  33. ParentFdo - The FDO of the bus who's config space we want
  34. Slot - The Device/Function of the device on that bus we are interested in
  35. Buffer - A buffer where the data will be read or written
  36. Offset - The byte offset in config space where we should start to read/write
  37. Length - The number of bytes to read/write
  38. Read - TRUE to read from config space, FALSE to write
  39. Return Value:
  40. None
  41. Notes:
  42. If the underlying HAL or ACPI access mechanism failes we bugcheck with a
  43. PCI_CONFIG_SPACE_ACCESS_FAILURE
  44. --*/
  45. {
  46. NTSTATUS status;
  47. PciReadWriteConfig busHandlerReadWrite;
  48. PCI_READ_WRITE_CONFIG interfaceReadWrite;
  49. ULONG count;
  50. PPCI_BUS_INTERFACE_STANDARD busInterface;
  51. ASSERT(PCI_IS_ROOT_FDO(ParentFdo->BusRootFdoExtension));
  52. busInterface = ParentFdo->BusRootFdoExtension->PciBusInterface;
  53. if (busInterface) {
  54. //
  55. // If we have a PCI_BUS_INTERFACE use it to access config space
  56. //
  57. if (Read) {
  58. interfaceReadWrite = busInterface->ReadConfig;
  59. } else {
  60. interfaceReadWrite = busInterface->WriteConfig;
  61. }
  62. //
  63. // The interface access to config space is at the root of each PCI
  64. // domain
  65. //
  66. count = interfaceReadWrite(
  67. busInterface->Context,
  68. ParentFdo->BaseBus,
  69. Slot.u.AsULONG,
  70. Buffer,
  71. Offset,
  72. Length
  73. );
  74. if (count != Length) {
  75. KeBugCheckEx(
  76. PCI_CONFIG_SPACE_ACCESS_FAILURE,
  77. (ULONG_PTR) ParentFdo->BaseBus, // Bus
  78. (ULONG_PTR) Slot.u.AsULONG, // Slot
  79. (ULONG_PTR) Offset, // Offset
  80. (ULONG_PTR) Read // Read/Write
  81. );
  82. }
  83. } else {
  84. //
  85. // The BusHandler interface is at the parent level.
  86. //
  87. // NOTE: This means that if hot-plug of bridges (aka Docking) is to be
  88. // supported then the HAL must provide a PCI_BUS_INTERFACE_STANDARD
  89. // because it will not have a bus handler for the new bridge so we
  90. // won't be able to use this code path.
  91. //
  92. ASSERT(ParentFdo->BusHandler);
  93. //
  94. // We had better not think we can do hot plug
  95. //
  96. ASSERT(!PciAssignBusNumbers);
  97. if (Read) {
  98. busHandlerReadWrite =
  99. ((PPCIBUSDATA)ParentFdo->BusHandler->BusData)->ReadConfig;
  100. } else {
  101. busHandlerReadWrite =
  102. ((PPCIBUSDATA)ParentFdo->BusHandler->BusData)->WriteConfig;
  103. }
  104. busHandlerReadWrite(ParentFdo->BusHandler,
  105. Slot,
  106. Buffer,
  107. Offset,
  108. Length
  109. );
  110. }
  111. }
  112. VOID
  113. PciReadDeviceConfig(
  114. IN PPCI_PDO_EXTENSION Pdo,
  115. IN PVOID Buffer,
  116. IN ULONG Offset,
  117. IN ULONG Length
  118. )
  119. /*++
  120. Routine Description:
  121. Read the config space for a specific device
  122. Arguments:
  123. Pdo - The PDO representing the device who's config space we want
  124. Buffer - A buffer where the data will be read or written
  125. Offset - The byte offset in config space where we should start to read/write
  126. Length - The number of bytes to read/write
  127. Return Value:
  128. None
  129. --*/
  130. {
  131. PciReadWriteConfigSpace(PCI_PARENT_FDOX(Pdo),
  132. Pdo->Slot,
  133. Buffer,
  134. Offset,
  135. Length,
  136. TRUE // read
  137. );
  138. }
  139. VOID
  140. PciWriteDeviceConfig(
  141. IN PPCI_PDO_EXTENSION Pdo,
  142. IN PVOID Buffer,
  143. IN ULONG Offset,
  144. IN ULONG Length
  145. )
  146. /*++
  147. Routine Description:
  148. Write the config space for a specific device
  149. Arguments:
  150. Pdo - The PDO representing the device who's config space we want
  151. Buffer - A buffer where the data will be read or written
  152. Offset - The byte offset in config space where we should start to read/write
  153. Length - The number of bytes to read/write
  154. Return Value:
  155. None
  156. --*/
  157. {
  158. #if 0
  159. //
  160. // Make sure we never change the interrupt line register
  161. //
  162. if ((Offset <= INT_LINE_OFFSET)
  163. && (Offset + Length > INT_LINE_OFFSET)) {
  164. PUCHAR interruptLine = (PUCHAR)Buffer + INT_LINE_OFFSET - Offset;
  165. ASSERT(*interruptLine == Pdo->RawInterruptLine);
  166. }
  167. #endif
  168. PciReadWriteConfigSpace(PCI_PARENT_FDOX(Pdo),
  169. Pdo->Slot,
  170. Buffer,
  171. Offset,
  172. Length,
  173. FALSE // write
  174. );
  175. }
  176. VOID
  177. PciReadSlotConfig(
  178. IN PPCI_FDO_EXTENSION ParentFdo,
  179. IN PCI_SLOT_NUMBER Slot,
  180. IN PVOID Buffer,
  181. IN ULONG Offset,
  182. IN ULONG Length
  183. )
  184. /*++
  185. Routine Description:
  186. Read config space for a specific bus/slot
  187. Arguments:
  188. ParentFdo - The FDO of the bus who's config space we want
  189. Slot - The Device/Function of the device on that bus we are interested in
  190. Buffer - A buffer where the data will be read or written
  191. Offset - The byte offset in config space where we should start to read/write
  192. Length - The number of bytes to read/write
  193. Return Value:
  194. None
  195. --*/
  196. {
  197. PciReadWriteConfigSpace(ParentFdo,
  198. Slot,
  199. Buffer,
  200. Offset,
  201. Length,
  202. TRUE // read
  203. );
  204. }
  205. VOID
  206. PciWriteSlotConfig(
  207. IN PPCI_FDO_EXTENSION ParentFdo,
  208. IN PCI_SLOT_NUMBER Slot,
  209. IN PVOID Buffer,
  210. IN ULONG Offset,
  211. IN ULONG Length
  212. )
  213. /*++
  214. Routine Description:
  215. Read config space for a specific bus/slot
  216. Arguments:
  217. ParentFdo - The FDO of the bus who's config space we want
  218. Slot - The Device/Function of the device on that bus we are interested in
  219. Buffer - A buffer where the data will be read or written
  220. Offset - The byte offset in config space where we should start to read/write
  221. Length - The number of bytes to read/write
  222. Return Value:
  223. None
  224. --*/
  225. {
  226. PciReadWriteConfigSpace(ParentFdo,
  227. Slot,
  228. Buffer,
  229. Offset,
  230. Length,
  231. FALSE // write
  232. );
  233. }
  234. UCHAR
  235. PciGetAdjustedInterruptLine(
  236. IN PPCI_PDO_EXTENSION Pdo
  237. )
  238. /*++
  239. Routine Description:
  240. This updates the interrupt line the HAL would like the world to see - this
  241. may or may not be different than the raw pin.
  242. Arguments:
  243. Pdo - The PDO representing the device who's config space we want
  244. Buffer - A buffer where the data will be read or written
  245. Offset - The byte offset in config space where we should start to read/write
  246. Length - The number of bytes to read/write
  247. Return Value:
  248. None
  249. --*/
  250. {
  251. UCHAR adjustedInterruptLine = 0;
  252. ULONG lengthRead;
  253. //
  254. // Just in case anyone messes up the structures
  255. //
  256. ASSERT(INT_LINE_OFFSET
  257. == (ULONG)FIELD_OFFSET(PCI_COMMON_CONFIG, u.type1.InterruptLine));
  258. ASSERT(INT_LINE_OFFSET
  259. == (ULONG)FIELD_OFFSET(PCI_COMMON_CONFIG, u.type2.InterruptLine));
  260. if (Pdo->InterruptPin != 0) {
  261. //
  262. // Get the adjusted line the HAL wants us to see
  263. //
  264. lengthRead = HalGetBusDataByOffset(
  265. PCIConfiguration,
  266. PCI_PARENT_FDOX(Pdo)->BaseBus,
  267. Pdo->Slot.u.AsULONG,
  268. &adjustedInterruptLine,
  269. INT_LINE_OFFSET,
  270. sizeof(adjustedInterruptLine));
  271. if (lengthRead != sizeof(adjustedInterruptLine)) {
  272. adjustedInterruptLine = Pdo->RawInterruptLine;
  273. }
  274. }
  275. return adjustedInterruptLine;
  276. }
  277. NTSTATUS
  278. PciQueryForPciBusInterface(
  279. IN PPCI_FDO_EXTENSION FdoExtension
  280. )
  281. /*++
  282. Routine Description:
  283. This routine sends an IRP to the parent PDO requesting
  284. handlers for PCI configuration reads and writes.
  285. Arguments:
  286. FdoExtension - this PCI bus's FDO extension
  287. Return Value:
  288. STATUS_SUCCESS, if the PDO provided handlers
  289. Notes:
  290. --*/
  291. {
  292. NTSTATUS status;
  293. PPCI_BUS_INTERFACE_STANDARD interface;
  294. PDEVICE_OBJECT targetDevice = NULL;
  295. KEVENT irpCompleted;
  296. IO_STATUS_BLOCK statusBlock;
  297. PIRP irp = NULL;
  298. PIO_STACK_LOCATION irpStack;
  299. PAGED_CODE();
  300. //
  301. // We only do this for root busses
  302. //
  303. ASSERT(PCI_IS_ROOT_FDO(FdoExtension));
  304. interface = ExAllocatePool(NonPagedPool, sizeof(PCI_BUS_INTERFACE_STANDARD));
  305. if (!interface) {
  306. return STATUS_INSUFFICIENT_RESOURCES;
  307. }
  308. //
  309. // Find out where we are sending the irp
  310. //
  311. targetDevice = IoGetAttachedDeviceReference(FdoExtension->PhysicalDeviceObject);
  312. //
  313. // Get an IRP
  314. //
  315. KeInitializeEvent(&irpCompleted, SynchronizationEvent, FALSE);
  316. irp = IoBuildSynchronousFsdRequest(IRP_MJ_PNP,
  317. targetDevice,
  318. NULL, // Buffer
  319. 0, // Length
  320. 0, // StartingOffset
  321. &irpCompleted,
  322. &statusBlock
  323. );
  324. if (!irp) {
  325. status = STATUS_INSUFFICIENT_RESOURCES;
  326. goto cleanup;
  327. }
  328. irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
  329. irp->IoStatus.Information = 0;
  330. //
  331. // Initialize the stack location
  332. //
  333. irpStack = IoGetNextIrpStackLocation(irp);
  334. ASSERT(irpStack->MajorFunction == IRP_MJ_PNP);
  335. irpStack->MinorFunction = IRP_MN_QUERY_INTERFACE;
  336. irpStack->Parameters.QueryInterface.InterfaceType = (PGUID) &GUID_PCI_BUS_INTERFACE_STANDARD;
  337. irpStack->Parameters.QueryInterface.Version = PCI_BUS_INTERFACE_STANDARD_VERSION;
  338. irpStack->Parameters.QueryInterface.Size = sizeof (PCI_BUS_INTERFACE_STANDARD);
  339. irpStack->Parameters.QueryInterface.Interface = (PINTERFACE) interface;
  340. irpStack->Parameters.QueryInterface.InterfaceSpecificData = NULL;
  341. //
  342. // Call the driver and wait for completion
  343. //
  344. status = IoCallDriver(targetDevice, irp);
  345. if (status == STATUS_PENDING) {
  346. KeWaitForSingleObject(&irpCompleted, Executive, KernelMode, FALSE, NULL);
  347. status = statusBlock.Status;
  348. }
  349. if (NT_SUCCESS(status)) {
  350. FdoExtension->PciBusInterface = interface;
  351. //
  352. // The interface is already referenced when we get it so we don't need
  353. // to reference it again.
  354. //
  355. } else {
  356. //
  357. // We don't have an interface
  358. //
  359. FdoExtension->PciBusInterface = NULL;
  360. ExFreePool(interface);
  361. }
  362. //
  363. // Ok we're done with this stack
  364. //
  365. ObDereferenceObject(targetDevice);
  366. return status;
  367. cleanup:
  368. if (targetDevice) {
  369. ObDereferenceObject(targetDevice);
  370. }
  371. if (interface) {
  372. ExFreePool(interface);
  373. }
  374. return status;
  375. }
  376. NTSTATUS
  377. PciGetConfigHandlers(
  378. IN PPCI_FDO_EXTENSION FdoExtension
  379. )
  380. /*++
  381. Routine Description:
  382. This routine attempts to get pnp style config handlers from the PCI busses
  383. enumerator and if they are not provided falls back on using the HAL bus
  384. handler method.
  385. Arguments:
  386. FdoExtension - this PCI bus's FDO extension
  387. Return Value:
  388. STATUS_SUCCESS, if the PDO provided handlers
  389. Notes:
  390. --*/
  391. {
  392. NTSTATUS status;
  393. PPCIBUSDATA BusData;
  394. ASSERT(FdoExtension->BusHandler == NULL);
  395. //
  396. // Check if this is a root bus
  397. //
  398. if (PCI_IS_ROOT_FDO(FdoExtension)) {
  399. ASSERT(FdoExtension->PciBusInterface == NULL);
  400. //
  401. // Check to see if our parent is offering
  402. // functions for reading and writing config space.
  403. //
  404. status = PciQueryForPciBusInterface(FdoExtension);
  405. if (NT_SUCCESS(status)) {
  406. //
  407. // If we have an interface we support numbering of busses
  408. //
  409. PciAssignBusNumbers = TRUE;
  410. } else {
  411. //
  412. // We better not think we can number busses - we should only ever
  413. // get here if one root provides an interface and the other does not
  414. //
  415. ASSERT(!PciAssignBusNumbers);
  416. }
  417. } else {
  418. //
  419. // Check if our root has a PciBusInterface - which it got from above
  420. //
  421. if (FdoExtension->BusRootFdoExtension->PciBusInterface) {
  422. return STATUS_SUCCESS;
  423. } else {
  424. //
  425. // Set status so we get a bus handler for this bus
  426. //
  427. status = STATUS_NOT_SUPPORTED;
  428. }
  429. }
  430. if (!NT_SUCCESS(status)) {
  431. ASSERT(status == STATUS_NOT_SUPPORTED);
  432. //
  433. // Make sure we arn't trying to get a bus handler for a hot plug
  434. // capable machine
  435. //
  436. ASSERT(!PciAssignBusNumbers);
  437. //
  438. // We couldn't find config handlers the PnP way,
  439. // build them from the HAL bus handlers.
  440. //
  441. FdoExtension->BusHandler =
  442. HalReferenceHandlerForBus(PCIBus, FdoExtension->BaseBus);
  443. if (!FdoExtension->BusHandler) {
  444. //
  445. // This must be a bus that arrived hot. We only support hot anything
  446. // on ACPI machines and they should have provided a PCI_BUS interface
  447. // at the root. Fail the add for this new bus.
  448. //
  449. return STATUS_INVALID_DEVICE_REQUEST; // better code?
  450. }
  451. }
  452. return STATUS_SUCCESS;
  453. }
  454. NTSTATUS
  455. PciExternalReadDeviceConfig(
  456. IN PPCI_PDO_EXTENSION Pdo,
  457. IN PVOID Buffer,
  458. IN ULONG Offset,
  459. IN ULONG Length
  460. )
  461. /*++
  462. Routine Description:
  463. Called when agents outside the PCI driver want to access config space
  464. (either from a READ_CONFIG IRP or through BUS_INTERFACE_STANDARD).
  465. This function performs extra sanity checks and sanitization on the
  466. arguments and also double buffers the data as Buffer might be
  467. pageable and we access config space at high IRQL.
  468. Arguments:
  469. Pdo - The PDO representing the device who's config space we want
  470. Buffer - A buffer where the data will be read or written
  471. Offset - The byte offset in config space where we should start to read/write
  472. Length - The number of bytes to read/write
  473. Return Value:
  474. None
  475. --*/
  476. {
  477. PUCHAR interruptLine;
  478. UCHAR doubleBuffer[sizeof(PCI_COMMON_CONFIG)];
  479. //
  480. // Validate the request
  481. //
  482. if ((Length + Offset) > sizeof(PCI_COMMON_CONFIG)) {
  483. return STATUS_INVALID_DEVICE_REQUEST;
  484. }
  485. //
  486. // Read the data into a buffer allocated on the stack with
  487. // is guaranteed to not be paged as we access config space
  488. // at > DISPATCH_LEVEL and the DDK says that the buffer
  489. // *should* be in paged pool.
  490. //
  491. PciReadDeviceConfig(Pdo, &doubleBuffer[Offset], Offset, Length);
  492. //
  493. // If we are reading the interrupt line register then adjust it.
  494. //
  495. if ((Pdo->InterruptPin != 0) &&
  496. (Offset <= INT_LINE_OFFSET) &&
  497. (Offset + Length > INT_LINE_OFFSET)) {
  498. doubleBuffer[INT_LINE_OFFSET] = Pdo->AdjustedInterruptLine;
  499. }
  500. RtlCopyMemory(Buffer, &doubleBuffer[Offset], Length);
  501. return STATUS_SUCCESS;
  502. }
  503. NTSTATUS
  504. PciExternalWriteDeviceConfig(
  505. IN PPCI_PDO_EXTENSION Pdo,
  506. IN PVOID Buffer,
  507. IN ULONG Offset,
  508. IN ULONG Length
  509. )
  510. /*++
  511. Routine Description:
  512. Called when agents outside the PCI driver want to access config space
  513. (either from a WRITE_CONFIG IRP or through BUS_INTERFACE_STANDARD).
  514. This function performs extra sanity checks and sanitization on the
  515. arguments and also double buffers the data as Buffer might be
  516. pageable and we access config space at high IRQL.
  517. Arguments:
  518. Pdo - The PDO representing the device who's config space we want
  519. Buffer - A buffer where the data will be read or written
  520. Offset - The byte offset in config space where we should start to read/write
  521. Length - The number of bytes to read/write
  522. Return Value:
  523. None
  524. --*/
  525. {
  526. PUCHAR interruptLine;
  527. UCHAR doubleBuffer[255];
  528. BOOLEAN illegalAccess = FALSE;
  529. PVERIFIER_DATA verifierData;
  530. //
  531. // Validate the request
  532. //
  533. if ((Length + Offset) > sizeof(PCI_COMMON_CONFIG)) {
  534. return STATUS_INVALID_DEVICE_REQUEST;
  535. }
  536. //
  537. // Make sure they are not touching registers they should not be. For
  538. // backward compatiblity we will just complain and let the request through.
  539. //
  540. switch (Pdo->HeaderType) {
  541. case PCI_DEVICE_TYPE:
  542. //
  543. // They should not be writing to their BARS including the ROM BAR
  544. //
  545. if (INTERSECT_CONFIG_FIELD(Offset, Length, u.type0.BaseAddresses)
  546. || INTERSECT_CONFIG_FIELD(Offset, Length, u.type0.ROMBaseAddress)) {
  547. illegalAccess = TRUE;
  548. }
  549. break;
  550. case PCI_BRIDGE_TYPE:
  551. //
  552. // For bridges they should not touch the bars, the base and limit registers,
  553. // the bus numbers or bridge control
  554. //
  555. if (INTERSECT_CONFIG_FIELD_RANGE(Offset, Length, u.type1.BaseAddresses, u.type1.SubordinateBus)
  556. || INTERSECT_CONFIG_FIELD_RANGE(Offset, Length, u.type1.IOBase, u.type1.IOLimit)
  557. || INTERSECT_CONFIG_FIELD_RANGE(Offset, Length, u.type1.MemoryBase, u.type1.IOLimitUpper16)
  558. || INTERSECT_CONFIG_FIELD(Offset, Length, u.type1.ROMBaseAddress)) {
  559. illegalAccess = TRUE;
  560. }
  561. break;
  562. case PCI_CARDBUS_BRIDGE_TYPE:
  563. //
  564. // For bridges they should not touch the bars, the base and limit registers
  565. // or the bus numbers. Bridge control is modified by PCICIA to control cardbus
  566. // IRQ routing so must be ok.
  567. //
  568. if (INTERSECT_CONFIG_FIELD(Offset, Length, u.type2.SocketRegistersBaseAddress)
  569. || INTERSECT_CONFIG_FIELD_RANGE(Offset, Length, u.type2.PrimaryBus, u.type2.SubordinateBus)
  570. || INTERSECT_CONFIG_FIELD(Offset, Length, u.type2.Range)) {
  571. illegalAccess = TRUE;
  572. }
  573. break;
  574. }
  575. if (illegalAccess) {
  576. verifierData = PciVerifierRetrieveFailureData(
  577. PCI_VERIFIER_PROTECTED_CONFIGSPACE_ACCESS
  578. );
  579. ASSERT(verifierData);
  580. //
  581. // We fail the devnode instead of the driver because we don't actually
  582. // have an address to pass to the driver verifier.
  583. //
  584. VfFailDeviceNode(
  585. Pdo->PhysicalDeviceObject,
  586. PCI_VERIFIER_DETECTED_VIOLATION,
  587. PCI_VERIFIER_PROTECTED_CONFIGSPACE_ACCESS,
  588. verifierData->FailureClass,
  589. &verifierData->Flags,
  590. verifierData->FailureText,
  591. "%DevObj%Ulong%Ulong",
  592. Pdo->PhysicalDeviceObject,
  593. Offset,
  594. Length
  595. );
  596. }
  597. //
  598. // Copy the data into a buffer allocated on the stack with
  599. // is guaranteed to not be paged as we access config space
  600. // at > DISPATCH_LEVEL and the DDK says that the buffer
  601. // *should* be in paged pool.
  602. //
  603. RtlCopyMemory(doubleBuffer, Buffer, Length);
  604. //
  605. // If we are writing the interrupt line register then adjust it so we write
  606. // the raw value back again
  607. //
  608. if ((Pdo->InterruptPin != 0) &&
  609. (Offset <= INT_LINE_OFFSET) &&
  610. (Offset + Length > INT_LINE_OFFSET)) {
  611. interruptLine = (PUCHAR)doubleBuffer + INT_LINE_OFFSET - Offset;
  612. //
  613. // Adjust the interrupt line with what the HAL wants us to see
  614. //
  615. *interruptLine = Pdo->RawInterruptLine;
  616. }
  617. PciWriteDeviceConfig(Pdo, doubleBuffer, Offset, Length);
  618. return STATUS_SUCCESS;
  619. }