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.

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