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.
1454 lines
39 KiB
1454 lines
39 KiB
/*++
|
|
|
|
Copyright (c) 1996-2000 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
init.c
|
|
|
|
Abstract:
|
|
|
|
This module contains the initialization code for PCI.SYS.
|
|
|
|
Author:
|
|
|
|
Forrest Foltz (forrestf) 22-May-1996
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "pcip.h"
|
|
#include <ntacpi.h>
|
|
|
|
NTSTATUS
|
|
DriverEntry(
|
|
IN PDRIVER_OBJECT DriverObject,
|
|
IN PUNICODE_STRING RegistryPath
|
|
);
|
|
|
|
VOID
|
|
PciDriverUnload(
|
|
IN PDRIVER_OBJECT DriverObject
|
|
);
|
|
|
|
NTSTATUS
|
|
PciBuildHackTable(
|
|
IN HANDLE HackTableKey
|
|
);
|
|
|
|
NTSTATUS
|
|
PciGetIrqRoutingTableFromRegistry(
|
|
PPCI_IRQ_ROUTING_TABLE *RoutingTable
|
|
);
|
|
|
|
NTSTATUS
|
|
PciGetDebugPorts(
|
|
IN HANDLE ServiceHandle
|
|
);
|
|
|
|
NTSTATUS
|
|
PciAcpiFindRsdt(
|
|
OUT PACPI_BIOS_MULTI_NODE *AcpiMulti
|
|
);
|
|
|
|
PVOID
|
|
PciGetAcpiTable(
|
|
IN ULONG Signature
|
|
);
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(INIT, DriverEntry)
|
|
#pragma alloc_text(INIT, PciBuildHackTable)
|
|
#pragma alloc_text(INIT, PciGetIrqRoutingTableFromRegistry)
|
|
#pragma alloc_text(INIT, PciGetDebugPorts)
|
|
#pragma alloc_text(INIT, PciAcpiFindRsdt)
|
|
#pragma alloc_text(INIT, PciGetAcpiTable)
|
|
#pragma alloc_text(PAGE, PciDriverUnload)
|
|
#endif
|
|
|
|
PDRIVER_OBJECT PciDriverObject;
|
|
BOOLEAN PciLockDeviceResources;
|
|
ULONG PciSystemWideHackFlags;
|
|
ULONG PciEnableNativeModeATA;
|
|
|
|
//
|
|
// List of FDOs created by this driver.
|
|
//
|
|
|
|
SINGLE_LIST_ENTRY PciFdoExtensionListHead;
|
|
LONG PciRootBusCount;
|
|
|
|
//
|
|
// PciAssignBusNumbers - this flag indicates whether we should try to assign
|
|
// bus numbers to an unconfigured bridge. It is set once we know if the enumerator
|
|
// of the PCI bus provides sufficient support.
|
|
//
|
|
|
|
BOOLEAN PciAssignBusNumbers = FALSE;
|
|
|
|
//
|
|
// PciRunningDatacenter - set to TRUE if we are running on the Datacenter SKU
|
|
//
|
|
BOOLEAN PciRunningDatacenter = FALSE;
|
|
|
|
//
|
|
// This locks all PCI's global data structures
|
|
//
|
|
|
|
FAST_MUTEX PciGlobalLock;
|
|
|
|
//
|
|
// This locks changes to bus numbers
|
|
//
|
|
|
|
FAST_MUTEX PciBusLock;
|
|
|
|
//
|
|
// Table of hacks for broken hardware read from the registry at init.
|
|
// Protected by PciGlobalSpinLock and in none paged pool as it is needed at
|
|
// dispatch level
|
|
//
|
|
|
|
PPCI_HACK_TABLE_ENTRY PciHackTable = NULL;
|
|
|
|
// Will point to PCI IRQ Routing Table if one was found in the registry.
|
|
PPCI_IRQ_ROUTING_TABLE PciIrqRoutingTable = NULL;
|
|
|
|
//
|
|
// Debug ports we support
|
|
//
|
|
PCI_DEBUG_PORT PciDebugPorts[MAX_DEBUGGING_DEVICES_SUPPORTED];
|
|
ULONG PciDebugPortsCount;
|
|
|
|
//
|
|
// Watchdog timer resource table
|
|
//
|
|
PWATCHDOG_TIMER_RESOURCE_TABLE WdTable;
|
|
|
|
#define PATH_CCS L"\\Registry\\Machine\\System\\CurrentControlSet"
|
|
|
|
#define KEY_BIOS_INFO L"Control\\BiosInfo\\PCI"
|
|
#define VALUE_PCI_LOCK L"PCILock"
|
|
|
|
#define KEY_PNP_PCI L"Control\\PnP\\PCI"
|
|
#define VALUE_PCI_HACKFLAGS L"HackFlags"
|
|
#define VALUE_ENABLE_NATA L"EnableNativeModeATA"
|
|
|
|
#define KEY_CONTROL L"Control"
|
|
#define VALUE_OSLOADOPT L"SystemStartOptions"
|
|
|
|
#define KEY_MULTIFUNCTION L"\\Registry\\Machine\\HARDWARE\\DESCRIPTION\\System\\MultiFunctionAdapter"
|
|
#define KEY_IRQ_ROUTING_TABLE L"RealModeIrqRoutingTable\\0"
|
|
#define VALUE_IDENTIFIER L"Identifier"
|
|
#define VALUE_CONFIGURATION_DATA L"Configuration Data"
|
|
#define PCIIR_IDENTIFIER L"PCI BIOS"
|
|
#define ACPI_BIOS_ID L"ACPI BIOS"
|
|
|
|
#define HACKFMT_VENDORDEV (sizeof(L"VVVVDDDD") - sizeof(UNICODE_NULL))
|
|
#define HACKFMT_VENDORDEVREVISION (sizeof(L"VVVVDDDDRR") - sizeof(UNICODE_NULL))
|
|
#define HACKFMT_SUBSYSTEM (sizeof(L"VVVVDDDDSSSSssss") - sizeof(UNICODE_NULL))
|
|
#define HACKFMT_SUBSYSTEMREVISION (sizeof(L"VVVVDDDDSSSSssssRR") - sizeof(UNICODE_NULL))
|
|
#define HACKFMT_MAX_LENGTH HACKFMT_SUBSYSTEMREVISION
|
|
|
|
#define HACKFMT_DEVICE_OFFSET 4
|
|
#define HACKFMT_SUBVENDOR_OFFSET 8
|
|
#define HACKFMT_SUBSYSTEM_OFFSET 12
|
|
|
|
|
|
NTSTATUS
|
|
DriverEntry(
|
|
IN PDRIVER_OBJECT DriverObject,
|
|
IN PUNICODE_STRING RegistryPath
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Entrypoint needed to initialize the PCI bus enumerator.
|
|
|
|
Arguments:
|
|
|
|
DriverObject - Pointer to the driver object created by the system.
|
|
RegistryPath - Pointer to the unicode registry service path.
|
|
|
|
Return Value:
|
|
|
|
NT status.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status;
|
|
ULONG length;
|
|
PWCHAR osLoadOptions;
|
|
HANDLE ccsHandle = NULL, serviceKey = NULL, paramsKey = NULL, debugKey = NULL;
|
|
PULONG registryValue;
|
|
ULONG registryValueLength;
|
|
OBJECT_ATTRIBUTES attributes;
|
|
UNICODE_STRING pciLockString, osLoadOptionsString;
|
|
|
|
//
|
|
// Fill in the driver object
|
|
//
|
|
|
|
DriverObject->MajorFunction[IRP_MJ_PNP] = PciDispatchIrp;
|
|
DriverObject->MajorFunction[IRP_MJ_POWER] = PciDispatchIrp;
|
|
DriverObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL] = PciDispatchIrp;
|
|
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = PciDispatchIrp;
|
|
|
|
DriverObject->DriverUnload = PciDriverUnload;
|
|
DriverObject->DriverExtension->AddDevice = PciAddDevice;
|
|
|
|
PciDriverObject = DriverObject;
|
|
|
|
//
|
|
// Open our service key and retrieve the hack table
|
|
//
|
|
|
|
InitializeObjectAttributes(&attributes,
|
|
RegistryPath,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
status = ZwOpenKey(&serviceKey,
|
|
KEY_READ,
|
|
&attributes
|
|
);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
return status;
|
|
}
|
|
|
|
//
|
|
// Get the Hack table from the registry
|
|
//
|
|
|
|
if (!PciOpenKey(L"Parameters", serviceKey, KEY_READ, ¶msKey, &status)) {
|
|
goto exit;
|
|
}
|
|
|
|
status = PciBuildHackTable(paramsKey);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
goto exit;
|
|
}
|
|
|
|
//
|
|
// Get any info about debugging ports from the registry so we don't perturb
|
|
// them
|
|
//
|
|
|
|
if (PciOpenKey(L"Debug", serviceKey, KEY_READ, &debugKey, &status)) {
|
|
|
|
status = PciGetDebugPorts(debugKey);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
goto exit;
|
|
}
|
|
|
|
}
|
|
//
|
|
// Initialize the list of FDO Extensions.
|
|
//
|
|
|
|
PciFdoExtensionListHead.Next = NULL;
|
|
PciRootBusCount = 0;
|
|
ExInitializeFastMutex(&PciGlobalLock);
|
|
ExInitializeFastMutex(&PciBusLock);
|
|
|
|
//
|
|
// Need access to the CurrentControlSet for various
|
|
// initialization chores.
|
|
//
|
|
|
|
if (!PciOpenKey(PATH_CCS, NULL, KEY_READ, &ccsHandle, &status)) {
|
|
goto exit;
|
|
}
|
|
|
|
//
|
|
// Get OSLOADOPTIONS and see if PCILOCK was specified.
|
|
// (Unless the driver is build to force PCILOCK).
|
|
// (Note: Can't check for leading '/', it was stripped
|
|
// before getting put in the registry).
|
|
//
|
|
|
|
PciLockDeviceResources = FALSE;
|
|
|
|
if (NT_SUCCESS(PciGetRegistryValue(VALUE_OSLOADOPT,
|
|
KEY_CONTROL,
|
|
ccsHandle,
|
|
REG_SZ,
|
|
&osLoadOptions,
|
|
&length
|
|
))) {
|
|
|
|
//
|
|
// Build counted versions of the stings we need to search
|
|
//
|
|
|
|
PciConstStringToUnicodeString(&pciLockString, L"PCILOCK");
|
|
|
|
//
|
|
// We assume that the string coming from the registry is NUL terminated
|
|
// and if this isn't the case, the MaximumLength in the counted string
|
|
// prevents us from over running our buffer. If the string is larger
|
|
// than MAX_USHORT bytes then we truncate it.
|
|
//
|
|
|
|
osLoadOptionsString.Buffer = osLoadOptions;
|
|
osLoadOptionsString.Length = (USHORT)(length - sizeof(UNICODE_NULL));
|
|
osLoadOptionsString.MaximumLength = (USHORT) length;
|
|
|
|
if (PciUnicodeStringStrStr(&osLoadOptionsString, &pciLockString, TRUE)) {
|
|
PciLockDeviceResources = TRUE;
|
|
}
|
|
|
|
ExFreePool(osLoadOptions);
|
|
|
|
|
|
}
|
|
|
|
if (!PciLockDeviceResources) {
|
|
PULONG pciLockValue;
|
|
ULONG pciLockLength;
|
|
|
|
if (NT_SUCCESS(PciGetRegistryValue( VALUE_PCI_LOCK,
|
|
KEY_BIOS_INFO,
|
|
ccsHandle,
|
|
REG_DWORD,
|
|
&pciLockValue,
|
|
&pciLockLength))) {
|
|
|
|
if (pciLockLength == sizeof(ULONG) && *pciLockValue == 1) {
|
|
|
|
PciLockDeviceResources = TRUE;
|
|
}
|
|
|
|
ExFreePool(pciLockValue);
|
|
}
|
|
}
|
|
|
|
PciSystemWideHackFlags = 0;
|
|
|
|
if (NT_SUCCESS(PciGetRegistryValue( VALUE_PCI_HACKFLAGS,
|
|
KEY_PNP_PCI,
|
|
ccsHandle,
|
|
REG_DWORD,
|
|
®istryValue,
|
|
®istryValueLength))) {
|
|
|
|
if (registryValueLength == sizeof(ULONG)) {
|
|
|
|
PciSystemWideHackFlags = *registryValue;
|
|
}
|
|
|
|
ExFreePool(registryValue);
|
|
}
|
|
|
|
PciEnableNativeModeATA = 0;
|
|
|
|
if (NT_SUCCESS(PciGetRegistryValue( VALUE_ENABLE_NATA,
|
|
KEY_PNP_PCI,
|
|
ccsHandle,
|
|
REG_DWORD,
|
|
®istryValue,
|
|
®istryValueLength))) {
|
|
|
|
if (registryValueLength == sizeof(ULONG)) {
|
|
|
|
PciEnableNativeModeATA = *registryValue;
|
|
}
|
|
|
|
ExFreePool(registryValue);
|
|
}
|
|
|
|
//
|
|
// Build some global data structures
|
|
//
|
|
|
|
status = PciBuildDefaultExclusionLists();
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
return status;
|
|
}
|
|
|
|
//
|
|
// If we don't find an IRQ routing table, no UI number information
|
|
// will be returned for the PDOs using this mechanism. ACPI may
|
|
// still filter in UI numbers.
|
|
//
|
|
PciGetIrqRoutingTableFromRegistry(&PciIrqRoutingTable);
|
|
|
|
//
|
|
// Override the functions that used to be in the HAL but are now in the
|
|
// PCI driver
|
|
//
|
|
|
|
PciHookHal();
|
|
|
|
//
|
|
// Enable the hardware verifier code if appropriate.
|
|
//
|
|
PciVerifierInit(DriverObject);
|
|
|
|
PciRunningDatacenter = PciIsDatacenter();
|
|
if (PciRunningDatacenter) {
|
|
|
|
PciDebugPrint(
|
|
PciDbgInformative,
|
|
"PCI running on datacenter build\n"
|
|
);
|
|
}
|
|
|
|
//
|
|
// Get the WD ACPI table
|
|
//
|
|
|
|
WdTable = (PWATCHDOG_TIMER_RESOURCE_TABLE) PciGetAcpiTable( WDTT_SIGNATURE );
|
|
|
|
status = STATUS_SUCCESS;
|
|
|
|
exit:
|
|
|
|
if (ccsHandle) {
|
|
ZwClose(ccsHandle);
|
|
}
|
|
|
|
if (serviceKey) {
|
|
ZwClose(serviceKey);
|
|
}
|
|
|
|
if (paramsKey) {
|
|
ZwClose(paramsKey);
|
|
}
|
|
|
|
if (debugKey) {
|
|
ZwClose(debugKey);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
VOID
|
|
PciDriverUnload(
|
|
IN PDRIVER_OBJECT DriverObject
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Entrypoint used to unload the PCI driver. Does nothing, the
|
|
PCI driver is never unloaded.
|
|
|
|
Arguments:
|
|
|
|
DriverObject - Pointer to the driver object created by the system.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
//
|
|
// Disable the hardware verifier code if appropriate.
|
|
//
|
|
PciVerifierUnload(DriverObject);
|
|
|
|
//
|
|
// Unallocate anything we can find.
|
|
//
|
|
|
|
RtlFreeRangeList(&PciIsaBitExclusionList);
|
|
RtlFreeRangeList(&PciVgaAndIsaBitExclusionList);
|
|
|
|
//
|
|
// Free IRQ routing table if we have one
|
|
//
|
|
|
|
if (PciIrqRoutingTable != NULL) {
|
|
ExFreePool(PciIrqRoutingTable);
|
|
}
|
|
|
|
//
|
|
// Attempt to remove our hooks in case we actually get unloaded.
|
|
//
|
|
|
|
PciUnhookHal();
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
PciBuildHackTable(
|
|
IN HANDLE HackTableKey
|
|
)
|
|
{
|
|
|
|
NTSTATUS status;
|
|
PKEY_FULL_INFORMATION keyInfo = NULL;
|
|
ULONG hackCount, size, index;
|
|
USHORT temp;
|
|
PPCI_HACK_TABLE_ENTRY entry;
|
|
ULONGLONG data;
|
|
PKEY_VALUE_FULL_INFORMATION valueInfo = NULL;
|
|
ULONG valueInfoSize = sizeof(KEY_VALUE_FULL_INFORMATION)
|
|
+ HACKFMT_MAX_LENGTH +
|
|
+ sizeof(ULONGLONG);
|
|
|
|
//
|
|
// Get the key info so we know how many hack values there are.
|
|
// This does not change during system initialization.
|
|
//
|
|
|
|
status = ZwQueryKey(HackTableKey,
|
|
KeyFullInformation,
|
|
NULL,
|
|
0,
|
|
&size
|
|
);
|
|
|
|
if (status != STATUS_BUFFER_TOO_SMALL) {
|
|
PCI_ASSERT(!NT_SUCCESS(status));
|
|
goto cleanup;
|
|
}
|
|
|
|
PCI_ASSERT(size > 0);
|
|
|
|
keyInfo = ExAllocatePool(PagedPool | POOL_COLD_ALLOCATION, size);
|
|
|
|
if (!keyInfo) {
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto cleanup;
|
|
}
|
|
|
|
status = ZwQueryKey(HackTableKey,
|
|
KeyFullInformation,
|
|
keyInfo,
|
|
size,
|
|
&size
|
|
);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
goto cleanup;
|
|
}
|
|
|
|
hackCount = keyInfo->Values;
|
|
|
|
ExFreePool(keyInfo);
|
|
keyInfo = NULL;
|
|
|
|
//
|
|
// Allocate and initialize the hack table
|
|
//
|
|
|
|
PciHackTable = ExAllocatePool(NonPagedPool,
|
|
(hackCount + 1) * sizeof(PCI_HACK_TABLE_ENTRY)
|
|
);
|
|
|
|
if (!PciHackTable) {
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto cleanup;
|
|
}
|
|
|
|
|
|
//
|
|
// Allocate a valueInfo buffer big enough for the biggest valid
|
|
// format and a ULONGLONG worth of data.
|
|
//
|
|
|
|
valueInfo = ExAllocatePool(PagedPool | POOL_COLD_ALLOCATION, valueInfoSize);
|
|
|
|
if (!valueInfo) {
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto cleanup;
|
|
}
|
|
|
|
entry = PciHackTable;
|
|
|
|
for (index = 0; index < hackCount; index++) {
|
|
|
|
status = ZwEnumerateValueKey(HackTableKey,
|
|
index,
|
|
KeyValueFullInformation,
|
|
valueInfo,
|
|
valueInfoSize,
|
|
&size
|
|
);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
if (status == STATUS_BUFFER_OVERFLOW || status == STATUS_BUFFER_TOO_SMALL) {
|
|
//
|
|
// All out data is of fixed length and the buffer is big enough
|
|
// so this can't be for us.
|
|
//
|
|
|
|
continue;
|
|
} else {
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Get pointer to the data if its of the right type
|
|
//
|
|
|
|
if ((valueInfo->Type == REG_BINARY) &&
|
|
(valueInfo->DataLength == sizeof(ULONGLONG))) {
|
|
data = *(ULONGLONG UNALIGNED *)(((PUCHAR)valueInfo) + valueInfo->DataOffset);
|
|
} else {
|
|
//
|
|
// We only deal in ULONGLONGs
|
|
//
|
|
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Now see if the name is formatted like we expect it to be:
|
|
// VVVVDDDD
|
|
// VVVVDDDDRR
|
|
// VVVVDDDDSSSSssss
|
|
// VVVVDDDDSSSSssssRR
|
|
|
|
if ((valueInfo->NameLength != HACKFMT_VENDORDEV) &&
|
|
(valueInfo->NameLength != HACKFMT_VENDORDEVREVISION) &&
|
|
(valueInfo->NameLength != HACKFMT_SUBSYSTEM) &&
|
|
(valueInfo->NameLength != HACKFMT_SUBSYSTEMREVISION)) {
|
|
|
|
//
|
|
// This isn't ours
|
|
//
|
|
|
|
PciDebugPrint(
|
|
PciDbgInformative,
|
|
"Skipping hack entry with invalid length name\n"
|
|
);
|
|
|
|
continue;
|
|
}
|
|
|
|
|
|
//
|
|
// This looks plausable - try to parse it and fill in a hack table
|
|
// entry
|
|
//
|
|
|
|
RtlZeroMemory(entry, sizeof(PCI_HACK_TABLE_ENTRY));
|
|
|
|
//
|
|
// Look for DeviceID and VendorID (VVVVDDDD)
|
|
//
|
|
|
|
if (!PciStringToUSHORT(valueInfo->Name, &entry->VendorID)) {
|
|
continue;
|
|
}
|
|
|
|
if (!PciStringToUSHORT(valueInfo->Name + HACKFMT_DEVICE_OFFSET,
|
|
&entry->DeviceID)) {
|
|
continue;
|
|
}
|
|
|
|
|
|
//
|
|
// Look for SubsystemVendorID/SubSystemID (SSSSssss)
|
|
//
|
|
|
|
if ((valueInfo->NameLength == HACKFMT_SUBSYSTEM) ||
|
|
(valueInfo->NameLength == HACKFMT_SUBSYSTEMREVISION)) {
|
|
|
|
if (!PciStringToUSHORT(valueInfo->Name + HACKFMT_SUBVENDOR_OFFSET,
|
|
&entry->SubVendorID)) {
|
|
continue;
|
|
}
|
|
|
|
if (!PciStringToUSHORT(valueInfo->Name + HACKFMT_SUBSYSTEM_OFFSET,
|
|
&entry->SubSystemID)) {
|
|
continue;
|
|
}
|
|
|
|
entry->Flags |= PCI_HACK_FLAG_SUBSYSTEM;
|
|
}
|
|
|
|
//
|
|
// Look for RevisionID (RR)
|
|
//
|
|
|
|
if ((valueInfo->NameLength == HACKFMT_VENDORDEVREVISION) ||
|
|
(valueInfo->NameLength == HACKFMT_SUBSYSTEMREVISION)) {
|
|
if (PciStringToUSHORT(valueInfo->Name +
|
|
(valueInfo->NameLength/sizeof(WCHAR) - 4), &temp)) {
|
|
entry->RevisionID = (UCHAR)temp & 0xFF;
|
|
entry->Flags |= PCI_HACK_FLAG_REVISION;
|
|
} else {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
PCI_ASSERT(entry->VendorID != 0xFFFF);
|
|
|
|
//
|
|
// Fill in the entry
|
|
//
|
|
|
|
entry->HackFlags = data;
|
|
|
|
PciDebugPrint(
|
|
PciDbgInformative,
|
|
"Adding Hack entry for Vendor:0x%04x Device:0x%04x ",
|
|
entry->VendorID, entry->DeviceID
|
|
);
|
|
|
|
if (entry->Flags & PCI_HACK_FLAG_SUBSYSTEM) {
|
|
PciDebugPrint(
|
|
PciDbgInformative,
|
|
"SybSys:0x%04x SubVendor:0x%04x ",
|
|
entry->SubSystemID, entry->SubVendorID
|
|
);
|
|
}
|
|
|
|
if (entry->Flags & PCI_HACK_FLAG_REVISION) {
|
|
PciDebugPrint(
|
|
PciDbgInformative,
|
|
"Revision:0x%02x",
|
|
(ULONG) entry->RevisionID
|
|
);
|
|
}
|
|
|
|
PciDebugPrint(
|
|
PciDbgInformative,
|
|
" = 0x%I64x\n",
|
|
entry->HackFlags
|
|
);
|
|
|
|
entry++;
|
|
}
|
|
|
|
PCI_ASSERT(entry < (PciHackTable + hackCount + 1));
|
|
|
|
//
|
|
// Terminate the table with an invalid VendorID
|
|
//
|
|
|
|
entry->VendorID = 0xFFFF;
|
|
|
|
ExFreePool(valueInfo);
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
cleanup:
|
|
|
|
PCI_ASSERT(!NT_SUCCESS(status));
|
|
|
|
if (keyInfo) {
|
|
ExFreePool(keyInfo);
|
|
}
|
|
|
|
if (valueInfo) {
|
|
ExFreePool(valueInfo);
|
|
}
|
|
|
|
if (PciHackTable) {
|
|
ExFreePool(PciHackTable);
|
|
PciHackTable = NULL;
|
|
}
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
NTSTATUS
|
|
PciGetIrqRoutingTableFromRegistry(
|
|
PPCI_IRQ_ROUTING_TABLE *RoutingTable
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Retrieve the IRQ routing table from the registry if present so it
|
|
can be used to determine the UI Number (slot #) that will be used
|
|
later when answering capabilities queries on the PDOs.
|
|
|
|
Searches HKLM\Hardware\Description\System\MultiFunctionAdapter for
|
|
a subkey with an "Identifier" value equal to "PCI BIOS". It then looks at
|
|
"RealModeIrqRoutingTable\0" from this subkey to find actual irq routing
|
|
table value. This value has a CM_FULL_RESOURCE_DESCRIPTOR in front of it.
|
|
|
|
Hals that suppirt irq routing tables have a similar routine.
|
|
|
|
Arguments:
|
|
|
|
RoutingTable - Pointer to a pointer to the routing table returned if any
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - failure indicates inability to get irq routing table
|
|
information from the registry.
|
|
|
|
--*/
|
|
{
|
|
PUCHAR irqTable = NULL;
|
|
PKEY_FULL_INFORMATION multiKeyInformation = NULL;
|
|
PKEY_BASIC_INFORMATION keyInfo = NULL;
|
|
PKEY_VALUE_PARTIAL_INFORMATION identifierValueInfo = NULL;
|
|
UNICODE_STRING unicodeString;
|
|
HANDLE keyMultifunction = NULL, keyTable = NULL;
|
|
ULONG i, length, maxKeyLength, identifierValueLen;
|
|
BOOLEAN result;
|
|
NTSTATUS status;
|
|
|
|
//
|
|
// Open the multifunction key
|
|
//
|
|
result = PciOpenKey(KEY_MULTIFUNCTION,
|
|
NULL,
|
|
KEY_READ,
|
|
&keyMultifunction,
|
|
&status);
|
|
if (!result) {
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Do allocation of buffers up front
|
|
//
|
|
|
|
//
|
|
// Determine maximum size of a keyname under the multifunction key
|
|
//
|
|
status = ZwQueryKey(keyMultifunction,
|
|
KeyFullInformation,
|
|
NULL,
|
|
sizeof(multiKeyInformation),
|
|
&length);
|
|
if (status != STATUS_BUFFER_TOO_SMALL) {
|
|
goto Cleanup;
|
|
}
|
|
multiKeyInformation = ExAllocatePool(PagedPool | POOL_COLD_ALLOCATION, length);
|
|
if (multiKeyInformation == NULL) {
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto Cleanup;
|
|
}
|
|
status = ZwQueryKey(keyMultifunction,
|
|
KeyFullInformation,
|
|
multiKeyInformation,
|
|
length,
|
|
&length);
|
|
if (!NT_SUCCESS(status)) {
|
|
goto Cleanup;
|
|
}
|
|
// includes space for a terminating null that will be added later.
|
|
maxKeyLength = multiKeyInformation->MaxNameLen +
|
|
sizeof(KEY_BASIC_INFORMATION) + sizeof(WCHAR);
|
|
|
|
//
|
|
// Allocate buffer used for storing subkeys that we are enumerated
|
|
// under multifunction.
|
|
//
|
|
keyInfo = ExAllocatePool(PagedPool | POOL_COLD_ALLOCATION, maxKeyLength);
|
|
if (keyInfo == NULL) {
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Allocate buffer large enough to store a value containing REG_SZ
|
|
// 'PCI BIOS'. We hope to find such a value under one of the
|
|
// multifunction subkeys
|
|
//
|
|
identifierValueLen = sizeof(PCIIR_IDENTIFIER) +
|
|
sizeof(KEY_VALUE_PARTIAL_INFORMATION);
|
|
identifierValueInfo = ExAllocatePool(PagedPool | POOL_COLD_ALLOCATION, identifierValueLen);
|
|
if (identifierValueInfo == NULL) {
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Enumerate subkeys of multifunction key looking for keys with an
|
|
// Identifier value of "PCI BIOS". If we find one, look for the
|
|
// irq routing table in the tree below.
|
|
//
|
|
i = 0;
|
|
do {
|
|
status = ZwEnumerateKey(keyMultifunction,
|
|
i,
|
|
KeyBasicInformation,
|
|
keyInfo,
|
|
maxKeyLength,
|
|
&length);
|
|
if (NT_SUCCESS(status)) {
|
|
//
|
|
// Found a key, now we need to open it and check the
|
|
// 'Identifier' value to see if it is 'PCI BIOS'
|
|
//
|
|
keyInfo->Name[keyInfo->NameLength / sizeof(WCHAR)] = UNICODE_NULL;
|
|
result = PciOpenKey(keyInfo->Name,
|
|
keyMultifunction,
|
|
KEY_READ,
|
|
&keyTable,
|
|
&status);
|
|
if (result) {
|
|
//
|
|
// Checking 'Identifier' value to see if it contains 'PCI BIOS'
|
|
//
|
|
RtlInitUnicodeString(&unicodeString, VALUE_IDENTIFIER);
|
|
status = ZwQueryValueKey(keyTable,
|
|
&unicodeString,
|
|
KeyValuePartialInformation,
|
|
identifierValueInfo,
|
|
identifierValueLen,
|
|
&length);
|
|
if (NT_SUCCESS(status) &&
|
|
RtlEqualMemory((PCHAR)identifierValueInfo->Data,
|
|
PCIIR_IDENTIFIER,
|
|
identifierValueInfo->DataLength))
|
|
{
|
|
//
|
|
// This is the PCI BIOS key. Try to get PCI IRQ
|
|
// routing table. This is the key we were looking
|
|
// for so regardless of succss, break out.
|
|
//
|
|
|
|
status = PciGetRegistryValue(VALUE_CONFIGURATION_DATA,
|
|
KEY_IRQ_ROUTING_TABLE,
|
|
keyTable,
|
|
REG_FULL_RESOURCE_DESCRIPTOR,
|
|
&irqTable,
|
|
&length);
|
|
ZwClose(keyTable);
|
|
break;
|
|
}
|
|
ZwClose(keyTable);
|
|
}
|
|
} else {
|
|
//
|
|
// If not NT_SUCCESS, only alowable value is
|
|
// STATUS_NO_MORE_ENTRIES,... otherwise, someone
|
|
// is playing with the keys as we enumerate
|
|
//
|
|
PCI_ASSERT(status == STATUS_NO_MORE_ENTRIES);
|
|
break;
|
|
}
|
|
i++;
|
|
|
|
} while (status != STATUS_NO_MORE_ENTRIES);
|
|
|
|
if (NT_SUCCESS(status) && irqTable) {
|
|
|
|
//
|
|
// The routing table is stored as a resource and thus we need
|
|
// to trim off the CM_FULL_RESOURCE_DESCRIPTOR that
|
|
// lives in front of the actual table.
|
|
//
|
|
|
|
//
|
|
// Perform sanity checks on the table.
|
|
//
|
|
|
|
if (length < (sizeof(CM_FULL_RESOURCE_DESCRIPTOR) +
|
|
sizeof(PCI_IRQ_ROUTING_TABLE))) {
|
|
ExFreePool(irqTable);
|
|
status = STATUS_UNSUCCESSFUL;
|
|
goto Cleanup;
|
|
}
|
|
|
|
length -= sizeof(CM_FULL_RESOURCE_DESCRIPTOR);
|
|
|
|
if (((PPCI_IRQ_ROUTING_TABLE) (irqTable + sizeof(CM_FULL_RESOURCE_DESCRIPTOR)))->TableSize > length) {
|
|
ExFreePool(irqTable);
|
|
status = STATUS_UNSUCCESSFUL;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Create a new table minus the header.
|
|
//
|
|
*RoutingTable = ExAllocatePool(PagedPool | POOL_COLD_ALLOCATION, length);
|
|
if (*RoutingTable) {
|
|
|
|
RtlMoveMemory(*RoutingTable,
|
|
((PUCHAR) irqTable) + sizeof(CM_FULL_RESOURCE_DESCRIPTOR),
|
|
length);
|
|
status = STATUS_SUCCESS;
|
|
} else {
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
ExFreePool(irqTable);
|
|
}
|
|
|
|
Cleanup:
|
|
if (identifierValueInfo != NULL) {
|
|
ExFreePool(identifierValueInfo);
|
|
}
|
|
|
|
if (keyInfo != NULL) {
|
|
ExFreePool(keyInfo);
|
|
}
|
|
|
|
if (multiKeyInformation != NULL) {
|
|
ExFreePool(multiKeyInformation);
|
|
}
|
|
|
|
if (keyMultifunction != NULL) {
|
|
ZwClose(keyMultifunction);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
PciGetDebugPorts(
|
|
IN HANDLE ServiceHandle
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Looks in the PCI service key for debug port info and puts in into
|
|
the PciDebugPorts global table.
|
|
|
|
Arguments:
|
|
|
|
ServiceHandle - handle to the PCI service key passed into DriverEntry
|
|
Return Value:
|
|
|
|
Status
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status;
|
|
ULONG index;
|
|
WCHAR indexString[sizeof("999")];
|
|
PULONG buffer = NULL;
|
|
ULONG segment, bus, device, function, length;
|
|
BOOLEAN ok;
|
|
|
|
C_ASSERT(MAX_DEBUGGING_DEVICES_SUPPORTED <= 999);
|
|
|
|
for (index = 0; index < MAX_DEBUGGING_DEVICES_SUPPORTED; index++) {
|
|
|
|
ok = SUCCEEDED(StringCbPrintfW(indexString, sizeof(indexString), L"%d", index));
|
|
|
|
ASSERT(ok);
|
|
|
|
status = PciGetRegistryValue(L"Bus",
|
|
indexString,
|
|
ServiceHandle,
|
|
REG_DWORD,
|
|
&buffer,
|
|
&length
|
|
);
|
|
|
|
if (!NT_SUCCESS(status) || length != sizeof(ULONG)) {
|
|
continue;
|
|
}
|
|
|
|
|
|
//
|
|
// This is formatted as 31:8 Segment Number, 7:0 Bus Number
|
|
//
|
|
|
|
segment = (*buffer & 0xFFFFFF00) >> 8;
|
|
bus = *buffer & 0x000000FF;
|
|
|
|
ExFreePool(buffer);
|
|
buffer = NULL;
|
|
|
|
status = PciGetRegistryValue(L"Slot",
|
|
indexString,
|
|
ServiceHandle,
|
|
REG_DWORD,
|
|
&buffer,
|
|
&length
|
|
);
|
|
|
|
|
|
if (!NT_SUCCESS(status) || length != sizeof(ULONG)) {
|
|
goto exit;
|
|
}
|
|
|
|
//
|
|
// This is formatted as 7:5 Function Number, 4:0 Device Number
|
|
//
|
|
|
|
device = *buffer & 0x0000001F;
|
|
function = (*buffer & 0x000000E0) >> 5;
|
|
|
|
ExFreePool(buffer);
|
|
buffer = NULL;
|
|
|
|
|
|
PciDebugPrint(PciDbgInformative,
|
|
"Debug device @ Segment %x, %x.%x.%x\n",
|
|
segment,
|
|
bus,
|
|
device,
|
|
function
|
|
);
|
|
//
|
|
// We don't currently handle segment numbers for config space...
|
|
//
|
|
|
|
PCI_ASSERT(segment == 0);
|
|
|
|
PciDebugPorts[index].Bus = bus;
|
|
PciDebugPorts[index].Slot.u.bits.DeviceNumber = device;
|
|
PciDebugPorts[index].Slot.u.bits.FunctionNumber = function;
|
|
|
|
//
|
|
// Remember we are using the debug port
|
|
//
|
|
PciDebugPortsCount++;
|
|
|
|
}
|
|
|
|
status = STATUS_SUCCESS;
|
|
|
|
exit:
|
|
|
|
if (buffer) {
|
|
ExFreePool(buffer);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
PciAcpiFindRsdt (
|
|
OUT PACPI_BIOS_MULTI_NODE *AcpiMulti
|
|
)
|
|
{
|
|
PKEY_FULL_INFORMATION multiKeyInformation = NULL;
|
|
PKEY_BASIC_INFORMATION keyInfo = NULL;
|
|
PKEY_VALUE_PARTIAL_INFORMATION identifierValueInfo = NULL;
|
|
UNICODE_STRING unicodeString;
|
|
HANDLE keyMultifunction = NULL, keyTable = NULL;
|
|
PCM_PARTIAL_RESOURCE_LIST prl = NULL;
|
|
PCM_PARTIAL_RESOURCE_DESCRIPTOR prd;
|
|
PACPI_BIOS_MULTI_NODE multiNode;
|
|
ULONG multiNodeSize;
|
|
ULONG i, length, maxKeyLength, identifierValueLen;
|
|
BOOLEAN result;
|
|
NTSTATUS status;
|
|
|
|
//
|
|
// Open the multifunction key
|
|
//
|
|
result = PciOpenKey(KEY_MULTIFUNCTION,
|
|
NULL,
|
|
KEY_READ,
|
|
&keyMultifunction,
|
|
&status);
|
|
if (!result) {
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Do allocation of buffers up front
|
|
//
|
|
|
|
//
|
|
// Determine maximum size of a keyname under the multifunction key
|
|
//
|
|
status = ZwQueryKey(keyMultifunction,
|
|
KeyFullInformation,
|
|
NULL,
|
|
sizeof(multiKeyInformation),
|
|
&length);
|
|
if (status != STATUS_BUFFER_TOO_SMALL) {
|
|
goto Cleanup;
|
|
}
|
|
multiKeyInformation = ExAllocatePool(PagedPool | POOL_COLD_ALLOCATION, length);
|
|
if (multiKeyInformation == NULL) {
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto Cleanup;
|
|
}
|
|
status = ZwQueryKey(keyMultifunction,
|
|
KeyFullInformation,
|
|
multiKeyInformation,
|
|
length,
|
|
&length);
|
|
if (!NT_SUCCESS(status)) {
|
|
goto Cleanup;
|
|
}
|
|
// includes space for a terminating null that will be added later.
|
|
maxKeyLength = multiKeyInformation->MaxNameLen +
|
|
sizeof(KEY_BASIC_INFORMATION) + sizeof(WCHAR);
|
|
|
|
//
|
|
// Allocate buffer used for storing subkeys that we are enumerated
|
|
// under multifunction.
|
|
//
|
|
keyInfo = ExAllocatePool(PagedPool | POOL_COLD_ALLOCATION, maxKeyLength);
|
|
if (keyInfo == NULL) {
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Allocate buffer large enough to store a value containing REG_SZ
|
|
// 'ACPI BIOS'. We hope to find such a value under one of the
|
|
// multifunction subkeys
|
|
//
|
|
identifierValueLen = sizeof(ACPI_BIOS_ID) + sizeof(KEY_VALUE_PARTIAL_INFORMATION);
|
|
identifierValueInfo = ExAllocatePool(PagedPool | POOL_COLD_ALLOCATION, identifierValueLen);
|
|
if (identifierValueInfo == NULL) {
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Enumerate subkeys of multifunction key looking for keys with an
|
|
// Identifier value of "ACPI BIOS". If we find one, look for the
|
|
// irq routing table in the tree below.
|
|
//
|
|
i = 0;
|
|
do {
|
|
status = ZwEnumerateKey(keyMultifunction,
|
|
i,
|
|
KeyBasicInformation,
|
|
keyInfo,
|
|
maxKeyLength,
|
|
&length);
|
|
if (NT_SUCCESS(status)) {
|
|
//
|
|
// Found a key, now we need to open it and check the
|
|
// 'Identifier' value to see if it is 'ACPI BIOS'
|
|
//
|
|
keyInfo->Name[keyInfo->NameLength / sizeof(WCHAR)] = UNICODE_NULL;
|
|
result = PciOpenKey(keyInfo->Name,
|
|
keyMultifunction,
|
|
KEY_READ,
|
|
&keyTable,
|
|
&status);
|
|
if (result) {
|
|
//
|
|
// Checking 'Identifier' value to see if it contains 'ACPI BIOS'
|
|
//
|
|
RtlInitUnicodeString(&unicodeString, VALUE_IDENTIFIER);
|
|
status = ZwQueryValueKey(keyTable,
|
|
&unicodeString,
|
|
KeyValuePartialInformation,
|
|
identifierValueInfo,
|
|
identifierValueLen,
|
|
&length);
|
|
if (NT_SUCCESS(status) &&
|
|
RtlEqualMemory((PCHAR)identifierValueInfo->Data,
|
|
ACPI_BIOS_ID,
|
|
identifierValueInfo->DataLength))
|
|
{
|
|
//
|
|
// This is the ACPI BIOS key. Try to get Configuration Data
|
|
// This is the key we were looking
|
|
// for so regardless of success, break out.
|
|
//
|
|
|
|
ZwClose(keyTable);
|
|
|
|
status = PciGetRegistryValue(VALUE_CONFIGURATION_DATA,
|
|
keyInfo->Name,
|
|
keyMultifunction,
|
|
REG_FULL_RESOURCE_DESCRIPTOR,
|
|
&prl,
|
|
&length);
|
|
|
|
break;
|
|
}
|
|
ZwClose(keyTable);
|
|
}
|
|
} else {
|
|
//
|
|
// If not NT_SUCCESS, only alowable value is
|
|
// STATUS_NO_MORE_ENTRIES,... otherwise, someone
|
|
// is playing with the keys as we enumerate
|
|
//
|
|
PCI_ASSERT(status == STATUS_NO_MORE_ENTRIES);
|
|
break;
|
|
}
|
|
i++;
|
|
}
|
|
while (status != STATUS_NO_MORE_ENTRIES);
|
|
|
|
if (NT_SUCCESS(status) && prl) {
|
|
|
|
prd = &prl->PartialDescriptors[0];
|
|
multiNode = (PACPI_BIOS_MULTI_NODE)((PCHAR) prd + sizeof(CM_PARTIAL_RESOURCE_LIST));
|
|
|
|
multiNodeSize = sizeof(ACPI_BIOS_MULTI_NODE) + ((ULONG)(multiNode->Count - 1) * sizeof(ACPI_E820_ENTRY));
|
|
|
|
*AcpiMulti = (PACPI_BIOS_MULTI_NODE) ExAllocatePool(NonPagedPool,multiNodeSize);
|
|
if (*AcpiMulti == NULL) {
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto Cleanup;
|
|
}
|
|
|
|
RtlCopyMemory(*AcpiMulti, multiNode, multiNodeSize);
|
|
}
|
|
|
|
Cleanup:
|
|
if (identifierValueInfo != NULL) {
|
|
ExFreePool(identifierValueInfo);
|
|
}
|
|
|
|
if (keyInfo != NULL) {
|
|
ExFreePool(keyInfo);
|
|
}
|
|
|
|
if (multiKeyInformation != NULL) {
|
|
ExFreePool(multiKeyInformation);
|
|
}
|
|
|
|
if (keyMultifunction != NULL) {
|
|
ZwClose(keyMultifunction);
|
|
}
|
|
|
|
if (prl) {
|
|
ExFreePool(prl);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
PVOID
|
|
PciGetAcpiTable(
|
|
IN ULONG Signature
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will retrieve any table referenced in the ACPI
|
|
RSDT.
|
|
|
|
Arguments:
|
|
|
|
Signature - Target table signature
|
|
|
|
Return Value:
|
|
|
|
pointer to a copy of the table, or NULL if not found
|
|
|
|
--*/
|
|
{
|
|
PACPI_BIOS_MULTI_NODE multiNode;
|
|
NTSTATUS status;
|
|
ULONG entry, rsdtEntries;
|
|
PDESCRIPTION_HEADER header;
|
|
PHYSICAL_ADDRESS physicalAddr;
|
|
PRSDT rsdt;
|
|
ULONG rsdtSize;
|
|
PVOID table = NULL;
|
|
|
|
|
|
//
|
|
// Get the physical address of the RSDT from the Registry
|
|
//
|
|
|
|
status = PciAcpiFindRsdt(&multiNode);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
DbgPrint("AcpiFindRsdt() Failed!\n");
|
|
return NULL;
|
|
}
|
|
|
|
|
|
//
|
|
// Map down header to get total RSDT table size
|
|
//
|
|
|
|
header = (PDESCRIPTION_HEADER) MmMapIoSpace(multiNode->RsdtAddress, sizeof(DESCRIPTION_HEADER), MmNonCached);
|
|
|
|
if (!header) {
|
|
return NULL;
|
|
}
|
|
|
|
rsdtSize = header->Length;
|
|
MmUnmapIoSpace(header, sizeof(DESCRIPTION_HEADER));
|
|
|
|
|
|
//
|
|
// Map down entire RSDT table
|
|
//
|
|
|
|
rsdt = (PRSDT) MmMapIoSpace(multiNode->RsdtAddress, rsdtSize, MmNonCached);
|
|
|
|
ExFreePool(multiNode);
|
|
|
|
if (!rsdt) {
|
|
return NULL;
|
|
}
|
|
|
|
|
|
//
|
|
// Do a sanity check on the RSDT.
|
|
//
|
|
|
|
if ((rsdt->Header.Signature != RSDT_SIGNATURE) &&
|
|
(rsdt->Header.Signature != XSDT_SIGNATURE)) {
|
|
|
|
DbgPrint("RSDT table contains invalid signature\n");
|
|
goto GetAcpiTableEnd;
|
|
}
|
|
|
|
|
|
//
|
|
// Calculate the number of entries in the RSDT.
|
|
//
|
|
|
|
rsdtEntries = rsdt->Header.Signature == XSDT_SIGNATURE ?
|
|
NumTableEntriesFromXSDTPointer(rsdt) :
|
|
NumTableEntriesFromRSDTPointer(rsdt);
|
|
|
|
|
|
//
|
|
// Look down the pointer in each entry to see if it points to
|
|
// the table we are looking for.
|
|
//
|
|
|
|
for (entry = 0; entry < rsdtEntries; entry++) {
|
|
|
|
if (rsdt->Header.Signature == XSDT_SIGNATURE) {
|
|
physicalAddr = ((PXSDT)rsdt)->Tables[entry];
|
|
} else {
|
|
physicalAddr.HighPart = 0;
|
|
physicalAddr.LowPart = (ULONG)rsdt->Tables[entry];
|
|
}
|
|
|
|
//
|
|
// Map down the header, check the signature
|
|
//
|
|
|
|
header = (PDESCRIPTION_HEADER) MmMapIoSpace(physicalAddr, sizeof(DESCRIPTION_HEADER), MmNonCached);
|
|
|
|
if (!header) {
|
|
goto GetAcpiTableEnd;
|
|
}
|
|
|
|
if (header->Signature == Signature) {
|
|
|
|
table = ExAllocatePool( PagedPool, header->Length );
|
|
if (table) {
|
|
RtlCopyMemory(table, header, header->Length);
|
|
}
|
|
|
|
MmUnmapIoSpace(header, sizeof(DESCRIPTION_HEADER));
|
|
break;
|
|
}
|
|
|
|
MmUnmapIoSpace(header, sizeof(DESCRIPTION_HEADER));
|
|
}
|
|
|
|
|
|
GetAcpiTableEnd:
|
|
|
|
MmUnmapIoSpace(rsdt, rsdtSize);
|
|
return table;
|
|
|
|
}
|