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
1167 lines
34 KiB
/*++
|
|
Copyright (c) 1998-2000 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
romimage.c
|
|
|
|
Abstract:
|
|
|
|
This module contains the code required to obtain a copy of a
|
|
device's ROM (Read Only Memory).
|
|
|
|
The PCI spec allows a device to share address decoding logic
|
|
between the ROM BAR (Base Address Registers) and other BARs.
|
|
Effectively, this means the ROM cannot be accessed at the same
|
|
time as the device is otherwise operating.
|
|
|
|
The ROM is accesible when both the ROM enabled bit is set and
|
|
memory decoding is enabled.
|
|
|
|
Author:
|
|
|
|
Peter Johnston (peterj) 15-Apr-1998
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
|
|
#include "pcip.h"
|
|
|
|
extern pHalTranslateBusAddress PcipSavedTranslateBusAddress;
|
|
|
|
typedef struct _PCI_ROM_HEADER {
|
|
USHORT Signature;
|
|
UCHAR RsvdArchitectureUnique[0x16];
|
|
USHORT DataStructureOffset;
|
|
} PCI_ROM_HEADER, *PPCI_ROM_HEADER;
|
|
|
|
typedef struct _PCI_DATA_STRUCTURE {
|
|
ULONG Signature;
|
|
USHORT VendorId;
|
|
USHORT DeviceId;
|
|
USHORT VitalProductDataOffset;
|
|
USHORT DataStructureLength;
|
|
UCHAR DataStructureRevision;
|
|
UCHAR ClassCode[3];
|
|
USHORT ImageLength;
|
|
USHORT ImageRevision;
|
|
UCHAR CodeType;
|
|
UCHAR Indicator;
|
|
USHORT Reserved;
|
|
} PCI_DATA_STRUCTURE, *PPCI_DATA_STRUCTURE;
|
|
|
|
typedef struct _PCI_ROM_ACCESS_PARAMETERS {
|
|
PVOID Buffer;
|
|
ULONG Length;
|
|
ULONG OriginalRomAddress;
|
|
ULONG NewRomAddress;
|
|
PUCHAR MappedRomAddress;
|
|
ULONG NewBarAddress;
|
|
ULONG DisplacedBarIndex;
|
|
BOOLEAN AcquiredResources;
|
|
ULONG OriginalStatusCommand;
|
|
} PCI_ROM_ACCESS_PARAMETERS, *PPCI_ROM_ACCESS_PARAMETERS;
|
|
|
|
#define PCI_ROM_HEADER_SIGNATURE 0xaa55
|
|
#define PCI_ROM_DATA_STRUCTURE_SIGNATURE 'RICP' // LE PCIR
|
|
|
|
//
|
|
// Prototypes for local routines.
|
|
//
|
|
|
|
NTSTATUS
|
|
PciRomTestWriteAccessToBuffer(
|
|
IN PUCHAR Buffer,
|
|
IN ULONG Length
|
|
);
|
|
|
|
VOID
|
|
PciTransferRomData(
|
|
IN PVOID RomAddress,
|
|
IN PVOID Buffer,
|
|
IN ULONG Length
|
|
);
|
|
|
|
VOID
|
|
PciAccessRom(
|
|
IN PPCI_PDO_EXTENSION PdoExtension,
|
|
IN PPCI_ROM_ACCESS_PARAMETERS RomAccessParameters
|
|
);
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
|
|
#pragma alloc_text(PAGE, PciReadRomImage)
|
|
#pragma alloc_text(PAGE, PciRomTestWriteAccessToBuffer)
|
|
|
|
#endif
|
|
|
|
VOID
|
|
PciTransferRomData(
|
|
IN PVOID RomAddress,
|
|
IN PVOID Buffer,
|
|
IN ULONG Length
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Simple abstraction of READ_REGISTER_BUFFER_Uxxxx()
|
|
|
|
Copies from ROM to an in memory buffer. Deals with alignment
|
|
and tries to use the most efficient means.
|
|
|
|
Arguments:
|
|
|
|
RomAddress Mapped/Translated address to copy from.
|
|
Buffer Memory address to copy to.
|
|
Length Number of BYTEs to copy.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
#define BLKSIZE sizeof(ULONG)
|
|
#define BLKMASK (BLKSIZE - 1)
|
|
|
|
ULONG temp;
|
|
|
|
if (Length > BLKSIZE) {
|
|
|
|
//
|
|
// Optimize for aligned case (typically, both will be perfectly
|
|
// aligned) and a multiple of DWORDs.
|
|
//
|
|
|
|
temp = (ULONG)((ULONG_PTR)RomAddress & BLKMASK);
|
|
if (temp == ((ULONG_PTR)Buffer & BLKMASK)) {
|
|
|
|
//
|
|
// Same alignment, (note: if not same alignment, we
|
|
// transfer byte by byte).
|
|
//
|
|
// Walk off any leading bytes...
|
|
//
|
|
|
|
if (temp != 0) {
|
|
|
|
//
|
|
// temp is offset from a dword boundary, get number of
|
|
// bytes to copy.
|
|
//
|
|
|
|
temp = BLKSIZE - temp;
|
|
|
|
READ_REGISTER_BUFFER_UCHAR(RomAddress, Buffer, temp);
|
|
|
|
Length -= temp;
|
|
Buffer = (PVOID)((PUCHAR)Buffer + temp);
|
|
RomAddress = (PVOID)((PUCHAR)RomAddress + temp);
|
|
}
|
|
|
|
if (Length > BLKSIZE) {
|
|
|
|
//
|
|
// Get as much as possible using DWORDS
|
|
//
|
|
|
|
temp = Length / BLKSIZE;
|
|
|
|
READ_REGISTER_BUFFER_ULONG(RomAddress, Buffer, temp);
|
|
|
|
temp = temp * BLKSIZE;
|
|
Length -= temp;
|
|
Buffer = (PVOID)((PUCHAR)Buffer + temp);
|
|
RomAddress = (PVOID)((PUCHAR)RomAddress + temp);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Finish any remaining bytes.
|
|
//
|
|
|
|
if (Length) {
|
|
READ_REGISTER_BUFFER_UCHAR(RomAddress, Buffer, Length);
|
|
}
|
|
|
|
#undef BLKMASK
|
|
#undef BLKSIZE
|
|
}
|
|
|
|
NTSTATUS
|
|
PciRomTestWriteAccessToBuffer(
|
|
IN PUCHAR Buffer,
|
|
IN ULONG Length
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Complete Paranoia. Make sure we can write every page in the
|
|
caller's buffer (assumes 4096 bytes per page) by writing to
|
|
every page.
|
|
|
|
We do this in a try block to avoid killing the system. The
|
|
hope is to avoid anything that might bugcheck the system while
|
|
we have changed the operating characteristics of the device.
|
|
|
|
Arguments:
|
|
|
|
Buffer Address of start of buffer.
|
|
Length Number of bytes in buffer.
|
|
|
|
Return Value:
|
|
|
|
Status.
|
|
|
|
--*/
|
|
|
|
{
|
|
PUCHAR endAddress = Buffer + Length - 1;
|
|
|
|
try {
|
|
|
|
while (Buffer <= endAddress) {
|
|
*Buffer = 0;
|
|
Buffer += 0x1000;
|
|
}
|
|
*endAddress = 0;
|
|
|
|
} except (EXCEPTION_EXECUTE_HANDLER) {
|
|
return GetExceptionCode();
|
|
}
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
PciReadRomImage(
|
|
IN PPCI_PDO_EXTENSION PdoExtension,
|
|
IN ULONG WhichSpace,
|
|
OUT PVOID Buffer,
|
|
IN ULONG Offset,
|
|
IN OUT PULONG Length
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Copy the device ROM to the caller's Buffer.
|
|
|
|
Arguments:
|
|
|
|
PdoExtension Device Extension of the device in question.
|
|
WhichSpace Indicates which part of the ROM image is required.
|
|
(Currently only the x86 BIOS image is supported,
|
|
can be expanded to pass back the Open FW image if
|
|
needed).
|
|
Buffer Address of the caller's data area.
|
|
Offset Offset from the start of the ROM image data should
|
|
be returned from. Currently not used, can be used
|
|
in the future to stage data.
|
|
Length Pointer to a ULONG containing the length of the
|
|
Buffer (requested length). The value is modified
|
|
to the actual data length.
|
|
|
|
|
|
Return Value:
|
|
|
|
Status of this operation.
|
|
|
|
--*/
|
|
|
|
{
|
|
PIO_RESOURCE_DESCRIPTOR requirement;
|
|
PIO_RESOURCE_DESCRIPTOR movedRequirement = NULL;
|
|
PCM_PARTIAL_RESOURCE_DESCRIPTOR resource;
|
|
CM_PARTIAL_RESOURCE_DESCRIPTOR tempResource;
|
|
BOOLEAN acquiredResources = TRUE;
|
|
BOOLEAN translated;
|
|
ULONG oldStatusCommand;
|
|
ULONG newStatusCommand;
|
|
ULONG oldRom;
|
|
ULONG newRom;
|
|
ULONG maximumLength;
|
|
NTSTATUS status;
|
|
PHYSICAL_ADDRESS translatedAddress;
|
|
ULONG addressIsIoSpace = 0;
|
|
PVOID mapped = NULL;
|
|
PVOID romBase;
|
|
PPCI_ARBITER_INSTANCE pciArbiter;
|
|
PARBITER_INSTANCE arbiter = NULL;
|
|
PHYSICAL_ADDRESS movedAddress;
|
|
ULONG movedIndex = MAXULONG ;
|
|
ULONGLONG tempResourceStart;
|
|
PCI_CRITICAL_ROUTINE_CONTEXT criticalContext;
|
|
PCI_ROM_ACCESS_PARAMETERS romParameters;
|
|
PUCHAR doubleBuffer;
|
|
|
|
PAGED_CODE();
|
|
|
|
PciDebugPrint(
|
|
PciDbgROM,
|
|
"PCI ROM entered for pdox %08x (buffer @ %08x %08x bytes)\n",
|
|
PdoExtension,
|
|
Buffer,
|
|
*Length
|
|
);
|
|
|
|
//
|
|
// Currently not very flexible, assert we can do what the
|
|
// caller wants.
|
|
//
|
|
|
|
PCI_ASSERT(Offset == 0);
|
|
PCI_ASSERT(WhichSpace == PCI_WHICHSPACE_ROM);
|
|
|
|
//
|
|
// Capture the length and set the returned length to 0. This
|
|
// will be set to the correct value any data is successfully
|
|
// returned.
|
|
//
|
|
|
|
maximumLength = *Length;
|
|
*Length = 0;
|
|
|
|
//
|
|
// Only do this for header type 0 (ie devices, not bridges,
|
|
// bridges actually can have ROMs,.... I don't know why and
|
|
// currently have no plan to support it).
|
|
//
|
|
|
|
if (PdoExtension->HeaderType != PCI_DEVICE_TYPE) {
|
|
return STATUS_INVALID_DEVICE_REQUEST;
|
|
}
|
|
|
|
//
|
|
// It's a device, does it use a ROM?
|
|
//
|
|
|
|
requirement = &PdoExtension->Resources->Limit[PCI_TYPE0_ADDRESSES];
|
|
|
|
if ((PdoExtension->Resources == NULL) ||
|
|
(requirement->Type == CmResourceTypeNull)) {
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// Special case. If Length == 0 on entry, caller wants to know
|
|
// what the length should be.
|
|
//
|
|
|
|
PCI_ASSERT((requirement->u.Generic.Length & 0x1ff) == 0);
|
|
|
|
if (maximumLength == 0) {
|
|
*Length = requirement->u.Generic.Length;
|
|
return STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
|
|
//
|
|
// Trim length to device maximum.
|
|
//
|
|
|
|
if (requirement->u.Generic.Length < maximumLength) {
|
|
maximumLength = requirement->u.Generic.Length;
|
|
}
|
|
|
|
//
|
|
// Paranoia1: This device is probably video. If the system
|
|
// bugchecks while we have the device' memory access in limbo,
|
|
// the system will appear to hung. Reduce the possibility of
|
|
// bugcheck by ensuring we have (write) access to the caller's
|
|
// buffer.
|
|
//
|
|
|
|
status = PciRomTestWriteAccessToBuffer(Buffer, maximumLength);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
PCI_ASSERT(NT_SUCCESS(status));
|
|
return status;
|
|
}
|
|
|
|
PCI_ASSERT(requirement->Type == CmResourceTypeMemory);
|
|
PCI_ASSERT(requirement->Flags == CM_RESOURCE_MEMORY_READ_ONLY);
|
|
|
|
//
|
|
// Get current settings for the command register and the ROM BAR.
|
|
//
|
|
|
|
PciReadDeviceConfig(
|
|
PdoExtension,
|
|
&oldStatusCommand,
|
|
FIELD_OFFSET(PCI_COMMON_CONFIG, Command),
|
|
sizeof(ULONG)
|
|
);
|
|
|
|
PciReadDeviceConfig(
|
|
PdoExtension,
|
|
&oldRom,
|
|
FIELD_OFFSET(PCI_COMMON_CONFIG, u.type0.ROMBaseAddress),
|
|
sizeof(ULONG)
|
|
);
|
|
|
|
//
|
|
// Zero the upper 16 bits of the Status/Command variable so the
|
|
// Status field in h/w is unchanged in subsequent writes. (Bits
|
|
// in the Status field are cleared by writing ones to them).
|
|
//
|
|
|
|
oldStatusCommand &= 0xffff;
|
|
|
|
newStatusCommand = oldStatusCommand;
|
|
newRom = oldRom;
|
|
|
|
//
|
|
// If access to the ROM is already enabled, and memory is
|
|
// currently enabled, we already have access to the image.
|
|
// (I've never actually seen that condition. plj).
|
|
// Otherwise, we need to get PnP to allocate the range.
|
|
//
|
|
|
|
|
|
if (PdoExtension->Resources->Current[PCI_TYPE0_ADDRESSES].Type ==
|
|
CmResourceTypeMemory) {
|
|
|
|
PCI_ASSERT(oldRom & PCI_ROMADDRESS_ENABLED);
|
|
|
|
if (oldStatusCommand & PCI_ENABLE_MEMORY_SPACE) {
|
|
|
|
//
|
|
// No need to acquire resources.
|
|
//
|
|
|
|
acquiredResources = FALSE;
|
|
}
|
|
} else {
|
|
PCI_ASSERT(PdoExtension->Resources->Current[PCI_TYPE0_ADDRESSES].Type ==
|
|
CmResourceTypeNull);
|
|
}
|
|
|
|
movedAddress.QuadPart = 0;
|
|
|
|
//
|
|
// Allocate a memory resource to access the ROM with.
|
|
//
|
|
|
|
if (acquiredResources == TRUE) {
|
|
|
|
ULONGLONG rangeMin, rangeMax;
|
|
PPCI_PDO_EXTENSION currentPdo, bridgePdo = NULL;
|
|
|
|
|
|
|
|
//
|
|
// Acquire the Arbiter lock for the parent FDO (ie the
|
|
// bridge this device lives under).
|
|
//
|
|
// Attempt to acquire the range needed. If that fails,
|
|
// attempt to find a memory range the device already has
|
|
// and move it to an invalid range then give the ROM the
|
|
// memory that used to be assigned to that memory window.
|
|
//
|
|
|
|
currentPdo = PdoExtension;
|
|
do {
|
|
|
|
//
|
|
// Find the PDO of the bridge - NULL for a root bus
|
|
//
|
|
|
|
if (PCI_PDO_ON_ROOT(currentPdo)) {
|
|
|
|
bridgePdo = NULL;
|
|
|
|
} else {
|
|
|
|
bridgePdo = PCI_BRIDGE_PDO(PCI_PARENT_FDOX(currentPdo));
|
|
|
|
}
|
|
|
|
pciArbiter = PciFindSecondaryExtension(PCI_PARENT_FDOX(currentPdo),
|
|
PciArb_Memory);
|
|
|
|
if (!pciArbiter) {
|
|
|
|
//
|
|
// If this device is on a root bus and the root doesn't have an
|
|
// arbiter something bad happened...
|
|
//
|
|
|
|
if (!bridgePdo) {
|
|
PCI_ASSERT(pciArbiter);
|
|
return STATUS_UNSUCCESSFUL;
|
|
};
|
|
|
|
//
|
|
// We didn't find an arbiter - probably because this is a
|
|
// subtractive decode bridge.
|
|
//
|
|
|
|
|
|
if (bridgePdo->Dependent.type1.SubtractiveDecode) {
|
|
|
|
//
|
|
// This is subtractive so we want to find the guy who
|
|
// arbitrates our resources (so we move on up the tree)
|
|
//
|
|
|
|
currentPdo = bridgePdo;
|
|
|
|
} else {
|
|
|
|
//
|
|
// We have a non-subtractive bridge without an arbiter -
|
|
// something is wrong...
|
|
//
|
|
|
|
PCI_ASSERT(pciArbiter);
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
}
|
|
|
|
} while (!pciArbiter);
|
|
|
|
|
|
arbiter = &pciArbiter->CommonInstance;
|
|
|
|
ArbAcquireArbiterLock(arbiter);
|
|
|
|
//
|
|
// Attempt to get this resource as an additional resource
|
|
// within the ranges supported by this bridge.
|
|
//
|
|
|
|
rangeMin = requirement->u.Memory.MinimumAddress.QuadPart;
|
|
rangeMax = requirement->u.Memory.MaximumAddress.QuadPart;
|
|
|
|
//
|
|
// If this is a PCI-PCI bridge then restrict this to the
|
|
// non-prefetchable memory. Currently we don't enable
|
|
// prefetchable memory cardbus so there is nothing to
|
|
// do there.
|
|
//
|
|
// Note: ROM BARs are 32 bit only so limit to low 4GB).
|
|
// Note: Is is not clear that we really need to limit to
|
|
// non-prefetchable memory.
|
|
//
|
|
|
|
if (bridgePdo) {
|
|
|
|
if (bridgePdo->HeaderType == PCI_BRIDGE_TYPE) {
|
|
|
|
//
|
|
// The 3 below is the index of the non-prefetchable
|
|
// memory bar for a PCI-PCI bridge within it's resources
|
|
// current settings.
|
|
//
|
|
|
|
resource = &bridgePdo->Resources->Current[3];
|
|
if (resource->Type == CmResourceTypeNull) {
|
|
|
|
//
|
|
// Bridge isn't passing memory,.... so reading
|
|
// ROMs isn't really an option.
|
|
//
|
|
|
|
PciDebugPrint(
|
|
PciDbgROM,
|
|
"PCI ROM pdo %p parent %p has no memory aperture.\n",
|
|
PdoExtension,
|
|
bridgePdo
|
|
);
|
|
ArbReleaseArbiterLock(arbiter);
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
PCI_ASSERT(resource->Type == CmResourceTypeMemory);
|
|
rangeMin = resource->u.Memory.Start.QuadPart;
|
|
rangeMax = rangeMin + (resource->u.Memory.Length - 1);
|
|
}
|
|
}
|
|
|
|
status = RtlFindRange(
|
|
arbiter->Allocation,
|
|
rangeMin,
|
|
rangeMax,
|
|
requirement->u.Memory.Length,
|
|
requirement->u.Memory.Alignment,
|
|
0,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
&tempResourceStart);
|
|
|
|
tempResource.u.Memory.Start.QuadPart = tempResourceStart;
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
ULONG i;
|
|
|
|
//
|
|
// If this is a cardbus controller then game over as stealing BARS
|
|
// is not something we encourage and is not fatal if we fail.
|
|
//
|
|
|
|
if (bridgePdo && bridgePdo->HeaderType == PCI_CARDBUS_BRIDGE_TYPE) {
|
|
ArbReleaseArbiterLock(arbiter);
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
//
|
|
// We were unable to get enough space on this bus
|
|
// given the existing ranges and resources being
|
|
// consumed. Run down the list of memory resources
|
|
// already assigned to this device and try to find
|
|
// one which is large enough to cover the ROM and
|
|
// appropriate aligned. (Note: look for the smallest
|
|
// one meeting these requirements).
|
|
//
|
|
// Note: ROM BARs are only 32 bits so we cannot steal
|
|
// a 64 bit BAR that has been assigned an address > 4GB-1.
|
|
// We could allow the replacement range to be > 4GB-1 if
|
|
// the BAR supports it but I'm not doing this on the first
|
|
// pass. (plj).
|
|
//
|
|
|
|
|
|
for (i = 0; i < PCI_TYPE0_ADDRESSES; i++) {
|
|
|
|
PIO_RESOURCE_DESCRIPTOR l = &PdoExtension->Resources->Limit[i];
|
|
|
|
if ((l->Type == CmResourceTypeMemory) &&
|
|
(l->u.Memory.Length >= requirement->u.Memory.Length) &&
|
|
(PdoExtension->Resources->Current[i].u.Memory.Start.HighPart == 0)) {
|
|
if ((!movedRequirement) ||
|
|
(movedRequirement->u.Memory.Length >
|
|
l->u.Memory.Length)) {
|
|
movedRequirement = l;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!movedRequirement) {
|
|
PciDebugPrint(
|
|
PciDbgROM,
|
|
"PCI ROM pdo %p could not get MEM resource len 0x%x.\n",
|
|
PdoExtension,
|
|
requirement->u.Memory.Length
|
|
);
|
|
ArbReleaseArbiterLock(arbiter);
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
//
|
|
// Ok, we found a suitable candidate to move. Let's see
|
|
// if we can find somewhere to put it that's out of the
|
|
// way. We do this by allowing a conflict with ranges
|
|
// not owned by this bus. We know the driver isn't
|
|
// using this range at this instant so we can put it
|
|
// somewhere where there's no way to use it then use
|
|
// the space it occupied for the ROM.
|
|
//
|
|
|
|
status = RtlFindRange(arbiter->Allocation,
|
|
0,
|
|
0xffffffff,
|
|
movedRequirement->u.Memory.Length,
|
|
movedRequirement->u.Memory.Alignment,
|
|
RTL_RANGE_LIST_NULL_CONFLICT_OK,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
&movedAddress.QuadPart);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
//
|
|
// We were unable to find somewhere to move the
|
|
// memory aperture to even allowing conflicts with
|
|
// ranges not on this bus. This can't happen
|
|
// unless the requirement is just plain bogus.
|
|
//
|
|
|
|
PciDebugPrint(
|
|
PciDbgROM,
|
|
"PCI ROM could find range to disable %x memory window.\n",
|
|
movedRequirement->u.Memory.Length
|
|
);
|
|
ArbReleaseArbiterLock(arbiter);
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
movedIndex = (ULONG)(movedRequirement - PdoExtension->Resources->Limit);
|
|
tempResource = PdoExtension->Resources->Current[movedIndex];
|
|
PciDebugPrint(
|
|
PciDbgROM,
|
|
"PCI ROM Moving existing memory resource from %p to %p\n",
|
|
tempResource.u.Memory.Start.LowPart,
|
|
movedAddress.LowPart);
|
|
}
|
|
} else {
|
|
|
|
//
|
|
// The ROM is currently enabled on this device, translate and
|
|
// map the current setting.
|
|
//
|
|
|
|
tempResource.u.Generic.Start.LowPart =
|
|
oldRom & PCI_ADDRESS_ROM_ADDRESS_MASK;
|
|
}
|
|
|
|
tempResource.Type = CmResourceTypeMemory;
|
|
tempResource.u.Memory.Start.HighPart = 0;
|
|
tempResource.u.Memory.Length = requirement->u.Memory.Length;
|
|
resource = &tempResource;
|
|
|
|
//
|
|
// The following need to be done regardless of whether
|
|
// or not we had to go acquire resources.
|
|
//
|
|
// HalTranslateBusAddress
|
|
// MmMapIoSpace
|
|
//
|
|
// Note: HalTranslateBusAddress has been hooked to call back
|
|
// into the PCI driver which will then attempt to acquire the
|
|
// arbiter lock on this bus. We can't release the lock as we
|
|
// haven't really acquired this resource we're about to use.
|
|
// We could trick PciTranslateBusAddress into not acquiring
|
|
// the lock by calling it at dispatch level, or, we could
|
|
// just call the saved (prehook) HAL function which is what
|
|
// that routine ends up doing anyway.
|
|
//
|
|
|
|
PCI_ASSERT(PcipSavedTranslateBusAddress);
|
|
|
|
translated = PcipSavedTranslateBusAddress(
|
|
PCIBus,
|
|
PCI_PARENT_FDOX(PdoExtension)->BaseBus,
|
|
resource->u.Generic.Start,
|
|
&addressIsIoSpace,
|
|
&translatedAddress
|
|
);
|
|
|
|
//
|
|
// NTRAID #62658 - 3/30/2001 - andrewth
|
|
// If the resource won't translate it may be because the HAL doesn't
|
|
// know about this bus. Try the translation of the root bus this is
|
|
// under instead
|
|
//
|
|
|
|
if (!translated) {
|
|
|
|
translated = PcipSavedTranslateBusAddress(
|
|
PCIBus,
|
|
PCI_PARENT_FDOX(PdoExtension)->BusRootFdoExtension->BaseBus,
|
|
resource->u.Generic.Start,
|
|
&addressIsIoSpace,
|
|
&translatedAddress
|
|
);
|
|
|
|
}
|
|
|
|
if (!translated) {
|
|
PciDebugPrint(PciDbgROM,
|
|
"PCI ROM range at %p FAILED to translate\n",
|
|
resource->u.Generic.Start.LowPart);
|
|
PCI_ASSERT(translated);
|
|
status = STATUS_UNSUCCESSFUL;
|
|
goto cleanup;
|
|
}
|
|
|
|
PciDebugPrint(PciDbgROM,
|
|
"PCI ROM range at %p translated to %p\n",
|
|
resource->u.Generic.Start.LowPart,
|
|
translatedAddress.LowPart);
|
|
|
|
if (!addressIsIoSpace) {
|
|
|
|
//
|
|
// Translated to memory, map it.
|
|
//
|
|
|
|
mapped = MmMapIoSpace(translatedAddress,
|
|
requirement->u.Generic.Length,
|
|
MmNonCached);
|
|
|
|
if (!mapped) {
|
|
|
|
//
|
|
// Failed to get mapping.
|
|
//
|
|
|
|
PCI_ASSERT(mapped);
|
|
status = STATUS_UNSUCCESSFUL;
|
|
goto cleanup;
|
|
}
|
|
|
|
romBase = mapped;
|
|
|
|
PciDebugPrint(
|
|
PciDbgROM,
|
|
"PCI ROM mapped b %08x t %08x to %p length %x bytes\n",
|
|
resource->u.Generic.Start.LowPart,
|
|
translatedAddress.LowPart,
|
|
mapped,
|
|
requirement->u.Generic.Length
|
|
);
|
|
|
|
} else {
|
|
|
|
romBase = (PVOID)((ULONG_PTR)translatedAddress.LowPart);
|
|
|
|
//
|
|
// NOTE - on alpha even if things are translated into ports from memory
|
|
// you still access them using HAL_READ_MEMORY_* routines - YUCK!
|
|
//
|
|
|
|
PciDebugPrint(
|
|
PciDbgROM,
|
|
"PCI ROM b %08x t %08x IO length %x bytes\n",
|
|
resource->u.Generic.Start.LowPart,
|
|
translatedAddress.LowPart,
|
|
requirement->u.Generic.Length
|
|
);
|
|
|
|
}
|
|
|
|
romParameters.AcquiredResources = acquiredResources;
|
|
romParameters.OriginalStatusCommand = oldStatusCommand;
|
|
romParameters.OriginalRomAddress = oldRom;
|
|
romParameters.NewRomAddress = resource->u.Memory.Start.LowPart;
|
|
romParameters.MappedRomAddress = (PUCHAR)romBase;
|
|
romParameters.NewBarAddress = movedAddress.LowPart;
|
|
romParameters.DisplacedBarIndex = movedIndex;
|
|
romParameters.Length = maximumLength;
|
|
|
|
if (acquiredResources &&
|
|
(PdoExtension->HackFlags & PCI_HACK_CRITICAL_DEVICE)) {
|
|
|
|
doubleBuffer = (PUCHAR)ExAllocatePool(NonPagedPool, maximumLength);
|
|
if (!doubleBuffer) {
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto cleanup;
|
|
}
|
|
romParameters.Buffer = doubleBuffer;
|
|
|
|
criticalContext.Gate = 1;
|
|
criticalContext.Barrier = 1;
|
|
criticalContext.Routine = PciAccessRom;
|
|
criticalContext.Extension = PdoExtension;
|
|
criticalContext.Context = &romParameters;
|
|
KeIpiGenericCall(PciExecuteCriticalSystemRoutine,
|
|
(ULONG_PTR)&criticalContext
|
|
);
|
|
|
|
if (romParameters.Length != 0) {
|
|
RtlCopyMemory(Buffer, doubleBuffer, romParameters.Length);
|
|
}
|
|
ExFreePool(doubleBuffer);
|
|
|
|
} else {
|
|
|
|
romParameters.Buffer = Buffer;
|
|
PciAccessRom(PdoExtension, &romParameters);
|
|
}
|
|
|
|
*Length = romParameters.Length;
|
|
|
|
cleanup:
|
|
|
|
if (acquiredResources == TRUE) {
|
|
|
|
//
|
|
// Release the arbiter lock (we're no longer using extraneous
|
|
// resources so it should be safe to let someone else allocate
|
|
// them.
|
|
//
|
|
if (arbiter) {
|
|
ArbReleaseArbiterLock(arbiter);
|
|
}
|
|
}
|
|
if (mapped) {
|
|
MmUnmapIoSpace(mapped, requirement->u.Generic.Length);
|
|
}
|
|
PciDebugPrint(
|
|
PciDbgROM,
|
|
"PCI ROM leaving pdox %08x (buffer @ %08x %08x bytes, status %08x)\n",
|
|
PdoExtension,
|
|
(PUCHAR)Buffer - *Length,
|
|
*Length,
|
|
status
|
|
);
|
|
return status;
|
|
}
|
|
|
|
VOID
|
|
PciAccessRom(
|
|
IN PPCI_PDO_EXTENSION PdoExtension,
|
|
IN PPCI_ROM_ACCESS_PARAMETERS RomAccessParameters
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Does the work of programming the hardware and transferring
|
|
the data to the caller's buffer after all the values that
|
|
should be programmed have already been configured above.
|
|
|
|
Arguments:
|
|
|
|
PdoExtension - Device Extension of the device in question.
|
|
|
|
RomAccessParameters - A structure containing the parameters
|
|
for the call. These are packed into this structure format
|
|
to fit into the calling convention required by
|
|
PciExecuteCriticalSystemRoutine. The parameters are:
|
|
|
|
Buffer - The buffer to write the ROM data into
|
|
Length - On input, the length of the buffer. On output,
|
|
contains the length of the data transferred.
|
|
OriginalRomAddress - The address programmed into the ROM BAR
|
|
when we started.
|
|
NewRomAddress - The new address to program into the ROM BAR
|
|
to transfer the data.
|
|
MappedRomAddress - A mapping of the ROM BAR for data transfer.
|
|
DisplacedBarIndex - If space for the ROM was found by stealing
|
|
space from a BAR, the index of the BAR that was displaced.
|
|
Otherwise, MAXULONG.
|
|
NewBarAddress - If DisplacedBarIndex is valid, the address
|
|
to write into the displaced BAR to prevent it from decoding.
|
|
AcquiredResources - TRUE if we had to find space for the ROM somewhere.
|
|
FALSE if the ROM was already active and decoding when we found it.
|
|
OriginalStatusCommand - The value of the status and command registers
|
|
when we found the device.
|
|
|
|
|
|
Return Value:
|
|
|
|
VOID.
|
|
|
|
--*/
|
|
{
|
|
ULONG statusCommand;
|
|
ULONG romBar = 0;
|
|
PUCHAR mappedRom;
|
|
PUCHAR imageBase;
|
|
ULONG imageLength;
|
|
ULONG maximumLength;
|
|
ULONG barIndex;
|
|
PCI_ROM_HEADER header;
|
|
PCI_DATA_STRUCTURE dataStructure;
|
|
PUCHAR bufferPointer;
|
|
ULONG lengthTransferred;
|
|
|
|
barIndex = RomAccessParameters->DisplacedBarIndex;
|
|
|
|
if (RomAccessParameters->AcquiredResources) {
|
|
|
|
//
|
|
// Disable IO, MEMory and DMA while we enable the rom h/w.
|
|
//
|
|
statusCommand = RomAccessParameters->OriginalStatusCommand &
|
|
~(PCI_ENABLE_IO_SPACE |
|
|
PCI_ENABLE_MEMORY_SPACE |
|
|
PCI_ENABLE_BUS_MASTER
|
|
);
|
|
|
|
PciWriteDeviceConfig(
|
|
PdoExtension,
|
|
&statusCommand,
|
|
FIELD_OFFSET(PCI_COMMON_CONFIG, Command),
|
|
sizeof(ULONG)
|
|
);
|
|
|
|
//
|
|
// WARNING: While in this state, the device cannot operate
|
|
// normally.
|
|
//
|
|
// If we have to move a memory aperture to access the ROM
|
|
// do so now.
|
|
//
|
|
if (barIndex < PCI_TYPE0_ADDRESSES) {
|
|
|
|
PciWriteDeviceConfig(
|
|
PdoExtension,
|
|
&RomAccessParameters->NewBarAddress,
|
|
FIELD_OFFSET(PCI_COMMON_CONFIG, u.type0.BaseAddresses[barIndex]),
|
|
sizeof(ULONG)
|
|
);
|
|
}
|
|
|
|
//
|
|
// Set the ROM address (+enable).
|
|
//
|
|
romBar = RomAccessParameters->NewRomAddress | PCI_ROMADDRESS_ENABLED;
|
|
|
|
PciWriteDeviceConfig(
|
|
PdoExtension,
|
|
&romBar,
|
|
FIELD_OFFSET(PCI_COMMON_CONFIG, u.type0.ROMBaseAddress),
|
|
sizeof(ULONG)
|
|
);
|
|
|
|
//
|
|
// Enable MEMory access to this device.
|
|
//
|
|
statusCommand |= PCI_ENABLE_MEMORY_SPACE;
|
|
|
|
PciWriteDeviceConfig(
|
|
PdoExtension,
|
|
&statusCommand,
|
|
FIELD_OFFSET(PCI_COMMON_CONFIG, Command),
|
|
sizeof(ULONG)
|
|
);
|
|
}
|
|
|
|
|
|
//
|
|
// Copy the ROM to the caller's buffer.
|
|
//
|
|
mappedRom = RomAccessParameters->MappedRomAddress;
|
|
imageBase = mappedRom;
|
|
bufferPointer = (PUCHAR)RomAccessParameters->Buffer;
|
|
maximumLength = RomAccessParameters->Length;
|
|
lengthTransferred = 0;
|
|
|
|
do {
|
|
|
|
//
|
|
// Get the header, check signature.
|
|
//
|
|
PciTransferRomData(imageBase, &header, sizeof(header));
|
|
|
|
if (header.Signature != PCI_ROM_HEADER_SIGNATURE) {
|
|
|
|
//
|
|
// Not a valid ROM image, don't transfer anything.
|
|
//
|
|
PciDebugPrint(
|
|
PciDbgROM,
|
|
"PCI ROM invalid signature, offset %x, expected %04x, got %04x\n",
|
|
imageBase - (PUCHAR)mappedRom,
|
|
PCI_ROM_HEADER_SIGNATURE,
|
|
header.Signature
|
|
);
|
|
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Get image data structure, check its signature and
|
|
// get actual length.
|
|
//
|
|
PciTransferRomData(imageBase + header.DataStructureOffset,
|
|
&dataStructure,
|
|
sizeof(dataStructure)
|
|
);
|
|
|
|
if (dataStructure.Signature != PCI_ROM_DATA_STRUCTURE_SIGNATURE) {
|
|
|
|
//
|
|
// Invalid data structure, bail.
|
|
//
|
|
PciDebugPrint(
|
|
PciDbgROM,
|
|
"PCI ROM invalid signature, offset %x, expected %08x, got %08x\n",
|
|
imageBase - (PUCHAR)mappedRom + header.DataStructureOffset,
|
|
PCI_ROM_DATA_STRUCTURE_SIGNATURE,
|
|
dataStructure.Signature
|
|
);
|
|
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Image length is in units of 512 bytes. We presume
|
|
// it's from the start of this image, ie imageBase, not
|
|
// from the start of the code,... 'coz that wouldn't make
|
|
// any sense.
|
|
//
|
|
imageLength = dataStructure.ImageLength * 512;
|
|
|
|
if (imageLength > maximumLength) {
|
|
|
|
//
|
|
// Truncate to available buffer space.
|
|
//
|
|
imageLength = maximumLength;
|
|
}
|
|
|
|
//
|
|
// Transfer this image to the caller's buffer.
|
|
//
|
|
PciTransferRomData(imageBase, bufferPointer, imageLength);
|
|
|
|
//
|
|
// Update pointers etc
|
|
//
|
|
bufferPointer += imageLength;
|
|
lengthTransferred += imageLength;
|
|
imageBase += imageLength;
|
|
maximumLength -= imageLength;
|
|
|
|
if (dataStructure.Indicator & 0x80) {
|
|
|
|
//
|
|
// Indicator bit 7 == 1 means this was the last image.
|
|
//
|
|
|
|
break;
|
|
}
|
|
} while (maximumLength);
|
|
|
|
|
|
if (RomAccessParameters->AcquiredResources) {
|
|
|
|
//
|
|
// Disable memory decoding and disable ROM access.
|
|
//
|
|
statusCommand &= ~PCI_ENABLE_MEMORY_SPACE;
|
|
|
|
PciWriteDeviceConfig(
|
|
PdoExtension,
|
|
&statusCommand,
|
|
FIELD_OFFSET(PCI_COMMON_CONFIG, Command),
|
|
sizeof(ULONG)
|
|
);
|
|
|
|
PciWriteDeviceConfig(
|
|
PdoExtension,
|
|
&RomAccessParameters->OriginalRomAddress,
|
|
FIELD_OFFSET(PCI_COMMON_CONFIG, u.type0.ROMBaseAddress),
|
|
sizeof(ULONG)
|
|
);
|
|
|
|
//
|
|
// If we moved someone to make room for the ROM, put them
|
|
// back where they started off.
|
|
//
|
|
if (barIndex < PCI_TYPE0_ADDRESSES) {
|
|
|
|
PciWriteDeviceConfig(
|
|
PdoExtension,
|
|
&PdoExtension->Resources->Current[barIndex].u.Memory.Start.LowPart,
|
|
FIELD_OFFSET(PCI_COMMON_CONFIG, u.type0.BaseAddresses[barIndex]),
|
|
sizeof(ULONG)
|
|
);
|
|
}
|
|
|
|
//
|
|
// Restore the command register to its original state.
|
|
//
|
|
PciWriteDeviceConfig(
|
|
PdoExtension,
|
|
&RomAccessParameters->OriginalStatusCommand,
|
|
FIELD_OFFSET(PCI_COMMON_CONFIG, Command),
|
|
sizeof(ULONG)
|
|
);
|
|
}
|
|
|
|
RomAccessParameters->Length = lengthTransferred;
|
|
}
|