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.

1063 lines
29 KiB

  1. /*++
  2. Copyright (c) 1998-2000 Microsoft Corporation
  3. Module Name:
  4. romimage.c
  5. Abstract:
  6. This module contains the code required to obtain a copy of a
  7. device's ROM (Read Only Memory).
  8. The PCI spec allows a device to share address decoding logic
  9. between the ROM BAR (Base Address Registers) and other BARs.
  10. Effectively, this means the ROM cannot be accessed at the same
  11. time as the device is otherwise operating.
  12. The ROM is accesible when both the ROM enabled bit is set and
  13. memory decoding is enabled.
  14. Author:
  15. Peter Johnston (peterj) 15-Apr-1998
  16. Revision History:
  17. --*/
  18. #include "pcip.h"
  19. extern pHalTranslateBusAddress PcipSavedTranslateBusAddress;
  20. typedef struct _PCI_ROM_HEADER {
  21. USHORT Signature;
  22. UCHAR RsvdArchitectureUnique[0x16];
  23. USHORT DataStructureOffset;
  24. } PCI_ROM_HEADER, *PPCI_ROM_HEADER;
  25. typedef struct _PCI_DATA_STRUCTURE {
  26. ULONG Signature;
  27. USHORT VendorId;
  28. USHORT DeviceId;
  29. USHORT VitalProductDataOffset;
  30. USHORT DataStructureLength;
  31. UCHAR DataStructureRevision;
  32. UCHAR ClassCode[3];
  33. USHORT ImageLength;
  34. USHORT ImageRevision;
  35. UCHAR CodeType;
  36. UCHAR Indicator;
  37. USHORT Reserved;
  38. } PCI_DATA_STRUCTURE, *PPCI_DATA_STRUCTURE;
  39. #define PCI_ROM_HEADER_SIGNATURE 0xaa55
  40. #define PCI_ROM_DATA_STRUCTURE_SIGNATURE 'RICP' // LE PCIR
  41. //
  42. // Prototypes for local routines.
  43. //
  44. NTSTATUS
  45. PciRomTestWriteAccessToBuffer(
  46. IN PUCHAR Buffer,
  47. IN ULONG Length
  48. );
  49. VOID
  50. PciTransferRomData(
  51. IN PVOID RomAddress,
  52. IN PVOID Buffer,
  53. IN ULONG Length
  54. );
  55. #ifdef ALLOC_PRAGMA
  56. #pragma alloc_text(PAGE, PciReadRomImage)
  57. #pragma alloc_text(PAGE, PciRomTestWriteAccessToBuffer)
  58. #pragma alloc_text(PAGE, PciTransferRomData)
  59. #endif
  60. VOID
  61. PciTransferRomData(
  62. IN PVOID RomAddress,
  63. IN PVOID Buffer,
  64. IN ULONG Length
  65. )
  66. /*++
  67. Routine Description:
  68. Simple abstraction of READ_REGISTER_BUFFER_Uxxxx()
  69. Copies from ROM to an in memory buffer. Deals with alignment
  70. and tries to use the most efficient means.
  71. Arguments:
  72. RomAddress Mapped/Translated address to copy from.
  73. Buffer Memory address to copy to.
  74. Length Number of BYTEs to copy.
  75. Return Value:
  76. None.
  77. --*/
  78. {
  79. #define BLKSIZE sizeof(ULONG)
  80. #define BLKMASK (BLKSIZE - 1)
  81. ULONG temp;
  82. if (Length > BLKSIZE) {
  83. //
  84. // Optimize for aligned case (typically, both will be perfectly
  85. // aligned) and a multiple of DWORDs.
  86. //
  87. temp = (ULONG)((ULONG_PTR)RomAddress & BLKMASK);
  88. if (temp == ((ULONG_PTR)Buffer & BLKMASK)) {
  89. //
  90. // Same alignment, (note: if not same alignment, we
  91. // transfer byte by byte).
  92. //
  93. // Walk off any leading bytes...
  94. //
  95. if (temp != 0) {
  96. //
  97. // temp is offset from a dword boundary, get number of
  98. // bytes to copy.
  99. //
  100. temp = BLKSIZE - temp;
  101. READ_REGISTER_BUFFER_UCHAR(RomAddress, Buffer, temp);
  102. Length -= temp;
  103. Buffer = (PVOID)((PUCHAR)Buffer + temp);
  104. RomAddress = (PVOID)((PUCHAR)RomAddress + temp);
  105. }
  106. if (Length > BLKSIZE) {
  107. //
  108. // Get as much as possible using DWORDS
  109. //
  110. temp = Length / BLKSIZE;
  111. READ_REGISTER_BUFFER_ULONG(RomAddress, Buffer, temp);
  112. temp = temp * BLKSIZE;
  113. Length -= temp;
  114. Buffer = (PVOID)((PUCHAR)Buffer + temp);
  115. RomAddress = (PVOID)((PUCHAR)RomAddress + temp);
  116. }
  117. }
  118. }
  119. //
  120. // Finish any remaining bytes.
  121. //
  122. if (Length) {
  123. READ_REGISTER_BUFFER_UCHAR(RomAddress, Buffer, Length);
  124. }
  125. #undef BLKMASK
  126. #undef BLKSIZE
  127. }
  128. NTSTATUS
  129. PciRomTestWriteAccessToBuffer(
  130. IN PUCHAR Buffer,
  131. IN ULONG Length
  132. )
  133. /*++
  134. Routine Description:
  135. Complete Paranoia. Make sure we can write every page in the
  136. caller's buffer (assumes 4096 bytes per page) by writing to
  137. every page.
  138. We do this in a try block to avoid killing the system. The
  139. hope is to avoid anything that might bugcheck the system while
  140. we have changed the operating characteristics of the device.
  141. Arguments:
  142. Buffer Address of start of buffer.
  143. Length Number of bytes in buffer.
  144. Return Value:
  145. Status.
  146. --*/
  147. {
  148. PUCHAR endAddress = Buffer + Length - 1;
  149. try {
  150. while (Buffer <= endAddress) {
  151. *Buffer = 0;
  152. Buffer += 0x1000;
  153. }
  154. *endAddress = 0;
  155. } except (EXCEPTION_EXECUTE_HANDLER) {
  156. return GetExceptionCode();
  157. }
  158. return STATUS_SUCCESS;
  159. }
  160. NTSTATUS
  161. PciReadRomImage(
  162. IN PPCI_PDO_EXTENSION PdoExtension,
  163. IN ULONG WhichSpace,
  164. OUT PVOID Buffer,
  165. IN ULONG Offset,
  166. IN OUT PULONG Length
  167. )
  168. /*++
  169. Routine Description:
  170. Copy the device ROM to the caller's Buffer.
  171. Arguments:
  172. PdoExtension Device Extension of the device in question.
  173. WhichSpace Indicates which part of the ROM image is required.
  174. (Currently only the x86 BIOS image is supported,
  175. can be expanded to pass back the Open FW image if
  176. needed).
  177. Buffer Address of the caller's data area.
  178. Offset Offset from the start of the ROM image data should
  179. be returned from. Currently not used, can be used
  180. in the future to stage data.
  181. Length Pointer to a ULONG containing the length of the
  182. Buffer (requested length). The value is modified
  183. to the actual data length.
  184. Return Value:
  185. Status of this operation.
  186. --*/
  187. {
  188. PIO_RESOURCE_DESCRIPTOR requirement;
  189. PIO_RESOURCE_DESCRIPTOR movedRequirement = NULL;
  190. PCM_PARTIAL_RESOURCE_DESCRIPTOR resource;
  191. CM_PARTIAL_RESOURCE_DESCRIPTOR tempResource;
  192. BOOLEAN acquiredResources = TRUE;
  193. BOOLEAN movedResource = FALSE;
  194. BOOLEAN translated;
  195. ULONG oldStatusCommand;
  196. ULONG newStatusCommand;
  197. ULONG oldRom;
  198. ULONG newRom;
  199. ULONG maximumLength;
  200. NTSTATUS status;
  201. PHYSICAL_ADDRESS translatedAddress;
  202. ULONG addressIsIoSpace = 0;
  203. PVOID mapped = NULL;
  204. PVOID romBase;
  205. PUCHAR imageBase;
  206. ULONG imageLength;
  207. PCI_ROM_HEADER header;
  208. PCI_DATA_STRUCTURE dataStructure;
  209. PPCI_ARBITER_INSTANCE pciArbiter;
  210. PARBITER_INSTANCE arbiter;
  211. PHYSICAL_ADDRESS movedAddress;
  212. ULONG movedIndex;
  213. ULONGLONG tempResourceStart;
  214. PAGED_CODE();
  215. PciDebugPrint(
  216. PciDbgROM,
  217. "PCI ROM entered for pdox %08x (buffer @ %08x %08x bytes)\n",
  218. PdoExtension,
  219. Buffer,
  220. *Length
  221. );
  222. //
  223. // Currently not very flexible, assert we can do what the
  224. // caller wants.
  225. //
  226. ASSERT(Offset == 0);
  227. ASSERT(WhichSpace == PCI_WHICHSPACE_ROM);
  228. //
  229. // Capture the length and set the returned length to 0. This
  230. // will be set to the correct value any data is successfully
  231. // returned.
  232. //
  233. maximumLength = *Length;
  234. *Length = 0;
  235. //
  236. // Only do this for header type 0 (ie devices, not bridges,
  237. // bridges actually can have ROMs,.... I don't know why and
  238. // currently have no plan to support it).
  239. //
  240. if (PdoExtension->HeaderType != PCI_DEVICE_TYPE) {
  241. return STATUS_INVALID_DEVICE_REQUEST;
  242. }
  243. //
  244. // It's a device, does it use a ROM?
  245. //
  246. requirement = &PdoExtension->Resources->Limit[PCI_TYPE0_ADDRESSES];
  247. if ((PdoExtension->Resources == NULL) ||
  248. (requirement->Type == CmResourceTypeNull)) {
  249. return STATUS_SUCCESS;
  250. }
  251. //
  252. // Special case. If Length == 0 on entry, caller wants to know
  253. // what the length should be.
  254. //
  255. ASSERT((requirement->u.Generic.Length & 0x1ff) == 0);
  256. if (maximumLength == 0) {
  257. *Length = requirement->u.Generic.Length;
  258. return STATUS_BUFFER_TOO_SMALL;
  259. }
  260. //
  261. // Trim length to device maximum.
  262. //
  263. if (requirement->u.Generic.Length < maximumLength) {
  264. maximumLength = requirement->u.Generic.Length;
  265. }
  266. //
  267. // Paranoia1: This device is probably video. If the system
  268. // bugchecks while we have the device' memory access in limbo,
  269. // the system will appear to hung. Reduce the possibility of
  270. // bugcheck by ensuring we have (write) access to the caller's
  271. // buffer.
  272. //
  273. status = PciRomTestWriteAccessToBuffer(Buffer, maximumLength);
  274. if (!NT_SUCCESS(status)) {
  275. ASSERT(NT_SUCCESS(status));
  276. return status;
  277. }
  278. ASSERT(requirement->Type == CmResourceTypeMemory);
  279. ASSERT(requirement->Flags == CM_RESOURCE_MEMORY_READ_ONLY);
  280. //
  281. // Get current settings for the command register and the ROM BAR.
  282. //
  283. PciReadDeviceConfig(
  284. PdoExtension,
  285. &oldStatusCommand,
  286. FIELD_OFFSET(PCI_COMMON_CONFIG, Command),
  287. sizeof(ULONG)
  288. );
  289. PciReadDeviceConfig(
  290. PdoExtension,
  291. &oldRom,
  292. FIELD_OFFSET(PCI_COMMON_CONFIG, u.type0.ROMBaseAddress),
  293. sizeof(ULONG)
  294. );
  295. //
  296. // Zero the upper 16 bits of the Status/Command variable so the
  297. // Status field in h/w is unchanged in subsequent writes. (Bits
  298. // in the Status field are cleared by writing ones to them).
  299. //
  300. oldStatusCommand &= 0xffff;
  301. newStatusCommand = oldStatusCommand;
  302. newRom = oldRom;
  303. //
  304. // If access to the ROM is already enabled, and memory is
  305. // currently enabled, we already have access to the image.
  306. // (I've never actually seen that condition. plj).
  307. // Otherwise, we need to get PnP to allocate the range.
  308. //
  309. if (PdoExtension->Resources->Current[PCI_TYPE0_ADDRESSES].Type ==
  310. CmResourceTypeMemory) {
  311. ASSERT(oldRom & PCI_ROMADDRESS_ENABLED);
  312. if (oldStatusCommand & PCI_ENABLE_MEMORY_SPACE) {
  313. //
  314. // No need to acquire resources.
  315. //
  316. acquiredResources = FALSE;
  317. }
  318. } else {
  319. ASSERT(PdoExtension->Resources->Current[PCI_TYPE0_ADDRESSES].Type ==
  320. CmResourceTypeNull);
  321. }
  322. //
  323. // Allocate a memory resource to access the ROM with.
  324. //
  325. if (acquiredResources == TRUE) {
  326. ULONGLONG rangeMin, rangeMax;
  327. PPCI_PDO_EXTENSION currentPdo, bridgePdo = NULL;
  328. //
  329. // Acquire the Arbiter lock for the parent FDO (ie the
  330. // bridge this device lives under).
  331. //
  332. // Attempt to acquire the range needed. If that fails,
  333. // attempt to find a memory range the device already has
  334. // and move it to an invalid range then give the ROM the
  335. // memory that used to be assigned to that memory window.
  336. //
  337. currentPdo = PdoExtension;
  338. do {
  339. //
  340. // Find the PDO of the bridge - NULL for a root bus
  341. //
  342. if (PCI_PDO_ON_ROOT(currentPdo)) {
  343. bridgePdo = NULL;
  344. } else {
  345. bridgePdo = PCI_BRIDGE_PDO(PCI_PARENT_FDOX(currentPdo));
  346. }
  347. pciArbiter = PciFindSecondaryExtension(PCI_PARENT_FDOX(currentPdo),
  348. PciArb_Memory);
  349. if (!pciArbiter) {
  350. //
  351. // If this device is on a root bus and the root doesn't have an
  352. // arbiter something bad happened...
  353. //
  354. if (!bridgePdo) {
  355. ASSERT(pciArbiter);
  356. return STATUS_UNSUCCESSFUL;
  357. };
  358. //
  359. // We didn't find an arbiter - probably because this is a
  360. // subtractive decode bridge.
  361. //
  362. if (bridgePdo->Dependent.type1.SubtractiveDecode) {
  363. //
  364. // This is subtractive so we want to find the guy who
  365. // arbitrates our resources (so we move on up the tree)
  366. //
  367. currentPdo = bridgePdo;
  368. } else {
  369. //
  370. // We have a non-subtractive bridge without an arbiter -
  371. // something is wrong...
  372. //
  373. ASSERT(pciArbiter);
  374. return STATUS_UNSUCCESSFUL;
  375. }
  376. }
  377. } while (!pciArbiter);
  378. arbiter = &pciArbiter->CommonInstance;
  379. ArbAcquireArbiterLock(arbiter);
  380. //
  381. // Attempt to get this resource as an additional resource
  382. // within the ranges supported by this bridge.
  383. //
  384. rangeMin = requirement->u.Memory.MinimumAddress.QuadPart;
  385. rangeMax = requirement->u.Memory.MaximumAddress.QuadPart;
  386. //
  387. // If this is a PCI-PCI bridge then restrict this to the
  388. // non-prefetchable memory. Currently we don't enable
  389. // prefetchable memory cardbus so there is nothing to
  390. // do there.
  391. //
  392. // Note: ROM BARs are 32 bit only so limit to low 4GB).
  393. // Note: Is is not clear that we really need to limit to
  394. // non-prefetchable memory.
  395. //
  396. if (bridgePdo) {
  397. if (bridgePdo->HeaderType == PCI_BRIDGE_TYPE) {
  398. //
  399. // The 3 below is the index of the non-prefetchable
  400. // memory bar for a PCI-PCI bridge within it's resources
  401. // current settings.
  402. //
  403. resource = &bridgePdo->Resources->Current[3];
  404. if (resource->Type == CmResourceTypeNull) {
  405. //
  406. // Bridge isn't passing memory,.... so reading
  407. // ROMs isn't really an option.
  408. //
  409. PciDebugPrint(
  410. PciDbgROM,
  411. "PCI ROM pdo %p parent %p has no memory aperture.\n",
  412. PdoExtension,
  413. bridgePdo
  414. );
  415. ArbReleaseArbiterLock(arbiter);
  416. return STATUS_UNSUCCESSFUL;
  417. }
  418. ASSERT(resource->Type == CmResourceTypeMemory);
  419. rangeMin = resource->u.Memory.Start.QuadPart;
  420. rangeMax = rangeMin + (resource->u.Memory.Length - 1);
  421. }
  422. }
  423. status = RtlFindRange(
  424. arbiter->Allocation,
  425. rangeMin,
  426. rangeMax,
  427. requirement->u.Memory.Length,
  428. requirement->u.Memory.Alignment,
  429. 0,
  430. 0,
  431. NULL,
  432. NULL,
  433. &tempResourceStart);
  434. tempResource.u.Memory.Start.QuadPart = tempResourceStart;
  435. if (!NT_SUCCESS(status)) {
  436. ULONG i;
  437. //
  438. // If this is a cardbus controller then game over as stealing BARS
  439. // is not something we encourage and is not fatal if we fail.
  440. //
  441. if (bridgePdo && bridgePdo->HeaderType == PCI_CARDBUS_BRIDGE_TYPE) {
  442. ArbReleaseArbiterLock(arbiter);
  443. return STATUS_UNSUCCESSFUL;
  444. }
  445. //
  446. // We were unable to get enough space on this bus
  447. // given the existing ranges and resources being
  448. // consumed. Run down the list of memory resources
  449. // already assigned to this device and try to find
  450. // one which is large enough to cover the ROM and
  451. // appropriate aligned. (Note: look for the smallest
  452. // one meeting these requirements).
  453. //
  454. // Note: ROM BARs are only 32 bits so we cannot steal
  455. // a 64 bit BAR that has been assigned an address > 4GB-1.
  456. // We could allow the replacement range to be > 4GB-1 if
  457. // the BAR supports it but I'm not doing this on the first
  458. // pass. (plj).
  459. //
  460. for (i = 0; i < PCI_TYPE0_ADDRESSES; i++) {
  461. PIO_RESOURCE_DESCRIPTOR l = &PdoExtension->Resources->Limit[i];
  462. if ((l->Type == CmResourceTypeMemory) &&
  463. (l->u.Memory.Length >= requirement->u.Memory.Length) &&
  464. (PdoExtension->Resources->Current[i].u.Memory.Start.HighPart == 0)) {
  465. if ((!movedRequirement) ||
  466. (movedRequirement->u.Memory.Length >
  467. l->u.Memory.Length)) {
  468. movedRequirement = l;
  469. }
  470. }
  471. }
  472. if (!movedRequirement) {
  473. PciDebugPrint(
  474. PciDbgROM,
  475. "PCI ROM pdo %p could not get MEM resource len 0x%x.\n",
  476. PdoExtension,
  477. requirement->u.Memory.Length
  478. );
  479. ArbReleaseArbiterLock(arbiter);
  480. return STATUS_UNSUCCESSFUL;
  481. }
  482. //
  483. // Ok, we found a suitable candidate to move. Let's see
  484. // if we can find somewhere to put it that's out of the
  485. // way. We do this by allowing a conflict with ranges
  486. // not owned by this bus. We know the driver isn't
  487. // using this range at this instant so we can put it
  488. // somewhere where there's no way to use it then use
  489. // the space it occupied for the ROM.
  490. //
  491. status = RtlFindRange(arbiter->Allocation,
  492. 0,
  493. 0xffffffff,
  494. movedRequirement->u.Memory.Length,
  495. movedRequirement->u.Memory.Alignment,
  496. RTL_RANGE_LIST_NULL_CONFLICT_OK,
  497. 0,
  498. NULL,
  499. NULL,
  500. &movedAddress.QuadPart);
  501. if (!NT_SUCCESS(status)) {
  502. //
  503. // We were unable to find somewhere to move the
  504. // memory aperture to even allowing conflicts with
  505. // ranges not on this bus. This can't happen
  506. // unless the requirement is just plain bogus.
  507. //
  508. PciDebugPrint(
  509. PciDbgROM,
  510. "PCI ROM could find range to disable %x memory window.\n",
  511. movedRequirement->u.Memory.Length
  512. );
  513. ArbReleaseArbiterLock(arbiter);
  514. return STATUS_UNSUCCESSFUL;
  515. }
  516. movedIndex = (ULONG)(movedRequirement - PdoExtension->Resources->Limit);
  517. tempResource = PdoExtension->Resources->Current[movedIndex];
  518. PciDebugPrint(
  519. PciDbgROM,
  520. "PCI ROM Moving existing memory resource from %p to %p\n",
  521. tempResource.u.Memory.Start.LowPart,
  522. movedAddress.LowPart);
  523. }
  524. } else {
  525. //
  526. // The ROM is currently enabled on this device, translate and
  527. // map the current setting.
  528. //
  529. tempResource.u.Generic.Start.LowPart =
  530. oldRom & PCI_ADDRESS_ROM_ADDRESS_MASK;
  531. }
  532. tempResource.Type = CmResourceTypeMemory;
  533. tempResource.u.Memory.Start.HighPart = 0;
  534. tempResource.u.Memory.Length = requirement->u.Memory.Length;
  535. resource = &tempResource;
  536. //
  537. // The following need to be done regardless of whether
  538. // or not we had to go acquire resources.
  539. //
  540. // HalTranslateBusAddress
  541. // MmMapIoSpace
  542. //
  543. // Note: HalTranslateBusAddress has been hooked to call back
  544. // into the PCI driver which will then attempt to acquire the
  545. // arbiter lock on this bus. We can't release the lock as we
  546. // haven't really acquired this resource we're about to use.
  547. // We could trick PciTranslateBusAddress into not acquiring
  548. // the lock by calling it at dispatch level, or, we could
  549. // just call the saved (prehook) HAL function which is what
  550. // that routine ends up doing anyway.
  551. //
  552. ASSERT(PcipSavedTranslateBusAddress);
  553. translated = PcipSavedTranslateBusAddress(
  554. PCIBus,
  555. PCI_PARENT_FDOX(PdoExtension)->BaseBus,
  556. resource->u.Generic.Start,
  557. &addressIsIoSpace,
  558. &translatedAddress
  559. );
  560. //
  561. // NTRAID #62658 - 3/30/2001 - andrewth
  562. // If the resource won't translate it may be because the HAL doesn't
  563. // know about this bus. Try the translation of the root bus this is
  564. // under instead
  565. //
  566. if (!translated) {
  567. translated = PcipSavedTranslateBusAddress(
  568. PCIBus,
  569. PCI_PARENT_FDOX(PdoExtension)->BusRootFdoExtension->BaseBus,
  570. resource->u.Generic.Start,
  571. &addressIsIoSpace,
  572. &translatedAddress
  573. );
  574. }
  575. if (!translated) {
  576. PciDebugPrint(PciDbgROM,
  577. "PCI ROM range at %p FAILED to translate\n",
  578. resource->u.Generic.Start.LowPart);
  579. ASSERT(translated);
  580. status = STATUS_UNSUCCESSFUL;
  581. goto cleanup;
  582. }
  583. PciDebugPrint(PciDbgROM,
  584. "PCI ROM range at %p translated to %p\n",
  585. resource->u.Generic.Start.LowPart,
  586. translatedAddress.LowPart);
  587. if (!addressIsIoSpace) {
  588. //
  589. // Translated to memory, map it.
  590. //
  591. mapped = MmMapIoSpace(translatedAddress,
  592. requirement->u.Generic.Length,
  593. MmNonCached);
  594. if (!mapped) {
  595. //
  596. // Failed to get mapping.
  597. //
  598. ASSERT(mapped);
  599. status = STATUS_UNSUCCESSFUL;
  600. goto cleanup;
  601. }
  602. romBase = mapped;
  603. PciDebugPrint(
  604. PciDbgROM,
  605. "PCI ROM mapped b %08x t %08x to %p length %x bytes\n",
  606. resource->u.Generic.Start.LowPart,
  607. translatedAddress.LowPart,
  608. mapped,
  609. requirement->u.Generic.Length
  610. );
  611. } else {
  612. romBase = (PVOID)translatedAddress.QuadPart;
  613. //
  614. // NOTE - on alpha even if things are translated into ports from memory
  615. // you still access them using HAL_READ_MEMORY_* routines - YUCK!
  616. //
  617. PciDebugPrint(
  618. PciDbgROM,
  619. "PCI ROM b %08x t %08x IO length %x bytes\n",
  620. resource->u.Generic.Start.LowPart,
  621. translatedAddress.LowPart,
  622. requirement->u.Generic.Length
  623. );
  624. }
  625. if (acquiredResources == TRUE) {
  626. newRom = tempResource.u.Memory.Start.LowPart | PCI_ROMADDRESS_ENABLED;
  627. //
  628. // Disable IO, MEMory and DMA while we enable the rom h/w.
  629. //
  630. newStatusCommand &= ~(PCI_ENABLE_IO_SPACE |
  631. PCI_ENABLE_MEMORY_SPACE |
  632. PCI_ENABLE_BUS_MASTER);
  633. PciWriteDeviceConfig(
  634. PdoExtension,
  635. &newStatusCommand,
  636. FIELD_OFFSET(PCI_COMMON_CONFIG, Command),
  637. sizeof(ULONG)
  638. );
  639. //
  640. // WARNING: While in this state, the device cannot operate
  641. // normally.
  642. //
  643. // If we have to move a memory aperture to access the ROM
  644. // do so now.
  645. //
  646. if (movedRequirement) {
  647. PciWriteDeviceConfig(
  648. PdoExtension,
  649. &movedAddress.LowPart,
  650. FIELD_OFFSET(PCI_COMMON_CONFIG, u.type0.BaseAddresses) +
  651. movedIndex * sizeof(ULONG),
  652. sizeof(ULONG)
  653. );
  654. }
  655. //
  656. // Set the ROM address (+enable).
  657. //
  658. PciWriteDeviceConfig(
  659. PdoExtension,
  660. &newRom,
  661. FIELD_OFFSET(PCI_COMMON_CONFIG, u.type0.ROMBaseAddress),
  662. sizeof(ULONG)
  663. );
  664. //
  665. // Enable MEMory access to this device.
  666. //
  667. newStatusCommand |= PCI_ENABLE_MEMORY_SPACE;
  668. PciWriteDeviceConfig(
  669. PdoExtension,
  670. &newStatusCommand,
  671. FIELD_OFFSET(PCI_COMMON_CONFIG, Command),
  672. sizeof(ULONG)
  673. );
  674. }
  675. //
  676. // Copy the ROM to the caller's buffer. Any failure prior to
  677. // this step will cause us to skip this step.
  678. //
  679. imageBase = (PUCHAR)romBase;
  680. do {
  681. //
  682. // Get the header, check signature.
  683. //
  684. PciTransferRomData(imageBase, &header, sizeof(header));
  685. if (header.Signature != PCI_ROM_HEADER_SIGNATURE) {
  686. //
  687. // Not a valid ROM image, don't transfer anything.
  688. //
  689. PciDebugPrint(
  690. PciDbgROM,
  691. "PCI ROM invalid signature, offset %x, expected %04x, got %04x\n",
  692. imageBase - (PUCHAR)romBase,
  693. PCI_ROM_HEADER_SIGNATURE,
  694. header.Signature
  695. );
  696. break;
  697. }
  698. //
  699. // Get image data structure, check its signature and
  700. // get actual length.
  701. //
  702. PciTransferRomData(imageBase + header.DataStructureOffset,
  703. &dataStructure,
  704. sizeof(dataStructure));
  705. if (dataStructure.Signature != PCI_ROM_DATA_STRUCTURE_SIGNATURE) {
  706. //
  707. // Invalid data structure, bail.
  708. //
  709. PciDebugPrint(
  710. PciDbgROM,
  711. "PCI ROM invalid signature, offset %x, expected %08x, got %08x\n",
  712. imageBase - (PUCHAR)romBase + header.DataStructureOffset,
  713. PCI_ROM_DATA_STRUCTURE_SIGNATURE,
  714. dataStructure.Signature
  715. );
  716. break;
  717. }
  718. //
  719. // Image length is in units of 512 bytes. We presume
  720. // it's from the start of this image, ie imageBase, not
  721. // from the start of the code,... 'coz that wouldn't make
  722. // any sense.
  723. //
  724. imageLength = dataStructure.ImageLength * 512;
  725. if (imageLength > maximumLength) {
  726. //
  727. // Truncate to available buffer space.
  728. //
  729. imageLength = maximumLength;
  730. }
  731. //
  732. // Transfer this image to the caller's buffer.
  733. //
  734. PciTransferRomData(imageBase, Buffer, imageLength);
  735. //
  736. // Update pointers etc
  737. //
  738. Buffer = (PVOID)((PUCHAR)Buffer + imageLength);
  739. *Length += imageLength;
  740. imageBase += imageLength;
  741. maximumLength -= imageLength;
  742. if (dataStructure.Indicator & 0x80) {
  743. //
  744. // Indicator bit 7 == 1 means this was the last image.
  745. //
  746. break;
  747. }
  748. } while (maximumLength);
  749. cleanup:
  750. if (acquiredResources == TRUE) {
  751. NTSTATUS tmpSta;
  752. //
  753. // Disable memory decoding and disable ROM access.
  754. //
  755. if (newRom != oldRom) {
  756. newStatusCommand &= ~PCI_ENABLE_MEMORY_SPACE;
  757. //
  758. // Not much we can do if this fails.
  759. //
  760. PciWriteDeviceConfig(
  761. PdoExtension,
  762. &newStatusCommand,
  763. FIELD_OFFSET(PCI_COMMON_CONFIG, Command),
  764. sizeof(ULONG)
  765. );
  766. PciWriteDeviceConfig(
  767. PdoExtension,
  768. &oldRom,
  769. FIELD_OFFSET(PCI_COMMON_CONFIG, u.type0.ROMBaseAddress),
  770. sizeof(ULONG)
  771. );
  772. }
  773. //
  774. // If we moved someone to make room for the ROM, put them
  775. // back where they started off.
  776. //
  777. if (movedRequirement) {
  778. PciWriteDeviceConfig(
  779. PdoExtension,
  780. &PdoExtension->Resources->Current[movedIndex].u.Memory.Start.LowPart,
  781. FIELD_OFFSET(PCI_COMMON_CONFIG, u.type0.BaseAddresses) +
  782. movedIndex * sizeof(ULONG),
  783. sizeof(ULONG)
  784. );
  785. }
  786. //
  787. // Restore the command register to its original state.
  788. //
  789. if (newStatusCommand != oldStatusCommand) {
  790. PciWriteDeviceConfig(
  791. PdoExtension,
  792. &oldStatusCommand,
  793. FIELD_OFFSET(PCI_COMMON_CONFIG, Command),
  794. sizeof(ULONG)
  795. );
  796. }
  797. //
  798. // Release the arbiter lock (we're no longer using extraneous
  799. // resources so it should be safe to let someone else allocate
  800. // them.
  801. //
  802. ArbReleaseArbiterLock(arbiter);
  803. }
  804. if (mapped) {
  805. MmUnmapIoSpace(mapped, requirement->u.Generic.Length);
  806. }
  807. PciDebugPrint(
  808. PciDbgROM,
  809. "PCI ROM leaving pdox %08x (buffer @ %08x %08x bytes, status %08x)\n",
  810. PdoExtension,
  811. (PUCHAR)Buffer - *Length,
  812. *Length,
  813. status
  814. );
  815. return status;
  816. }