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.
2343 lines
60 KiB
2343 lines
60 KiB
/*++
|
|
|
|
Copyright (c) 1997 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
pciopregion.c
|
|
|
|
Abstract:
|
|
|
|
This module implements PCI Operational Region
|
|
support, which allows AML code to read and
|
|
write PCI configuration space.
|
|
|
|
Author:
|
|
|
|
Jake Oshins (jakeo) 7-14-97
|
|
|
|
Environment:
|
|
|
|
NT Kernel Model Driver only
|
|
|
|
--*/
|
|
#include "pch.h"
|
|
|
|
NTSTATUS
|
|
AcpiRegisterPciRegionSupport(
|
|
PDEVICE_OBJECT PciDeviceFilter
|
|
);
|
|
|
|
NTSTATUS
|
|
GetPciAddress(
|
|
IN PNSOBJ PciObj,
|
|
IN PFNACB CompletionRoutine,
|
|
IN PVOID Context,
|
|
IN OUT PUCHAR Bus,
|
|
IN OUT PPCI_SLOT_NUMBER Slot
|
|
);
|
|
|
|
NTSTATUS
|
|
EXPORT
|
|
GetPciAddressWorker(
|
|
IN PNSOBJ AcpiObject,
|
|
IN NTSTATUS Status,
|
|
IN POBJDATA Result,
|
|
IN PVOID Context
|
|
);
|
|
|
|
NTSTATUS
|
|
GetOpRegionScope(
|
|
IN PNSOBJ OpRegion,
|
|
IN PFNACB CompletionHandler,
|
|
IN PVOID CompletionContext,
|
|
OUT PNSOBJ *PciObj
|
|
);
|
|
|
|
NTSTATUS
|
|
EXPORT
|
|
GetOpRegionScopeWorker(
|
|
IN PNSOBJ AcpiObject,
|
|
IN NTSTATUS Status,
|
|
IN POBJDATA Result,
|
|
IN PVOID Context
|
|
);
|
|
|
|
UCHAR
|
|
GetBusNumberFromCRS(
|
|
IN PDEVICE_EXTENSION DeviceExtension,
|
|
IN PUCHAR CRS
|
|
);
|
|
|
|
#define MAX(a, b) \
|
|
((a) > (b) ? (a) : (b))
|
|
|
|
#define MIN(a, b) \
|
|
((a) < (b) ? (a) : (b))
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(PAGE, AcpiRegisterPciRegionSupport)
|
|
#pragma alloc_text(PAGE, ACPIInitBusInterfaces)
|
|
#pragma alloc_text(PAGE, ACPIDeleteFilterInterfaceReferences)
|
|
#pragma alloc_text(PAGE, IsPciBus)
|
|
#pragma alloc_text(PAGE, IsNsobjPciBus)
|
|
#pragma alloc_text(PAGE, EnableDisableRegions)
|
|
#endif
|
|
|
|
|
|
VOID
|
|
ACPIInitBusInterfaces(
|
|
PDEVICE_OBJECT Filter
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine determines whether this filter is for a PCI
|
|
device. If it is, then we call AcpiRegisterPciRegionSupport.
|
|
|
|
Arguments:
|
|
|
|
Filter - device object for the filter we are looking at
|
|
|
|
Return Value:
|
|
|
|
Status
|
|
|
|
Notes:
|
|
|
|
--*/
|
|
{
|
|
PDEVICE_EXTENSION filterExt = Filter->DeviceExtension;
|
|
PDEVICE_EXTENSION parentExt;
|
|
NTSTATUS status;
|
|
|
|
PAGED_CODE();
|
|
|
|
parentExt = filterExt->ParentExtension;
|
|
|
|
if (!IsPciBus(parentExt->DeviceObject)) {
|
|
return;
|
|
}
|
|
|
|
AcpiRegisterPciRegionSupport(Filter);
|
|
}
|
|
|
|
VOID
|
|
ACPIDeleteFilterInterfaceReferences(
|
|
IN PDEVICE_EXTENSION DeviceExtension
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called for all filters when they are removed to see if
|
|
we need to free some interfaces
|
|
|
|
Arguments:
|
|
|
|
DeviceExtension - The device whose extension we have to dereference
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
{
|
|
AMLISUPP_CONTEXT_PASSIVE isPciDeviceContext;
|
|
BOOLEAN pciDevice;
|
|
NTSTATUS status;
|
|
|
|
PAGED_CODE();
|
|
|
|
if ( (DeviceExtension->Flags & DEV_PROP_NO_OBJECT) ) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
KeInitializeEvent(&isPciDeviceContext.Event, SynchronizationEvent, FALSE);
|
|
isPciDeviceContext.Status = STATUS_NOT_FOUND;
|
|
status = IsPciDevice(
|
|
DeviceExtension->AcpiObject,
|
|
AmlisuppCompletePassive,
|
|
(PVOID)&isPciDeviceContext,
|
|
&pciDevice);
|
|
if (status == STATUS_PENDING) {
|
|
|
|
KeWaitForSingleObject(
|
|
&isPciDeviceContext.Event,
|
|
Executive,
|
|
KernelMode,
|
|
FALSE,
|
|
NULL);
|
|
status = isPciDeviceContext.Status;
|
|
|
|
}
|
|
if (!NT_SUCCESS(status) || !pciDevice) {
|
|
|
|
return;
|
|
}
|
|
|
|
//
|
|
// This is a PCI device, so we need to relinquish
|
|
// the interfaces that we got from the PCI driver.
|
|
//
|
|
if (!DeviceExtension->Filter.Interface) {
|
|
|
|
//
|
|
// There were no interfaces to release.
|
|
//
|
|
return;
|
|
|
|
}
|
|
|
|
//
|
|
// Dereference it.
|
|
//
|
|
DeviceExtension->Filter.Interface->InterfaceDereference(
|
|
DeviceExtension->Filter.Interface->Context
|
|
);
|
|
ExFreePool(DeviceExtension->Filter.Interface);
|
|
DeviceExtension->Filter.Interface = NULL;
|
|
}
|
|
|
|
NTSTATUS
|
|
AcpiRegisterPciRegionSupport(
|
|
PDEVICE_OBJECT PciDeviceFilter
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine queries the PCI driver for read and write functions
|
|
for PCI config space. It then attaches these interfaces to the
|
|
device extension for this filter. Then, if it hasn't been done
|
|
already, it registers PCI Operational Region support with the
|
|
AML interpretter.
|
|
|
|
Arguments:
|
|
|
|
PciDeviceFilter - A filter for a PCI device
|
|
|
|
Return Value:
|
|
|
|
Status
|
|
|
|
Notes:
|
|
|
|
--*/
|
|
{
|
|
PBUS_INTERFACE_STANDARD interface;
|
|
PCI_COMMON_CONFIG pciData;
|
|
NTSTATUS status;
|
|
IO_STACK_LOCATION irpSp;
|
|
PWSTR buffer;
|
|
PDEVICE_EXTENSION pciFilterExt;
|
|
PDEVICE_OBJECT topDeviceInStack;
|
|
ULONG bytes;
|
|
|
|
PAGED_CODE();
|
|
|
|
RtlZeroMemory( &irpSp, sizeof(IO_STACK_LOCATION) );
|
|
|
|
//
|
|
// If we have already registered a handler for this
|
|
// device, then we don't need to do it again.
|
|
//
|
|
|
|
pciFilterExt = PciDeviceFilter->DeviceExtension;
|
|
|
|
if (pciFilterExt->Filter.Interface) {
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
interface = ExAllocatePoolWithTag(NonPagedPool, sizeof(BUS_INTERFACE_STANDARD), ACPI_INTERFACE_POOLTAG);
|
|
|
|
if (!interface) {
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
topDeviceInStack = IoGetAttachedDeviceReference(pciFilterExt->TargetDeviceObject);
|
|
|
|
//
|
|
// Set the function codes and parameters.
|
|
//
|
|
irpSp.MajorFunction = IRP_MJ_PNP;
|
|
irpSp.MinorFunction = IRP_MN_QUERY_INTERFACE;
|
|
irpSp.Parameters.QueryInterface.InterfaceType = (LPGUID) &GUID_BUS_INTERFACE_STANDARD;
|
|
irpSp.Parameters.QueryInterface.Version = 1;
|
|
irpSp.Parameters.QueryInterface.Size = sizeof (BUS_INTERFACE_STANDARD);
|
|
irpSp.Parameters.QueryInterface.Interface = (PINTERFACE) interface;
|
|
irpSp.Parameters.QueryInterface.InterfaceSpecificData = NULL;
|
|
|
|
//
|
|
// Call the PCI driver.
|
|
//
|
|
status = ACPIInternalSendSynchronousIrp(topDeviceInStack,
|
|
&irpSp,
|
|
&buffer);
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
//
|
|
// Attach this interface to the PCI bus PDO.
|
|
//
|
|
|
|
pciFilterExt->Filter.Interface = interface;
|
|
|
|
//
|
|
// Reference it.
|
|
//
|
|
|
|
pciFilterExt->Filter.Interface->InterfaceReference(pciFilterExt->Filter.Interface->Context);
|
|
|
|
//
|
|
// HACKHACK. The ACPI HAL doesn't really know much about busses. But
|
|
// it needs to maintain legacy HAL behavior. And to do that, it needs to
|
|
// know how many PCI busses are in the system. Since we are now looking
|
|
// at a PCI bus that we are filtering, we now give the HAL a heads-up that
|
|
// this bus exists.
|
|
//
|
|
|
|
bytes = interface->GetBusData(interface->Context,
|
|
0,
|
|
&pciData,
|
|
0,
|
|
PCI_COMMON_HDR_LENGTH);
|
|
|
|
ASSERT(bytes != 0);
|
|
|
|
if ((PCI_CONFIGURATION_TYPE((&pciData)) == PCI_BRIDGE_TYPE) ||
|
|
(PCI_CONFIGURATION_TYPE((&pciData)) == PCI_CARDBUS_BRIDGE_TYPE)) {
|
|
|
|
//
|
|
// This is actually a PCI to PCI bridge.
|
|
//
|
|
|
|
if (pciData.u.type1.SecondaryBus != 0) {
|
|
|
|
//
|
|
// And it has a bus number. So notify the HAL.
|
|
//
|
|
|
|
HalSetMaxLegacyPciBusNumber(pciData.u.type1.SecondaryBus);
|
|
}
|
|
}
|
|
|
|
} else {
|
|
|
|
ExFreePool(interface);
|
|
}
|
|
|
|
ObDereferenceObject(topDeviceInStack);
|
|
|
|
return status;
|
|
}
|
|
|
|
typedef struct {
|
|
//
|
|
// Arguments to PciConfigSpaceHandler
|
|
//
|
|
ULONG AccessType;
|
|
PNSOBJ OpRegion;
|
|
ULONG Address;
|
|
ULONG Size;
|
|
PULONG Data;
|
|
ULONG Context;
|
|
PVOID CompletionHandler;
|
|
PVOID CompletionContext;
|
|
|
|
//
|
|
// Function state
|
|
//
|
|
PNSOBJ PciObj;
|
|
PNSOBJ ParentObj;
|
|
ULONG CompletionHandlerType;
|
|
ULONG Flags;
|
|
LONG RunCompletion;
|
|
PCI_SLOT_NUMBER Slot;
|
|
UCHAR Bus;
|
|
BOOLEAN IsPciDeviceResult;
|
|
} PCI_CONFIG_STATE, *PPCI_CONFIG_STATE;
|
|
|
|
NTSTATUS
|
|
EXPORT
|
|
PciConfigSpaceHandler (
|
|
ULONG AccessType,
|
|
PNSOBJ OpRegion,
|
|
ULONG Address,
|
|
ULONG Size,
|
|
PULONG Data,
|
|
ULONG Context,
|
|
PFNAA CompletionHandler,
|
|
PVOID CompletionContext
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine handles requests to service the PCI operation region
|
|
|
|
Arguments:
|
|
|
|
AccessType - Read or Write data
|
|
OpRegion - Operation region object
|
|
Address - Address within PCI Configuration space
|
|
Size - Number of bytes to transfer
|
|
Data - Data buffer to transfer to/from
|
|
Context - unused
|
|
CompletionHandler - AMLI handler to call when operation is complete
|
|
CompletionContext - Context to pass to the AMLI handler
|
|
|
|
Return Value:
|
|
|
|
Status
|
|
|
|
Notes:
|
|
|
|
--*/
|
|
{
|
|
PPCI_CONFIG_STATE state;
|
|
|
|
state = ExAllocatePoolWithTag(NonPagedPool, sizeof(PCI_CONFIG_STATE), ACPI_INTERFACE_POOLTAG);
|
|
|
|
if (!state) {
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
RtlZeroMemory(state, sizeof(PCI_CONFIG_STATE));
|
|
|
|
state->AccessType = AccessType;
|
|
state->OpRegion = OpRegion;
|
|
state->Address = Address;
|
|
state->Size = Size;
|
|
state->Data = Data;
|
|
state->Context = Context;
|
|
state->CompletionHandler = CompletionHandler;
|
|
state->CompletionContext = CompletionContext;
|
|
state->PciObj = OpRegion->pnsParent;
|
|
state->RunCompletion = INITIAL_RUN_COMPLETION;
|
|
|
|
return PciConfigSpaceHandlerWorker(state->PciObj,
|
|
STATUS_SUCCESS,
|
|
NULL,
|
|
(PVOID)state);
|
|
}
|
|
|
|
typedef struct {
|
|
PCI_CONFIG_STATE HandlerState;
|
|
NSOBJ FakeOpRegion;
|
|
} PCI_INTERNAL_STATE, *PPCI_INTERNAL_STATE;
|
|
|
|
NTSTATUS
|
|
PciConfigInternal(
|
|
IN ULONG AccessType,
|
|
IN PNSOBJ PciObject,
|
|
IN ULONG Offset,
|
|
IN ULONG Length,
|
|
IN PFNACB CompletionHandler,
|
|
IN PVOID CompletionContext,
|
|
IN OUT PUCHAR Data
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine does PCI configuration space reads or writes.
|
|
It does the same thing as PciConfigSpaceHandler, except
|
|
that it takes an arbitrary PNSOBJ instead of an OpRegion.
|
|
|
|
Arguments:
|
|
|
|
AccessType - Read or Write data
|
|
PciObject - name space object for the PCI device
|
|
Offset - Address within PCI Configuration space
|
|
Length - Number of bytes to transfer
|
|
Context - unused
|
|
CompletionHandler - AMLI handler to call when operation is complete
|
|
CompletionContext - Context to pass to the AMLI handler
|
|
Data - Data buffer to transfer to/from
|
|
|
|
Return Value:
|
|
|
|
Status
|
|
|
|
Notes:
|
|
|
|
(1) This function is intended to be used only internally. It does
|
|
not check to see if the PNSOBJ actually represents a PCI device.
|
|
|
|
(2) This function will not allow writes to the first 0x40 bytes of
|
|
any device's PCI configuration space. This is the common area
|
|
and it is owned by the PCI driver.
|
|
|
|
--*/
|
|
{
|
|
PPCI_INTERNAL_STATE internal;
|
|
PPCI_CONFIG_STATE state;
|
|
PNSOBJ opRegion;
|
|
|
|
internal = ExAllocatePoolWithTag(NonPagedPool, sizeof(PCI_INTERNAL_STATE), ACPI_INTERFACE_POOLTAG);
|
|
|
|
if (!internal) {
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
RtlZeroMemory(internal, sizeof(PCI_INTERNAL_STATE));
|
|
|
|
internal->FakeOpRegion.Context = PciObject;
|
|
|
|
state = (PPCI_CONFIG_STATE)internal;
|
|
|
|
state->AccessType = AccessType;
|
|
state->OpRegion = &internal->FakeOpRegion;
|
|
state->Address = Offset;
|
|
state->Size = Length;
|
|
state->Data = (PULONG)Data;
|
|
state->Context = 0;
|
|
state->CompletionHandler = CompletionHandler;
|
|
state->CompletionContext = CompletionContext;
|
|
state->PciObj = PciObject;
|
|
state->CompletionHandlerType = PCISUPP_COMPLETION_HANDLER_PFNACB;
|
|
state->RunCompletion = INITIAL_RUN_COMPLETION;
|
|
|
|
return PciConfigSpaceHandlerWorker(PciObject,
|
|
STATUS_SUCCESS,
|
|
NULL,
|
|
(PVOID)state);
|
|
}
|
|
|
|
//
|
|
// This structure defines ranges in PCI configuration
|
|
// space that AML may not write. This list must be
|
|
// monotonic increasing.
|
|
//
|
|
USHORT PciOpRegionDisallowedRanges[4][2] =
|
|
{ //
|
|
// Everything below the subsystem ID registers
|
|
//
|
|
{0,0x2b},
|
|
|
|
//
|
|
// Everthing between the subsystem ID registers and
|
|
// the Max_Lat register
|
|
//
|
|
{0x30, 0x3b},
|
|
|
|
//
|
|
// Disallow anything above MAXUCHAR
|
|
//
|
|
{0x100, 0xffff},
|
|
|
|
// End tag.
|
|
{0,0}
|
|
};
|
|
|
|
NTSTATUS
|
|
EXPORT
|
|
PciConfigSpaceHandlerWorker(
|
|
IN PNSOBJ AcpiObject,
|
|
IN NTSTATUS CompletionStatus,
|
|
IN POBJDATA Result,
|
|
IN PVOID Context
|
|
)
|
|
{
|
|
PBUS_INTERFACE_STANDARD interface;
|
|
PDEVICE_EXTENSION pciDeviceFilter;
|
|
PPCI_CONFIG_STATE state;
|
|
NTSTATUS status;
|
|
ULONG range, offset, length, bytes = 0;
|
|
ULONG bytesWritten;
|
|
PFNAA simpleCompletion;
|
|
PFNACB lessSimpleCompletion;
|
|
KIRQL oldIrql;
|
|
#if DBG
|
|
BOOLEAN Complain = FALSE;
|
|
#endif
|
|
|
|
state = (PPCI_CONFIG_STATE)Context;
|
|
status = CompletionStatus;
|
|
|
|
//
|
|
// Entering this function twice with the same state
|
|
// means that we need to run the completion routine.
|
|
//
|
|
|
|
InterlockedIncrement(&state->RunCompletion);
|
|
|
|
//
|
|
// If the interpretter failed, just bail.
|
|
//
|
|
if (!NT_SUCCESS(CompletionStatus)) {
|
|
status = STATUS_SUCCESS;
|
|
#if DBG
|
|
Complain = TRUE;
|
|
#endif
|
|
goto PciConfigSpaceHandlerWorkerDone;
|
|
}
|
|
|
|
//
|
|
// If we have not seen this OpRegion before, we need to
|
|
// fill in the dwContext with the PNSOBJ of the
|
|
// PCI device which the OpRegion relates to.
|
|
//
|
|
|
|
if (!state->OpRegion->Context) {
|
|
|
|
if (!(state->Flags & PCISUPP_GOT_SCOPE)) {
|
|
|
|
state->Flags |= PCISUPP_GOT_SCOPE;
|
|
|
|
status = GetOpRegionScope(state->OpRegion,
|
|
PciConfigSpaceHandlerWorker,
|
|
(PVOID)state,
|
|
&((PNSOBJ)(state->OpRegion->Context)));
|
|
|
|
if (status == STATUS_PENDING) {
|
|
return status;
|
|
}
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
status = STATUS_SUCCESS;
|
|
goto PciConfigSpaceHandlerWorkerDone;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Identify the PCI device, that device's extension,
|
|
// and the pointer to the interface within the PCI
|
|
// driver that does PCI config space reads and writes.
|
|
//
|
|
|
|
state->PciObj = (PNSOBJ)state->OpRegion->Context;
|
|
|
|
pciDeviceFilter = (PDEVICE_EXTENSION)state->PciObj->Context;
|
|
|
|
if (pciDeviceFilter == NULL) {
|
|
|
|
//
|
|
// The device has not been initialized yet, we cannot perform
|
|
// PCI config cycles to it. Fail gracefully and return all 0xFF
|
|
//
|
|
bytes = 0;
|
|
status = STATUS_SUCCESS;
|
|
goto PciConfigSpaceHandlerWorkerDone;
|
|
}
|
|
|
|
ASSERT(pciDeviceFilter);
|
|
|
|
interface = pciDeviceFilter->Filter.Interface;
|
|
|
|
ASSERT(interface ? (interface->Size == sizeof(BUS_INTERFACE_STANDARD)) : TRUE);
|
|
|
|
//
|
|
// If interface is non-zero, we have enumerated this PCI
|
|
// device. So use the PCI driver to do config ops.
|
|
// If it is zero, make some attempt to figure out what
|
|
// device this request is for. The result will be
|
|
// used in calls to the HAL.
|
|
//
|
|
|
|
if (!interface) {
|
|
|
|
if (!(state->Flags & PCISUPP_GOT_SLOT_INFO)) {
|
|
|
|
state->Flags |= PCISUPP_GOT_SLOT_INFO;
|
|
|
|
status = GetPciAddress(state->PciObj,
|
|
PciConfigSpaceHandlerWorker,
|
|
(PVOID)state,
|
|
&state->Bus,
|
|
&state->Slot);
|
|
|
|
if (status == STATUS_PENDING) {
|
|
return status;
|
|
}
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
status = STATUS_SUCCESS;
|
|
goto PciConfigSpaceHandlerWorkerDone;
|
|
}
|
|
}
|
|
}
|
|
|
|
status = STATUS_SUCCESS;
|
|
|
|
oldIrql = KeGetCurrentIrql();
|
|
|
|
switch (state->AccessType) {
|
|
case RSACCESS_READ:
|
|
|
|
if (interface) {
|
|
|
|
//
|
|
// Do config space op through PCI driver. Do it
|
|
// at DISPATCH_LEVEL because the PCI driver expects
|
|
// that, if we are running at passive level, it can
|
|
// do things that page. Which may not be true here
|
|
// after we have powered off the disk.
|
|
//
|
|
|
|
if (oldIrql < DISPATCH_LEVEL) {
|
|
KeRaiseIrql(DISPATCH_LEVEL,
|
|
&oldIrql);
|
|
}
|
|
|
|
bytes = interface->GetBusData(interface->Context,
|
|
0,
|
|
state->Data,
|
|
state->Address,
|
|
state->Size);
|
|
|
|
if (oldIrql < DISPATCH_LEVEL) {
|
|
KeLowerIrql(oldIrql);
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// Do config space op through HAL
|
|
//
|
|
|
|
bytes = HalGetBusDataByOffset(PCIConfiguration,
|
|
state->Bus,
|
|
state->Slot.u.AsULONG,
|
|
state->Data,
|
|
state->Address,
|
|
state->Size);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case RSACCESS_WRITE:
|
|
{
|
|
static BOOLEAN ErrorLogged = FALSE;
|
|
|
|
offset = state->Address;
|
|
length = state->Size;
|
|
bytesWritten = 0;
|
|
|
|
//
|
|
// Crop any writes down to the regions that are allowed.
|
|
//
|
|
|
|
range = 0;
|
|
|
|
while (PciOpRegionDisallowedRanges[range][1] != 0) {
|
|
|
|
if (offset < PciOpRegionDisallowedRanges[range][0]) {
|
|
|
|
//
|
|
// At least part of this write falls below this
|
|
// disallowed range. Write all the data up to
|
|
// the beggining of the next allowed range.
|
|
//
|
|
|
|
length = MIN(state->Address + state->Size - offset,
|
|
PciOpRegionDisallowedRanges[range][0] - offset);
|
|
|
|
if (interface) {
|
|
|
|
if (oldIrql < DISPATCH_LEVEL) {
|
|
KeRaiseIrql(DISPATCH_LEVEL,
|
|
&oldIrql);
|
|
}
|
|
|
|
bytes = interface->SetBusData(interface->Context,
|
|
0,
|
|
(PUCHAR)(state->Data + offset - state->Address),
|
|
offset,
|
|
length);
|
|
|
|
if (oldIrql < DISPATCH_LEVEL) {
|
|
KeLowerIrql(oldIrql);
|
|
}
|
|
|
|
} else {
|
|
|
|
bytes = HalSetBusDataByOffset(PCIConfiguration,
|
|
state->Bus,
|
|
state->Slot.u.AsULONG,
|
|
(PUCHAR)(state->Data + offset - state->Address),
|
|
offset,
|
|
length);
|
|
}
|
|
|
|
//
|
|
// Keep track of what we wrote.
|
|
//
|
|
|
|
bytesWritten += length;
|
|
}
|
|
|
|
//
|
|
// Now advance offset past the end of the disallowed range.
|
|
//
|
|
|
|
offset = MAX(state->Address,
|
|
(ULONG)(PciOpRegionDisallowedRanges[range][1] + 1));
|
|
|
|
if (offset >= state->Address + state->Size) {
|
|
|
|
//
|
|
// The current possible write is beyond the end
|
|
// of the requested buffer. So we are done.
|
|
//
|
|
|
|
break;
|
|
}
|
|
|
|
range++;
|
|
}
|
|
|
|
if (bytesWritten == 0) {
|
|
|
|
if(!ErrorLogged) {
|
|
PWCHAR IllegalPCIOpRegionAddress[2];
|
|
WCHAR ACPIName[] = L"ACPI";
|
|
WCHAR addressBuffer[13];
|
|
|
|
//
|
|
// None of this write was possible. Log the problem.
|
|
//
|
|
|
|
//
|
|
// Turn the address into a string
|
|
//
|
|
swprintf( addressBuffer, L"0x%x", state->Address );
|
|
|
|
//
|
|
// Build the list of arguments to pass to the function that will write the
|
|
// error log to the registry
|
|
//
|
|
IllegalPCIOpRegionAddress[0] = ACPIName;
|
|
IllegalPCIOpRegionAddress[1] = addressBuffer;
|
|
|
|
//
|
|
// Log error to event log
|
|
//
|
|
ACPIWriteEventLogEntry(ACPI_ERR_ILLEGAL_PCIOPREGION_WRITE,
|
|
IllegalPCIOpRegionAddress,
|
|
2,
|
|
NULL,
|
|
0
|
|
);
|
|
ErrorLogged = TRUE;
|
|
}
|
|
#if DBG
|
|
Complain = TRUE;
|
|
#endif
|
|
goto PciConfigSpaceHandlerWorkerExit;
|
|
}
|
|
|
|
bytes = bytesWritten;
|
|
break;
|
|
}
|
|
default:
|
|
status = STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
PciConfigSpaceHandlerWorkerDone:
|
|
|
|
if (bytes == 0) {
|
|
|
|
//
|
|
// The handler from the HAL or the PCI driver didn't
|
|
// succeed for some reason. Fill the buffer with 0xff,
|
|
// which is what the AML will expect on failure.
|
|
//
|
|
|
|
RtlFillMemory(state->Data, state->Size, 0xff);
|
|
}
|
|
|
|
PciConfigSpaceHandlerWorkerExit:
|
|
|
|
if (state->RunCompletion) {
|
|
|
|
if (state->CompletionHandlerType ==
|
|
PCISUPP_COMPLETION_HANDLER_PFNAA) {
|
|
|
|
simpleCompletion = (PFNAA)state->CompletionHandler;
|
|
|
|
simpleCompletion(state->CompletionContext);
|
|
|
|
} else {
|
|
|
|
lessSimpleCompletion = (PFNACB)state->CompletionHandler;
|
|
|
|
lessSimpleCompletion(state->PciObj,
|
|
status,
|
|
NULL,
|
|
state->CompletionContext);
|
|
}
|
|
}
|
|
|
|
#if DBG
|
|
if ((!NT_SUCCESS(status)) || Complain) {
|
|
UCHAR opRegion[5] = {0};
|
|
UCHAR parent[5] = {0};
|
|
|
|
RtlCopyMemory(opRegion, ACPIAmliNameObject(state->OpRegion), 4);
|
|
|
|
if (state->PciObj) {
|
|
RtlCopyMemory(parent, ACPIAmliNameObject(state->PciObj), 4);
|
|
}
|
|
|
|
ACPIPrint( (
|
|
ACPI_PRINT_WARNING,
|
|
"Op Region %s failed (parent PCI device was %s)\n",
|
|
opRegion, parent
|
|
) );
|
|
}
|
|
#endif
|
|
ExFreePool(state);
|
|
return status;
|
|
}
|
|
|
|
typedef struct {
|
|
PNSOBJ PciObject;
|
|
PUCHAR Bus;
|
|
PPCI_SLOT_NUMBER Slot;
|
|
|
|
UCHAR ParentBus;
|
|
PCI_SLOT_NUMBER ParentSlot;
|
|
ULONG Flags;
|
|
ULONG Address;
|
|
ULONG BaseBusNumber;
|
|
LONG RunCompletion;
|
|
PFNACB CompletionRoutine;
|
|
PVOID CompletionContext;
|
|
|
|
} GET_ADDRESS_CONTEXT, *PGET_ADDRESS_CONTEXT;
|
|
|
|
NTSTATUS
|
|
GetPciAddress(
|
|
IN PNSOBJ PciObj,
|
|
IN PFNACB CompletionRoutine,
|
|
IN PVOID Context,
|
|
IN OUT PUCHAR Bus,
|
|
IN OUT PPCI_SLOT_NUMBER Slot
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine takes a PNSOBJ that represents a PCI device
|
|
and returns the Bus/Slot information for that device.
|
|
|
|
Arguments:
|
|
|
|
PciObj - PNSOBJ that represents a PCI device
|
|
CompletionRoutine - funtion to call after a STATUS_PENDING
|
|
Context - argument to the CompletionRoutine
|
|
Bus - pointer to fill in with the bus number
|
|
Slot - pointer to fill in with the slot information
|
|
|
|
Return Value:
|
|
|
|
Status
|
|
|
|
Notes:
|
|
|
|
N.B. This is not guaranteed to produce correct results.
|
|
It is intended to be used only when before the PCI
|
|
driver takes control of a device. It is a best-effort
|
|
function that will almost always work early in the
|
|
boot process.
|
|
|
|
--*/
|
|
{
|
|
PGET_ADDRESS_CONTEXT state;
|
|
|
|
ASSERT(CompletionRoutine);
|
|
|
|
state = ExAllocatePoolWithTag(NonPagedPool, sizeof(GET_ADDRESS_CONTEXT), ACPI_INTERFACE_POOLTAG);
|
|
|
|
if (!state) {
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
RtlZeroMemory(state, sizeof(GET_ADDRESS_CONTEXT));
|
|
|
|
state->PciObject = PciObj;
|
|
state->CompletionRoutine = CompletionRoutine;
|
|
state->CompletionContext = Context;
|
|
state->Bus = Bus;
|
|
state->Slot = Slot;
|
|
state->RunCompletion = INITIAL_RUN_COMPLETION;
|
|
|
|
return GetPciAddressWorker(PciObj,
|
|
STATUS_SUCCESS,
|
|
NULL,
|
|
(PVOID)state);
|
|
|
|
}
|
|
|
|
NTSTATUS
|
|
EXPORT
|
|
GetPciAddressWorker(
|
|
IN PNSOBJ AcpiObject,
|
|
IN NTSTATUS Status,
|
|
IN POBJDATA Result,
|
|
IN PVOID Context
|
|
)
|
|
{
|
|
PIO_RESOURCE_REQUIREMENTS_LIST resources;
|
|
PGET_ADDRESS_CONTEXT state;
|
|
PPCI_COMMON_CONFIG pciConfig;
|
|
NTSTATUS status;
|
|
PNSOBJ bus;
|
|
PNSOBJ tempObj;
|
|
ULONG bytesRead, i;
|
|
UCHAR buffer[PCI_COMMON_HDR_LENGTH];
|
|
|
|
ASSERT(Context);
|
|
state = (PGET_ADDRESS_CONTEXT)Context;
|
|
status = Status;
|
|
|
|
//
|
|
// Entering this function twice with the same state
|
|
// means that we need to run the completion routine.
|
|
//
|
|
InterlockedIncrement(&state->RunCompletion);
|
|
|
|
//
|
|
// If Status isn't success, then one of the worker
|
|
// functions we called puked. Bail.
|
|
//
|
|
if (!NT_SUCCESS(Status)) {
|
|
goto GetPciAddressWorkerExit;
|
|
|
|
}
|
|
|
|
//
|
|
// First, determine the slot number.
|
|
//
|
|
if (!(state->Flags & PCISUPP_CHECKED_ADR)) {
|
|
|
|
//
|
|
// Get the _ADR.
|
|
//
|
|
state->Flags |= PCISUPP_CHECKED_ADR;
|
|
status = ACPIGetNSAddressAsync(
|
|
state->PciObject,
|
|
GetPciAddressWorker,
|
|
(PVOID)state,
|
|
&(state->Address),
|
|
NULL
|
|
);
|
|
|
|
if (status == STATUS_PENDING) {
|
|
return status;
|
|
}
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
goto GetPciAddressWorkerExit;
|
|
}
|
|
}
|
|
|
|
if (!(state->Flags & PCISUPP_GOT_SLOT_INFO)) {
|
|
|
|
//
|
|
// Build a PCI_SLOT_NUMBER out of the integer returned
|
|
// from the interpretter.
|
|
//
|
|
state->Slot->u.bits.FunctionNumber = (state->Address) & 0x7;
|
|
state->Slot->u.bits.DeviceNumber = ( (state->Address) >> 16) & 0x1f;
|
|
state->Flags |= PCISUPP_GOT_SLOT_INFO;
|
|
|
|
}
|
|
|
|
//
|
|
// Next, get the bus number, if possible.
|
|
//
|
|
*state->Bus = 0; // default value, in case we have to guess
|
|
|
|
//
|
|
// Check first to see if this bus has a _HID.
|
|
// (It might be a root PCI bridge.)
|
|
//
|
|
bus = state->PciObject;
|
|
tempObj = ACPIAmliGetNamedChild(bus, PACKED_HID);
|
|
if (!tempObj) {
|
|
|
|
//
|
|
// This device had no _HID. So look up
|
|
// to the parent and see if it is a
|
|
// root PCI bridge.
|
|
//
|
|
bus = state->PciObject->pnsParent;
|
|
tempObj = ACPIAmliGetNamedChild(bus, PACKED_HID);
|
|
|
|
}
|
|
|
|
if (!tempObj) {
|
|
|
|
//
|
|
// This PCI device is on a PCI bus that
|
|
// is created by a PCI-PCI bridge.
|
|
//
|
|
if (!(state->Flags & PCISUPP_CHECKED_PARENT)) {
|
|
|
|
state->Flags |= PCISUPP_CHECKED_PARENT;
|
|
status = GetPciAddress(
|
|
bus,
|
|
GetPciAddressWorker,
|
|
(PVOID)state,
|
|
&state->ParentBus,
|
|
&state->ParentSlot
|
|
);
|
|
|
|
if (status == STATUS_PENDING) {
|
|
return status;
|
|
}
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
goto GetPciAddressWorkerExit;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Read the config space for this device.
|
|
//
|
|
bytesRead = HalGetBusDataByOffset(PCIConfiguration,
|
|
state->ParentBus,
|
|
state->ParentSlot.u.AsULONG,
|
|
buffer,
|
|
0,
|
|
PCI_COMMON_HDR_LENGTH);
|
|
|
|
if (bytesRead == 0) {
|
|
//
|
|
// Make a guess that the bus number was 0.
|
|
//
|
|
status = STATUS_SUCCESS;
|
|
goto GetPciAddressWorkerExit;
|
|
}
|
|
|
|
pciConfig = (PPCI_COMMON_CONFIG)buffer;
|
|
|
|
if (pciConfig->HeaderType != PCI_BRIDGE_TYPE) {
|
|
|
|
//
|
|
// Make a guess that the bus number was 0.
|
|
//
|
|
status = STATUS_SUCCESS;
|
|
goto GetPciAddressWorkerExit;
|
|
}
|
|
|
|
//
|
|
// Success. Record the actual bus number of
|
|
// the secondary PCI bus and exit.
|
|
//
|
|
*state->Bus = pciConfig->u.type1.SecondaryBus;
|
|
|
|
status = STATUS_SUCCESS;
|
|
goto GetPciAddressWorkerExit;
|
|
|
|
}
|
|
|
|
//
|
|
// Is there a _BBN to run?
|
|
//
|
|
tempObj = ACPIAmliGetNamedChild(bus, PACKED_BBN);
|
|
if (tempObj) {
|
|
|
|
//
|
|
// This device must be the child of a root PCI bus.
|
|
//
|
|
if (!(state->Flags & PCISUPP_CHECKED_BBN)) {
|
|
|
|
state->Flags |= PCISUPP_CHECKED_BBN;
|
|
status = ACPIGetNSIntegerAsync(
|
|
bus,
|
|
PACKED_BBN,
|
|
GetPciAddressWorker,
|
|
(PVOID)state,
|
|
&(state->BaseBusNumber),
|
|
NULL
|
|
);
|
|
|
|
if (status == STATUS_PENDING) {
|
|
return(status);
|
|
}
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
goto GetPciAddressWorkerExit;
|
|
}
|
|
}
|
|
|
|
//
|
|
// At this point, we must have a Boot Bus Number. This is the correct
|
|
// number for this bus
|
|
//
|
|
ASSERT( state->BaseBusNumber <= 0xFF );
|
|
*(state->Bus) = (UCHAR) (state->BaseBusNumber);
|
|
|
|
//
|
|
// HACKHACK. The ACPI HAL doesn't really know much about busses. But
|
|
// it needs to maintain legacy HAL behavior. And to do that, it needs to
|
|
// know how many PCI busses are in the system. Since we just looked at
|
|
// a root PCI bus, we now give the HAL a heads-up that this bus exists.
|
|
//
|
|
|
|
HalSetMaxLegacyPciBusNumber(state->BaseBusNumber);
|
|
|
|
status = STATUS_SUCCESS;
|
|
|
|
} else {
|
|
|
|
//
|
|
// There is a no _BBN, so the bus number MUST be Zero
|
|
//
|
|
*(state->Bus) = 0;
|
|
status = STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
GetPciAddressWorkerExit:
|
|
|
|
if (state->RunCompletion) {
|
|
|
|
state->CompletionRoutine(AcpiObject,
|
|
status,
|
|
NULL,
|
|
state->CompletionContext);
|
|
|
|
}
|
|
|
|
ExFreePool(state);
|
|
return status;
|
|
}
|
|
|
|
typedef struct {
|
|
PNSOBJ AcpiObject;
|
|
ULONG Flags;
|
|
ULONG Adr;
|
|
PUCHAR Hid;
|
|
PUCHAR Cid;
|
|
BOOLEAN IsPciDeviceResult;
|
|
LONG RunCompletion;
|
|
PFNACB CompletionHandler;
|
|
PVOID CompletionContext;
|
|
BOOLEAN *Result;
|
|
} IS_PCI_DEVICE_STATE, *PIS_PCI_DEVICE_STATE;
|
|
|
|
NTSTATUS
|
|
IsPciDevice(
|
|
IN PNSOBJ AcpiObject,
|
|
IN PFNACB CompletionHandler,
|
|
IN PVOID CompletionContext,
|
|
OUT BOOLEAN *Result
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This checks to see if the PNSOBJ is a PCI device.
|
|
|
|
Arguments:
|
|
|
|
AcpiObject - the object to be checked
|
|
Result - pointer to a boolean for the result
|
|
|
|
Return Value:
|
|
|
|
Status
|
|
|
|
Notes:
|
|
|
|
--*/
|
|
{
|
|
PIS_PCI_DEVICE_STATE state;
|
|
NTSTATUS status;
|
|
|
|
state = ExAllocatePoolWithTag(NonPagedPool, sizeof(IS_PCI_DEVICE_STATE), ACPI_INTERFACE_POOLTAG);
|
|
|
|
if (!state) {
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
RtlZeroMemory(state, sizeof(IS_PCI_DEVICE_STATE));
|
|
|
|
state->AcpiObject = AcpiObject;
|
|
state->CompletionHandler = CompletionHandler;
|
|
state->CompletionContext = CompletionContext;
|
|
state->Result = Result;
|
|
state->RunCompletion = INITIAL_RUN_COMPLETION;
|
|
|
|
return IsPciDeviceWorker(AcpiObject,
|
|
STATUS_SUCCESS,
|
|
NULL,
|
|
(PVOID)state);
|
|
}
|
|
|
|
NTSTATUS
|
|
EXPORT
|
|
IsPciDeviceWorker(
|
|
IN PNSOBJ AcpiObject,
|
|
IN NTSTATUS Status,
|
|
IN POBJDATA Result,
|
|
IN PVOID Context
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the worker function for determining whether
|
|
or not a namespace object represents a PCI device.
|
|
The algorithm is as follows:
|
|
|
|
1) Does this device have a _HID of PNP0A03? If
|
|
so, it is a PCI device.
|
|
|
|
2) Does this device have a _CID of PNP0A03? If
|
|
so, it is a PCI device.
|
|
|
|
3) Does this device have an _ADR?
|
|
|
|
a) No, not a PCI device.
|
|
|
|
b) Yes, check to see if the parent qualifies
|
|
as a PCI device. If it does, this must
|
|
also be a PCI device. If not, then it is not.
|
|
|
|
Arguments:
|
|
|
|
AcpiObject - the object most recently under scrutiny
|
|
Status - current status
|
|
Result - OBJDATA structure necessary for worker functions
|
|
Context - pointer to the context structure
|
|
|
|
Return Value:
|
|
|
|
Status
|
|
|
|
Notes:
|
|
|
|
This function is re-entrant. It may block at any time and
|
|
return. All state is in the Context structure.
|
|
|
|
--*/
|
|
{
|
|
PIS_PCI_DEVICE_STATE state;
|
|
NTSTATUS status;
|
|
PNSOBJ hidObj;
|
|
PNSOBJ cidObj;
|
|
|
|
state = (PIS_PCI_DEVICE_STATE)Context;
|
|
status = Status;
|
|
|
|
//
|
|
// Entering this function twice with the same state
|
|
// means that we need to run the completion routine.
|
|
//
|
|
InterlockedIncrement(&state->RunCompletion);
|
|
|
|
//
|
|
// If Status isn't success, then one of the worker
|
|
// functions we called puked. Bail.
|
|
//
|
|
if (!NT_SUCCESS(status)) {
|
|
*state->Result = FALSE;
|
|
goto IsPciDeviceExit;
|
|
}
|
|
|
|
//
|
|
// Step 0), check to see if this is actually a "device" type
|
|
// namespace object.
|
|
//
|
|
|
|
if (NSGETOBJTYPE(state->AcpiObject) != OBJTYPE_DEVICE) {
|
|
*state->Result = FALSE;
|
|
goto IsPciDeviceExit;
|
|
}
|
|
|
|
//
|
|
// Step 1), check the _HID.
|
|
//
|
|
|
|
if (!(state->Flags & PCISUPP_CHECKED_HID)) {
|
|
|
|
state->Flags |= PCISUPP_CHECKED_HID;
|
|
state->Hid = NULL;
|
|
|
|
hidObj = ACPIAmliGetNamedChild( state->AcpiObject, PACKED_HID );
|
|
|
|
if (hidObj) {
|
|
|
|
status = ACPIGetNSPnpIDAsync(
|
|
state->AcpiObject,
|
|
IsPciDeviceWorker,
|
|
(PVOID)state,
|
|
&state->Hid,
|
|
NULL);
|
|
|
|
if (status == STATUS_PENDING) {
|
|
return status;
|
|
}
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
*state->Result = FALSE;
|
|
goto IsPciDeviceExit;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (state->Hid) {
|
|
|
|
if (strstr(state->Hid, PCI_PNP_ID)) {
|
|
//
|
|
// Was PCI.
|
|
//
|
|
*state->Result = TRUE;
|
|
goto IsPciDeviceExit;
|
|
}
|
|
ExFreePool(state->Hid);
|
|
state->Hid = NULL;
|
|
}
|
|
|
|
//
|
|
// Step 2), check the _CID.
|
|
//
|
|
|
|
if (!(state->Flags & PCISUPP_CHECKED_CID)) {
|
|
|
|
state->Flags |= PCISUPP_CHECKED_CID;
|
|
state->Cid = NULL;
|
|
|
|
cidObj = ACPIAmliGetNamedChild( state->AcpiObject, PACKED_CID );
|
|
|
|
if (cidObj) {
|
|
|
|
status = ACPIGetNSCompatibleIDAsync(
|
|
state->AcpiObject,
|
|
IsPciDeviceWorker,
|
|
(PVOID)state,
|
|
&state->Cid,
|
|
NULL);
|
|
|
|
if (status == STATUS_PENDING) {
|
|
return status;
|
|
}
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
*state->Result = FALSE;
|
|
goto IsPciDeviceExit;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (state->Cid) {
|
|
|
|
if (strstr(state->Cid, PCI_PNP_ID)) {
|
|
//
|
|
// Was PCI.
|
|
//
|
|
*state->Result = TRUE;
|
|
goto IsPciDeviceExit;
|
|
}
|
|
ExFreePool(state->Cid);
|
|
state->Cid = NULL;
|
|
}
|
|
|
|
//
|
|
// Step 3), check the _ADR.
|
|
//
|
|
|
|
if (!(state->Flags & PCISUPP_CHECKED_ADR)) {
|
|
|
|
state->Flags |= PCISUPP_CHECKED_ADR;
|
|
status = ACPIGetNSAddressAsync(
|
|
state->AcpiObject,
|
|
IsPciDeviceWorker,
|
|
(PVOID)state,
|
|
&(state->Adr),
|
|
NULL);
|
|
|
|
if (status == STATUS_PENDING) {
|
|
return status;
|
|
}
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
*state->Result = FALSE;
|
|
goto IsPciDeviceExit;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If we got here, it has an _ADR. Check to see if the
|
|
// parent device is a PCI device.
|
|
//
|
|
|
|
if (!(state->Flags & PCISUPP_CHECKED_PARENT)) {
|
|
|
|
state->Flags |= PCISUPP_CHECKED_PARENT;
|
|
state->IsPciDeviceResult = FALSE;
|
|
status = IsPciDevice(state->AcpiObject->pnsParent,
|
|
IsPciDeviceWorker,
|
|
(PVOID)state,
|
|
&state->IsPciDeviceResult);
|
|
|
|
if (status == STATUS_PENDING) {
|
|
return status;
|
|
}
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
*state->Result = FALSE;
|
|
goto IsPciDeviceExit;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Fall through to the result. If the parent was a PCI
|
|
// device, IsPciDeviceResult will now be TRUE.
|
|
//
|
|
|
|
IsPciDeviceExit:
|
|
|
|
if (state->IsPciDeviceResult) {
|
|
|
|
//
|
|
// Record the result.
|
|
//
|
|
|
|
*state->Result = state->IsPciDeviceResult;
|
|
}
|
|
|
|
if (status == STATUS_OBJECT_NAME_NOT_FOUND) {
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
|
|
if (state->RunCompletion) {
|
|
|
|
state->CompletionHandler(state->AcpiObject,
|
|
status,
|
|
NULL,
|
|
state->CompletionContext);
|
|
}
|
|
|
|
if (state->Hid) ExFreePool(state->Hid);
|
|
if (state->Cid) ExFreePool(state->Cid);
|
|
ExFreePool(state);
|
|
return status;
|
|
}
|
|
|
|
typedef struct {
|
|
PNSOBJ AcpiObject;
|
|
ULONG Flags;
|
|
PUCHAR Hid;
|
|
PUCHAR Cid;
|
|
ULONG Adr;
|
|
BOOLEAN IsPciDevice;
|
|
LONG RunCompletion;
|
|
PFNACB CompletionHandler;
|
|
PVOID CompletionContext;
|
|
BOOLEAN *Result;
|
|
UCHAR Buffer[PCI_COMMON_HDR_LENGTH];
|
|
|
|
} IS_PCI_BUS_STATE, *PIS_PCI_BUS_STATE;
|
|
|
|
NTSTATUS
|
|
IsPciBusAsync(
|
|
IN PNSOBJ AcpiObject,
|
|
IN PFNACB CompletionHandler,
|
|
IN PVOID CompletionContext,
|
|
OUT BOOLEAN *Result
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This checks to see if the PNSOBJ represents a PCI bus.
|
|
|
|
Arguments:
|
|
|
|
AcpiObject - the object to be checked
|
|
Result - pointer to a boolean for the result
|
|
|
|
Return Value:
|
|
|
|
Status
|
|
|
|
Notes:
|
|
|
|
The PNSOBJ may also be a PCI device, in which case
|
|
it is a PCI to PCI bridge.
|
|
|
|
--*/
|
|
{
|
|
PIS_PCI_BUS_STATE state;
|
|
|
|
state = ExAllocatePoolWithTag(NonPagedPool, sizeof(IS_PCI_BUS_STATE), ACPI_INTERFACE_POOLTAG);
|
|
|
|
if (!state) {
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
RtlZeroMemory(state, sizeof(IS_PCI_BUS_STATE));
|
|
|
|
state->AcpiObject = AcpiObject;
|
|
state->CompletionHandler = CompletionHandler;
|
|
state->CompletionContext = CompletionContext;
|
|
state->Result = Result;
|
|
state->RunCompletion = INITIAL_RUN_COMPLETION;
|
|
|
|
*Result = FALSE;
|
|
|
|
return IsPciBusAsyncWorker(AcpiObject,
|
|
STATUS_SUCCESS,
|
|
NULL,
|
|
(PVOID)state);
|
|
}
|
|
|
|
NTSTATUS
|
|
EXPORT
|
|
IsPciBusAsyncWorker(
|
|
IN PNSOBJ AcpiObject,
|
|
IN NTSTATUS Status,
|
|
IN POBJDATA Result,
|
|
IN PVOID Context
|
|
)
|
|
{
|
|
PIS_PCI_BUS_STATE state;
|
|
PNSOBJ hidObj;
|
|
PNSOBJ cidObj;
|
|
PPCI_COMMON_CONFIG pciData;
|
|
NTSTATUS status;
|
|
|
|
ASSERT(Context);
|
|
|
|
state = (PIS_PCI_BUS_STATE)Context;
|
|
status = Status;
|
|
|
|
//
|
|
// Entering this function twice with the same state
|
|
// means that we need to run the completion routine.
|
|
//
|
|
InterlockedIncrement(&state->RunCompletion);
|
|
|
|
//
|
|
// Definitely not a PCI bus...
|
|
//
|
|
if (state->AcpiObject == NULL) {
|
|
|
|
*state->Result = FALSE;
|
|
goto IsPciBusAsyncExit;
|
|
}
|
|
|
|
//
|
|
// If Status isn't success, then one of the worker
|
|
// functions we called puked. Bail.
|
|
//
|
|
if (!NT_SUCCESS(status)) {
|
|
*state->Result = FALSE;
|
|
goto IsPciBusAsyncExit;
|
|
}
|
|
|
|
if (!(state->Flags & PCISUPP_CHECKED_HID)) {
|
|
|
|
state->Flags |= PCISUPP_CHECKED_HID;
|
|
state->Hid = NULL;
|
|
|
|
//
|
|
// Is there an _HID?
|
|
//
|
|
hidObj = ACPIAmliGetNamedChild( state->AcpiObject, PACKED_HID );
|
|
|
|
if (hidObj) {
|
|
|
|
status = ACPIGetNSPnpIDAsync(
|
|
state->AcpiObject,
|
|
IsPciBusAsyncWorker,
|
|
(PVOID)state,
|
|
&(state->Hid),
|
|
NULL);
|
|
|
|
if (status == STATUS_PENDING) {
|
|
return status;
|
|
}
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
*state->Result = FALSE;
|
|
goto IsPciBusAsyncExit;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (state->Hid) {
|
|
|
|
if (strstr(state->Hid, PCI_PNP_ID)) {
|
|
//
|
|
// Was PCI.
|
|
//
|
|
*state->Result = TRUE;
|
|
goto IsPciBusAsyncExit;
|
|
}
|
|
ExFreePool(state->Hid);
|
|
state->Hid = NULL;
|
|
}
|
|
|
|
if (!(state->Flags & PCISUPP_CHECKED_CID)) {
|
|
|
|
state->Flags |= PCISUPP_CHECKED_CID;
|
|
state->Cid = NULL;
|
|
|
|
//
|
|
// Is there a _CID?
|
|
//
|
|
cidObj = ACPIAmliGetNamedChild( state->AcpiObject, PACKED_CID );
|
|
if (cidObj) {
|
|
|
|
status = ACPIGetNSCompatibleIDAsync(
|
|
state->AcpiObject,
|
|
IsPciBusAsyncWorker,
|
|
(PVOID)state,
|
|
&(state->Cid),
|
|
NULL);
|
|
|
|
if (status == STATUS_PENDING) {
|
|
return status;
|
|
}
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
*state->Result = FALSE;
|
|
goto IsPciBusAsyncExit;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (state->Cid) {
|
|
|
|
if (strstr(state->Cid, PCI_PNP_ID)) {
|
|
//
|
|
// Was PCI.
|
|
//
|
|
*state->Result = TRUE;
|
|
goto IsPciBusAsyncExit;
|
|
}
|
|
ExFreePool(state->Cid);
|
|
state->Cid = NULL;
|
|
}
|
|
|
|
if (!(state->Flags & PCISUPP_CHECKED_PCI_DEVICE)) {
|
|
|
|
state->Flags |= PCISUPP_CHECKED_PCI_DEVICE;
|
|
status = IsPciDevice(state->AcpiObject,
|
|
IsPciBusAsyncWorker,
|
|
(PVOID)state,
|
|
&state->IsPciDevice);
|
|
|
|
if (status == STATUS_PENDING) {
|
|
return status;
|
|
}
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
*state->Result = FALSE;
|
|
goto IsPciBusAsyncExit;
|
|
}
|
|
}
|
|
|
|
if (state->IsPciDevice) {
|
|
|
|
if (!(state->Flags & PCISUPP_CHECKED_ADR)) {
|
|
|
|
state->Flags |= PCISUPP_CHECKED_ADR;
|
|
status = ACPIGetNSAddressAsync(
|
|
state->AcpiObject,
|
|
IsPciBusAsyncWorker,
|
|
(PVOID)state,
|
|
&(state->Adr),
|
|
NULL
|
|
);
|
|
|
|
if (status == STATUS_PENDING) {
|
|
return status;
|
|
}
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
*state->Result = FALSE;
|
|
goto IsPciBusAsyncExit;
|
|
}
|
|
}
|
|
|
|
if (!(state->Flags & PCISUPP_CHECKED_PCI_BRIDGE)) {
|
|
|
|
//
|
|
// Now read PCI config space to see if this is a bridge.
|
|
//
|
|
state->Flags |= PCISUPP_CHECKED_PCI_BRIDGE;
|
|
status = PciConfigInternal(RSACCESS_READ,
|
|
state->AcpiObject,
|
|
0,
|
|
PCI_COMMON_HDR_LENGTH,
|
|
IsPciBusAsyncWorker,
|
|
(PVOID)state,
|
|
state->Buffer);
|
|
|
|
if (status == STATUS_PENDING) {
|
|
return status;
|
|
}
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
*state->Result = FALSE;
|
|
goto IsPciBusAsyncExit;
|
|
}
|
|
}
|
|
|
|
pciData = (PPCI_COMMON_CONFIG)state->Buffer;
|
|
|
|
if ((PCI_CONFIGURATION_TYPE(pciData) == PCI_BRIDGE_TYPE) ||
|
|
(PCI_CONFIGURATION_TYPE(pciData) == PCI_CARDBUS_BRIDGE_TYPE)) {
|
|
|
|
*state->Result = TRUE;
|
|
|
|
} else {
|
|
|
|
*state->Result = FALSE;
|
|
}
|
|
|
|
}
|
|
|
|
IsPciBusAsyncExit:
|
|
|
|
if (status == STATUS_OBJECT_NAME_NOT_FOUND) {
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
|
|
if (state->RunCompletion) {
|
|
|
|
state->CompletionHandler(state->AcpiObject,
|
|
status,
|
|
NULL,
|
|
state->CompletionContext);
|
|
}
|
|
|
|
if (state->Hid) ExFreePool(state->Hid);
|
|
if (state->Cid) ExFreePool(state->Cid);
|
|
ExFreePool(state);
|
|
return status;
|
|
}
|
|
|
|
BOOLEAN
|
|
IsPciBus(
|
|
IN PDEVICE_OBJECT DeviceObject
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This checks to see if the DeviceObject represents a PCI bus.
|
|
|
|
Arguments:
|
|
|
|
AcpiObject - the object to be checked
|
|
Result - pointer to a boolean for the result
|
|
|
|
Return Value:
|
|
|
|
Status
|
|
|
|
Notes:
|
|
|
|
--*/
|
|
{
|
|
AMLISUPP_CONTEXT_PASSIVE getDataContext;
|
|
PDEVICE_EXTENSION devExt = ACPIInternalGetDeviceExtension(DeviceObject);
|
|
NTSTATUS status;
|
|
BOOLEAN result = FALSE;
|
|
|
|
PAGED_CODE();
|
|
|
|
ASSERT(devExt->Signature == ACPI_SIGNATURE);
|
|
|
|
KeInitializeEvent(&getDataContext.Event, SynchronizationEvent, FALSE);
|
|
getDataContext.Status = STATUS_NOT_FOUND;
|
|
|
|
if (!(devExt->Flags & DEV_PROP_NO_OBJECT) ) {
|
|
|
|
status = IsPciBusAsync( devExt->AcpiObject,
|
|
AmlisuppCompletePassive,
|
|
(PVOID)&getDataContext,
|
|
&result );
|
|
|
|
if (status == STATUS_PENDING) {
|
|
|
|
KeWaitForSingleObject(&getDataContext.Event,
|
|
Executive,
|
|
KernelMode,
|
|
FALSE,
|
|
NULL);
|
|
}
|
|
|
|
}
|
|
return result;
|
|
}
|
|
|
|
BOOLEAN
|
|
IsPciBusExtension(
|
|
IN PDEVICE_EXTENSION DeviceExtension
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This checks to see if the DeviceExtension represents a PCI bus.
|
|
|
|
Arguments:
|
|
|
|
AcpiObject - the object to be checked
|
|
Result - pointer to a boolean for the result
|
|
|
|
Return Value:
|
|
|
|
Status
|
|
|
|
Notes:
|
|
|
|
--*/
|
|
{
|
|
AMLISUPP_CONTEXT_PASSIVE getDataContext;
|
|
NTSTATUS status;
|
|
BOOLEAN result = FALSE;
|
|
|
|
PAGED_CODE();
|
|
|
|
ASSERT(DeviceExtension->Signature == ACPI_SIGNATURE);
|
|
|
|
KeInitializeEvent(&getDataContext.Event, SynchronizationEvent, FALSE);
|
|
getDataContext.Status = STATUS_NOT_FOUND;
|
|
|
|
if ( (DeviceExtension->Flags & DEV_PROP_NO_OBJECT) ) {
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
status = IsPciBusAsync(
|
|
DeviceExtension->AcpiObject,
|
|
AmlisuppCompletePassive,
|
|
(PVOID)&getDataContext,
|
|
&result
|
|
);
|
|
if (status == STATUS_PENDING) {
|
|
|
|
KeWaitForSingleObject(
|
|
&getDataContext.Event,
|
|
Executive,
|
|
KernelMode,
|
|
FALSE,
|
|
NULL
|
|
);
|
|
|
|
}
|
|
return result;
|
|
}
|
|
|
|
BOOLEAN
|
|
IsNsobjPciBus(
|
|
IN PNSOBJ Device
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This checks to see if the DeviceObject represents a PCI bus.
|
|
|
|
Arguments:
|
|
|
|
AcpiObject - the object to be checked
|
|
Result - pointer to a boolean for the result
|
|
|
|
Return Value:
|
|
|
|
Status
|
|
|
|
Notes:
|
|
|
|
--*/
|
|
{
|
|
AMLISUPP_CONTEXT_PASSIVE getDataContext;
|
|
NTSTATUS status;
|
|
BOOLEAN result = FALSE;
|
|
|
|
PAGED_CODE();
|
|
|
|
KeInitializeEvent(&getDataContext.Event, SynchronizationEvent, FALSE);
|
|
getDataContext.Status = STATUS_NOT_FOUND;
|
|
|
|
status = IsPciBusAsync( Device,
|
|
AmlisuppCompletePassive,
|
|
(PVOID)&getDataContext,
|
|
&result );
|
|
|
|
if (status == STATUS_PENDING) {
|
|
|
|
KeWaitForSingleObject(&getDataContext.Event,
|
|
Executive,
|
|
KernelMode,
|
|
FALSE,
|
|
NULL);
|
|
|
|
status = getDataContext.Status;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
typedef struct {
|
|
PNSOBJ OpRegion;
|
|
PNSOBJ Parent;
|
|
ULONG Flags;
|
|
BOOLEAN IsPciDeviceResult;
|
|
LONG RunCompletion;
|
|
PFNACB CompletionHandler;
|
|
PVOID CompletionContext;
|
|
PNSOBJ *PciObj;
|
|
} OP_REGION_SCOPE_STATE, *POP_REGION_SCOPE_STATE;
|
|
|
|
NTSTATUS
|
|
GetOpRegionScope(
|
|
IN PNSOBJ OpRegion,
|
|
IN PFNACB CompletionHandler,
|
|
IN PVOID CompletionContext,
|
|
OUT PNSOBJ *PciObj
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine takes a pointer to an OpRegion and
|
|
returns a pointer to the PCI device that it operates
|
|
on.
|
|
|
|
Arguments:
|
|
|
|
OpRegion - the operational region
|
|
PciObj - the object the region operates on
|
|
|
|
Return Value:
|
|
|
|
Status
|
|
|
|
Notes:
|
|
|
|
--*/
|
|
{
|
|
POP_REGION_SCOPE_STATE state;
|
|
|
|
state = ExAllocatePoolWithTag(NonPagedPool, sizeof(OP_REGION_SCOPE_STATE), ACPI_INTERFACE_POOLTAG);
|
|
|
|
if (!state) {
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
RtlZeroMemory(state, sizeof(OP_REGION_SCOPE_STATE));
|
|
|
|
state->OpRegion = OpRegion;
|
|
state->Parent = OpRegion->pnsParent;
|
|
state->CompletionHandler = CompletionHandler;
|
|
state->CompletionContext = CompletionContext;
|
|
state->PciObj = PciObj;
|
|
state->RunCompletion = INITIAL_RUN_COMPLETION;
|
|
|
|
return GetOpRegionScopeWorker(OpRegion,
|
|
STATUS_SUCCESS,
|
|
NULL,
|
|
(PVOID)state);
|
|
}
|
|
|
|
NTSTATUS
|
|
EXPORT
|
|
GetOpRegionScopeWorker(
|
|
IN PNSOBJ AcpiObject,
|
|
IN NTSTATUS Status,
|
|
IN POBJDATA Result,
|
|
IN PVOID Context
|
|
)
|
|
{
|
|
POP_REGION_SCOPE_STATE state;
|
|
NTSTATUS status;
|
|
BOOLEAN found = FALSE;
|
|
|
|
ASSERT(Context);
|
|
|
|
state = (POP_REGION_SCOPE_STATE)Context;
|
|
status = Status;
|
|
|
|
//
|
|
// Entering this function twice with the same state
|
|
// means that we need to run the completion routine.
|
|
//
|
|
|
|
InterlockedIncrement(&state->RunCompletion);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
goto GetOpRegionScopeWorkerExit;
|
|
}
|
|
|
|
//
|
|
// Need to find the PNSOBJ for the PCI device. Do it by
|
|
// looking up the tree.
|
|
//
|
|
|
|
while ((state->Parent != NULL) &&
|
|
(state->Parent->pnsParent != state->Parent)) {
|
|
|
|
if ( !(state->Flags & PCISUPP_COMPLETING_IS_PCI) ) {
|
|
|
|
state->Flags |= PCISUPP_COMPLETING_IS_PCI;
|
|
|
|
status = IsPciDevice(state->Parent,
|
|
GetOpRegionScopeWorker,
|
|
(PVOID)state,
|
|
&state->IsPciDeviceResult);
|
|
|
|
if (status == STATUS_PENDING) {
|
|
return status;
|
|
}
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
goto GetOpRegionScopeWorkerExit;
|
|
}
|
|
}
|
|
|
|
state->Flags &= ~PCISUPP_COMPLETING_IS_PCI;
|
|
|
|
if (state->IsPciDeviceResult) {
|
|
|
|
found = TRUE;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Look one step higher.
|
|
//
|
|
state->Parent = state->Parent->pnsParent;
|
|
}
|
|
|
|
if (found) {
|
|
|
|
*state->PciObj = state->Parent;
|
|
status = STATUS_SUCCESS;
|
|
|
|
} else {
|
|
|
|
status = STATUS_NOT_FOUND;
|
|
}
|
|
|
|
GetOpRegionScopeWorkerExit:
|
|
|
|
if (state->RunCompletion) {
|
|
|
|
state->CompletionHandler(state->OpRegion,
|
|
status,
|
|
NULL,
|
|
state->CompletionContext);
|
|
}
|
|
|
|
ExFreePool(state);
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
EnableDisableRegions(
|
|
IN PNSOBJ NameSpaceObj,
|
|
IN BOOLEAN Enable
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine runs the _REG method for all PCI op-regions
|
|
underneath NameSpaceObj and all its children, except
|
|
additional PCI to PCI bridges.
|
|
|
|
Arguments:
|
|
|
|
NameSpaceObj - A device in the namespace
|
|
|
|
Enable - boolean specifying whether this function should
|
|
enable or disable the regions
|
|
|
|
Return Value:
|
|
|
|
Status
|
|
|
|
Notes:
|
|
|
|
--*/
|
|
#define CONNECT_HANDLER 1
|
|
#define DISCONNECT_HANDLER 0
|
|
{
|
|
PNSOBJ sibling;
|
|
PNSOBJ regMethod = NULL;
|
|
OBJDATA objdata[2];
|
|
NTSTATUS status, returnStatus;
|
|
|
|
PAGED_CODE();
|
|
|
|
ASSERT(NameSpaceObj->dwNameSeg);
|
|
|
|
//
|
|
// Find a _REG that is a child of this device.
|
|
//
|
|
regMethod = ACPIAmliGetNamedChild( NameSpaceObj, PACKED_REG );
|
|
if (regMethod != NULL) {
|
|
|
|
//
|
|
// Construct arguments for _REG method.
|
|
//
|
|
RtlZeroMemory(objdata, sizeof(objdata));
|
|
|
|
objdata[0].dwDataType = OBJTYPE_INTDATA;
|
|
objdata[0].uipDataValue = REGSPACE_PCICFG;
|
|
objdata[1].dwDataType = OBJTYPE_INTDATA;
|
|
objdata[1].uipDataValue = (Enable ? CONNECT_HANDLER : DISCONNECT_HANDLER );
|
|
|
|
status = AMLIEvalNameSpaceObject(
|
|
regMethod,
|
|
NULL,
|
|
2,
|
|
objdata
|
|
);
|
|
|
|
}
|
|
|
|
//
|
|
// Recurse to all of the children. Propagate any errors,
|
|
// but don't stop for them.
|
|
//
|
|
|
|
returnStatus = STATUS_SUCCESS;
|
|
|
|
sibling = NSGETFIRSTCHILD(NameSpaceObj);
|
|
|
|
if (!sibling) {
|
|
return returnStatus;
|
|
}
|
|
|
|
do {
|
|
|
|
switch (NSGETOBJTYPE(sibling)) {
|
|
case OBJTYPE_DEVICE:
|
|
|
|
if (IsNsobjPciBus(sibling)) {
|
|
|
|
//
|
|
// Don't recurse past a child PCI to PCI bridge.
|
|
//
|
|
break;
|
|
}
|
|
|
|
status = EnableDisableRegions(sibling, Enable);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
returnStatus = status;
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
} while (sibling = NSGETNEXTSIBLING(sibling));
|
|
|
|
return returnStatus;
|
|
}
|
|
|
|
UCHAR
|
|
GetBusNumberFromCRS(
|
|
IN PDEVICE_EXTENSION DeviceExtension,
|
|
IN PUCHAR CRS
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Grovels through the _CRS buffer looking for an address
|
|
descriptor for the bus number
|
|
|
|
Arguments:
|
|
|
|
DeviceExtension - Pointer to the PCI root bus
|
|
CRS - Supplies the CRS.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
|
|
{
|
|
PPNP_DWORD_ADDRESS_DESCRIPTOR DwordAddress;
|
|
PPNP_QWORD_ADDRESS_DESCRIPTOR QwordAddress;
|
|
PPNP_WORD_ADDRESS_DESCRIPTOR WordAddress;
|
|
PUCHAR Current;
|
|
UCHAR TagName;
|
|
USHORT Increment;
|
|
|
|
Current = CRS;
|
|
while ( *Current ) {
|
|
|
|
TagName = *Current;
|
|
if ( !(TagName & LARGE_RESOURCE_TAG)) {
|
|
Increment = (USHORT) (TagName & SMALL_TAG_SIZE_MASK) + 1;
|
|
TagName &= SMALL_TAG_MASK;
|
|
} else {
|
|
Increment = ( *(USHORT UNALIGNED *)(Current + 1) ) + 3;
|
|
}
|
|
|
|
if (TagName == TAG_END) {
|
|
break;
|
|
}
|
|
|
|
switch(TagName) {
|
|
case TAG_DOUBLE_ADDRESS:
|
|
|
|
DwordAddress = (PPNP_DWORD_ADDRESS_DESCRIPTOR) Current;
|
|
if (DwordAddress->RFlag == PNP_ADDRESS_BUS_NUMBER_TYPE) {
|
|
ASSERT(DwordAddress->MinimumAddress <= 0xFF);
|
|
return (UCHAR) DwordAddress->MinimumAddress;
|
|
}
|
|
break;
|
|
|
|
case TAG_QUAD_ADDRESS:
|
|
|
|
QwordAddress = (PPNP_QWORD_ADDRESS_DESCRIPTOR) Current;
|
|
if (QwordAddress->RFlag == PNP_ADDRESS_BUS_NUMBER_TYPE) {
|
|
ASSERT(QwordAddress->MinimumAddress <= 0xFF);
|
|
return (UCHAR) QwordAddress->MinimumAddress;
|
|
}
|
|
break;
|
|
|
|
case TAG_WORD_ADDRESS:
|
|
|
|
WordAddress = (PPNP_WORD_ADDRESS_DESCRIPTOR) Current;
|
|
if (WordAddress->RFlag == PNP_ADDRESS_BUS_NUMBER_TYPE) {
|
|
ASSERT(WordAddress->MinimumAddress <= 0xFF);
|
|
return (UCHAR) WordAddress->MinimumAddress;
|
|
}
|
|
break;
|
|
|
|
}
|
|
|
|
Current += Increment;
|
|
}
|
|
|
|
//
|
|
// No Bus address was found. This is an error in the BIOS.
|
|
//
|
|
KeBugCheckEx(
|
|
ACPI_BIOS_ERROR,
|
|
ACPI_ROOT_PCI_RESOURCE_FAILURE,
|
|
(ULONG_PTR) DeviceExtension,
|
|
3,
|
|
(ULONG_PTR) CRS
|
|
);
|
|
return((UCHAR)-1);
|
|
}
|
|
|