mirror of
https://github.com/reactos/reactos.git
synced 2024-08-19 18:02:55 +00:00
878 lines
32 KiB
C
878 lines
32 KiB
C
/*
|
|
* PROJECT: ReactOS PCI Bus Driver
|
|
* LICENSE: BSD - See COPYING.ARM in the top level directory
|
|
* FILE: drivers/bus/pci/init.c
|
|
* PURPOSE: Driver Initialization
|
|
* PROGRAMMERS: ReactOS Portable Systems Group
|
|
*/
|
|
|
|
/* INCLUDES *******************************************************************/
|
|
|
|
#include <pci.h>
|
|
|
|
#define NDEBUG
|
|
#include <debug.h>
|
|
|
|
/* GLOBALS ********************************************************************/
|
|
|
|
BOOLEAN PciRunningDatacenter;
|
|
PDRIVER_OBJECT PciDriverObject;
|
|
KEVENT PciGlobalLock;
|
|
KEVENT PciBusLock;
|
|
KEVENT PciLegacyDescriptionLock;
|
|
BOOLEAN PciLockDeviceResources;
|
|
BOOLEAN PciEnableNativeModeATA;
|
|
ULONG PciSystemWideHackFlags;
|
|
PPCI_IRQ_ROUTING_TABLE PciIrqRoutingTable;
|
|
PWATCHDOG_TABLE WdTable;
|
|
PPCI_HACK_ENTRY PciHackTable;
|
|
|
|
/* FUNCTIONS ******************************************************************/
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
PciAcpiFindRsdt(OUT PACPI_BIOS_MULTI_NODE *AcpiMultiNode)
|
|
{
|
|
BOOLEAN Result;
|
|
NTSTATUS Status;
|
|
HANDLE KeyHandle, SubKey;
|
|
ULONG NumberOfBytes, i, Length;
|
|
PKEY_FULL_INFORMATION FullInfo;
|
|
PKEY_BASIC_INFORMATION KeyInfo;
|
|
PKEY_VALUE_PARTIAL_INFORMATION ValueInfo;
|
|
PACPI_BIOS_MULTI_NODE NodeData;
|
|
UNICODE_STRING ValueName;
|
|
struct
|
|
{
|
|
CM_FULL_RESOURCE_DESCRIPTOR Descriptor;
|
|
ACPI_BIOS_MULTI_NODE Node;
|
|
} *Package;
|
|
|
|
/* So we know what to free at the end of the body */
|
|
ValueInfo = NULL;
|
|
KeyInfo = NULL;
|
|
KeyHandle = NULL;
|
|
FullInfo = NULL;
|
|
Package = NULL;
|
|
do
|
|
{
|
|
/* Open the ACPI BIOS key */
|
|
Result = PciOpenKey(L"\\Registry\\Machine\\HARDWARE\\DESCRIPTION\\"
|
|
L"System\\MultiFunctionAdapter",
|
|
NULL,
|
|
KEY_QUERY_VALUE,
|
|
&KeyHandle,
|
|
&Status);
|
|
if (!Result) break;
|
|
|
|
/* Query how much space should be allocated for the key information */
|
|
Status = ZwQueryKey(KeyHandle,
|
|
KeyFullInformation,
|
|
NULL,
|
|
sizeof(ULONG),
|
|
&NumberOfBytes);
|
|
if (Status != STATUS_BUFFER_TOO_SMALL) break;
|
|
|
|
/* Allocate the space required */
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
FullInfo = ExAllocatePoolWithTag(PagedPool, NumberOfBytes, PCI_POOL_TAG);
|
|
if ( !FullInfo ) break;
|
|
|
|
/* Now query the key information that's needed */
|
|
Status = ZwQueryKey(KeyHandle,
|
|
KeyFullInformation,
|
|
FullInfo,
|
|
NumberOfBytes,
|
|
&NumberOfBytes);
|
|
if (!NT_SUCCESS(Status)) break;
|
|
|
|
/* Allocate enough space to hold the value information plus the name */
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
Length = FullInfo->MaxNameLen + 26;
|
|
KeyInfo = ExAllocatePoolWithTag(PagedPool, Length, PCI_POOL_TAG);
|
|
if ( !KeyInfo ) break;
|
|
|
|
/* Allocate the value information and name we expect to find */
|
|
ValueInfo = ExAllocatePoolWithTag(PagedPool,
|
|
sizeof(KEY_VALUE_PARTIAL_INFORMATION) +
|
|
sizeof(L"ACPI BIOS"),
|
|
PCI_POOL_TAG);
|
|
if (!ValueInfo) break;
|
|
|
|
/* Loop each sub-key */
|
|
i = 0;
|
|
while (TRUE)
|
|
{
|
|
/* Query each sub-key */
|
|
Status = ZwEnumerateKey(KeyHandle,
|
|
i++,
|
|
KeyBasicInformation,
|
|
KeyInfo,
|
|
Length,
|
|
&NumberOfBytes);
|
|
if (Status == STATUS_NO_MORE_ENTRIES) break;
|
|
|
|
/* Null-terminate the keyname, because the kernel does not */
|
|
KeyInfo->Name[KeyInfo->NameLength / sizeof(WCHAR)] = UNICODE_NULL;
|
|
|
|
/* Open this subkey */
|
|
Result = PciOpenKey(KeyInfo->Name,
|
|
KeyHandle,
|
|
KEY_QUERY_VALUE,
|
|
&SubKey,
|
|
&Status);
|
|
if (Result)
|
|
{
|
|
/* Query the identifier value for this subkey */
|
|
RtlInitUnicodeString(&ValueName, L"Identifier");
|
|
Status = ZwQueryValueKey(SubKey,
|
|
&ValueName,
|
|
KeyValuePartialInformation,
|
|
ValueInfo,
|
|
sizeof(KEY_VALUE_PARTIAL_INFORMATION) +
|
|
sizeof(L"ACPI BIOS"),
|
|
&NumberOfBytes);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
/* Check if this is the PCI BIOS subkey */
|
|
if (!wcsncmp((PWCHAR)ValueInfo->Data,
|
|
L"ACPI BIOS",
|
|
ValueInfo->DataLength))
|
|
{
|
|
/* It is, proceed to query the PCI IRQ routing table */
|
|
Status = PciGetRegistryValue(L"Configuration Data",
|
|
KeyInfo->Name,
|
|
KeyHandle,
|
|
REG_FULL_RESOURCE_DESCRIPTOR,
|
|
(PVOID*)&Package,
|
|
&NumberOfBytes);
|
|
ZwClose(SubKey);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Close the subkey and try the next one */
|
|
ZwClose(SubKey);
|
|
}
|
|
}
|
|
|
|
/* Check if we got here because the routing table was found */
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
/* This should only fail if we're out of entries */
|
|
ASSERT(Status == STATUS_NO_MORE_ENTRIES);
|
|
break;
|
|
}
|
|
|
|
/* Check if a descriptor was found */
|
|
if (!Package) break;
|
|
|
|
/* The configuration data is a resource list, and the BIOS node follows */
|
|
NodeData = &Package->Node;
|
|
|
|
/* How many E820 memory entries are there? */
|
|
Length = sizeof(ACPI_BIOS_MULTI_NODE) +
|
|
(NodeData->Count - 1) * sizeof(ACPI_E820_ENTRY);
|
|
|
|
/* Allocate the buffer needed to copy the information */
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
*AcpiMultiNode = ExAllocatePoolWithTag(NonPagedPool, Length, PCI_POOL_TAG);
|
|
if (!*AcpiMultiNode) break;
|
|
|
|
/* Copy the data */
|
|
RtlCopyMemory(*AcpiMultiNode, NodeData, Length);
|
|
Status = STATUS_SUCCESS;
|
|
} while (FALSE);
|
|
|
|
/* Close any opened keys, free temporary allocations, and return status */
|
|
if (Package) ExFreePoolWithTag(Package, 0);
|
|
if (ValueInfo) ExFreePoolWithTag(ValueInfo, 0);
|
|
if (KeyInfo) ExFreePoolWithTag(KeyInfo, 0);
|
|
if (FullInfo) ExFreePoolWithTag(FullInfo, 0);
|
|
if (KeyHandle) ZwClose(KeyHandle);
|
|
return Status;
|
|
}
|
|
|
|
PVOID
|
|
NTAPI
|
|
PciGetAcpiTable(IN ULONG TableCode)
|
|
{
|
|
PDESCRIPTION_HEADER Header;
|
|
PACPI_BIOS_MULTI_NODE AcpiMultiNode;
|
|
PRSDT Rsdt;
|
|
PXSDT Xsdt;
|
|
ULONG EntryCount, TableLength, Offset, CurrentEntry;
|
|
PVOID TableBuffer, MappedAddress;
|
|
PHYSICAL_ADDRESS PhysicalAddress;
|
|
NTSTATUS Status;
|
|
|
|
/* Try to find the RSDT or XSDT */
|
|
Status = PciAcpiFindRsdt(&AcpiMultiNode);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
/* No ACPI on the machine */
|
|
DPRINT1("AcpiFindRsdt() Failed!\n");
|
|
return NULL;
|
|
}
|
|
|
|
/* Map the RSDT with the minimum size allowed */
|
|
MappedAddress = MmMapIoSpace(AcpiMultiNode->RsdtAddress,
|
|
sizeof(DESCRIPTION_HEADER),
|
|
MmNonCached);
|
|
Header = MappedAddress;
|
|
if (!Header) return NULL;
|
|
|
|
/* Check how big the table really is and get rid of the temporary header */
|
|
TableLength = Header->Length;
|
|
MmUnmapIoSpace(Header, sizeof(DESCRIPTION_HEADER));
|
|
Header = NULL;
|
|
|
|
/* Map its true size */
|
|
MappedAddress = MmMapIoSpace(AcpiMultiNode->RsdtAddress,
|
|
TableLength,
|
|
MmNonCached);
|
|
Rsdt = MappedAddress;
|
|
Xsdt = MappedAddress;
|
|
ExFreePoolWithTag(AcpiMultiNode, 0);
|
|
if (!Rsdt) return NULL;
|
|
|
|
/* Validate the table's signature */
|
|
if ((Rsdt->Header.Signature != RSDT_SIGNATURE) &&
|
|
(Rsdt->Header.Signature != XSDT_SIGNATURE))
|
|
{
|
|
/* Very bad: crash */
|
|
HalDisplayString("RSDT table contains invalid signature\r\n");
|
|
MmUnmapIoSpace(Rsdt, TableLength);
|
|
return NULL;
|
|
}
|
|
|
|
/* Smallest RSDT/XSDT is one without table entries */
|
|
Offset = FIELD_OFFSET(RSDT, Tables);
|
|
if (Rsdt->Header.Signature == XSDT_SIGNATURE)
|
|
{
|
|
/* Figure out total size of table and the offset */
|
|
TableLength = Xsdt->Header.Length;
|
|
if (TableLength < Offset) Offset = Xsdt->Header.Length;
|
|
|
|
/* The entries are each 64-bits, so count them */
|
|
EntryCount = (TableLength - Offset) / sizeof(PHYSICAL_ADDRESS);
|
|
}
|
|
else
|
|
{
|
|
/* Figure out total size of table and the offset */
|
|
TableLength = Rsdt->Header.Length;
|
|
if (TableLength < Offset) Offset = Rsdt->Header.Length;
|
|
|
|
/* The entries are each 32-bits, so count them */
|
|
EntryCount = (TableLength - Offset) / sizeof(ULONG);
|
|
}
|
|
|
|
/* Start at the beginning of the array and loop it */
|
|
for (CurrentEntry = 0; CurrentEntry < EntryCount; CurrentEntry++)
|
|
{
|
|
/* Are we using the XSDT? */
|
|
if (Rsdt->Header.Signature != XSDT_SIGNATURE)
|
|
{
|
|
/* Read the 32-bit physical address */
|
|
PhysicalAddress.QuadPart = Rsdt->Tables[CurrentEntry];
|
|
}
|
|
else
|
|
{
|
|
/* Read the 64-bit physical address */
|
|
PhysicalAddress = Xsdt->Tables[CurrentEntry];
|
|
}
|
|
|
|
/* Map this table */
|
|
Header = MmMapIoSpace(PhysicalAddress,
|
|
sizeof(DESCRIPTION_HEADER),
|
|
MmNonCached);
|
|
if (!Header) break;
|
|
|
|
/* Check if this is the table that's being asked for */
|
|
if (Header->Signature == TableCode)
|
|
{
|
|
/* Allocate a buffer for it */
|
|
TableBuffer = ExAllocatePoolWithTag(PagedPool,
|
|
Header->Length,
|
|
PCI_POOL_TAG);
|
|
if (!TableBuffer) break;
|
|
|
|
/* Copy the table into the buffer */
|
|
RtlCopyMemory(TableBuffer, Header, Header->Length);
|
|
}
|
|
|
|
/* Done with this table, keep going */
|
|
MmUnmapIoSpace(Header, sizeof(DESCRIPTION_HEADER));
|
|
}
|
|
|
|
if (Header) MmUnmapIoSpace(Header, sizeof(DESCRIPTION_HEADER));
|
|
return NULL;
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
PciGetIrqRoutingTableFromRegistry(OUT PPCI_IRQ_ROUTING_TABLE *PciRoutingTable)
|
|
{
|
|
BOOLEAN Result;
|
|
NTSTATUS Status;
|
|
HANDLE KeyHandle, SubKey;
|
|
ULONG NumberOfBytes, i, Length;
|
|
PKEY_FULL_INFORMATION FullInfo;
|
|
PKEY_BASIC_INFORMATION KeyInfo;
|
|
PKEY_VALUE_PARTIAL_INFORMATION ValueInfo;
|
|
UNICODE_STRING ValueName;
|
|
struct
|
|
{
|
|
CM_FULL_RESOURCE_DESCRIPTOR Descriptor;
|
|
PCI_IRQ_ROUTING_TABLE Table;
|
|
} *Package;
|
|
|
|
/* So we know what to free at the end of the body */
|
|
Package = NULL;
|
|
ValueInfo = NULL;
|
|
KeyInfo = NULL;
|
|
KeyHandle = NULL;
|
|
FullInfo = NULL;
|
|
do
|
|
{
|
|
/* Open the BIOS key */
|
|
Result = PciOpenKey(L"\\Registry\\Machine\\HARDWARE\\DESCRIPTION\\"
|
|
L"System\\MultiFunctionAdapter",
|
|
NULL,
|
|
KEY_QUERY_VALUE,
|
|
&KeyHandle,
|
|
&Status);
|
|
if (!Result) break;
|
|
|
|
/* Query how much space should be allocated for the key information */
|
|
Status = ZwQueryKey(KeyHandle,
|
|
KeyFullInformation,
|
|
NULL,
|
|
sizeof(ULONG),
|
|
&NumberOfBytes);
|
|
if (Status != STATUS_BUFFER_TOO_SMALL) break;
|
|
|
|
/* Allocate the space required */
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
FullInfo = ExAllocatePoolWithTag(PagedPool, NumberOfBytes, PCI_POOL_TAG);
|
|
if ( !FullInfo ) break;
|
|
|
|
/* Now query the key information that's needed */
|
|
Status = ZwQueryKey(KeyHandle,
|
|
KeyFullInformation,
|
|
FullInfo,
|
|
NumberOfBytes,
|
|
&NumberOfBytes);
|
|
if (!NT_SUCCESS(Status)) break;
|
|
|
|
/* Allocate enough space to hold the value information plus the name */
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
Length = FullInfo->MaxNameLen + 26;
|
|
KeyInfo = ExAllocatePoolWithTag(PagedPool, Length, PCI_POOL_TAG);
|
|
if (!KeyInfo) break;
|
|
|
|
/* Allocate the value information and name we expect to find */
|
|
ValueInfo = ExAllocatePoolWithTag(PagedPool,
|
|
sizeof(KEY_VALUE_PARTIAL_INFORMATION) +
|
|
sizeof(L"PCI BIOS"),
|
|
PCI_POOL_TAG);
|
|
if (!ValueInfo) break;
|
|
|
|
/* Loop each sub-key */
|
|
i = 0;
|
|
while (TRUE)
|
|
{
|
|
/* Query each sub-key */
|
|
Status = ZwEnumerateKey(KeyHandle,
|
|
i++,
|
|
KeyBasicInformation,
|
|
KeyInfo,
|
|
Length,
|
|
&NumberOfBytes);
|
|
if (Status == STATUS_NO_MORE_ENTRIES) break;
|
|
|
|
/* Null-terminate the keyname, because the kernel does not */
|
|
KeyInfo->Name[KeyInfo->NameLength / sizeof(WCHAR)] = UNICODE_NULL;
|
|
|
|
/* Open this subkey */
|
|
Result = PciOpenKey(KeyInfo->Name,
|
|
KeyHandle,
|
|
KEY_QUERY_VALUE,
|
|
&SubKey,
|
|
&Status);
|
|
if (Result)
|
|
{
|
|
/* Query the identifier value for this subkey */
|
|
RtlInitUnicodeString(&ValueName, L"Identifier");
|
|
Status = ZwQueryValueKey(SubKey,
|
|
&ValueName,
|
|
KeyValuePartialInformation,
|
|
ValueInfo,
|
|
sizeof(KEY_VALUE_PARTIAL_INFORMATION) +
|
|
sizeof(L"PCI BIOS"),
|
|
&NumberOfBytes);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
/* Check if this is the PCI BIOS subkey */
|
|
if (!wcsncmp((PWCHAR)ValueInfo->Data,
|
|
L"PCI BIOS",
|
|
ValueInfo->DataLength))
|
|
{
|
|
/* It is, proceed to query the PCI IRQ routing table */
|
|
Status = PciGetRegistryValue(L"Configuration Data",
|
|
L"RealModeIrqRoutingTable"
|
|
L"\\0",
|
|
SubKey,
|
|
REG_FULL_RESOURCE_DESCRIPTOR,
|
|
(PVOID*)&Package,
|
|
&NumberOfBytes);
|
|
ZwClose(SubKey);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Close the subkey and try the next one */
|
|
ZwClose(SubKey);
|
|
}
|
|
}
|
|
|
|
/* Check if we got here because the routing table was found */
|
|
if (!NT_SUCCESS(Status)) break;
|
|
|
|
/* Check if a descriptor was found */
|
|
if (!Package) break;
|
|
|
|
/* Make sure the buffer is large enough to hold the table */
|
|
if ((NumberOfBytes < sizeof(*Package)) ||
|
|
(Package->Table.TableSize >
|
|
(NumberOfBytes - sizeof(CM_FULL_RESOURCE_DESCRIPTOR))))
|
|
{
|
|
/* Invalid package size */
|
|
Status = STATUS_UNSUCCESSFUL;
|
|
break;
|
|
}
|
|
|
|
/* Allocate space for the table */
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
*PciRoutingTable = ExAllocatePoolWithTag(PagedPool,
|
|
NumberOfBytes,
|
|
PCI_POOL_TAG);
|
|
if (!*PciRoutingTable) break;
|
|
|
|
/* Copy the registry data */
|
|
RtlCopyMemory(*PciRoutingTable,
|
|
&Package->Table,
|
|
NumberOfBytes - sizeof(CM_FULL_RESOURCE_DESCRIPTOR));
|
|
Status = STATUS_SUCCESS;
|
|
} while (FALSE);
|
|
|
|
/* Close any opened keys, free temporary allocations, and return status */
|
|
if (Package) ExFreePoolWithTag(Package, 0);
|
|
if (ValueInfo) ExFreePoolWithTag(ValueInfo, 0);
|
|
if (KeyInfo) ExFreePoolWithTag(KeyInfo, 0);
|
|
if (FullInfo) ExFreePoolWithTag(FullInfo, 0);
|
|
if (KeyHandle) ZwClose(KeyHandle);
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
PciBuildHackTable(IN HANDLE KeyHandle)
|
|
{
|
|
PKEY_FULL_INFORMATION FullInfo;
|
|
ULONG i, HackCount;
|
|
PKEY_VALUE_FULL_INFORMATION ValueInfo;
|
|
PPCI_HACK_ENTRY Entry;
|
|
NTSTATUS Status;
|
|
ULONG NameLength, ResultLength;
|
|
ULONGLONG HackFlags;
|
|
|
|
/* So we know what to free at the end of the body */
|
|
FullInfo = NULL;
|
|
ValueInfo = NULL;
|
|
do
|
|
{
|
|
/* Query the size required for full key information */
|
|
Status = ZwQueryKey(KeyHandle,
|
|
KeyFullInformation,
|
|
NULL,
|
|
0,
|
|
&ResultLength);
|
|
if (Status != STATUS_BUFFER_TOO_SMALL) break;
|
|
|
|
/* Allocate the space required to hold the full key information */
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
ASSERT(ResultLength > 0);
|
|
FullInfo = ExAllocatePoolWithTag(PagedPool, ResultLength, PCI_POOL_TAG);
|
|
if (!FullInfo) break;
|
|
|
|
/* Go ahead and query the key information */
|
|
Status = ZwQueryKey(KeyHandle,
|
|
KeyFullInformation,
|
|
FullInfo,
|
|
ResultLength,
|
|
&ResultLength);
|
|
if (!NT_SUCCESS(Status)) break;
|
|
|
|
/* The only piece of information that's needed is the count of values */
|
|
HackCount = FullInfo->Values;
|
|
|
|
/* Free the structure now */
|
|
ExFreePoolWithTag(FullInfo, 0);
|
|
FullInfo = NULL;
|
|
|
|
/* Allocate the hack table, now that the number of entries is known */
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
ResultLength = sizeof(PCI_HACK_ENTRY) * HackCount;
|
|
PciHackTable = ExAllocatePoolWithTag(NonPagedPool,
|
|
ResultLength +
|
|
sizeof(PCI_HACK_ENTRY),
|
|
PCI_POOL_TAG);
|
|
if (!PciHackTable) break;
|
|
|
|
/* Allocate the space needed to hold the full value information */
|
|
ValueInfo = ExAllocatePoolWithTag(NonPagedPool,
|
|
sizeof(KEY_VALUE_FULL_INFORMATION) +
|
|
PCI_HACK_ENTRY_FULL_SIZE,
|
|
PCI_POOL_TAG);
|
|
if (!PciHackTable) break;
|
|
|
|
/* Loop each value in the registry */
|
|
Entry = &PciHackTable[0];
|
|
for (i = 0; i < HackCount; i++)
|
|
{
|
|
/* Get the entry for this value */
|
|
Entry = &PciHackTable[i];
|
|
|
|
/* Query the value in the key */
|
|
Status = ZwEnumerateValueKey(KeyHandle,
|
|
i,
|
|
KeyValueFullInformation,
|
|
ValueInfo,
|
|
sizeof(KEY_VALUE_FULL_INFORMATION) +
|
|
PCI_HACK_ENTRY_FULL_SIZE,
|
|
&ResultLength);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
/* Check why the call failed */
|
|
if ((Status != STATUS_BUFFER_OVERFLOW) &&
|
|
(Status != STATUS_BUFFER_TOO_SMALL))
|
|
{
|
|
/* The call failed due to an unknown error, bail out */
|
|
break;
|
|
}
|
|
|
|
/* The data seems to mismatch, try the next key in the list */
|
|
continue;
|
|
}
|
|
|
|
/* Check if the value data matches what's expected */
|
|
if ((ValueInfo->Type != REG_BINARY) ||
|
|
(ValueInfo->DataLength != sizeof(ULONGLONG)))
|
|
{
|
|
/* It doesn't, try the next key in the list */
|
|
continue;
|
|
}
|
|
|
|
/* Read the actual hack flags */
|
|
HackFlags = *(PULONGLONG)((ULONG_PTR)ValueInfo +
|
|
ValueInfo->DataOffset);
|
|
|
|
/* Check what kind of errata entry this is, based on the name */
|
|
NameLength = ValueInfo->NameLength;
|
|
if ((NameLength != PCI_HACK_ENTRY_SIZE) &&
|
|
(NameLength != PCI_HACK_ENTRY_REV_SIZE) &&
|
|
(NameLength != PCI_HACK_ENTRY_SUBSYS_SIZE) &&
|
|
(NameLength != PCI_HACK_ENTRY_FULL_SIZE))
|
|
{
|
|
/* It's an invalid entry, skip it */
|
|
DPRINT1("Skipping hack entry with invalid length name\n");
|
|
continue;
|
|
}
|
|
|
|
/* Initialize the entry */
|
|
RtlZeroMemory(Entry, sizeof(PCI_HACK_ENTRY));
|
|
|
|
/* Get the vendor and device data */
|
|
if (!(PciStringToUSHORT(ValueInfo->Name, &Entry->VendorID)) ||
|
|
!(PciStringToUSHORT(&ValueInfo->Name[4], &Entry->DeviceID)))
|
|
{
|
|
/* This failed, try the next entry */
|
|
continue;
|
|
}
|
|
|
|
/* Check if the entry contains subsystem information */
|
|
if ((NameLength == PCI_HACK_ENTRY_SUBSYS_SIZE) ||
|
|
(NameLength == PCI_HACK_ENTRY_FULL_SIZE))
|
|
{
|
|
/* Get the data */
|
|
if (!(PciStringToUSHORT(&ValueInfo->Name[8],
|
|
&Entry->SubVendorID)) ||
|
|
!(PciStringToUSHORT(&ValueInfo->Name[12],
|
|
&Entry->SubSystemID)))
|
|
{
|
|
/* This failed, try the next entry */
|
|
continue;
|
|
}
|
|
|
|
/* Save the fact this entry has finer controls */
|
|
Entry->Flags |= PCI_HACK_HAS_SUBSYSTEM_INFO;
|
|
}
|
|
|
|
/* Check if the entry contains revision information */
|
|
if ((NameLength == PCI_HACK_ENTRY_REV_SIZE) ||
|
|
(NameLength == PCI_HACK_ENTRY_FULL_SIZE))
|
|
{
|
|
/* Get the data */
|
|
if (!PciStringToUSHORT(&ValueInfo->Name[16],
|
|
&Entry->RevisionID))
|
|
{
|
|
/* This failed, try the next entry */
|
|
continue;
|
|
}
|
|
|
|
/* Save the fact this entry has finer controls */
|
|
Entry->Flags |= PCI_HACK_HAS_REVISION_INFO;
|
|
}
|
|
|
|
/* Only the last entry should have this set */
|
|
ASSERT(Entry->VendorID != PCI_INVALID_VENDORID);
|
|
|
|
/* Save the actual hack flags */
|
|
Entry->HackFlags = HackFlags;
|
|
|
|
/* Print out for the debugger's sake */
|
|
#ifdef HACK_DEBUG
|
|
DPRINT1("Adding Hack entry for Vendor:0x%04x Device:0x%04x ",
|
|
Entry->VendorID, Entry->DeviceID);
|
|
if (Entry->Flags & PCI_HACK_HAS_SUBSYSTEM_INFO)
|
|
DbgPrint("SybSys:0x%04x SubVendor:0x%04x ",
|
|
Entry->SubSystemID, Entry->SubVendorID);
|
|
if (Entry->Flags & PCI_HACK_HAS_REVISION_INFO)
|
|
DbgPrint("Revision:0x%02x", Entry->RevisionID);
|
|
DbgPrint(" = 0x%I64x\n", Entry->HackFlags);
|
|
#endif
|
|
}
|
|
|
|
/* Bail out in case of failure */
|
|
if (!NT_SUCCESS(Status)) break;
|
|
|
|
/* Terminate the table with an invalid entry */
|
|
ASSERT(Entry < (PciHackTable + HackCount + 1));
|
|
Entry->VendorID = PCI_INVALID_VENDORID;
|
|
|
|
/* Success path, free the temporary registry data */
|
|
ExFreePoolWithTag(ValueInfo, 0);
|
|
return STATUS_SUCCESS;
|
|
} while (TRUE);
|
|
|
|
/* Failure path, free temporary allocations and return failure code */
|
|
ASSERT(!NT_SUCCESS(Status));
|
|
if (FullInfo) ExFreePool(FullInfo);
|
|
if (ValueInfo) ExFreePool(ValueInfo);
|
|
if (PciHackTable) ExFreePool(PciHackTable);
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
PciGetDebugPorts(IN HANDLE DebugKey)
|
|
{
|
|
UNREFERENCED_PARAMETER(DebugKey);
|
|
/* This function is not yet implemented */
|
|
UNIMPLEMENTED_DBGBREAK();
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
DRIVER_UNLOAD PciDriverUnload;
|
|
|
|
VOID
|
|
NTAPI
|
|
PciDriverUnload(IN PDRIVER_OBJECT DriverObject)
|
|
{
|
|
UNREFERENCED_PARAMETER(DriverObject);
|
|
/* This function is not yet implemented */
|
|
UNIMPLEMENTED_DBGBREAK("PCI: Unload\n");
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
DriverEntry(IN PDRIVER_OBJECT DriverObject,
|
|
IN PUNICODE_STRING RegistryPath)
|
|
{
|
|
HANDLE KeyHandle, ParametersKey, DebugKey, ControlSetKey;
|
|
BOOLEAN Result;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
ULONG ResultLength;
|
|
PULONG Value;
|
|
PWCHAR StartOptions;
|
|
UNICODE_STRING OptionString, PciLockString;
|
|
NTSTATUS Status;
|
|
DPRINT1("PCI: DriverEntry!\n");
|
|
|
|
/* Setup initial loop variables */
|
|
KeyHandle = NULL;
|
|
ParametersKey = NULL;
|
|
DebugKey = NULL;
|
|
ControlSetKey = NULL;
|
|
do
|
|
{
|
|
/* Remember our object so we can get it to it later */
|
|
PciDriverObject = DriverObject;
|
|
|
|
/* Setup the IRP dispatcher */
|
|
DriverObject->MajorFunction[IRP_MJ_POWER] = PciDispatchIrp;
|
|
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = PciDispatchIrp;
|
|
DriverObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL] = PciDispatchIrp;
|
|
DriverObject->MajorFunction[IRP_MJ_PNP] = PciDispatchIrp;
|
|
DriverObject->DriverUnload = PciDriverUnload;
|
|
|
|
/* This is how we'll detect a new PCI bus */
|
|
DriverObject->DriverExtension->AddDevice = PciAddDevice;
|
|
|
|
/* Open the PCI key */
|
|
InitializeObjectAttributes(&ObjectAttributes,
|
|
RegistryPath,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL);
|
|
Status = ZwOpenKey(&KeyHandle, KEY_QUERY_VALUE, &ObjectAttributes);
|
|
if (!NT_SUCCESS(Status)) break;
|
|
|
|
/* Open the Parameters subkey */
|
|
Result = PciOpenKey(L"Parameters",
|
|
KeyHandle,
|
|
KEY_QUERY_VALUE,
|
|
&ParametersKey,
|
|
&Status);
|
|
//if (!Result) break;
|
|
|
|
/* Build the list of all known PCI erratas */
|
|
Status = PciBuildHackTable(ParametersKey);
|
|
//if (!NT_SUCCESS(Status)) break;
|
|
|
|
/* Open the debug key, if it exists */
|
|
Result = PciOpenKey(L"Debug",
|
|
KeyHandle,
|
|
KEY_QUERY_VALUE,
|
|
&DebugKey,
|
|
&Status);
|
|
if (Result)
|
|
{
|
|
/* There are PCI debug devices, go discover them */
|
|
Status = PciGetDebugPorts(DebugKey);
|
|
if (!NT_SUCCESS(Status)) break;
|
|
}
|
|
|
|
/* Initialize the synchronization locks */
|
|
KeInitializeEvent(&PciGlobalLock, SynchronizationEvent, TRUE);
|
|
KeInitializeEvent(&PciBusLock, SynchronizationEvent, TRUE);
|
|
KeInitializeEvent(&PciLegacyDescriptionLock, SynchronizationEvent, TRUE);
|
|
|
|
/* Open the control set key */
|
|
Result = PciOpenKey(L"\\Registry\\Machine\\System\\CurrentControlSet",
|
|
NULL,
|
|
KEY_QUERY_VALUE,
|
|
&ControlSetKey,
|
|
&Status);
|
|
if (!Result) break;
|
|
|
|
/* Read the command line */
|
|
Status = PciGetRegistryValue(L"SystemStartOptions",
|
|
L"Control",
|
|
ControlSetKey,
|
|
REG_SZ,
|
|
(PVOID*)&StartOptions,
|
|
&ResultLength);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
/* Initialize the command-line as a string */
|
|
OptionString.Buffer = StartOptions;
|
|
OptionString.MaximumLength = OptionString.Length = ResultLength;
|
|
|
|
/* Check if the command-line has the PCILOCK argument */
|
|
RtlInitUnicodeString(&PciLockString, L"PCILOCK");
|
|
if (PciUnicodeStringStrStr(&OptionString, &PciLockString, TRUE))
|
|
{
|
|
/* The PCI Bus driver will keep the BIOS-assigned resources */
|
|
PciLockDeviceResources = TRUE;
|
|
}
|
|
|
|
/* This data isn't needed anymore */
|
|
ExFreePoolWithTag(StartOptions, 0);
|
|
}
|
|
|
|
/* The PCILOCK feature can also be enabled per-system in the registry */
|
|
Status = PciGetRegistryValue(L"PCILock",
|
|
L"Control\\BiosInfo\\PCI",
|
|
ControlSetKey,
|
|
REG_DWORD,
|
|
(PVOID*)&Value,
|
|
&ResultLength);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
/* Read the value it's been set to. This overrides /PCILOCK */
|
|
if (ResultLength == sizeof(ULONG)) PciLockDeviceResources = *Value;
|
|
ExFreePoolWithTag(Value, 0);
|
|
}
|
|
|
|
/* The system can have global PCI erratas in the registry */
|
|
Status = PciGetRegistryValue(L"HackFlags",
|
|
L"Control\\PnP\\PCI",
|
|
ControlSetKey,
|
|
REG_DWORD,
|
|
(PVOID*)&Value,
|
|
&ResultLength);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
/* Read them in */
|
|
if (ResultLength == sizeof(ULONG)) PciSystemWideHackFlags = *Value;
|
|
ExFreePoolWithTag(Value, 0);
|
|
}
|
|
|
|
/* Check if the system should allow native ATA support */
|
|
Status = PciGetRegistryValue(L"EnableNativeModeATA",
|
|
L"Control\\PnP\\PCI",
|
|
ControlSetKey,
|
|
REG_DWORD,
|
|
(PVOID*)&Value,
|
|
&ResultLength);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
/* This key is typically set by drivers, but users can force it */
|
|
if (ResultLength == sizeof(ULONG)) PciEnableNativeModeATA = *Value;
|
|
ExFreePoolWithTag(Value, 0);
|
|
}
|
|
|
|
/* Build the range lists for all the excluded resource areas */
|
|
Status = PciBuildDefaultExclusionLists();
|
|
if (!NT_SUCCESS(Status)) break;
|
|
|
|
/* Read the PCI IRQ Routing Table that the loader put in the registry */
|
|
PciGetIrqRoutingTableFromRegistry(&PciIrqRoutingTable);
|
|
|
|
/* Take over the HAL's default PCI Bus Handler routines */
|
|
PciHookHal();
|
|
|
|
/* Initialize verification of PCI BIOS and devices, if requested */
|
|
PciVerifierInit(DriverObject);
|
|
|
|
/* Check if this is a Datacenter SKU, which impacts IRQ alignment */
|
|
PciRunningDatacenter = PciIsDatacenter();
|
|
if (PciRunningDatacenter) DPRINT1("PCI running on datacenter build\n");
|
|
|
|
/* Check if the system has an ACPI Hardware Watchdog Timer */
|
|
//WdTable = PciGetAcpiTable(WDRT_SIGNATURE);
|
|
Status = STATUS_SUCCESS;
|
|
} while (FALSE);
|
|
|
|
/* Close all opened keys, return driver status to PnP Manager */
|
|
if (KeyHandle) ZwClose(KeyHandle);
|
|
if (ControlSetKey) ZwClose(ControlSetKey);
|
|
if (ParametersKey) ZwClose(ParametersKey);
|
|
if (DebugKey) ZwClose(DebugKey);
|
|
return Status;
|
|
}
|
|
|
|
/* EOF */
|