2344 lines
58 KiB
C
2344 lines
58 KiB
C
/*++
|
||
|
||
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);
|
||
}
|
||
|