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.

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