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.
5263 lines
147 KiB
5263 lines
147 KiB
/*++
|
|
|
|
Copyright (c) 1997 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
irqarb.c
|
|
|
|
Abstract:
|
|
|
|
This module implements an arbiter for IRQs.
|
|
|
|
In a traditional machine, the BIOS sets up the
|
|
mapping of PCI interrupt sources (i.e. Bus 0, slot 4,
|
|
funtion 1, INT B maps to IRQ 10.) This mapping is
|
|
then forever fixed. On the other hand, an ACPI
|
|
machine can possibly change these mappings by
|
|
manipulating the "link nodes" in the AML namespace.
|
|
Since the ACPI driver is the agent of change, it is the
|
|
place to implement an arbiter.
|
|
|
|
Author:
|
|
|
|
Jake Oshins (jakeo) 6-2-97
|
|
|
|
Environment:
|
|
|
|
NT Kernel Model Driver only
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
#include "pch.h"
|
|
#include "string.h"
|
|
#include "stdlib.h"
|
|
#include "stdio.h"
|
|
|
|
#if DBG
|
|
extern LONG ArbDebugLevel;
|
|
|
|
#define DEBUG_PRINT(Level, Message) \
|
|
if (ArbDebugLevel >= Level) DbgPrint Message
|
|
#else
|
|
#define DEBUG_PRINT(Level, Message)
|
|
#endif
|
|
|
|
#define PciBridgeSwizzle(device, pin) \
|
|
((((pin - 1) + (device % 4)) % 4) + 1)
|
|
|
|
NTSTATUS
|
|
AcpiArbUnpackRequirement(
|
|
IN PIO_RESOURCE_DESCRIPTOR Descriptor,
|
|
OUT PULONGLONG Minimum,
|
|
OUT PULONGLONG Maximum,
|
|
OUT PULONG Length,
|
|
OUT PULONG Alignment
|
|
);
|
|
|
|
NTSTATUS
|
|
AcpiArbPackResource(
|
|
IN PIO_RESOURCE_DESCRIPTOR Requirement,
|
|
IN ULONGLONG Start,
|
|
OUT PCM_PARTIAL_RESOURCE_DESCRIPTOR Descriptor
|
|
);
|
|
|
|
LONG
|
|
AcpiArbScoreRequirement(
|
|
IN PIO_RESOURCE_DESCRIPTOR Descriptor
|
|
);
|
|
|
|
NTSTATUS
|
|
AcpiArbUnpackResource(
|
|
IN PCM_PARTIAL_RESOURCE_DESCRIPTOR Descriptor,
|
|
OUT PULONGLONG Start,
|
|
OUT PULONG Length
|
|
);
|
|
|
|
NTSTATUS
|
|
AcpiArbTestAllocation(
|
|
IN PARBITER_INSTANCE Arbiter,
|
|
IN OUT PLIST_ENTRY ArbitrationList
|
|
);
|
|
|
|
NTSTATUS
|
|
AcpiArbBootAllocation(
|
|
IN PARBITER_INSTANCE Arbiter,
|
|
IN OUT PLIST_ENTRY ArbitrationList
|
|
);
|
|
|
|
NTSTATUS
|
|
AcpiArbRetestAllocation(
|
|
IN PARBITER_INSTANCE Arbiter,
|
|
IN OUT PLIST_ENTRY ArbitrationList
|
|
);
|
|
|
|
NTSTATUS
|
|
AcpiArbRollbackAllocation(
|
|
PARBITER_INSTANCE Arbiter
|
|
);
|
|
|
|
NTSTATUS
|
|
AcpiArbCommitAllocation(
|
|
PARBITER_INSTANCE Arbiter
|
|
);
|
|
|
|
BOOLEAN
|
|
AcpiArbGetNextAllocationRange(
|
|
IN PARBITER_INSTANCE Arbiter,
|
|
IN OUT PARBITER_ALLOCATION_STATE State
|
|
);
|
|
|
|
NTSTATUS
|
|
AcpiArbCrackPRT(
|
|
IN PDEVICE_OBJECT Pdo,
|
|
IN OUT PNSOBJ *LinkNode,
|
|
IN OUT ULONG *Vector
|
|
);
|
|
|
|
PDEVICE_OBJECT
|
|
AcpiGetFilter(
|
|
IN PDEVICE_OBJECT Root,
|
|
IN PDEVICE_OBJECT Pdo
|
|
);
|
|
|
|
BOOLEAN
|
|
ArbFindSuitableRange(
|
|
PARBITER_INSTANCE Arbiter,
|
|
PARBITER_ALLOCATION_STATE State
|
|
);
|
|
|
|
BOOLEAN
|
|
AcpiArbFindSuitableRange(
|
|
PARBITER_INSTANCE Arbiter,
|
|
PARBITER_ALLOCATION_STATE State
|
|
);
|
|
|
|
VOID
|
|
AcpiArbAddAllocation(
|
|
IN PARBITER_INSTANCE Arbiter,
|
|
IN PARBITER_ALLOCATION_STATE State
|
|
);
|
|
|
|
VOID
|
|
AcpiArbBacktrackAllocation(
|
|
IN PARBITER_INSTANCE Arbiter,
|
|
IN PARBITER_ALLOCATION_STATE State
|
|
);
|
|
|
|
BOOLEAN
|
|
AcpiArbOverrideConflict(
|
|
IN PARBITER_INSTANCE Arbiter,
|
|
IN PARBITER_ALLOCATION_STATE State
|
|
);
|
|
|
|
BOOLEAN
|
|
LinkNodeInUse(
|
|
IN PARBITER_INSTANCE Arbiter,
|
|
IN PNSOBJ LinkNode,
|
|
IN OUT ULONG *Irq, OPTIONAL
|
|
IN OUT UCHAR *Flags OPTIONAL
|
|
);
|
|
|
|
NTSTATUS
|
|
AcpiArbReferenceLinkNode(
|
|
IN PARBITER_INSTANCE Arbiter,
|
|
IN PNSOBJ LinkNode,
|
|
IN ULONG Irq
|
|
);
|
|
|
|
NTSTATUS
|
|
AcpiArbDereferenceLinkNode(
|
|
IN PARBITER_INSTANCE Arbiter,
|
|
IN PNSOBJ LinkNode
|
|
);
|
|
|
|
NTSTATUS
|
|
AcpiArbSetLinkNodeIrq(
|
|
IN PNSOBJ LinkNode,
|
|
IN PCM_PARTIAL_RESOURCE_DESCRIPTOR LinkNodeIrq
|
|
);
|
|
|
|
NTSTATUS
|
|
EXPORT
|
|
AcpiArbSetLinkNodeIrqWorker(
|
|
IN PNSOBJ AcpiObject,
|
|
IN NTSTATUS Status,
|
|
IN POBJDATA Result,
|
|
IN PVOID Context
|
|
);
|
|
|
|
NTSTATUS
|
|
AcpiArbSetLinkNodeIrqAsync(
|
|
IN PNSOBJ LinkNode,
|
|
IN PCM_PARTIAL_RESOURCE_DESCRIPTOR LinkNodeIrq,
|
|
IN PFNACB CompletionHandler,
|
|
IN PVOID CompletionContext
|
|
);
|
|
|
|
NTSTATUS
|
|
AcpiArbPreprocessEntry(
|
|
IN PARBITER_INSTANCE Arbiter,
|
|
IN PARBITER_ALLOCATION_STATE State
|
|
);
|
|
|
|
NTSTATUS
|
|
AcpiArbQueryConflict(
|
|
IN PARBITER_INSTANCE Arbiter,
|
|
IN PDEVICE_OBJECT PhysicalDeviceObject,
|
|
IN PIO_RESOURCE_DESCRIPTOR ConflictingResource,
|
|
OUT PULONG ConflictCount,
|
|
OUT PARBITER_CONFLICT_INFO *Conflicts
|
|
);
|
|
|
|
NTSTATUS
|
|
EXPORT
|
|
IrqArbRestoreIrqRoutingWorker(
|
|
IN PNSOBJ AcpiObject,
|
|
IN NTSTATUS Status,
|
|
IN POBJDATA Result,
|
|
IN PVOID Context
|
|
);
|
|
|
|
NTSTATUS
|
|
DisableLinkNodesAsync(
|
|
IN PNSOBJ Root,
|
|
IN PFNACB CompletionHandler,
|
|
IN PVOID CompletionContext
|
|
);
|
|
|
|
NTSTATUS
|
|
EXPORT
|
|
DisableLinkNodesAsyncWorker(
|
|
IN PNSOBJ AcpiObject,
|
|
IN NTSTATUS Status,
|
|
IN POBJDATA Result,
|
|
IN PVOID Context
|
|
);
|
|
|
|
NTSTATUS
|
|
UnreferenceArbitrationList(
|
|
IN PARBITER_INSTANCE Arbiter,
|
|
IN OUT PLIST_ENTRY ArbitrationList
|
|
);
|
|
|
|
NTSTATUS
|
|
ClearTempLinkNodeCounts(
|
|
IN PARBITER_INSTANCE Arbiter
|
|
);
|
|
|
|
NTSTATUS
|
|
MakeTempLinkNodeCountsPermanent(
|
|
IN PARBITER_INSTANCE Arbiter
|
|
);
|
|
|
|
PVECTOR_BLOCK
|
|
HashVector(
|
|
IN ULONG Vector
|
|
);
|
|
|
|
NTSTATUS
|
|
AddVectorToTable(
|
|
IN ULONG Vector,
|
|
IN UCHAR ReferenceCount,
|
|
IN UCHAR TempRefCount,
|
|
IN UCHAR Flags
|
|
);
|
|
|
|
VOID
|
|
ClearTempVectorCounts(
|
|
VOID
|
|
);
|
|
|
|
VOID
|
|
MakeTempVectorCountsPermanent(
|
|
VOID
|
|
);
|
|
|
|
VOID
|
|
DumpVectorTable(
|
|
VOID
|
|
);
|
|
|
|
VOID
|
|
DereferenceVector(
|
|
IN ULONG Vector
|
|
);
|
|
|
|
VOID
|
|
ReferenceVector(
|
|
IN ULONG Vector,
|
|
IN UCHAR Flags
|
|
);
|
|
|
|
NTSTATUS
|
|
LookupIsaVectorOverride(
|
|
IN ULONG IsaVector,
|
|
IN OUT ULONG *RedirectionVector OPTIONAL,
|
|
IN OUT UCHAR *Flags OPTIONAL
|
|
);
|
|
|
|
NTSTATUS
|
|
GetLinkNodeFlags(
|
|
IN PARBITER_INSTANCE Arbiter,
|
|
IN PNSOBJ LinkNode,
|
|
IN OUT UCHAR *Flags
|
|
);
|
|
|
|
NTSTATUS
|
|
GetIsaVectorFlags(
|
|
IN ULONG Vector,
|
|
IN OUT UCHAR *Flags
|
|
);
|
|
|
|
VOID
|
|
TrackDevicesConnectedToLinkNode(
|
|
IN PNSOBJ LinkNode,
|
|
IN PDEVICE_OBJECT Pdo
|
|
);
|
|
|
|
NTSTATUS
|
|
FindVectorInAlternatives(
|
|
IN PARBITER_INSTANCE Arbiter,
|
|
IN PARBITER_ALLOCATION_STATE State,
|
|
IN ULONGLONG Vector,
|
|
OUT ULONG *Alternative
|
|
);
|
|
|
|
NTSTATUS
|
|
FindBootConfig(
|
|
IN PARBITER_INSTANCE Arbiter,
|
|
IN PARBITER_ALLOCATION_STATE State,
|
|
IN ULONGLONG *Vector
|
|
);
|
|
|
|
//
|
|
// The following is a hash table. It is VECTOR_HASH_TABLE_LENGTH entries
|
|
// long and VECTOR_HASH_TABLE_WIDTH entries wide. We hash on the numerical
|
|
// value of the IRQ modulo the length of the table. We look across the
|
|
// table until we find the entry that matches the vector. If we get to
|
|
// the end of the row and find an entry marked with TOKEN_VALUE, we follow
|
|
// the pointer to an extension of this row in the table.
|
|
//
|
|
// ---------------------------------------------------------------------
|
|
//| (IRQ number, ref counts, flags) | (IRQ number, ref counts, flags)
|
|
//| (IRQ number, ref counts, flags) | (IRQ number, ref counts, flags)
|
|
//| (IRQ number, ref counts, flags) | (TOKEN_VALUE, pointer to new table row)
|
|
//| (IRQ number, ref counts, flags) | (unused entry (0))
|
|
//| (IRQ number, ref counts, flags) | (IRQ number, ref counts, flags)
|
|
//----------------------------------------------------------------------
|
|
//
|
|
// New table row, pointed to by pointer following TOKEN_VALUE:
|
|
//
|
|
//----------------------------------------------------------
|
|
//| (IRQ number, ref counts, flags) | (unused entry (0))
|
|
//----------------------------------------------------------
|
|
//
|
|
|
|
#define HASH_ENTRY(x, y) \
|
|
(IrqHashTable + (x * VECTOR_HASH_TABLE_WIDTH) + y)
|
|
|
|
PVECTOR_BLOCK IrqHashTable;
|
|
ULONG InterruptModel = 0;
|
|
ULONG AcpiSciVector;
|
|
UCHAR AcpiIrqDefaultBootConfig = 0;
|
|
UCHAR AcpiArbPciAlternativeRotation = 0;
|
|
BOOLEAN AcpiArbCardbusPresent = FALSE;
|
|
|
|
enum {
|
|
AcpiIrqDistributionDispositionDontCare = 0,
|
|
AcpiIrqDistributionDispositionSpreadOut,
|
|
AcpiIrqDistributionDispositionStackUp
|
|
} AcpiIrqDistributionDisposition = 0;
|
|
|
|
typedef enum {
|
|
AcpiIrqNextRangeMinState = 0xfff,
|
|
AcpiIrqNextRangeInit,
|
|
AcpiIrqNextRangeInitPolicyNeutral,
|
|
AcpiIrqNextRangeInitPic,
|
|
AcpiIrqNextRangeInitLegacy,
|
|
AcpiIrqNextRangeBootRegAlternative,
|
|
AcpiIrqNextRangeSciAlternative,
|
|
AcpiIrqNextRangeUseBootConfig,
|
|
AcpiIrqNextRangeAlternativeZero,
|
|
AcpiIrqNextRangeAlternativeN,
|
|
AcpiIrqNextRangeMaxState
|
|
} NEXT_RANGE_STATE, *PNEXT_RANGE_STATE;
|
|
|
|
#define ARBITER_INTERRUPT_LEVEL_SENSATIVE 0x10
|
|
#define ARBITER_INTERRUPT_LATCHED 0x20
|
|
#define ARBITER_INTERRUPT_BITS (ARBITER_INTERRUPT_LATCHED | ARBITER_INTERRUPT_LEVEL_SENSATIVE)
|
|
|
|
#define ISA_PIC_VECTORS 16
|
|
#define ALTERNATIVE_SHUFFLE_SIZE 0x10
|
|
|
|
extern BOOLEAN AcpiInterruptRoutingFailed;
|
|
extern PACPIInformation AcpiInformation;
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(PAGE, AcpiInitIrqArbiter)
|
|
#pragma alloc_text(PAGE, AcpiArbInitializePciRouting)
|
|
#pragma alloc_text(PAGE, AcpiArbUnpackRequirement)
|
|
#pragma alloc_text(PAGE, AcpiArbPackResource)
|
|
#pragma alloc_text(PAGE, AcpiArbScoreRequirement)
|
|
#pragma alloc_text(PAGE, AcpiArbUnpackResource)
|
|
#pragma alloc_text(PAGE, AcpiArbFindSuitableRange)
|
|
#pragma alloc_text(PAGE, AcpiArbAddAllocation)
|
|
#pragma alloc_text(PAGE, AcpiArbBacktrackAllocation)
|
|
#pragma alloc_text(PAGE, AcpiArbGetLinkNodeOptions)
|
|
#pragma alloc_text(PAGE, AcpiArbTestAllocation)
|
|
#pragma alloc_text(PAGE, AcpiArbBootAllocation)
|
|
#pragma alloc_text(PAGE, AcpiArbRetestAllocation)
|
|
#pragma alloc_text(PAGE, AcpiArbRollbackAllocation)
|
|
#pragma alloc_text(PAGE, AcpiArbCommitAllocation)
|
|
#pragma alloc_text(PAGE, AcpiArbReferenceLinkNode)
|
|
#pragma alloc_text(PAGE, AcpiArbDereferenceLinkNode)
|
|
#pragma alloc_text(PAGE, AcpiArbSetLinkNodeIrq)
|
|
#pragma alloc_text(PAGE, AcpiArbPreprocessEntry)
|
|
#pragma alloc_text(PAGE, AcpiArbOverrideConflict)
|
|
#pragma alloc_text(PAGE, AcpiArbQueryConflict)
|
|
#pragma alloc_text(PAGE, AcpiArbGetNextAllocationRange)
|
|
#pragma alloc_text(PAGE, LinkNodeInUse)
|
|
#pragma alloc_text(PAGE, GetLinkNodeFlags)
|
|
#pragma alloc_text(PAGE, UnreferenceArbitrationList)
|
|
#pragma alloc_text(PAGE, ClearTempLinkNodeCounts)
|
|
#pragma alloc_text(PAGE, MakeTempLinkNodeCountsPermanent)
|
|
#pragma alloc_text(PAGE, HashVector)
|
|
#pragma alloc_text(PAGE, GetVectorProperties)
|
|
#pragma alloc_text(PAGE, AddVectorToTable)
|
|
#pragma alloc_text(PAGE, ReferenceVector)
|
|
#pragma alloc_text(PAGE, DereferenceVector)
|
|
#pragma alloc_text(PAGE, ClearTempVectorCounts)
|
|
#pragma alloc_text(PAGE, MakeTempVectorCountsPermanent)
|
|
#pragma alloc_text(PAGE, TrackDevicesConnectedToLinkNode)
|
|
#pragma alloc_text(PAGE, LookupIsaVectorOverride)
|
|
#pragma alloc_text(PAGE, GetIsaVectorFlags)
|
|
#pragma alloc_text(PAGE, FindVectorInAlternatives)
|
|
#pragma alloc_text(PAGE, FindBootConfig)
|
|
#endif
|
|
|
|
|
|
NTSTATUS
|
|
AcpiInitIrqArbiter(
|
|
PDEVICE_OBJECT RootFdo
|
|
)
|
|
{
|
|
AMLISUPP_CONTEXT_PASSIVE context;
|
|
PARBITER_EXTENSION arbExt;
|
|
NTSTATUS status;
|
|
ULONG rawVector, adjVector, level;
|
|
UCHAR flags;
|
|
UCHAR buffer[PCI_COMMON_HDR_LENGTH];
|
|
PPCI_COMMON_CONFIG pciData;
|
|
BOOLEAN foundBootConfig, noBootConfigAgreement;
|
|
ULONG deviceNum, funcNum;
|
|
UCHAR lastBus, currentBus;
|
|
PCI_SLOT_NUMBER pciSlot;
|
|
UNICODE_STRING driverKey;
|
|
HANDLE driverKeyHandle = NULL;
|
|
PKEY_VALUE_PARTIAL_INFORMATION_ALIGN64 regValue=NULL;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Set up arbiter.
|
|
//
|
|
|
|
arbExt = ExAllocatePoolWithTag(NonPagedPool, sizeof(ARBITER_EXTENSION), ACPI_ARBITER_POOLTAG);
|
|
|
|
if (!arbExt) {
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
RtlZeroMemory(arbExt, sizeof(ARBITER_EXTENSION));
|
|
|
|
InitializeListHead(&(arbExt->LinkNodeHead));
|
|
|
|
AcpiArbiter.ArbiterState.Extension = arbExt;
|
|
|
|
AcpiArbiter.DeviceObject = RootFdo;
|
|
AcpiArbiter.ArbiterState.UnpackRequirement = AcpiArbUnpackRequirement;
|
|
AcpiArbiter.ArbiterState.PackResource = AcpiArbPackResource;
|
|
AcpiArbiter.ArbiterState.UnpackResource = AcpiArbUnpackResource;
|
|
AcpiArbiter.ArbiterState.ScoreRequirement = AcpiArbScoreRequirement;
|
|
AcpiArbiter.ArbiterState.FindSuitableRange = AcpiArbFindSuitableRange;
|
|
AcpiArbiter.ArbiterState.TestAllocation = AcpiArbTestAllocation;
|
|
AcpiArbiter.ArbiterState.BootAllocation = AcpiArbBootAllocation;
|
|
AcpiArbiter.ArbiterState.RetestAllocation = AcpiArbRetestAllocation;
|
|
AcpiArbiter.ArbiterState.RollbackAllocation = AcpiArbRollbackAllocation;
|
|
AcpiArbiter.ArbiterState.CommitAllocation = AcpiArbCommitAllocation;
|
|
AcpiArbiter.ArbiterState.AddAllocation = AcpiArbAddAllocation;
|
|
AcpiArbiter.ArbiterState.BacktrackAllocation = AcpiArbBacktrackAllocation;
|
|
AcpiArbiter.ArbiterState.PreprocessEntry = AcpiArbPreprocessEntry;
|
|
AcpiArbiter.ArbiterState.OverrideConflict = AcpiArbOverrideConflict;
|
|
AcpiArbiter.ArbiterState.QueryConflict = AcpiArbQueryConflict;
|
|
AcpiArbiter.ArbiterState.GetNextAllocationRange = AcpiArbGetNextAllocationRange;
|
|
|
|
IrqHashTable = ExAllocatePoolWithTag(PagedPool,
|
|
VECTOR_HASH_TABLE_SIZE,
|
|
ACPI_ARBITER_POOLTAG
|
|
);
|
|
|
|
if (!IrqHashTable) {
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto AcpiInitIrqArbiterError;
|
|
}
|
|
|
|
RtlFillMemory(IrqHashTable,
|
|
VECTOR_HASH_TABLE_SIZE,
|
|
(UCHAR)(EMPTY_BLOCK_VALUE & 0xff));
|
|
|
|
//
|
|
// Do the generic part of initialization.
|
|
//
|
|
status = ArbInitializeArbiterInstance(&AcpiArbiter.ArbiterState,
|
|
RootFdo,
|
|
CmResourceTypeInterrupt,
|
|
L"ACPI_IRQ",
|
|
L"Root",
|
|
NULL
|
|
);
|
|
if (!NT_SUCCESS(status)) {
|
|
status = STATUS_UNSUCCESSFUL;
|
|
goto AcpiInitIrqArbiterError;
|
|
}
|
|
|
|
//
|
|
// Now claim the IRQ that ACPI itself is using.
|
|
//
|
|
|
|
rawVector = AcpiInformation->FixedACPIDescTable->sci_int_vector;
|
|
|
|
//
|
|
// Assume that the ACPI vector is active low,
|
|
// level triggered. (This may be changed
|
|
// by the MAPIC table.)
|
|
//
|
|
|
|
flags = VECTOR_LEVEL | VECTOR_ACTIVE_LOW;
|
|
|
|
adjVector = rawVector;
|
|
LookupIsaVectorOverride(adjVector,
|
|
&adjVector,
|
|
&flags);
|
|
|
|
RtlAddRange(AcpiArbiter.ArbiterState.Allocation,
|
|
(ULONGLONG)adjVector,
|
|
(ULONGLONG)adjVector,
|
|
0,
|
|
RTL_RANGE_LIST_ADD_SHARED,
|
|
NULL,
|
|
((PDEVICE_EXTENSION)RootFdo->DeviceExtension)->PhysicalDeviceObject
|
|
);
|
|
|
|
//
|
|
// Record the status for this vector
|
|
//
|
|
|
|
ReferenceVector(adjVector,
|
|
flags);
|
|
|
|
AcpiSciVector = adjVector;
|
|
|
|
MakeTempVectorCountsPermanent();
|
|
|
|
//
|
|
// Disable all the link nodes in the namespace so that we
|
|
// have a fresh slate to work with.
|
|
//
|
|
|
|
KeInitializeEvent(&context.Event, SynchronizationEvent, FALSE);
|
|
context.Status = STATUS_UNSUCCESSFUL;
|
|
|
|
status = DisableLinkNodesAsync(((PDEVICE_EXTENSION)RootFdo->DeviceExtension)->AcpiObject,
|
|
AmlisuppCompletePassive,
|
|
(PVOID)&context);
|
|
|
|
if (status == STATUS_PENDING) {
|
|
|
|
KeWaitForSingleObject(&context.Event,
|
|
Executive,
|
|
KernelMode,
|
|
FALSE,
|
|
NULL);
|
|
|
|
status = context.Status;
|
|
}
|
|
|
|
//
|
|
// Scan the machine looking at its initial configuration. If
|
|
// it a) has a cardbus controller and b) all the boot configs
|
|
// for PCI devices are the same, then record that boot config
|
|
// vector for use in AcpiArbGetNextAllocationRange.
|
|
//
|
|
// Note: This algorithm only scans the first PCI root and
|
|
// its children. The assumption is that multiple root machines
|
|
// will be running in APIC mode or have many different boot
|
|
// configs.
|
|
//
|
|
|
|
pciData = (PPCI_COMMON_CONFIG)buffer;
|
|
lastBus = 0;
|
|
currentBus = 0;
|
|
foundBootConfig = FALSE;
|
|
noBootConfigAgreement = FALSE;
|
|
|
|
while (TRUE) {
|
|
|
|
pciSlot.u.AsULONG = 0;
|
|
|
|
for (deviceNum = 0; deviceNum < PCI_MAX_DEVICES; deviceNum++) {
|
|
for (funcNum = 0; funcNum < PCI_MAX_FUNCTION; funcNum++) {
|
|
|
|
pciSlot.u.bits.DeviceNumber = deviceNum;
|
|
pciSlot.u.bits.FunctionNumber = funcNum;
|
|
|
|
HalPciInterfaceReadConfig(NULL,
|
|
currentBus,
|
|
pciSlot.u.AsULONG,
|
|
pciData,
|
|
0,
|
|
PCI_COMMON_HDR_LENGTH);
|
|
|
|
if (pciData->VendorID != PCI_INVALID_VENDORID) {
|
|
|
|
if (PCI_CONFIGURATION_TYPE(pciData) == PCI_DEVICE_TYPE) {
|
|
|
|
if (pciData->u.type0.InterruptPin) {
|
|
|
|
//
|
|
// This device generates an interrupt.
|
|
//
|
|
|
|
if ((pciData->u.type0.InterruptLine > 0) &&
|
|
(pciData->u.type0.InterruptLine < 0xff)) {
|
|
|
|
//
|
|
// And it has a boot config.
|
|
//
|
|
|
|
if (foundBootConfig) {
|
|
|
|
if (pciData->u.type0.InterruptLine != AcpiIrqDefaultBootConfig) {
|
|
|
|
noBootConfigAgreement = TRUE;
|
|
break;
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// Record this boot config
|
|
//
|
|
|
|
AcpiIrqDefaultBootConfig = pciData->u.type0.InterruptLine;
|
|
foundBootConfig = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// This is a bridge. Update lastBus with the Subordinate
|
|
// bus if it is higher.
|
|
//
|
|
|
|
lastBus = lastBus > pciData->u.type1.SubordinateBus ?
|
|
lastBus : pciData->u.type1.SubordinateBus;
|
|
|
|
if (PCI_CONFIGURATION_TYPE(pciData) == PCI_CARDBUS_BRIDGE_TYPE) {
|
|
AcpiArbCardbusPresent = TRUE;
|
|
}
|
|
}
|
|
|
|
if (!PCI_MULTIFUNCTION_DEVICE(pciData) &&
|
|
(funcNum == 0)) {
|
|
break;
|
|
}
|
|
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (lastBus == currentBus++) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!foundBootConfig ||
|
|
noBootConfigAgreement ||
|
|
!AcpiArbCardbusPresent) {
|
|
|
|
//
|
|
// There is no single default boot config.
|
|
//
|
|
|
|
AcpiIrqDefaultBootConfig = 0;
|
|
}
|
|
|
|
//
|
|
// Now look in the registry for configuration flags.
|
|
//
|
|
|
|
RtlInitUnicodeString( &driverKey,
|
|
L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\ACPI\\Parameters");
|
|
|
|
|
|
status = OSOpenUnicodeHandle(
|
|
&driverKey,
|
|
NULL,
|
|
&driverKeyHandle);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
status = OSGetRegistryValue(
|
|
driverKeyHandle,
|
|
L"IRQDistribution",
|
|
®Value);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
if ((regValue->DataLength != 0) &&
|
|
(regValue->Type == REG_DWORD)) {
|
|
|
|
//
|
|
// We have successfully found the key for
|
|
// IRQ Distribution Disposition.
|
|
//
|
|
|
|
AcpiIrqDistributionDisposition =
|
|
*((ULONG*)( ((PUCHAR)regValue->Data)));
|
|
}
|
|
|
|
ExFreePool(regValue);
|
|
}
|
|
|
|
status = OSGetRegistryValue(
|
|
driverKeyHandle,
|
|
L"ForcePCIBootConfig",
|
|
®Value);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
if ((regValue->DataLength != 0) &&
|
|
(regValue->Type == REG_DWORD)) {
|
|
|
|
//
|
|
// We have successfully found the key for
|
|
// PCI Boot Configs.
|
|
//
|
|
|
|
AcpiIrqDefaultBootConfig =
|
|
*(PUCHAR)regValue->Data;
|
|
}
|
|
|
|
ExFreePool(regValue);
|
|
}
|
|
|
|
OSCloseHandle(driverKeyHandle);
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
AcpiInitIrqArbiterError:
|
|
|
|
if (arbExt) ExFreePool(arbExt);
|
|
if (IrqHashTable) ExFreePool(IrqHashTable);
|
|
if (driverKeyHandle) OSCloseHandle(driverKeyHandle);
|
|
if (regValue) ExFreePool(regValue);
|
|
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
AcpiArbInitializePciRouting(
|
|
PDEVICE_OBJECT PciPdo
|
|
)
|
|
{
|
|
PINT_ROUTE_INTERFACE_STANDARD interface;
|
|
NTSTATUS status;
|
|
IO_STACK_LOCATION irpSp;
|
|
PWSTR buffer;
|
|
PDEVICE_OBJECT topDeviceInStack;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Send an IRP to the PCI driver to get the Interrupt Routing Interface.
|
|
//
|
|
|
|
RtlZeroMemory( &irpSp, sizeof(IO_STACK_LOCATION) );
|
|
|
|
interface = ExAllocatePoolWithTag(NonPagedPool, sizeof(INT_ROUTE_INTERFACE_STANDARD), ACPI_ARBITER_POOLTAG);
|
|
|
|
if (!interface) {
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
topDeviceInStack = IoGetAttachedDeviceReference(PciPdo);
|
|
|
|
//
|
|
// Set the function codes and parameters.
|
|
//
|
|
irpSp.MajorFunction = IRP_MJ_PNP;
|
|
irpSp.MinorFunction = IRP_MN_QUERY_INTERFACE;
|
|
irpSp.Parameters.QueryInterface.InterfaceType = (LPGUID) &GUID_INT_ROUTE_INTERFACE_STANDARD;
|
|
irpSp.Parameters.QueryInterface.Version = PCI_INT_ROUTE_INTRF_STANDARD_VER;
|
|
irpSp.Parameters.QueryInterface.Size = sizeof (INT_ROUTE_INTERFACE_STANDARD);
|
|
irpSp.Parameters.QueryInterface.Interface = (PINTERFACE) interface;
|
|
irpSp.Parameters.QueryInterface.InterfaceSpecificData = NULL;
|
|
|
|
//
|
|
// Call the PCI driver (indirectly.)
|
|
//
|
|
|
|
status = ACPIInternalSendSynchronousIrp(topDeviceInStack,
|
|
&irpSp,
|
|
&buffer);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
//
|
|
// Attach this interface to the Arbiter Extension.
|
|
//
|
|
((PARBITER_EXTENSION)AcpiArbiter.ArbiterState.Extension)->InterruptRouting = interface;
|
|
|
|
//
|
|
// Reference it.
|
|
//
|
|
interface->InterfaceReference(interface->Context);
|
|
|
|
PciInterfacesInstantiated = TRUE;
|
|
|
|
} else {
|
|
|
|
ExFreePool(interface);
|
|
}
|
|
|
|
ObDereferenceObject(topDeviceInStack);
|
|
return status;
|
|
}
|
|
|
|
|
|
//
|
|
// Arbiter callbacks
|
|
//
|
|
|
|
NTSTATUS
|
|
AcpiArbUnpackRequirement(
|
|
IN PIO_RESOURCE_DESCRIPTOR Descriptor,
|
|
OUT PULONGLONG Minimum,
|
|
OUT PULONGLONG Maximum,
|
|
OUT PULONG Length,
|
|
OUT PULONG Alignment
|
|
)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
ASSERT(Descriptor);
|
|
ASSERT(Descriptor->Type == CmResourceTypeInterrupt);
|
|
|
|
*Minimum = (ULONGLONG) Descriptor->u.Interrupt.MinimumVector;
|
|
*Maximum = (ULONGLONG) Descriptor->u.Interrupt.MaximumVector;
|
|
*Length = 1;
|
|
*Alignment = 1;
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
LONG
|
|
AcpiArbScoreRequirement(
|
|
IN PIO_RESOURCE_DESCRIPTOR Descriptor
|
|
)
|
|
{
|
|
LONG score;
|
|
|
|
ASSERT(Descriptor);
|
|
ASSERT(Descriptor->Type == CmResourceTypeInterrupt);
|
|
|
|
//
|
|
// TEMPTEMP HACKHACK
|
|
// (Possibly) temporary hack that allows the PnP
|
|
// manager to include invalid resources in the
|
|
// arbitration list.
|
|
//
|
|
if (Descriptor->u.Interrupt.MinimumVector >
|
|
Descriptor->u.Interrupt.MaximumVector) {
|
|
|
|
return 0;
|
|
}
|
|
|
|
ASSERT(Descriptor->u.Interrupt.MinimumVector <=
|
|
Descriptor->u.Interrupt.MaximumVector);
|
|
|
|
score = Descriptor->u.Interrupt.MaximumVector -
|
|
Descriptor->u.Interrupt.MinimumVector + 1;
|
|
|
|
//
|
|
// Give a little boost to any request above the
|
|
// traditional ISA range.
|
|
// N.B. This will probably never matter, as
|
|
// most machines will present all the choices
|
|
// either inside or outside of the ISA range.
|
|
//
|
|
if (Descriptor->u.Interrupt.MaximumVector >= 16) {
|
|
score += 5;
|
|
}
|
|
|
|
return score;
|
|
}
|
|
|
|
NTSTATUS
|
|
AcpiArbPackResource(
|
|
IN PIO_RESOURCE_DESCRIPTOR Requirement,
|
|
IN ULONGLONG Start,
|
|
OUT PCM_PARTIAL_RESOURCE_DESCRIPTOR Descriptor
|
|
)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
ASSERT(Descriptor);
|
|
ASSERT(Start < ((ULONG)-1));
|
|
ASSERT(Requirement);
|
|
ASSERT(Requirement->Type == CmResourceTypeInterrupt);
|
|
|
|
Descriptor->Type = CmResourceTypeInterrupt;
|
|
Descriptor->Flags = Requirement->Flags;
|
|
Descriptor->ShareDisposition = Requirement->ShareDisposition;
|
|
Descriptor->u.Interrupt.Vector = (ULONG) Start;
|
|
Descriptor->u.Interrupt.Level = (ULONG) Start;
|
|
Descriptor->u.Interrupt.Affinity = 0xFFFFFFFF;
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
AcpiArbUnpackResource(
|
|
IN PCM_PARTIAL_RESOURCE_DESCRIPTOR Descriptor,
|
|
OUT PULONGLONG Start,
|
|
OUT PULONG Length
|
|
)
|
|
{
|
|
|
|
ASSERT(Descriptor);
|
|
ASSERT(Descriptor->Type == CmResourceTypeInterrupt);
|
|
|
|
*Start = Descriptor->u.Interrupt.Vector;
|
|
*Length = 1;
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
BOOLEAN
|
|
AcpiArbOverrideConflict(
|
|
IN PARBITER_INSTANCE Arbiter,
|
|
IN PARBITER_ALLOCATION_STATE State
|
|
)
|
|
{
|
|
//
|
|
// Self-conflicts are not allowable with this arbiter.
|
|
//
|
|
|
|
PAGED_CODE();
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
AcpiArbPreprocessEntry(
|
|
IN PARBITER_INSTANCE Arbiter,
|
|
IN PARBITER_ALLOCATION_STATE State
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called from AllocateEntry to allow preprocessing of
|
|
entries
|
|
|
|
Arguments:
|
|
|
|
Arbiter - The instance data of the arbiter who was called.
|
|
|
|
State - The state of the current arbitration.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
|
|
#define CM_RESOURE_INTERRUPT_LEVEL_LATCHED_BITS 0x0001
|
|
|
|
PARBITER_ALTERNATIVE current;
|
|
USHORT flags;
|
|
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Check if this is a level (PCI) or latched (ISA) interrupt and set
|
|
// RangeAttributes accordingly so we set the appropriate flag when we add the
|
|
// range
|
|
//
|
|
|
|
if ((State->Alternatives[0].Descriptor->Flags
|
|
& CM_RESOURE_INTERRUPT_LEVEL_LATCHED_BITS)
|
|
== CM_RESOURCE_INTERRUPT_LEVEL_SENSITIVE) {
|
|
|
|
State->RangeAttributes &= ~ARBITER_INTERRUPT_BITS;
|
|
State->RangeAttributes |= ARBITER_INTERRUPT_LEVEL_SENSATIVE;
|
|
flags = CM_RESOURCE_INTERRUPT_LEVEL_SENSITIVE;
|
|
|
|
} else {
|
|
|
|
ASSERT(State->Alternatives[0].Descriptor->Flags
|
|
& CM_RESOURCE_INTERRUPT_LATCHED);
|
|
|
|
State->RangeAttributes &= ~ARBITER_INTERRUPT_BITS;
|
|
State->RangeAttributes |= ARBITER_INTERRUPT_LATCHED;
|
|
flags = CM_RESOURCE_INTERRUPT_LATCHED;
|
|
}
|
|
|
|
#if 0
|
|
|
|
//
|
|
// Make sure that all the alternatives are of the same type
|
|
//
|
|
|
|
FOR_ALL_IN_ARRAY(State->Alternatives,
|
|
State->Entry->AlternativeCount,
|
|
current) {
|
|
|
|
ASSERT((current->Descriptor->Flags
|
|
& CM_RESOURE_INTERRUPT_LEVEL_LATCHED_BITS) == flags);
|
|
}
|
|
|
|
#endif
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
AcpiArbFindSuitableRange(
|
|
PARBITER_INSTANCE Arbiter,
|
|
PARBITER_ALLOCATION_STATE State
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine finds an IRQ for a device object. For
|
|
non-PCI devices, this is as simple as returning the
|
|
result from PnpFindSuitableRange. For PCI devices,
|
|
this is done by examining the state of the "link
|
|
nodes" described in the ACPI namespace.
|
|
|
|
Arguments:
|
|
|
|
Arbiter - the ACPI IRQ arbiter
|
|
State - the current allocation under consideration
|
|
|
|
Return Value:
|
|
|
|
TRUE if a suitable vector has been found,
|
|
FALSE otherwise.
|
|
|
|
Notes:
|
|
|
|
Statement of algorithm for PCI devices:
|
|
|
|
1) Find the entry in the _PRT that corresponds
|
|
to the device for which we are arbitrating
|
|
resources.
|
|
|
|
2) Determine, from the _PRT information, whether
|
|
this device is connected to a "link node."
|
|
(A PCI device will typically be connected to
|
|
a link node while in PIC mode but not in
|
|
APIC mode.)
|
|
|
|
3) If it is not, use the static mapping from the
|
|
_PRT.
|
|
|
|
4) If it is connected to a "link node," check
|
|
to see if the "link node" is in use.
|
|
|
|
5) If the link node is in use, then this
|
|
device must use the same IRQ that the link node is currently
|
|
using. This implies that there is already
|
|
some other device in the system connected to
|
|
this interrupt and now the two (or more) will
|
|
be sharing. This is acceptable and inevitable.
|
|
Two devices that have their interrupt lines
|
|
wire-or'd should be sharing. Two devices that
|
|
don't have their interrupt lines wire-or'd
|
|
should be represented by separate link nodes
|
|
in the namespace.
|
|
|
|
|
|
6) If the link node is not in use, pick an IRQ
|
|
from the list that the link node can support
|
|
and grant it to the device. There is some
|
|
attempt to pick an IRQ that is not currently
|
|
in use.
|
|
--*/
|
|
{
|
|
|
|
PCM_PARTIAL_RESOURCE_DESCRIPTOR potentialIrq;
|
|
PIO_RESOURCE_DESCRIPTOR alternative;
|
|
PARBITER_EXTENSION arbExtension;
|
|
PCM_RESOURCE_LIST linkNodeResList = NULL;
|
|
NTSTATUS status;
|
|
BOOLEAN possibleAllocation;
|
|
PNSOBJ linkNode = NULL;
|
|
ULONG deviceIrq = 0;
|
|
ULONG linkNodeIrqCount, i;
|
|
UCHAR vectorFlags, deviceFlags;
|
|
|
|
PAGED_CODE();
|
|
|
|
arbExtension = (PARBITER_EXTENSION)Arbiter->Extension;
|
|
ASSERT(arbExtension);
|
|
|
|
//
|
|
// First, see if this resource could even be made to work at all.
|
|
// (I.e. Has something already claimed this as non-sharable?)
|
|
//
|
|
|
|
possibleAllocation = ArbFindSuitableRange(Arbiter, State);
|
|
|
|
if (!possibleAllocation) {
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Is this Device connected to a link node?
|
|
//
|
|
|
|
status = AcpiArbCrackPRT(State->Entry->PhysicalDeviceObject,
|
|
&linkNode,
|
|
&deviceIrq);
|
|
|
|
//
|
|
// If this PDO is connected to a link node, we want to clip
|
|
// the list of possible IRQ settings down.
|
|
//
|
|
switch (status) {
|
|
case STATUS_SUCCESS:
|
|
|
|
//
|
|
// AcpiArbCrackPRT fills in either linkNode or deviceIrq.
|
|
// If linkNode is filled in, then we need to look at it.
|
|
// If deviceIrq is filled in, then we only need to clip
|
|
// the list to that single IRQ.
|
|
//
|
|
if (linkNode) {
|
|
|
|
//
|
|
// If the link node is currently in use, then we can
|
|
// just connect this device to the IRQ that the link
|
|
// node is currently using.
|
|
//
|
|
if (LinkNodeInUse(Arbiter, linkNode, &deviceIrq, NULL)) {
|
|
|
|
if ((State->CurrentMinimum <= deviceIrq) &&
|
|
(State->CurrentMaximum >= deviceIrq)) {
|
|
|
|
State->Start = deviceIrq;
|
|
State->End = deviceIrq;
|
|
State->CurrentAlternative->Length = 1;
|
|
|
|
DEBUG_PRINT(1, ("FindSuitableRange found %x from a link node that is in use.\n",
|
|
(ULONG)(State->Start & 0xffffffff)));
|
|
ASSERT(HalIsVectorValid(deviceIrq));
|
|
return TRUE;
|
|
|
|
} else {
|
|
DEBUG_PRINT(1, ("FindSuitableRange found %x from a link node that is in use.\n",
|
|
deviceIrq));
|
|
DEBUG_PRINT(1, (" This was, however, not within the range of possibilites (%x-%x).\n",
|
|
(ULONG)(State->Start & 0xffffffff),
|
|
(ULONG)(State->End & 0xffffffff)));
|
|
return FALSE;
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// Get the set of IRQs that this link node can
|
|
// connect to.
|
|
//
|
|
|
|
status = AcpiArbGetLinkNodeOptions(linkNode,
|
|
&linkNodeResList,
|
|
&deviceFlags);
|
|
|
|
DEBUG_PRINT(1, ("Link node contained CM(%p)\n", linkNodeResList));
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
ASSERT(linkNodeResList->Count == 1);
|
|
|
|
linkNodeIrqCount =
|
|
linkNodeResList->List[0].PartialResourceList.Count;
|
|
|
|
|
|
for (i = 0; i < linkNodeIrqCount; i++) {
|
|
|
|
potentialIrq =
|
|
&(linkNodeResList->List[0].PartialResourceList.PartialDescriptors[(i + AcpiArbPciAlternativeRotation) % linkNodeIrqCount]);
|
|
|
|
ASSERT(potentialIrq->Type == CmResourceTypeInterrupt);
|
|
|
|
//
|
|
// Check for a conflict in mode.
|
|
//
|
|
status = GetVectorProperties(potentialIrq->u.Interrupt.Vector,
|
|
&vectorFlags);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
//
|
|
// Success here means that this vector is currently allocated
|
|
// to somebody. Check to see whether the link node being
|
|
// considered has the same mode and polarity as the other
|
|
// thing(s) assigned to this vector.
|
|
//
|
|
|
|
if (deviceFlags != vectorFlags) {
|
|
|
|
//
|
|
// The flags don't match. So skip this possibility.
|
|
//
|
|
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if ((potentialIrq->u.Interrupt.Vector >= State->CurrentMinimum) &&
|
|
(potentialIrq->u.Interrupt.Vector <= State->CurrentMaximum)) {
|
|
|
|
if (!HalIsVectorValid(potentialIrq->u.Interrupt.Vector)) {
|
|
deviceIrq = potentialIrq->u.Interrupt.Vector;
|
|
ExFreePool(linkNodeResList);
|
|
goto FindSuitableRangeError;
|
|
}
|
|
|
|
State->Start = potentialIrq->u.Interrupt.Vector;
|
|
State->End = potentialIrq->u.Interrupt.Vector;
|
|
State->CurrentAlternative->Length = 1;
|
|
|
|
DEBUG_PRINT(1, ("FindSuitableRange found %x from an unused link node.\n",
|
|
(ULONG)(State->Start & 0xffffffff)));
|
|
|
|
ExFreePool(linkNodeResList);
|
|
|
|
//
|
|
// Record the link node that we got this from.
|
|
//
|
|
|
|
arbExtension->CurrentLinkNode = linkNode;
|
|
|
|
//
|
|
// Record this as the last PCI IRQ that we are handing out.
|
|
//
|
|
|
|
arbExtension->LastPciIrq[arbExtension->LastPciIrqIndex] =
|
|
State->Start;
|
|
|
|
arbExtension->LastPciIrqIndex =
|
|
(arbExtension->LastPciIrqIndex + 1) % LAST_PCI_IRQ_BUFFER_SIZE;
|
|
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
ExFreePool(linkNodeResList);
|
|
}
|
|
|
|
DEBUG_PRINT(1, ("FindSuitableRange: AcpiArbGetLinkNodeOptions returned %x.\n\tlinkNodeResList: %p\n",
|
|
status, linkNodeResList));
|
|
// We didn't find a match.
|
|
return FALSE;
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// This is the case where the _PRT contains a static mapping. Static
|
|
// Mappings imply active-low, level-triggered interrupts.
|
|
//
|
|
|
|
status = GetVectorProperties(deviceIrq,
|
|
&vectorFlags);
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
//
|
|
// The vector is in use.
|
|
//
|
|
|
|
if (((vectorFlags & VECTOR_MODE) != VECTOR_LEVEL) ||
|
|
((vectorFlags & VECTOR_POLARITY) != VECTOR_ACTIVE_LOW)) {
|
|
|
|
//
|
|
// And it's flags don't match.
|
|
//
|
|
return FALSE;
|
|
}
|
|
|
|
}
|
|
|
|
// Valid static vector
|
|
|
|
if ((State->CurrentMinimum <= deviceIrq) &&
|
|
(State->CurrentMaximum >= deviceIrq)) {
|
|
|
|
DEBUG_PRINT(1, ("FindSuitableRange found %x from a static mapping.\n",
|
|
(ULONG)(State->Start & 0xffffffff)));
|
|
|
|
if (!HalIsVectorValid(deviceIrq)) {
|
|
goto FindSuitableRangeError;
|
|
}
|
|
|
|
State->Start = deviceIrq;
|
|
State->End = deviceIrq;
|
|
State->CurrentAlternative->Length = 1;
|
|
|
|
return TRUE;
|
|
|
|
} else {
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
case STATUS_UNSUCCESSFUL:
|
|
|
|
return FALSE;
|
|
break;
|
|
|
|
case STATUS_RESOURCE_REQUIREMENTS_CHANGED:
|
|
|
|
//
|
|
// Fall through to default.
|
|
//
|
|
|
|
default:
|
|
|
|
//
|
|
// Not PCI.
|
|
//
|
|
|
|
for (deviceIrq = (ULONG)(State->Start & 0xffffffff);
|
|
deviceIrq <= (ULONG)(State->End & 0xffffffff); deviceIrq++) {
|
|
|
|
status = GetIsaVectorFlags((ULONG)deviceIrq,
|
|
&deviceFlags);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
//
|
|
// Not overridden. Assume that the device flags conform to bus.
|
|
//
|
|
|
|
deviceFlags = (State->CurrentAlternative->Descriptor->Flags
|
|
== CM_RESOURCE_INTERRUPT_LATCHED) ?
|
|
VECTOR_EDGE | VECTOR_ACTIVE_HIGH :
|
|
VECTOR_LEVEL | VECTOR_ACTIVE_LOW;
|
|
|
|
}
|
|
|
|
status = GetVectorProperties((ULONG)deviceIrq,
|
|
&vectorFlags);
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
//
|
|
// This vector is currently in use. So if this is to be a suitable
|
|
// range, then the flags must match.
|
|
//
|
|
|
|
if (deviceFlags != vectorFlags) {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (!HalIsVectorValid(deviceIrq)) {
|
|
goto FindSuitableRangeError;
|
|
}
|
|
|
|
State->Start = deviceIrq;
|
|
State->End = deviceIrq;
|
|
State->CurrentAlternative->Length = 1;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
return FALSE;
|
|
|
|
FindSuitableRangeError:
|
|
|
|
{
|
|
UNICODE_STRING vectorName;
|
|
PWCHAR prtEntry[2];
|
|
WCHAR IRQARBname[20];
|
|
WCHAR vectorBuff[10];
|
|
|
|
//
|
|
// Make an errorlog entry saying that the chosen IRQ doesn't
|
|
// exist.
|
|
//
|
|
|
|
swprintf( IRQARBname, L"IRQARB");
|
|
RtlInitUnicodeString(&vectorName, vectorBuff);
|
|
|
|
if (!NT_SUCCESS(RtlIntegerToUnicodeString(deviceIrq, 0, &vectorName))) {
|
|
return FALSE;
|
|
}
|
|
|
|
prtEntry[0] = IRQARBname;
|
|
prtEntry[1] = vectorBuff;
|
|
|
|
ACPIWriteEventLogEntry(ACPI_ERR_ILLEGAL_IRQ_NUMBER,
|
|
prtEntry,
|
|
2,
|
|
NULL,
|
|
0);
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
VOID
|
|
AcpiArbAddAllocation(
|
|
IN PARBITER_INSTANCE Arbiter,
|
|
IN PARBITER_ALLOCATION_STATE State
|
|
)
|
|
{
|
|
NTSTATUS status;
|
|
PNSOBJ linkNode;
|
|
ULONG sourceIndex;
|
|
ULONG rangeFlags = 0;
|
|
PVOID referencedNode = NULL;
|
|
UCHAR flags, previousFlags;
|
|
ROUTING_TOKEN token;
|
|
BOOLEAN inUse;
|
|
UCHAR attributes = 0;
|
|
|
|
PAGED_CODE();
|
|
ASSERT(State->CurrentAlternative->Descriptor->Type == CmResourceTypeInterrupt);
|
|
|
|
DEBUG_PRINT(1, ("Adding allocation for IRQ %x for device %p\n",
|
|
(ULONG)(State->Start & 0xffffffff),
|
|
State->Entry->PhysicalDeviceObject));
|
|
|
|
//
|
|
// Identify the potential link node.
|
|
//
|
|
|
|
status = AcpiArbCrackPRT(State->Entry->PhysicalDeviceObject,
|
|
&linkNode,
|
|
&sourceIndex);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
//
|
|
// PCI device. Default flags are standard for PCI.
|
|
//
|
|
|
|
flags = VECTOR_LEVEL | VECTOR_ACTIVE_LOW;
|
|
ASSERT(State->Start == State->End);
|
|
|
|
if (!(State->Flags & ARBITER_STATE_FLAG_BOOT)) {
|
|
|
|
//
|
|
// Only keep track of link nodes manipulation if this is not
|
|
// a boot config allocation.
|
|
//
|
|
|
|
//
|
|
// If this device is connected to a link node, reference it.
|
|
//
|
|
|
|
if (linkNode) {
|
|
|
|
AcpiArbReferenceLinkNode(Arbiter,
|
|
linkNode,
|
|
(ULONG)State->Start);
|
|
|
|
referencedNode = (PVOID)linkNode;
|
|
|
|
//
|
|
// Find out what the flags for this link node are.
|
|
// Note that this is only guaranteed to be valid
|
|
// after we have referenced the link node.
|
|
//
|
|
|
|
inUse = LinkNodeInUse(Arbiter,
|
|
linkNode,
|
|
NULL,
|
|
&flags);
|
|
|
|
ASSERT(inUse);
|
|
ASSERT((flags & ~(VECTOR_MODE | VECTOR_POLARITY | VECTOR_TYPE)) == 0);
|
|
ASSERT(State->CurrentAlternative->Descriptor->Flags
|
|
== CM_RESOURCE_INTERRUPT_LEVEL_SENSITIVE ?
|
|
(flags & VECTOR_MODE) == VECTOR_LEVEL :
|
|
(flags & VECTOR_MODE) == VECTOR_EDGE);
|
|
|
|
|
|
#if DBG
|
|
TrackDevicesConnectedToLinkNode(linkNode,
|
|
State->Entry->PhysicalDeviceObject);
|
|
|
|
status = GetVectorProperties((ULONG)State->Start,
|
|
&previousFlags);
|
|
|
|
//
|
|
// This next bit is a hack. We need to make sure that
|
|
// the boot config code doesn't try to allocate the same
|
|
// vector for two different devices that need conflicting
|
|
// modes. This should never happen, as translation
|
|
// should filter out the problemating ones before we
|
|
// get to arbitration.
|
|
//
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
//
|
|
// This vector is already in use for something.
|
|
//
|
|
|
|
ASSERT((previousFlags & ~(VECTOR_MODE | VECTOR_POLARITY | VECTOR_TYPE)) == 0);
|
|
ASSERT(flags == previousFlags);
|
|
}
|
|
#endif
|
|
|
|
} else {
|
|
|
|
//
|
|
// This is a PCI device that is not connected to a
|
|
// link node.
|
|
//
|
|
|
|
ASSERT(sourceIndex == State->Start);
|
|
}
|
|
} else {
|
|
|
|
if (InterruptModel == 1) {
|
|
//
|
|
// We are running in APIC mode. And we know that
|
|
// the PCI driver builds boot configs based on
|
|
// the Interrupt Line register, which only relates
|
|
// to PIC mode. So just say no to boot configs.
|
|
//
|
|
|
|
DEBUG_PRINT(1, ("Skipping this allocation. It's for a PCI device in APIC mode\n"));
|
|
return;
|
|
}
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// Not a PCI device.
|
|
//
|
|
|
|
status = GetIsaVectorFlags((ULONG)State->Start,
|
|
&flags);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
//
|
|
// Not overridden. Assume that the device flags conform to bus.
|
|
//
|
|
|
|
flags = (State->CurrentAlternative->Descriptor->Flags
|
|
== CM_RESOURCE_INTERRUPT_LATCHED) ?
|
|
VECTOR_EDGE | VECTOR_ACTIVE_HIGH :
|
|
VECTOR_LEVEL | VECTOR_ACTIVE_LOW;
|
|
|
|
}
|
|
|
|
ASSERT((flags & ~(VECTOR_MODE | VECTOR_POLARITY | VECTOR_TYPE)) == 0);
|
|
}
|
|
|
|
//
|
|
// There is a possibility that this allocation is impossible.
|
|
// (We may be setting a boot-config for a device after we
|
|
// have already started using the vector elsewhere.) Just
|
|
// don't do it.
|
|
//
|
|
|
|
if (State->Flags & ARBITER_STATE_FLAG_BOOT) {
|
|
|
|
attributes |= ARBITER_RANGE_BOOT_ALLOCATED;
|
|
|
|
status = GetVectorProperties((ULONG)State->Start,
|
|
&previousFlags);
|
|
|
|
if ((NT_SUCCESS(status)) &&
|
|
((flags & ~VECTOR_TYPE) != (previousFlags & ~VECTOR_TYPE))) {
|
|
DEBUG_PRINT(1, ("Skipping this allocation. It's for a vector that's incompatible.\n"));
|
|
return;
|
|
}
|
|
}
|
|
|
|
ReferenceVector((ULONG)State->Start,
|
|
flags);
|
|
|
|
// Figure out what flags we need to add the range
|
|
|
|
if ((flags & VECTOR_TYPE) == VECTOR_SIGNAL) {
|
|
|
|
// Non-MSI vectors can sometimes be shared and thus can have range conflicts, etc.
|
|
|
|
rangeFlags = RTL_RANGE_LIST_ADD_IF_CONFLICT +
|
|
(State->CurrentAlternative->Flags & ARBITER_ALTERNATIVE_FLAG_SHARED
|
|
? RTL_RANGE_LIST_ADD_SHARED : 0);
|
|
}
|
|
|
|
//
|
|
// Now do what the default function would do, marking this
|
|
// allocation as new.
|
|
//
|
|
|
|
status = RtlAddRange(
|
|
Arbiter->PossibleAllocation,
|
|
State->Start,
|
|
State->End,
|
|
attributes,
|
|
rangeFlags,
|
|
referencedNode, // This line is different from the default function
|
|
State->Entry->PhysicalDeviceObject
|
|
);
|
|
|
|
ASSERT(NT_SUCCESS(status));
|
|
}
|
|
|
|
VOID
|
|
AcpiArbBacktrackAllocation(
|
|
IN PARBITER_INSTANCE Arbiter,
|
|
IN PARBITER_ALLOCATION_STATE State
|
|
)
|
|
{
|
|
RTL_RANGE_LIST_ITERATOR iterator;
|
|
PRTL_RANGE current;
|
|
PNSOBJ linkNode;
|
|
|
|
PAGED_CODE();
|
|
|
|
DEBUG_PRINT(1, ("Backtracking allocation for IRQ %x for device %p\n",
|
|
State->CurrentAlternative->Descriptor->u.Interrupt.MinimumVector,
|
|
State->Entry->PhysicalDeviceObject));
|
|
|
|
ASSERT(!(State->Flags & ARBITER_STATE_FLAG_BOOT));
|
|
|
|
//
|
|
// Backtrack this assignment in the Edge/Level table.
|
|
//
|
|
|
|
DereferenceVector((ULONG)State->Start);
|
|
|
|
//
|
|
// Look for the range that we are backing out.
|
|
//
|
|
|
|
FOR_ALL_RANGES(Arbiter->PossibleAllocation, &iterator, current) {
|
|
|
|
if ((State->Entry->PhysicalDeviceObject == current->Owner) &&
|
|
(State->End == current->End) &&
|
|
(State->Start == current->Start)) {
|
|
|
|
//
|
|
// We stash the link node that we refereneced
|
|
// into Workspace.
|
|
//
|
|
|
|
linkNode = (PNSOBJ)current->UserData;
|
|
|
|
if (linkNode) {
|
|
|
|
//
|
|
// Dereference the link node that we referenced in
|
|
// AcpiArbAddAllocation.
|
|
//
|
|
|
|
AcpiArbDereferenceLinkNode(Arbiter,
|
|
linkNode);
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Now call the default function.
|
|
//
|
|
|
|
ArbBacktrackAllocation(Arbiter, State);
|
|
}
|
|
|
|
NTSTATUS
|
|
UnreferenceArbitrationList(
|
|
IN PARBITER_INSTANCE Arbiter,
|
|
IN OUT PLIST_ENTRY ArbitrationList
|
|
)
|
|
{
|
|
RTL_RANGE_LIST_ITERATOR iterator;
|
|
PARBITER_LIST_ENTRY currentListEntry;
|
|
PRTL_RANGE currentRange;
|
|
NTSTATUS status;
|
|
PNSOBJ linkNode;
|
|
ULONG vector;
|
|
UCHAR flags;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// In order to keep the reference counts in line,
|
|
// we need to remove the counts that were added
|
|
// by previous allocations for this device. I.e.
|
|
// if the device is being rebalanced, we need to
|
|
// start by getting rid of the reference to the old
|
|
// vector.
|
|
//
|
|
// This is also necessary for references that were
|
|
// added as part of boot configs. But boot configs
|
|
// are a special case. There are a (very) few devices
|
|
// that want more than one IRQ. And it is possible
|
|
// that the boot config only reserved a single IRQ.
|
|
// (There is a Lucent Winmodem that actually does
|
|
// this.) We need to make sure that we only
|
|
// dereference the vector as many times as it was
|
|
// previously referenced.
|
|
//
|
|
// Note that there are still a few cases that this
|
|
// function doesn't handle. If a device lowers
|
|
// the number of IRQs that it wants dynamically,
|
|
// separate from its boot config, we will get out
|
|
// of synch. If a vector has both a boot config
|
|
// and a device that is not boot config'd on it,
|
|
// then we may get out of synch, depending on
|
|
// what combinations the PnP manager throws at us.
|
|
// Fixing either of these would involve tagging all
|
|
// vectors with a list of the PDOs that are connected
|
|
// which would be a lot of work. So unless these
|
|
// situations actually exist in the future, I'm not
|
|
// bothering to code for them now. 11/14/2000
|
|
//
|
|
|
|
FOR_ALL_RANGES(Arbiter->Allocation, &iterator, currentRange) {
|
|
|
|
DEBUG_PRINT(4, ("Looking at range: %x-%x %p\n",
|
|
(ULONG)(currentRange->Start & 0xffffffff),
|
|
(ULONG)(currentRange->End & 0xffffffff),
|
|
currentRange->Owner));
|
|
|
|
FOR_ALL_IN_LIST(ARBITER_LIST_ENTRY, ArbitrationList, currentListEntry) {
|
|
|
|
DEBUG_PRINT(2, ("Unreferencing allocations for device %p\n",
|
|
currentListEntry->PhysicalDeviceObject));
|
|
|
|
if (currentRange->Owner == currentListEntry->PhysicalDeviceObject) {
|
|
|
|
//
|
|
// Dereference the vector until there are no more
|
|
// references.
|
|
//
|
|
|
|
for (vector = (ULONG)(currentRange->Start & 0xffffffff);
|
|
vector <= (ULONG)(currentRange->End & 0xffffffff);
|
|
vector++) {
|
|
|
|
status = GetVectorProperties(vector, &flags);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
DEBUG_PRINT(2, ("Dereferencing %x\n", vector));
|
|
DereferenceVector(vector);
|
|
}
|
|
}
|
|
|
|
if (!(currentRange->Attributes & ARBITER_RANGE_BOOT_ALLOCATED)) {
|
|
|
|
//
|
|
// Now find out if we have to dereference a link node too.
|
|
//
|
|
|
|
status = AcpiArbCrackPRT(currentListEntry->PhysicalDeviceObject,
|
|
&linkNode,
|
|
&vector);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
if (linkNode) {
|
|
|
|
//
|
|
// This device is connected to a link node. So temporarily
|
|
// dereference this node.
|
|
//
|
|
|
|
ASSERT(LinkNodeInUse(Arbiter,
|
|
linkNode,
|
|
NULL,
|
|
NULL));
|
|
|
|
AcpiArbDereferenceLinkNode(Arbiter,
|
|
linkNode);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
AcpiArbBootAllocation(
|
|
IN PARBITER_INSTANCE Arbiter,
|
|
IN OUT PLIST_ENTRY ArbitrationList
|
|
)
|
|
{
|
|
NTSTATUS status;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Start a new arbiter transaction and clear any
|
|
// temporary vector counts.
|
|
//
|
|
// N.B. This arbiter doesn't keep track of link
|
|
// node data for boot configs. This means that we don't have
|
|
// to worry about link node counts in this funtion.
|
|
//
|
|
|
|
ClearTempVectorCounts();
|
|
|
|
status = ArbBootAllocation(Arbiter, ArbitrationList);
|
|
|
|
MakeTempVectorCountsPermanent();
|
|
|
|
return status;
|
|
}
|
|
NTSTATUS
|
|
AcpiArbTestAllocation(
|
|
IN PARBITER_INSTANCE Arbiter,
|
|
IN OUT PLIST_ENTRY ArbitrationList
|
|
)
|
|
{
|
|
NTSTATUS status;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// ArbTestAllocation is the beginning of a
|
|
// new arbitration transaction. So clear
|
|
// out all temporary edge-level status and
|
|
// link node ref counts.
|
|
//
|
|
|
|
ClearTempVectorCounts();
|
|
|
|
status = ClearTempLinkNodeCounts(Arbiter);
|
|
ASSERT(NT_SUCCESS(status));
|
|
|
|
status = UnreferenceArbitrationList(Arbiter, ArbitrationList);
|
|
ASSERT(NT_SUCCESS(status));
|
|
|
|
return ArbTestAllocation(Arbiter, ArbitrationList);
|
|
}
|
|
|
|
NTSTATUS
|
|
AcpiArbRetestAllocation(
|
|
IN PARBITER_INSTANCE Arbiter,
|
|
IN OUT PLIST_ENTRY ArbitrationList
|
|
)
|
|
{
|
|
NTSTATUS status;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// ArbRetestAllocation (also) is the beginning
|
|
// of a new arbitration transaction. So clear
|
|
// out all temporary edge-level status and
|
|
// link node ref counts.
|
|
//
|
|
|
|
ClearTempVectorCounts();
|
|
|
|
status = ClearTempLinkNodeCounts(Arbiter);
|
|
ASSERT(NT_SUCCESS(status));
|
|
|
|
status = UnreferenceArbitrationList(Arbiter, ArbitrationList);
|
|
ASSERT(NT_SUCCESS(status));
|
|
|
|
return ArbRetestAllocation(Arbiter, ArbitrationList);
|
|
}
|
|
|
|
NTSTATUS
|
|
AcpiArbRollbackAllocation(
|
|
PARBITER_INSTANCE Arbiter
|
|
)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
return ArbRollbackAllocation(Arbiter);
|
|
}
|
|
|
|
NTSTATUS
|
|
AcpiArbCommitAllocation(
|
|
PARBITER_INSTANCE Arbiter
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This provides the implementation of the CommitAllocation
|
|
action. It frees the old allocation and replaces it with
|
|
the new allocation. After that, it dereferences all the
|
|
link nodes in the old allocation and references the ones
|
|
in the new allocation. This potentially results in the
|
|
IRQ router being reprogrammed to match the new set of
|
|
allocations.
|
|
|
|
Parameters:
|
|
|
|
Arbiter - The arbiter instance data for the arbiter being called.
|
|
|
|
Return Value:
|
|
|
|
Status code that indicates whether or not the function was successful.
|
|
|
|
--*/
|
|
|
|
{
|
|
PINT_ROUTE_INTERFACE_STANDARD pciInterface = NULL;
|
|
RTL_RANGE_LIST_ITERATOR iterator;
|
|
PRTL_RANGE_LIST temp;
|
|
PRTL_RANGE current;
|
|
NTSTATUS status;
|
|
PNSOBJ linkNode;
|
|
ULONG sourceIndex;
|
|
|
|
ULONG pciBus;
|
|
PCI_SLOT_NUMBER pciSlot;
|
|
UCHAR interruptLine, newInterruptLine;
|
|
ULONG_PTR dummy;
|
|
ROUTING_TOKEN token;
|
|
|
|
PAGED_CODE();
|
|
|
|
|
|
if (PciInterfacesInstantiated) {
|
|
|
|
pciInterface = ((PARBITER_EXTENSION)AcpiArbiter.ArbiterState.Extension)->InterruptRouting;
|
|
ASSERT(pciInterface);
|
|
|
|
FOR_ALL_RANGES(Arbiter->PossibleAllocation, &iterator, current) {
|
|
|
|
if (current->Owner) {
|
|
|
|
//
|
|
// Make sure that the InterruptLine register
|
|
// matches the assignment. (This is so that
|
|
// broken drivers that rely on the contents
|
|
// of the InterruptLine register instead of
|
|
// the resources handed back in a StartDevice
|
|
// still work.
|
|
//
|
|
|
|
pciBus = (ULONG)-1;
|
|
pciSlot.u.AsULONG = (ULONG)-1;
|
|
status = pciInterface->GetInterruptRouting(current->Owner,
|
|
&pciBus,
|
|
&pciSlot.u.AsULONG,
|
|
&interruptLine,
|
|
(PUCHAR)&dummy,
|
|
(PUCHAR)&dummy,
|
|
(PUCHAR)&dummy,
|
|
(PDEVICE_OBJECT*)&dummy,
|
|
&token,
|
|
(PUCHAR)&dummy);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
//
|
|
// The interrupt line register is only 8 bits wide, but some
|
|
// machines have more than 256 interrupt inputs. If the interrupt
|
|
// input assigned to the device is small enough to fit in the
|
|
// interrupt line register, write it out. If the interrupt input
|
|
// assigned to the device is too large, just write 0 to the interrupt
|
|
// line register.
|
|
//
|
|
|
|
newInterruptLine = ( current->Start > MAXUCHAR )
|
|
? 0
|
|
: (UCHAR)current->Start;
|
|
|
|
if (interruptLine != newInterruptLine) {
|
|
|
|
//
|
|
// We need to update the hardware.
|
|
//
|
|
|
|
//ASSERT(current->Start < MAXUCHAR);
|
|
|
|
pciInterface->UpdateInterruptLine(current->Owner,
|
|
newInterruptLine
|
|
);
|
|
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Free up the current allocation
|
|
//
|
|
|
|
RtlFreeRangeList(Arbiter->Allocation);
|
|
|
|
//
|
|
// Swap the allocated and duplicate lists
|
|
//
|
|
|
|
temp = Arbiter->Allocation;
|
|
Arbiter->Allocation = Arbiter->PossibleAllocation;
|
|
Arbiter->PossibleAllocation = temp;
|
|
|
|
//
|
|
// Since we have committed the new allocation, we
|
|
// need to make the edge-level state and the new
|
|
// link node counts permanent.
|
|
//
|
|
|
|
MakeTempVectorCountsPermanent();
|
|
|
|
status = MakeTempLinkNodeCountsPermanent(Arbiter);
|
|
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
AcpiArbQueryConflict(
|
|
IN PARBITER_INSTANCE Arbiter,
|
|
IN PDEVICE_OBJECT PhysicalDeviceObject,
|
|
IN PIO_RESOURCE_DESCRIPTOR ConflictingResource,
|
|
OUT PULONG ConflictCount,
|
|
OUT PARBITER_CONFLICT_INFO *Conflicts
|
|
)
|
|
{
|
|
PINT_ROUTE_INTERFACE_STANDARD pciInterface = NULL;
|
|
NTSTATUS status;
|
|
ROUTING_TOKEN routingToken;
|
|
ULONG_PTR dummy;
|
|
|
|
PAGED_CODE();
|
|
|
|
if (PciInterfacesInstantiated) {
|
|
|
|
pciInterface = ((PARBITER_EXTENSION)AcpiArbiter.ArbiterState.Extension)->InterruptRouting;
|
|
ASSERT(pciInterface);
|
|
|
|
status = pciInterface->GetInterruptRouting(PhysicalDeviceObject,
|
|
(PULONG)&dummy,
|
|
(PULONG)&dummy,
|
|
&(UCHAR)dummy,
|
|
&(UCHAR)dummy,
|
|
&(UCHAR)dummy,
|
|
&(UCHAR)dummy,
|
|
(PDEVICE_OBJECT*)&dummy,
|
|
&routingToken,
|
|
&(UCHAR)dummy);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
//
|
|
// This is a PCI device. It's interrupt should not ever
|
|
// show a conflict.
|
|
//
|
|
|
|
*ConflictCount = 0;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// This isn't a PCI device. Call the base arbiter code.
|
|
//
|
|
|
|
return ArbQueryConflict(Arbiter,
|
|
PhysicalDeviceObject,
|
|
ConflictingResource,
|
|
ConflictCount,
|
|
Conflicts);
|
|
}
|
|
|
|
NTSTATUS
|
|
FindVectorInAlternatives(
|
|
IN PARBITER_INSTANCE Arbiter,
|
|
IN PARBITER_ALLOCATION_STATE State,
|
|
IN ULONGLONG Vector,
|
|
OUT ULONG *Alternative
|
|
)
|
|
{
|
|
ULONG alt;
|
|
|
|
for (alt = 0; alt < State->AlternativeCount; alt++) {
|
|
|
|
if ((State->Alternatives[alt].Minimum <= Vector) &&
|
|
(State->Alternatives[alt].Maximum >= Vector)) {
|
|
|
|
*Alternative = alt;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
}
|
|
|
|
return STATUS_NOT_FOUND;
|
|
}
|
|
|
|
NTSTATUS
|
|
FindBootConfig(
|
|
IN PARBITER_INSTANCE Arbiter,
|
|
IN PARBITER_ALLOCATION_STATE State,
|
|
IN ULONGLONG *Vector
|
|
)
|
|
{
|
|
RTL_RANGE_LIST_ITERATOR iterator;
|
|
PRTL_RANGE currentRange;
|
|
|
|
FOR_ALL_RANGES(Arbiter->Allocation, &iterator, currentRange) {
|
|
|
|
if (currentRange->Attributes & ARBITER_RANGE_BOOT_ALLOCATED) {
|
|
|
|
//
|
|
// We're only interested in boot configs.
|
|
//
|
|
|
|
if (State->Entry->PhysicalDeviceObject == currentRange->Owner) {
|
|
|
|
//
|
|
// This boot config is the one we are looking for.
|
|
//
|
|
|
|
ASSERT(currentRange->Start == currentRange->End);
|
|
*Vector = currentRange->Start;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
}
|
|
}
|
|
|
|
return STATUS_NOT_FOUND;
|
|
}
|
|
|
|
BOOLEAN
|
|
AcpiArbGetNextAllocationRange(
|
|
IN PARBITER_INSTANCE Arbiter,
|
|
IN OUT PARBITER_ALLOCATION_STATE State
|
|
)
|
|
{
|
|
BOOLEAN nextRange = FALSE;
|
|
PINT_ROUTE_INTERFACE_STANDARD pciInterface;
|
|
NTSTATUS status;
|
|
ROUTING_TOKEN routingToken;
|
|
ULONG_PTR dummy;
|
|
BOOLEAN legacyFreeMachine;
|
|
ULONGLONG vector;
|
|
ULONG alternative;
|
|
|
|
PAGED_CODE();
|
|
|
|
if (State->Entry->PhysicalDeviceObject->DriverObject == AcpiDriverObject) {
|
|
|
|
//
|
|
// This is one of our PDOs.
|
|
//
|
|
|
|
ASSERT(((PDEVICE_EXTENSION)State->Entry->PhysicalDeviceObject->DeviceExtension)->Flags & DEV_TYPE_PDO);
|
|
ASSERT(((PDEVICE_EXTENSION)State->Entry->PhysicalDeviceObject->DeviceExtension)->Signature == ACPI_SIGNATURE);
|
|
|
|
if (((PDEVICE_EXTENSION)State->Entry->PhysicalDeviceObject->DeviceExtension)->Flags & DEV_CAP_PCI) {
|
|
|
|
//
|
|
// It's a PCI PDO, which means a root PCI bus,
|
|
// which means that we should just handle this
|
|
// as an ISA device.
|
|
//
|
|
|
|
return ArbGetNextAllocationRange(Arbiter, State);
|
|
}
|
|
}
|
|
|
|
status = STATUS_NOT_FOUND;
|
|
|
|
if (PciInterfacesInstantiated) {
|
|
|
|
pciInterface = ((PARBITER_EXTENSION)AcpiArbiter.ArbiterState.Extension)->InterruptRouting;
|
|
ASSERT(pciInterface);
|
|
|
|
status = pciInterface->GetInterruptRouting(State->Entry->PhysicalDeviceObject,
|
|
(PULONG)&dummy,
|
|
(PULONG)&dummy,
|
|
&(UCHAR)dummy,
|
|
&(UCHAR)dummy,
|
|
&(UCHAR)dummy,
|
|
&(UCHAR)dummy,
|
|
(PDEVICE_OBJECT*)&dummy,
|
|
&routingToken,
|
|
&(UCHAR)dummy);
|
|
|
|
}
|
|
|
|
if (status != STATUS_SUCCESS) {
|
|
|
|
//
|
|
// This is not a PCI device. Use the base function.
|
|
//
|
|
|
|
return ArbGetNextAllocationRange(Arbiter, State);
|
|
}
|
|
|
|
#if defined(_X86_)
|
|
legacyFreeMachine = (AcpiInformation->FixedACPIDescTable->Header.Revision > 1) &&
|
|
!(AcpiInformation->FixedACPIDescTable->boot_arch & LEGACY_DEVICES);
|
|
#else
|
|
legacyFreeMachine = TRUE;
|
|
#endif
|
|
|
|
//
|
|
// A PCI device.
|
|
//
|
|
|
|
if (!State->CurrentAlternative) {
|
|
|
|
//
|
|
// This is the first time we've called this function
|
|
// with this alternative list. Set up the state machine.
|
|
//
|
|
|
|
State->WorkSpace = AcpiIrqNextRangeInit;
|
|
}
|
|
|
|
while (TRUE) {
|
|
|
|
ASSERT((State->WorkSpace > AcpiIrqNextRangeMinState) &&
|
|
(State->WorkSpace < AcpiIrqNextRangeMaxState));
|
|
|
|
DEBUG_PRINT(4, ("GetNextRange, State: %x\n", State->WorkSpace));
|
|
|
|
switch (State->WorkSpace) {
|
|
case AcpiIrqNextRangeInit:
|
|
|
|
//
|
|
// Top of the state machine. See if the registry
|
|
// contained policy.
|
|
//
|
|
|
|
switch (AcpiIrqDistributionDisposition) {
|
|
case AcpiIrqDistributionDispositionSpreadOut:
|
|
State->WorkSpace = AcpiIrqNextRangeAlternativeZero;
|
|
break;
|
|
case AcpiIrqDistributionDispositionStackUp:
|
|
State->WorkSpace = AcpiIrqNextRangeInitLegacy;
|
|
break;
|
|
case AcpiIrqDistributionDispositionDontCare:
|
|
default:
|
|
State->WorkSpace = AcpiIrqNextRangeInitPolicyNeutral;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case AcpiIrqNextRangeInitPolicyNeutral:
|
|
|
|
//
|
|
// Look at the interrupt controller model.
|
|
//
|
|
|
|
if (InterruptModel == 0) {
|
|
State->WorkSpace = AcpiIrqNextRangeInitPic;
|
|
} else {
|
|
State->WorkSpace = AcpiIrqNextRangeUseBootConfig;
|
|
}
|
|
break;
|
|
|
|
case AcpiIrqNextRangeInitPic:
|
|
|
|
//
|
|
// There is a PIC interrupt controller. So we are somewhat
|
|
// IRQ constrained. If this is a legacy-free machine, or if there
|
|
// is no cardbus controller, we want to spread interrupts.
|
|
//
|
|
|
|
if (legacyFreeMachine || !AcpiArbCardbusPresent) {
|
|
State->WorkSpace = AcpiIrqNextRangeUseBootConfig;
|
|
} else {
|
|
State->WorkSpace = AcpiIrqNextRangeInitLegacy;
|
|
}
|
|
break;
|
|
|
|
case AcpiIrqNextRangeInitLegacy:
|
|
|
|
//
|
|
// See if all the devices were boot configged on the same
|
|
// vector, or if there was a registry override specifying
|
|
// the vector that we should favor.
|
|
//
|
|
|
|
if (AcpiIrqDefaultBootConfig) {
|
|
State->WorkSpace = AcpiIrqNextRangeBootRegAlternative;
|
|
} else {
|
|
State->WorkSpace = AcpiIrqNextRangeSciAlternative;
|
|
}
|
|
break;
|
|
|
|
case AcpiIrqNextRangeBootRegAlternative:
|
|
|
|
//
|
|
// If we re-enter this state machine after this state,
|
|
// then it means that this alternative wasn't available.
|
|
// So set the next state to AcpiIrqNextRangeAlternativeZero,
|
|
// assuming that we failed.
|
|
//
|
|
|
|
State->WorkSpace = AcpiIrqNextRangeAlternativeZero;
|
|
|
|
//
|
|
// See if the machine-wide boot config or the registry
|
|
// override is within the alternatives.
|
|
//
|
|
|
|
status = FindVectorInAlternatives(Arbiter,
|
|
State,
|
|
(ULONGLONG)AcpiIrqDefaultBootConfig,
|
|
&alternative);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
State->CurrentAlternative = &State->Alternatives[alternative];
|
|
State->CurrentMinimum = (ULONGLONG)AcpiIrqDefaultBootConfig;
|
|
State->CurrentMaximum = (ULONGLONG)AcpiIrqDefaultBootConfig;
|
|
goto GetNextAllocationSuccess;
|
|
}
|
|
break;
|
|
|
|
case AcpiIrqNextRangeSciAlternative:
|
|
|
|
//
|
|
// If we re-enter this state machine after this state,
|
|
// then it means that this alternative wasn't available.
|
|
// So set the next state to AcpiIrqNextRangeUseBootConfig,
|
|
// assuming that we failed.
|
|
//
|
|
|
|
State->WorkSpace = AcpiIrqNextRangeUseBootConfig;
|
|
|
|
//
|
|
// See if the SCI vector is within the alternatives.
|
|
//
|
|
|
|
status = FindVectorInAlternatives(Arbiter,
|
|
State,
|
|
(ULONGLONG)AcpiSciVector,
|
|
&alternative);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
State->CurrentAlternative = &State->Alternatives[alternative];
|
|
State->CurrentMinimum = (ULONGLONG)AcpiSciVector;
|
|
State->CurrentMaximum = (ULONGLONG)AcpiSciVector;
|
|
goto GetNextAllocationSuccess;
|
|
}
|
|
break;
|
|
|
|
case AcpiIrqNextRangeUseBootConfig:
|
|
|
|
//
|
|
// If we re-enter this state machine after this state,
|
|
// then it means that this alternative wasn't available.
|
|
// So set the next state to AcpiIrqNextRangeAlternativeZero,
|
|
// assuming that we failed.
|
|
//
|
|
|
|
State->WorkSpace = AcpiIrqNextRangeAlternativeZero;
|
|
|
|
//
|
|
// See if there is a boot config for this device
|
|
// within the alternatives.
|
|
//
|
|
|
|
status = FindBootConfig(Arbiter,
|
|
State,
|
|
&vector);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
status = FindVectorInAlternatives(Arbiter,
|
|
State,
|
|
vector,
|
|
&alternative);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
State->CurrentAlternative = &State->Alternatives[alternative];
|
|
State->CurrentMinimum = vector;
|
|
State->CurrentMaximum = vector;
|
|
goto GetNextAllocationSuccess;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case AcpiIrqNextRangeAlternativeZero:
|
|
|
|
//
|
|
// If we re-enter this state machine after this state,
|
|
// then it means that this alternative wasn't available.
|
|
// So set the next state to AcpiIrqNextRangeAlternativeN,
|
|
// assuming that we failed.
|
|
//
|
|
|
|
State->WorkSpace = AcpiIrqNextRangeAlternativeN;
|
|
|
|
//
|
|
// Try alternative 0.
|
|
//
|
|
|
|
State->CurrentAlternative = &State->Alternatives[0];
|
|
State->CurrentMinimum = State->CurrentAlternative->Minimum;
|
|
State->CurrentMaximum = State->CurrentAlternative->Maximum;
|
|
goto GetNextAllocationSuccess;
|
|
break;
|
|
|
|
case AcpiIrqNextRangeAlternativeN:
|
|
|
|
if (++State->CurrentAlternative < &State->Alternatives[State->AlternativeCount]) {
|
|
|
|
//
|
|
// There are multiple ranges. Cycle through them.
|
|
//
|
|
|
|
DEBUG_PRINT(3, ("No next allocation range, exhausted all %08X alternatives", State->AlternativeCount));
|
|
State->CurrentMinimum = State->CurrentAlternative->Minimum;
|
|
State->CurrentMaximum = State->CurrentAlternative->Maximum;
|
|
goto GetNextAllocationSuccess;
|
|
|
|
} else {
|
|
|
|
//
|
|
// We're done. There is no solution among these alternatives.
|
|
//
|
|
|
|
return FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
GetNextAllocationSuccess:
|
|
|
|
DEBUG_PRINT(3, ("Next allocation range 0x%I64x-0x%I64x\n", State->CurrentMinimum, State->CurrentMaximum));
|
|
AcpiArbPciAlternativeRotation++;
|
|
return TRUE;
|
|
}
|
|
|
|
VOID
|
|
ReferenceVector(
|
|
IN ULONG Vector,
|
|
IN UCHAR Flags
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine adds one to either the permanent or the
|
|
temporary reference count.
|
|
|
|
Parameters:
|
|
|
|
Vector - the IRQ
|
|
|
|
Flags - mode and polarity
|
|
|
|
Return Value:
|
|
|
|
none
|
|
|
|
--*/
|
|
{
|
|
PVECTOR_BLOCK block;
|
|
|
|
PAGED_CODE();
|
|
ASSERT((Flags & ~(VECTOR_MODE | VECTOR_POLARITY | VECTOR_TYPE)) == 0);
|
|
|
|
block = HashVector(Vector);
|
|
|
|
DEBUG_PRINT(5, ("Referencing vector %x : %d %d\n", Vector,
|
|
block ? block->Entry.Count : 0,
|
|
block ? block->Entry.TempCount : 0));
|
|
|
|
if (block == NULL) {
|
|
|
|
AddVectorToTable(Vector,
|
|
0,
|
|
1,
|
|
Flags);
|
|
return;
|
|
}
|
|
|
|
if ((block->Entry.TempCount + block->Entry.Count) == 0) {
|
|
|
|
//
|
|
// This vector has been temporarily set to an
|
|
// aggregate count of zero. This means that the arbiter
|
|
// is re-allocating it. Record the new flags.
|
|
//
|
|
|
|
block->Entry.TempFlags = Flags;
|
|
}
|
|
|
|
block->Entry.TempCount++;
|
|
|
|
ASSERT(Flags == block->Entry.TempFlags);
|
|
ASSERT(block->Entry.Count <= 255);
|
|
}
|
|
|
|
VOID
|
|
DereferenceVector(
|
|
IN ULONG Vector
|
|
)
|
|
{
|
|
PVECTOR_BLOCK block;
|
|
|
|
PAGED_CODE();
|
|
|
|
block = HashVector(Vector);
|
|
|
|
ASSERT(block);
|
|
|
|
DEBUG_PRINT(5, ("Dereferencing vector %x : %d %d\n", Vector,
|
|
block->Entry.Count,
|
|
block->Entry.TempCount));
|
|
|
|
block->Entry.TempCount--;
|
|
ASSERT((block->Entry.TempCount * -1) <= block->Entry.Count);
|
|
}
|
|
|
|
|
|
PVECTOR_BLOCK
|
|
HashVector(
|
|
IN ULONG Vector
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function takes a "Global System Interrupt Vector"
|
|
and returns a pointer to its entry in the hash table.
|
|
|
|
Arguments:
|
|
|
|
Vector - an IRQ
|
|
|
|
Return Value:
|
|
|
|
pointer to the entry in the hash table, or NULL if not found
|
|
|
|
--*/
|
|
{
|
|
PVECTOR_BLOCK block;
|
|
ULONG row, column;
|
|
|
|
PAGED_CODE();
|
|
|
|
row = Vector % VECTOR_HASH_TABLE_LENGTH;
|
|
|
|
block = HASH_ENTRY(row, 0);
|
|
|
|
while (TRUE) {
|
|
|
|
//
|
|
// Search across the hash table looking for our Vector
|
|
//
|
|
|
|
for (column = 0; column < VECTOR_HASH_TABLE_WIDTH; column++) {
|
|
|
|
//
|
|
// Check to see if we should follow a chain
|
|
//
|
|
|
|
if (block->Chain.Token == TOKEN_VALUE) {
|
|
break;
|
|
}
|
|
|
|
if (block->Entry.Vector == Vector) {
|
|
return block;
|
|
}
|
|
|
|
if ((block->Entry.Vector == EMPTY_BLOCK_VALUE) ||
|
|
(column == VECTOR_HASH_TABLE_WIDTH - 1)) {
|
|
|
|
//
|
|
// Didn't find this vector in the table.
|
|
//
|
|
|
|
return NULL;
|
|
}
|
|
|
|
block += 1;
|
|
}
|
|
|
|
ASSERT(block->Chain.Token == TOKEN_VALUE);
|
|
|
|
block = block->Chain.Next;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
NTSTATUS
|
|
GetVectorProperties(
|
|
IN ULONG Vector,
|
|
OUT UCHAR *Flags
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function takes a "Global System Interrupt Vector"
|
|
and returns the associated flags.
|
|
|
|
N.B. This function returns flags based on the
|
|
*temporary* reference count. I.e. if the vector
|
|
has been temporarily dereferenced, then the
|
|
funtion will indicate that the vector is available
|
|
for allocation by returning STATUS_NOT_FOUND.
|
|
|
|
Arguments:
|
|
|
|
Vector - an IRQ
|
|
|
|
Flags - to be filled in with the flags
|
|
|
|
Return Value:
|
|
|
|
status
|
|
|
|
--*/
|
|
{
|
|
PVECTOR_BLOCK block;
|
|
|
|
PAGED_CODE();
|
|
|
|
block = HashVector(Vector);
|
|
|
|
if (!block) {
|
|
return STATUS_NOT_FOUND;
|
|
}
|
|
|
|
if (block->Entry.Vector == EMPTY_BLOCK_VALUE) {
|
|
return STATUS_NOT_FOUND;
|
|
}
|
|
|
|
ASSERT(block->Entry.Vector == Vector);
|
|
|
|
if (block->Entry.Count + block->Entry.TempCount == 0) {
|
|
|
|
//
|
|
// This vector has an aggregate reference count of
|
|
// zero. This means that it is effectively
|
|
// unallocated.
|
|
//
|
|
|
|
return STATUS_NOT_FOUND;
|
|
}
|
|
|
|
*Flags = block->Entry.TempFlags;
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
AddVectorToTable(
|
|
IN ULONG Vector,
|
|
IN UCHAR ReferenceCount,
|
|
IN UCHAR TempRefCount,
|
|
IN UCHAR Flags
|
|
)
|
|
{
|
|
PVECTOR_BLOCK block, newRow;
|
|
ULONG row, column;
|
|
|
|
PAGED_CODE();
|
|
ASSERT((Flags & ~(VECTOR_MODE | VECTOR_POLARITY | VECTOR_TYPE)) == 0);
|
|
|
|
row = Vector % VECTOR_HASH_TABLE_LENGTH;
|
|
|
|
block = HASH_ENTRY(row, 0);
|
|
|
|
while (TRUE) {
|
|
|
|
//
|
|
// Search across the hash table looking for our Vector
|
|
//
|
|
|
|
for (column = 0; column < VECTOR_HASH_TABLE_WIDTH; column++) {
|
|
|
|
//
|
|
// Check to see if we should follow a chain
|
|
//
|
|
|
|
if (block->Chain.Token == TOKEN_VALUE) {
|
|
break;
|
|
}
|
|
|
|
if (block->Entry.Vector == EMPTY_BLOCK_VALUE) {
|
|
|
|
block->Entry.Vector = Vector;
|
|
block->Entry.Count = ReferenceCount;
|
|
block->Entry.TempCount = TempRefCount;
|
|
block->Entry.Flags = Flags;
|
|
block->Entry.TempFlags = Flags;
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
if (column == VECTOR_HASH_TABLE_WIDTH - 1) {
|
|
|
|
//
|
|
// We have just looked at the last entry in
|
|
// the row and it wasn't empty. Create
|
|
// an extension to this row.
|
|
//
|
|
|
|
newRow = ExAllocatePoolWithTag(PagedPool,
|
|
sizeof(VECTOR_BLOCK)
|
|
* VECTOR_HASH_TABLE_WIDTH,
|
|
ACPI_ARBITER_POOLTAG
|
|
);
|
|
|
|
if (!newRow) {
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
RtlFillMemory(newRow,
|
|
sizeof(VECTOR_BLOCK) * VECTOR_HASH_TABLE_WIDTH,
|
|
(UCHAR)(EMPTY_BLOCK_VALUE & 0xff));
|
|
|
|
//
|
|
// Move last entry into new row.
|
|
//
|
|
|
|
RtlMoveMemory(newRow, block, sizeof(VECTOR_BLOCK));
|
|
|
|
//
|
|
// Chain the old row to the new row.
|
|
//
|
|
|
|
block->Chain.Token = TOKEN_VALUE;
|
|
block->Chain.Next = newRow;
|
|
|
|
break;
|
|
}
|
|
|
|
block += 1;
|
|
}
|
|
|
|
block = block->Chain.Next;
|
|
}
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
VOID
|
|
ClearTempVectorCounts(
|
|
VOID
|
|
)
|
|
{
|
|
PVECTOR_BLOCK block;
|
|
ULONG row, column;
|
|
|
|
PAGED_CODE();
|
|
|
|
|
|
for (row = 0; row < VECTOR_HASH_TABLE_LENGTH; row++) {
|
|
|
|
block = HASH_ENTRY(row, 0);
|
|
|
|
//
|
|
// Search across the hash table looking for our Vector
|
|
//
|
|
|
|
ClearTempCountsStartRow:
|
|
|
|
for (column = 0; column < VECTOR_HASH_TABLE_WIDTH; column++) {
|
|
|
|
//
|
|
// Check to see if we should follow a chain
|
|
//
|
|
|
|
if (block->Chain.Token == TOKEN_VALUE) {
|
|
block = block->Chain.Next;
|
|
goto ClearTempCountsStartRow;
|
|
}
|
|
|
|
if (block->Entry.Vector == EMPTY_BLOCK_VALUE) {
|
|
break;
|
|
}
|
|
|
|
//
|
|
// This must be a valid entry.
|
|
//
|
|
|
|
block->Entry.TempCount = 0;
|
|
block->Entry.TempFlags = block->Entry.Flags;
|
|
block += 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
VOID
|
|
MakeTempVectorCountsPermanent(
|
|
VOID
|
|
)
|
|
{
|
|
PVECTOR_BLOCK block;
|
|
ULONG row, column;
|
|
|
|
PAGED_CODE();
|
|
|
|
|
|
for (row = 0; row < VECTOR_HASH_TABLE_LENGTH; row++) {
|
|
|
|
block = HASH_ENTRY(row, 0);
|
|
|
|
//
|
|
// Search across the hash table looking for our Vector
|
|
//
|
|
|
|
MakeTempVectorCountsPermanentStartRow:
|
|
|
|
for (column = 0; column < VECTOR_HASH_TABLE_WIDTH; column++) {
|
|
|
|
//
|
|
// Check to see if we should follow a chain
|
|
//
|
|
|
|
if (block->Chain.Token == TOKEN_VALUE) {
|
|
block = block->Chain.Next;
|
|
goto MakeTempVectorCountsPermanentStartRow;
|
|
}
|
|
|
|
if (block->Entry.Vector == EMPTY_BLOCK_VALUE) {
|
|
break;
|
|
}
|
|
|
|
//
|
|
// This must be a valid entry.
|
|
//
|
|
|
|
if ((block->Entry.Count + block->Entry.TempCount != 0) &&
|
|
((block->Entry.Count == 0) ||
|
|
(block->Entry.TempFlags != block->Entry.Flags))) {
|
|
|
|
//
|
|
// This vector has just been allocated or it has
|
|
// been re-allocated. Tell the HAL which flags
|
|
// to use.
|
|
//
|
|
|
|
HalSetVectorState(block->Entry.Vector,
|
|
block->Entry.TempFlags);
|
|
}
|
|
|
|
//
|
|
// Record new flags and aggregate count.
|
|
//
|
|
|
|
block->Entry.Flags = block->Entry.TempFlags;
|
|
block->Entry.Count += block->Entry.TempCount;
|
|
|
|
block += 1;
|
|
}
|
|
}
|
|
}
|
|
#ifdef DBG
|
|
VOID
|
|
DumpVectorTable(
|
|
VOID
|
|
)
|
|
{
|
|
PVECTOR_BLOCK block;
|
|
ULONG row, column;
|
|
|
|
PAGED_CODE();
|
|
|
|
DEBUG_PRINT(1, ("\nIRQARB: Dumping vector table\n"));
|
|
|
|
for (row = 0; row < VECTOR_HASH_TABLE_LENGTH; row++) {
|
|
|
|
block = HASH_ENTRY(row, 0);
|
|
|
|
//
|
|
// Search across the hash table looking for our Vector
|
|
//
|
|
|
|
DumpVectorTableStartRow:
|
|
|
|
for (column = 0; column < VECTOR_HASH_TABLE_WIDTH; column++) {
|
|
|
|
//
|
|
// Check to see if we should follow a chain
|
|
//
|
|
|
|
if (block->Chain.Token == TOKEN_VALUE) {
|
|
block = block->Chain.Next;
|
|
goto DumpVectorTableStartRow;
|
|
}
|
|
|
|
if (block->Entry.Vector == EMPTY_BLOCK_VALUE) {
|
|
break;
|
|
}
|
|
|
|
DEBUG_PRINT(1, ("Vector: %x\tP: %d T: %d\t%s %s\n",
|
|
block->Entry.Vector,
|
|
block->Entry.Count,
|
|
(LONG)block->Entry.TempCount,
|
|
IS_LEVEL_TRIGGERED(block->Entry.Flags) ? "level" : "edge",
|
|
IS_ACTIVE_LOW(block->Entry.Flags) ? "low" : "high"));
|
|
|
|
block += 1;
|
|
}
|
|
|
|
}
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// This section of the file contains functions used for
|
|
// reading and manipulating the AML code.
|
|
//
|
|
NTSTATUS
|
|
AcpiArbGetLinkNodeOptions(
|
|
IN PNSOBJ LinkNode,
|
|
IN OUT PCM_RESOURCE_LIST *LinkNodeIrqs,
|
|
IN OUT UCHAR *Flags
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine looks in the AML namespace for the named
|
|
link node and returns the range of IRQs that it can
|
|
trigger.
|
|
|
|
Arguments:
|
|
|
|
LinkNodeName - The name of the IRQ router (link node)
|
|
LInkNodeIrqs - The list of possible settings for the link node
|
|
Flags - flags associated with this link node
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
{
|
|
PIO_RESOURCE_REQUIREMENTS_LIST ioList = NULL;
|
|
PCM_RESOURCE_LIST cmList = NULL;
|
|
PUCHAR prsBuff = NULL;
|
|
NTSTATUS status;
|
|
PULONG polarity;
|
|
|
|
PAGED_CODE();
|
|
|
|
ASSERT(LinkNode);
|
|
|
|
//
|
|
// Read the _PRS
|
|
//
|
|
|
|
ACPIGetNSBufferSync(
|
|
LinkNode,
|
|
PACKED_PRS,
|
|
&prsBuff,
|
|
NULL);
|
|
|
|
if (!prsBuff) {
|
|
|
|
return STATUS_NOT_FOUND;
|
|
}
|
|
|
|
status = PnpBiosResourcesToNtResources(prsBuff, 0, &ioList);
|
|
|
|
ExFreePool(prsBuff);
|
|
|
|
//
|
|
// if the ioList is empty then we can assume there are no valuable
|
|
// resources in this prs, so we can cleanup.
|
|
//
|
|
if(!ioList){
|
|
|
|
status = STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
return status;
|
|
}
|
|
|
|
//
|
|
// Huge HACK! Get the polarity for the Flags.
|
|
//
|
|
// An IO_RES_LIST has no real way of representing the polarity
|
|
// of an interrupt. So, in PnpiBiosExtendedIrqToIoDescriptor I
|
|
// stuck the information in the DWORD past 'MaximumVector.'
|
|
//
|
|
|
|
*Flags = 0;
|
|
|
|
ASSERT(ioList->AlternativeLists == 1);
|
|
polarity = (PULONG)(&ioList->List[0].Descriptors[0].u.Interrupt.MaximumVector) + 1;
|
|
|
|
*Flags |= (UCHAR)*polarity;
|
|
|
|
//
|
|
// Get the mode for the flags.
|
|
//
|
|
|
|
*Flags |= (ioList->List[0].Descriptors[0].Flags == CM_RESOURCE_INTERRUPT_LATCHED) ?
|
|
VECTOR_EDGE : VECTOR_LEVEL;
|
|
|
|
//
|
|
// Turn the list into a CM_RESOURCE_LIST
|
|
//
|
|
status = PnpIoResourceListToCmResourceList(
|
|
ioList,
|
|
&cmList
|
|
);
|
|
|
|
ExFreePool(ioList);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
return status;
|
|
}
|
|
|
|
*LinkNodeIrqs = cmList;
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
typedef enum {
|
|
StateInitial,
|
|
StateGotPrs,
|
|
StateRanSrs
|
|
} SET_LINK_WORKER_STATE;
|
|
|
|
typedef struct {
|
|
PNSOBJ LinkNode;
|
|
PCM_PARTIAL_RESOURCE_DESCRIPTOR LinkNodeIrq;
|
|
PUCHAR PrsBuff;
|
|
PUCHAR SrsBuff;
|
|
SET_LINK_WORKER_STATE State;
|
|
LONG RunCompletionHandler;
|
|
OBJDATA ObjData;
|
|
PFNACB CompletionHandler;
|
|
PVOID CompletionContext;
|
|
|
|
} SET_LINK_NODE_STATE, *PSET_LINK_NODE_STATE;
|
|
|
|
NTSTATUS
|
|
AcpiArbSetLinkNodeIrq(
|
|
IN PNSOBJ LinkNode,
|
|
IN PCM_PARTIAL_RESOURCE_DESCRIPTOR LinkNodeIrq
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine sets the named link node to trigger
|
|
a particular IRQ.
|
|
|
|
N.B. This routine could simply build the right
|
|
buffer and call the _SRS method, but there
|
|
isn't enough information in a
|
|
CM_PARTIAL_RESOURCE_DESCRIPTOR to know whether
|
|
an interrupt will be delivered active-high
|
|
or active-low. So the algorithm here runs
|
|
the _PRS method and copies the buffer returned
|
|
by _PRS into the buffer sent to _SRS. This
|
|
way all the flags are preserved.
|
|
|
|
Arguments:
|
|
|
|
LinkNodeName - The name of the IRQ router (link node)
|
|
LinkNodeIrq - The IRQ that the link node will be programmed
|
|
to trigger. If it is NULL, then the link node
|
|
will be disabled.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
{
|
|
AMLISUPP_CONTEXT_PASSIVE getDataContext;
|
|
NTSTATUS status;
|
|
|
|
PAGED_CODE();
|
|
|
|
KeInitializeEvent(&getDataContext.Event, SynchronizationEvent, FALSE);
|
|
getDataContext.Status = STATUS_NOT_FOUND;
|
|
|
|
status = AcpiArbSetLinkNodeIrqAsync(LinkNode,
|
|
LinkNodeIrq,
|
|
AmlisuppCompletePassive,
|
|
(PVOID)&getDataContext
|
|
);
|
|
|
|
if (status == STATUS_PENDING) {
|
|
|
|
KeWaitForSingleObject(&getDataContext.Event,
|
|
Executive,
|
|
KernelMode,
|
|
FALSE,
|
|
NULL);
|
|
|
|
status = getDataContext.Status;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
AcpiArbSetLinkNodeIrqAsync(
|
|
IN PNSOBJ LinkNode,
|
|
IN PCM_PARTIAL_RESOURCE_DESCRIPTOR LinkNodeIrq,
|
|
IN PFNACB CompletionHandler,
|
|
IN PVOID CompletionContext
|
|
)
|
|
{
|
|
PSET_LINK_NODE_STATE state;
|
|
NTSTATUS status;
|
|
|
|
ASSERT(LinkNode);
|
|
|
|
state = ExAllocatePoolWithTag(NonPagedPool,
|
|
sizeof(SET_LINK_NODE_STATE),
|
|
ACPI_ARBITER_POOLTAG);
|
|
|
|
if (!state) {
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
RtlZeroMemory(state, sizeof(SET_LINK_NODE_STATE));
|
|
|
|
state->LinkNode = LinkNode;
|
|
state->LinkNodeIrq = LinkNodeIrq;
|
|
state->CompletionHandler = CompletionHandler;
|
|
state->CompletionContext = CompletionContext;
|
|
state->State = StateInitial;
|
|
state->RunCompletionHandler = INITIAL_RUN_COMPLETION;
|
|
|
|
return AcpiArbSetLinkNodeIrqWorker(LinkNode,
|
|
STATUS_SUCCESS,
|
|
NULL,
|
|
(PVOID)state
|
|
);
|
|
}
|
|
|
|
NTSTATUS
|
|
EXPORT
|
|
AcpiArbSetLinkNodeIrqWorker(
|
|
IN PNSOBJ AcpiObject,
|
|
IN NTSTATUS Status,
|
|
IN POBJDATA Result,
|
|
IN PVOID Context
|
|
)
|
|
{
|
|
BOOLEAN foundTag = FALSE;
|
|
BOOLEAN useEndChecksum = FALSE;
|
|
BOOLEAN useExtendedTag = FALSE;
|
|
NTSTATUS status;
|
|
PNSOBJ childobj;
|
|
PPNP_EXTENDED_IRQ_DESCRIPTOR largeIrq;
|
|
PPNP_IRQ_DESCRIPTOR smallIrq;
|
|
PSET_LINK_NODE_STATE state;
|
|
PUCHAR resource = NULL;
|
|
PUCHAR irqTag = NULL;
|
|
PUCHAR sumchar;
|
|
UCHAR sum = 0;
|
|
UCHAR tagName;
|
|
ULONG length = 0;
|
|
USHORT increment;
|
|
USHORT irqTagLength = 0;
|
|
|
|
state = (PSET_LINK_NODE_STATE)Context;
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
status = Status;
|
|
goto AcpiArbSetLinkNodeIrqWorkerExit;
|
|
}
|
|
|
|
ASSERT(state->LinkNodeIrq->Type == CmResourceTypeInterrupt);
|
|
|
|
//
|
|
// Entering this function twice with the same state
|
|
// means that we need to run the completion routine.
|
|
//
|
|
|
|
InterlockedIncrement(&state->RunCompletionHandler);
|
|
|
|
switch (state->State) {
|
|
case StateInitial:
|
|
|
|
//
|
|
// Read the _PRS, so that we can choose the appropriate
|
|
// entry and write that back into the _SRS.
|
|
//
|
|
|
|
state->State = StateGotPrs;
|
|
status = ACPIGetNSBufferAsync(
|
|
state->LinkNode,
|
|
PACKED_PRS,
|
|
AcpiArbSetLinkNodeIrqWorker,
|
|
(PVOID)state,
|
|
&state->PrsBuff,
|
|
NULL
|
|
);
|
|
if (status == STATUS_PENDING) {
|
|
return status;
|
|
} else if (!NT_SUCCESS(status)) {
|
|
goto AcpiArbSetLinkNodeIrqWorkerExit;
|
|
}
|
|
|
|
//
|
|
// Fallthrough to next state
|
|
//
|
|
case StateGotPrs:
|
|
state->State = StateRanSrs;
|
|
if (!state->PrsBuff) {
|
|
status = STATUS_NOT_FOUND;
|
|
goto AcpiArbSetLinkNodeIrqWorkerExit;
|
|
}
|
|
|
|
DEBUG_PRINT(7, ("Read _PRS buffer %p\n", state->PrsBuff));
|
|
|
|
resource = state->PrsBuff;
|
|
while ( *resource ) {
|
|
|
|
tagName = *resource;
|
|
if ( !(tagName & LARGE_RESOURCE_TAG)) {
|
|
increment = (USHORT) (tagName & SMALL_TAG_SIZE_MASK) + 1;
|
|
tagName &= SMALL_TAG_MASK;
|
|
} else {
|
|
increment = ( *(USHORT UNALIGNED *)(resource + 1) ) + 3;
|
|
|
|
}
|
|
|
|
if (tagName == TAG_END) {
|
|
length += increment;
|
|
if (increment > 1) {
|
|
useEndChecksum = TRUE;
|
|
}
|
|
break;
|
|
}
|
|
|
|
//
|
|
// This is the check to see if find a resource that correctly
|
|
// matches the assignment
|
|
//
|
|
// This code is weak. It need to check to see
|
|
// if the flags and interrupt match the descriptor we just found.
|
|
// It is possible for a vendor to use overlapping descriptors that
|
|
// would describe different interrupt settings.
|
|
//
|
|
if (tagName == TAG_IRQ || tagName == TAG_EXTENDED_IRQ) {
|
|
irqTag = resource;
|
|
if (tagName == TAG_EXTENDED_IRQ) {
|
|
irqTagLength = sizeof(PNP_EXTENDED_IRQ_DESCRIPTOR);
|
|
useExtendedTag = TRUE;
|
|
} else {
|
|
irqTagLength = increment;
|
|
|
|
}
|
|
length += (ULONG) irqTagLength;
|
|
foundTag = TRUE;
|
|
}
|
|
|
|
resource += increment;
|
|
}
|
|
|
|
//
|
|
// Did we find the tag that we are looking for?
|
|
//
|
|
if (foundTag == FALSE) {
|
|
ExFreePool( state->PrsBuff );
|
|
status = STATUS_NOT_FOUND;
|
|
goto AcpiArbSetLinkNodeIrqWorkerExit;
|
|
}
|
|
|
|
//
|
|
// The next task is to fashion a buffer containing an ACPI-style
|
|
// resource descriptor with exactly one interrupt destination in
|
|
// it. We do this by allocating one
|
|
//
|
|
|
|
state->SrsBuff = ExAllocatePoolWithTag(
|
|
NonPagedPool,
|
|
length,
|
|
ACPI_ARBITER_POOLTAG
|
|
);
|
|
|
|
if (!state->SrsBuff) {
|
|
ExFreePool(state->PrsBuff);
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto AcpiArbSetLinkNodeIrqWorkerExit;
|
|
}
|
|
|
|
ASSERT(irqTagLength <= length);
|
|
RtlCopyMemory(state->SrsBuff, irqTag, irqTagLength);
|
|
ExFreePool(state->PrsBuff);
|
|
|
|
//
|
|
// Change the buffer to reflect our choice of interrupts.
|
|
//
|
|
|
|
if (!useExtendedTag) {
|
|
|
|
// small IRQ
|
|
smallIrq = (PPNP_IRQ_DESCRIPTOR)state->SrsBuff;
|
|
smallIrq->IrqMask = (USHORT)(1 << state->LinkNodeIrq->u.Interrupt.Level);
|
|
|
|
} else {
|
|
|
|
DEBUG_PRINT(7, ("Found large IRQ descriptor\n"));
|
|
|
|
// large IRQ
|
|
largeIrq = (PPNP_EXTENDED_IRQ_DESCRIPTOR)state->SrsBuff;
|
|
largeIrq->Length = irqTagLength - 3;
|
|
largeIrq->TableSize = 1;
|
|
largeIrq->Table[0] = state->LinkNodeIrq->u.Interrupt.Level;
|
|
|
|
}
|
|
|
|
//
|
|
// Work on the END descriptor
|
|
//
|
|
resource = (state->SrsBuff + irqTagLength);
|
|
*resource = TAG_END;
|
|
if (useEndChecksum) {
|
|
|
|
*resource |= 1; // The one is to represent the checksum
|
|
|
|
//
|
|
// Calculate the Checksum
|
|
sumchar = state->SrsBuff;
|
|
while (*sumchar != *resource) {
|
|
sum = *sumchar++;
|
|
}
|
|
*(resource+1) = 256 - sum;
|
|
|
|
}
|
|
|
|
//
|
|
// Now run the _SRS method with this buffer
|
|
//
|
|
|
|
//
|
|
// Get the object that we are looking for
|
|
//
|
|
childobj = ACPIAmliGetNamedChild(
|
|
state->LinkNode,
|
|
PACKED_SRS
|
|
);
|
|
if (childobj == NULL) {
|
|
status = STATUS_OBJECT_NAME_NOT_FOUND;
|
|
ExFreePool( state->SrsBuff );
|
|
goto AcpiArbSetLinkNodeIrqWorkerExit;
|
|
}
|
|
|
|
state->ObjData.dwDataType = OBJTYPE_BUFFDATA;
|
|
state->ObjData.dwDataLen = length;
|
|
state->ObjData.pbDataBuff = state->SrsBuff;
|
|
|
|
DEBUG_PRINT(7, ("Running _SRS\n"));
|
|
|
|
status = AMLIAsyncEvalObject(
|
|
childobj,
|
|
NULL,
|
|
1,
|
|
&state->ObjData,
|
|
AcpiArbSetLinkNodeIrqWorker,
|
|
(PVOID)state
|
|
);
|
|
if (status == STATUS_PENDING) {
|
|
return status;
|
|
} else if (!NT_SUCCESS(status)) {
|
|
goto AcpiArbSetLinkNodeIrqWorkerExit;
|
|
}
|
|
|
|
case StateRanSrs:
|
|
//
|
|
// We are done.
|
|
//
|
|
ExFreePool(state->SrsBuff);
|
|
status = STATUS_SUCCESS;
|
|
break;
|
|
|
|
default:
|
|
ACPIInternalError( ACPI_IRQARB );
|
|
}
|
|
|
|
AcpiArbSetLinkNodeIrqWorkerExit:
|
|
|
|
if (state->RunCompletionHandler) {
|
|
|
|
state->CompletionHandler(
|
|
AcpiObject,
|
|
status,
|
|
NULL,
|
|
state->CompletionContext
|
|
);
|
|
|
|
}
|
|
ExFreePool(state);
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
AcpiArbCrackPRT(
|
|
IN PDEVICE_OBJECT Pdo,
|
|
IN OUT PNSOBJ *LinkNode,
|
|
IN OUT ULONG *Vector
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine takes a PDO for a device and returns the
|
|
associated link node, if any. The ACPI spec says that
|
|
a _PRT can optionally return a single interrupt vector
|
|
instead of a link node. If this is the case, this function
|
|
returns that vector.
|
|
|
|
Arguments:
|
|
|
|
Pdo - The PDO of the device that needs to be granted an
|
|
IRQ.
|
|
|
|
LinkNode - A pointer to the link node, or NULL if the device
|
|
isn't connected to a link node.
|
|
|
|
Vector - The global system interrupt vector that this PCI
|
|
device is connected to. This is meaningless if
|
|
LinkNode is not NULL.
|
|
|
|
Return Value:
|
|
|
|
If we find a link node or a vector for this device, STATUS_SUCCESS.
|
|
|
|
If this isn't a PCI device, STATUS_DEVICE_NOT_FOUND.
|
|
|
|
If this is an IDE device, then we have to treat it specially, so
|
|
we return STATUS_RESOURCE_REQUIREMENTS_CHANGED.
|
|
|
|
--*/
|
|
{
|
|
PINT_ROUTE_INTERFACE_STANDARD pciInterface;
|
|
NTSTATUS status;
|
|
PDEVICE_OBJECT filter;
|
|
PDEVICE_OBJECT parent;
|
|
PDEVICE_EXTENSION filterExtension;
|
|
OBJDATA adrData;
|
|
OBJDATA pinData;
|
|
OBJDATA prtData;
|
|
OBJDATA linkData;
|
|
OBJDATA indexData;
|
|
PNSOBJ pciBusObj;
|
|
PNSOBJ prtObj;
|
|
ULONG prtElement = 0;
|
|
BOOLEAN found = FALSE;
|
|
KIRQL oldIrql;
|
|
|
|
PCI_SLOT_NUMBER pciSlot;
|
|
PCI_SLOT_NUMBER parentSlot;
|
|
ULONG pciBus;
|
|
UCHAR interruptLine;
|
|
UCHAR interruptPin;
|
|
UCHAR parentPin;
|
|
UCHAR classCode;
|
|
UCHAR subClassCode;
|
|
UCHAR flags;
|
|
UCHAR interfaceByte;
|
|
ROUTING_TOKEN routingToken;
|
|
ULONG dummy;
|
|
ULONG bus;
|
|
|
|
|
|
if (Pdo->DriverObject == AcpiDriverObject) {
|
|
|
|
//
|
|
// This is one of our PDOs.
|
|
//
|
|
|
|
ASSERT(((PDEVICE_EXTENSION)Pdo->DeviceExtension)->Flags & DEV_TYPE_PDO);
|
|
ASSERT(((PDEVICE_EXTENSION)Pdo->DeviceExtension)->Signature == ACPI_SIGNATURE);
|
|
|
|
if (((PDEVICE_EXTENSION)Pdo->DeviceExtension)->Flags & DEV_CAP_PCI) {
|
|
|
|
//
|
|
// It's a PCI PDO, which means a root PCI bus,
|
|
// which means that we should just handle this
|
|
// as an ISA device.
|
|
//
|
|
|
|
return STATUS_NOT_FOUND;
|
|
}
|
|
}
|
|
|
|
ASSERT(PciInterfacesInstantiated);
|
|
|
|
*LinkNode = NULL;
|
|
|
|
pciInterface = ((PARBITER_EXTENSION)AcpiArbiter.ArbiterState.Extension)->InterruptRouting;
|
|
|
|
ASSERT(pciInterface);
|
|
|
|
//
|
|
// Call into the PCI driver to find out what we are dealing with.
|
|
//
|
|
|
|
pciBus = (ULONG)-1;
|
|
pciSlot.u.AsULONG = (ULONG)-1;
|
|
status = pciInterface->GetInterruptRouting(Pdo,
|
|
&pciBus,
|
|
&pciSlot.u.AsULONG,
|
|
&interruptLine,
|
|
&interruptPin,
|
|
&classCode,
|
|
&subClassCode,
|
|
&parent,
|
|
&routingToken,
|
|
&flags);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
return STATUS_NOT_FOUND;
|
|
}
|
|
|
|
if ((classCode == PCI_CLASS_MASS_STORAGE_CTLR) &&
|
|
(subClassCode == PCI_SUBCLASS_MSC_IDE_CTLR)) {
|
|
|
|
HalPciInterfaceReadConfig(NULL,
|
|
(UCHAR)pciBus,
|
|
pciSlot.u.AsULONG,
|
|
&interfaceByte,
|
|
FIELD_OFFSET (PCI_COMMON_CONFIG,
|
|
ProgIf),
|
|
1);
|
|
|
|
if ((interfaceByte & 0x5) == 0) {
|
|
|
|
//
|
|
// PCI IDE devices in legacy mode don't use interrupts
|
|
// the PCI way. So bail if this is an IDE device without
|
|
// any native-mode bits set.
|
|
//
|
|
|
|
return STATUS_RESOURCE_REQUIREMENTS_CHANGED;
|
|
}
|
|
}
|
|
|
|
//
|
|
// See if we have cached this lookup.
|
|
//
|
|
|
|
if ((routingToken.LinkNode != 0) ||
|
|
(routingToken.Flags & PCI_STATIC_ROUTING)) {
|
|
|
|
if (routingToken.LinkNode) {
|
|
|
|
*LinkNode = routingToken.LinkNode;
|
|
|
|
} else {
|
|
|
|
*Vector = routingToken.StaticVector;
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// Now look for a parent PCI bus that has a _PRT. We may have to
|
|
// look up the tree a bit.
|
|
//
|
|
|
|
while (TRUE) {
|
|
|
|
//
|
|
// Find the parent's filter
|
|
//
|
|
KeAcquireSpinLock( &AcpiDeviceTreeLock, &oldIrql );
|
|
filter = AcpiGetFilter(AcpiArbiter.DeviceObject, parent);
|
|
KeReleaseSpinLock( &AcpiDeviceTreeLock, oldIrql );
|
|
|
|
if (filter) {
|
|
//
|
|
// This is a PCI bus that we either enumerated or
|
|
// filtered.
|
|
//
|
|
|
|
ASSERT(IsPciBus(filter));
|
|
|
|
filterExtension = filter->DeviceExtension;
|
|
pciBusObj = filterExtension->AcpiObject;
|
|
|
|
//
|
|
// Look for a _PRT for this PCI bus.
|
|
//
|
|
prtObj = ACPIAmliGetNamedChild(pciBusObj, PACKED_PRT);
|
|
|
|
if (prtObj) {
|
|
|
|
//
|
|
// We found the _PRT we are looking for.
|
|
//
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// We didn't find a _PRT. So go up the PCI tree one
|
|
// and look again.
|
|
//
|
|
|
|
bus = (ULONG)-1;
|
|
parentSlot.u.AsULONG = (ULONG)-1;
|
|
status = pciInterface->GetInterruptRouting(parent,
|
|
&bus,
|
|
&parentSlot.u.AsULONG,
|
|
(PUCHAR)&dummy,
|
|
&parentPin,
|
|
&classCode,
|
|
&subClassCode,
|
|
&parent,
|
|
&routingToken,
|
|
(PUCHAR)&dummy);
|
|
|
|
if (!NT_SUCCESS(status) ||
|
|
classCode != PCI_CLASS_BRIDGE_DEV) {
|
|
//
|
|
// The parent was not also a PCI device. So
|
|
// this means that there is no _PRT related to
|
|
// this device. Just return the contents of
|
|
// the Interrupt Line register.
|
|
//
|
|
|
|
*Vector = interruptLine;
|
|
|
|
AcpiInterruptRoutingFailed = TRUE;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
if (subClassCode == PCI_SUBCLASS_BR_PCI_TO_PCI) {
|
|
|
|
//
|
|
// Swizzle the interrupt pin according to
|
|
// the PCI-PCI bridge spec.
|
|
//
|
|
|
|
interruptPin = PciBridgeSwizzle((UCHAR)pciSlot.u.bits.DeviceNumber, interruptPin);
|
|
pciSlot.u.AsULONG = parentSlot.u.AsULONG;
|
|
|
|
} else if (subClassCode == PCI_SUBCLASS_BR_CARDBUS) {
|
|
|
|
//
|
|
// Swizzle the interrupt pin according to
|
|
// the Cardbus bridge spec.
|
|
//
|
|
|
|
interruptPin = parentPin;
|
|
pciSlot.u.AsULONG = parentSlot.u.AsULONG;
|
|
|
|
} else {
|
|
|
|
//
|
|
// Bail.
|
|
//
|
|
|
|
*Vector = interruptLine;
|
|
AcpiInterruptRoutingFailed = TRUE;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
}
|
|
|
|
if (AcpiInterruptRoutingFailed == TRUE) {
|
|
|
|
//
|
|
// We succeeded in finding a _PRT to work with,
|
|
// but we have failed in the past. This situation
|
|
// is unrecoverable because we now have dependencies
|
|
// on IRQ routers that we might now accidentally
|
|
// change
|
|
//
|
|
|
|
KeBugCheckEx(ACPI_BIOS_ERROR,
|
|
ACPI_CANNOT_ROUTE_INTERRUPTS,
|
|
(ULONG_PTR) Pdo,
|
|
(ULONG_PTR)parent,
|
|
(ULONG_PTR)prtObj);
|
|
|
|
}
|
|
|
|
// convert interrupt pin from PCI units to ACPI units
|
|
interruptPin--;
|
|
|
|
DEBUG_PRINT(2, ("PCI Device %p had _ADR of %x\n", Pdo, pciSlot.u.AsULONG));
|
|
DEBUG_PRINT(2, ("This device connected to Pin %x\n", interruptPin));
|
|
DEBUG_PRINT(2, ("prtObj: %p\n", prtObj));
|
|
|
|
//
|
|
// Cycle through all the elements in the _PRT package
|
|
// (each one of which is also a package) looking for
|
|
// the one that describes the link node that we are
|
|
// looking for.
|
|
//
|
|
do {
|
|
status = AMLIEvalPackageElement(prtObj,
|
|
prtElement++,
|
|
&prtData);
|
|
|
|
if (!NT_SUCCESS(status)) break;
|
|
|
|
ASSERT(prtData.dwDataType == OBJTYPE_PKGDATA);
|
|
|
|
if (NT_SUCCESS(AMLIEvalPkgDataElement(&prtData,
|
|
0,
|
|
&adrData))) {
|
|
|
|
if (pciSlot.u.bits.DeviceNumber == (adrData.uipDataValue >> 16)) {
|
|
|
|
if ((adrData.uipDataValue & 0xffff) != 0xffff) {
|
|
////
|
|
//// An _ADR in a _PRT must be of the form xxxxFFFF,
|
|
//// which means that the PCI Device Number is specified,
|
|
//// but the Function Number isn't. If it isn't done this
|
|
//// way, then the machine vendor can introduce
|
|
//// dangerous ambiguities. (Beside that, Pierre makes
|
|
//// Memphis bugcheck if it sees this and I'm trying to
|
|
//// be consistent.) So bugcheck.
|
|
////
|
|
//KeBugCheckEx(ACPI_BIOS_ERROR,
|
|
// ACPI_PRT_HAS_INVALID_FUNCTION_NUMBERS,
|
|
// (ULONG_PTR)prtObj,
|
|
// prtElement,
|
|
// adrData.uipDataValue);
|
|
|
|
|
|
DEBUG_PRINT(0, ("PRT entry has ambiguous address %x\n", adrData.uipDataValue));
|
|
|
|
status = STATUS_INVALID_PARAMETER;
|
|
pciSlot.u.bits.DeviceNumber = (ULONG)(adrData.uipDataValue >> 16) & 0xffff;
|
|
pciSlot.u.bits.FunctionNumber = (ULONG)(adrData.uipDataValue & 0xffff);
|
|
AMLIFreeDataBuffs(&adrData, 1);
|
|
AMLIFreeDataBuffs(&prtData, 1);
|
|
goto AcpiArbCrackPRTError;
|
|
}
|
|
|
|
//
|
|
// This sub-package does refer to the PCI device
|
|
// that we are concerned with. Now look to see if
|
|
// we have found the link node that is connected
|
|
// to the PCI interrupt PIN that this device will trigger.
|
|
//
|
|
// N.B. We only have to compare the top 16 bits
|
|
// because the function number is irrelevent
|
|
// when considering interrupts. We get the
|
|
// pin from config space.
|
|
//
|
|
|
|
if (NT_SUCCESS(AMLIEvalPkgDataElement(&prtData,
|
|
1,
|
|
&pinData))) {
|
|
|
|
if (pinData.uipDataValue == interruptPin) {
|
|
//
|
|
// This is the package that describes the link node we
|
|
// are interested in. Get the name of the link node.
|
|
//
|
|
if (NT_SUCCESS(AMLIEvalPkgDataElement(&prtData,
|
|
2,
|
|
&linkData))) {
|
|
found = TRUE;
|
|
}
|
|
|
|
//
|
|
// Look at the Source Index, too.
|
|
//
|
|
if (NT_SUCCESS(AMLIEvalPkgDataElement(&prtData,
|
|
3,
|
|
&indexData))) {
|
|
found = TRUE;
|
|
}
|
|
}
|
|
AMLIFreeDataBuffs(&pinData, 1);
|
|
}
|
|
}
|
|
AMLIFreeDataBuffs(&adrData, 1);
|
|
}
|
|
|
|
AMLIFreeDataBuffs(&prtData, 1);
|
|
|
|
} while (found == FALSE);
|
|
|
|
status = STATUS_NOT_FOUND;
|
|
|
|
if (found) {
|
|
|
|
//
|
|
// First check to see if linkData is valid. If it is,
|
|
// then we use it.
|
|
//
|
|
if (linkData.dwDataType == OBJTYPE_STRDATA) {
|
|
if (linkData.pbDataBuff) {
|
|
|
|
status = AMLIGetNameSpaceObject(linkData.pbDataBuff,
|
|
prtObj,
|
|
LinkNode,
|
|
0);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
routingToken.LinkNode = *LinkNode;
|
|
routingToken.StaticVector = 0;
|
|
routingToken.Flags = 0;
|
|
|
|
pciInterface->SetInterruptRoutingToken(Pdo,
|
|
&routingToken);
|
|
|
|
goto AcpiArbCrackPRTExit;
|
|
}
|
|
|
|
status = STATUS_OBJECT_NAME_NOT_FOUND;
|
|
goto AcpiArbCrackPRTError;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// If linkData didn't pan out, then use indexData.
|
|
//
|
|
if (indexData.dwDataType == OBJTYPE_INTDATA) {
|
|
//
|
|
// We have an integer which describes the "Global System Interrupt Vector"
|
|
// that this PCI device will trigger.
|
|
//
|
|
*Vector = (ULONG)indexData.uipDataValue;
|
|
|
|
status = STATUS_SUCCESS;
|
|
|
|
routingToken.LinkNode = 0;
|
|
routingToken.StaticVector = *Vector;
|
|
routingToken.Flags = PCI_STATIC_ROUTING;
|
|
|
|
pciInterface->SetInterruptRoutingToken(Pdo,
|
|
&routingToken);
|
|
|
|
goto AcpiArbCrackPRTExit;
|
|
|
|
}
|
|
|
|
status = STATUS_INVALID_IMAGE_FORMAT;
|
|
|
|
AcpiArbCrackPRTExit:
|
|
AMLIFreeDataBuffs(&linkData, 1);
|
|
AMLIFreeDataBuffs(&indexData, 1);
|
|
|
|
}
|
|
else
|
|
|
|
AcpiArbCrackPRTError:
|
|
{
|
|
ANSI_STRING ansiString;
|
|
UNICODE_STRING unicodeString;
|
|
UNICODE_STRING slotName;
|
|
UNICODE_STRING funcName;
|
|
PWCHAR prtEntry[4];
|
|
WCHAR IRQARBname[20];
|
|
WCHAR slotBuff[10];
|
|
WCHAR funcBuff[10];
|
|
|
|
swprintf( IRQARBname, L"IRQARB");
|
|
RtlInitUnicodeString(&slotName, slotBuff);
|
|
RtlInitUnicodeString(&funcName, funcBuff);
|
|
|
|
if (!NT_SUCCESS(RtlIntegerToUnicodeString(pciSlot.u.bits.DeviceNumber, 0, &slotName))) {
|
|
return status;
|
|
}
|
|
|
|
if (!NT_SUCCESS(RtlIntegerToUnicodeString(pciSlot.u.bits.FunctionNumber, 0, &funcName))) {
|
|
return status;
|
|
}
|
|
|
|
prtEntry[0] = IRQARBname;
|
|
prtEntry[1] = slotBuff;
|
|
prtEntry[2] = funcBuff;
|
|
|
|
switch (status) {
|
|
case STATUS_OBJECT_NAME_NOT_FOUND:
|
|
|
|
RtlInitAnsiString(&ansiString,
|
|
linkData.pbDataBuff);
|
|
|
|
if(STATUS_SUCCESS == RtlAnsiStringToUnicodeString(&unicodeString,
|
|
&ansiString,
|
|
TRUE)){
|
|
|
|
prtEntry[3] = unicodeString.Buffer;
|
|
|
|
ACPIWriteEventLogEntry(ACPI_ERR_MISSING_LINK_NODE,
|
|
prtEntry,
|
|
4,
|
|
NULL,
|
|
0);
|
|
|
|
RtlFreeUnicodeString(&unicodeString);
|
|
}
|
|
|
|
DEBUG_PRINT(0, ("Couldn't find link node (%s)\n", linkData.pbDataBuff));
|
|
//KeBugCheckEx(ACPI_BIOS_ERROR,
|
|
// ACPI_PRT_CANNOT_FIND_LINK_NODE,
|
|
// (ULONG_PTR)Pdo,
|
|
// (ULONG_PTR)linkData.pbDataBuff,
|
|
// (ULONG_PTR)prtObj);
|
|
|
|
break;
|
|
|
|
case STATUS_NOT_FOUND:
|
|
|
|
ACPIWriteEventLogEntry(ACPI_ERR_MISSING_PRT_ENTRY,
|
|
prtEntry,
|
|
3,
|
|
NULL,
|
|
0);
|
|
|
|
DEBUG_PRINT(0, ("The ACPI _PRT package didn't contain a mapping for the PCI\n"));
|
|
DEBUG_PRINT(0, ("device at _ADR %x\n", pciSlot.u.AsULONG));
|
|
//KeBugCheckEx(ACPI_BIOS_ERROR,
|
|
// ACPI_PRT_CANNOT_FIND_DEVICE_ENTRY,
|
|
// (ULONG_PTR)Pdo,
|
|
// pciSlot.u.AsULONG,
|
|
// (ULONG_PTR)prtObj);
|
|
break;
|
|
|
|
case STATUS_INVALID_PARAMETER:
|
|
|
|
ACPIWriteEventLogEntry(ACPI_ERR_AMBIGUOUS_DEVICE_ADDRESS,
|
|
prtEntry,
|
|
3,
|
|
NULL,
|
|
0);
|
|
break;
|
|
}
|
|
|
|
status = STATUS_UNSUCCESSFUL;
|
|
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
PDEVICE_OBJECT
|
|
AcpiGetFilter(
|
|
IN PDEVICE_OBJECT Root,
|
|
IN PDEVICE_OBJECT Pdo
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine takes a PDO for a device and returns the
|
|
DO of the filter that ACPI has slapped onto it. In the
|
|
case that this PDO belongs to the ACPI driver, then
|
|
it is returned.
|
|
|
|
Arguments:
|
|
|
|
Root - The device object that we are using as the
|
|
root of the search.
|
|
|
|
Pdo - The PDO of the device who's filter we seek
|
|
|
|
Return Value:
|
|
|
|
a DEVICE_OBJECT, if ACPI is filtering this Pdo, NULL otherwise
|
|
|
|
--*/
|
|
{
|
|
PDEVICE_EXTENSION deviceExtension;
|
|
PDEVICE_EXTENSION childExtension;
|
|
PDEVICE_EXTENSION firstChild;
|
|
PDEVICE_OBJECT filter;
|
|
|
|
deviceExtension = Root->DeviceExtension;
|
|
|
|
//
|
|
// If Root is the filter, we are done.
|
|
//
|
|
if (((deviceExtension->Flags & DEV_TYPE_PDO) ||
|
|
(deviceExtension->Flags & DEV_TYPE_FILTER)) &&
|
|
(deviceExtension->PhysicalDeviceObject == Pdo)) {
|
|
|
|
ASSERT(Root->Type == IO_TYPE_DEVICE);
|
|
|
|
return Root;
|
|
}
|
|
|
|
//
|
|
// Return NULL if this device has no children,
|
|
// (which is signified by the ChildDeviceList pointer
|
|
// pointing to itself.
|
|
//
|
|
if (deviceExtension->ChildDeviceList.Flink ==
|
|
(PVOID)&(deviceExtension->ChildDeviceList.Flink)) {
|
|
|
|
return NULL;
|
|
}
|
|
|
|
firstChild = (PDEVICE_EXTENSION) CONTAINING_RECORD(
|
|
deviceExtension->ChildDeviceList.Flink,
|
|
DEVICE_EXTENSION,
|
|
SiblingDeviceList );
|
|
|
|
childExtension = firstChild;
|
|
|
|
do {
|
|
//
|
|
// Make sure the device extension is complete.
|
|
//
|
|
if (childExtension->DeviceObject) {
|
|
filter = AcpiGetFilter(childExtension->DeviceObject, Pdo);
|
|
|
|
if (filter) {
|
|
return filter;
|
|
}
|
|
}
|
|
|
|
childExtension = (PDEVICE_EXTENSION) CONTAINING_RECORD(
|
|
childExtension->SiblingDeviceList.Flink,
|
|
DEVICE_EXTENSION,
|
|
SiblingDeviceList );
|
|
|
|
} while (childExtension != firstChild);
|
|
|
|
//
|
|
// Must not be on this branch...
|
|
//
|
|
|
|
return NULL;
|
|
}
|
|
|
|
BOOLEAN
|
|
LinkNodeInUse(
|
|
IN PARBITER_INSTANCE Arbiter,
|
|
IN PNSOBJ LinkNode,
|
|
IN OUT ULONG *Irq, OPTIONAL
|
|
IN OUT UCHAR *Flags OPTIONAL
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine indicates whether a link node is current
|
|
in use and, if so, returns the IRQ that it is currently
|
|
connected to.
|
|
|
|
Arguments:
|
|
|
|
Arbiter - current arbiter state
|
|
|
|
LinkNode - link node in question
|
|
|
|
Irq - "Global System Interrupt Vector" that
|
|
the link node is currently using.
|
|
|
|
Flags - flags associated with the vector that
|
|
the link node is connected to.
|
|
|
|
Return Value:
|
|
|
|
TRUE if the link node is currently being used.
|
|
|
|
--*/
|
|
{
|
|
|
|
PLIST_ENTRY linkNodes;
|
|
PLINK_NODE linkNode;
|
|
NTSTATUS status;
|
|
|
|
PAGED_CODE();
|
|
|
|
ASSERT(LinkNode);
|
|
|
|
linkNodes = &((PARBITER_EXTENSION)(Arbiter->Extension))->LinkNodeHead;
|
|
|
|
if (IsListEmpty(linkNodes)) {
|
|
//
|
|
// There are no link nodes in use.
|
|
//
|
|
DEBUG_PRINT(3, ("LinkNode list empty\n"));
|
|
return FALSE;
|
|
}
|
|
|
|
linkNode = (PLINK_NODE)linkNodes->Flink;
|
|
|
|
while (linkNode != (PLINK_NODE)linkNodes) {
|
|
//
|
|
// Is this the node we were looking for?
|
|
//
|
|
if (linkNode->NameSpaceObject == LinkNode) {
|
|
|
|
if((LONG)(linkNode->ReferenceCount + linkNode->TempRefCount) > 0) {
|
|
|
|
//
|
|
// This link node is on the list and it is currently referenced.
|
|
//
|
|
|
|
if (Irq) *Irq = (ULONG)linkNode->TempIrq;
|
|
if (Flags) *Flags = linkNode->Flags;
|
|
|
|
DEBUG_PRINT(3, ("Link Node %p is in use\n", LinkNode));
|
|
return TRUE;
|
|
|
|
} else {
|
|
DEBUG_PRINT(3, ("Link Node %p is currently unreferenced\n", LinkNode));
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
linkNode = (PLINK_NODE)linkNode->List.Flink;
|
|
}
|
|
|
|
DEBUG_PRINT(3, ("Didn't find our link node (%p) on the Link Node List\n", LinkNode));
|
|
//
|
|
// Didn't ever find the link node we were looking for.
|
|
//
|
|
return FALSE;
|
|
}
|
|
|
|
NTSTATUS
|
|
GetLinkNodeFlags(
|
|
IN PARBITER_INSTANCE Arbiter,
|
|
IN PNSOBJ LinkNode,
|
|
IN OUT UCHAR *Flags
|
|
)
|
|
{
|
|
NTSTATUS status;
|
|
BOOLEAN inUse;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// This guarantees that LinkNodeInUse will succeed
|
|
// and will contain the valid flags.
|
|
//
|
|
status = AcpiArbReferenceLinkNode(Arbiter,
|
|
LinkNode,
|
|
0);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
return status;
|
|
}
|
|
|
|
inUse = LinkNodeInUse(Arbiter,
|
|
LinkNode,
|
|
NULL,
|
|
Flags);
|
|
|
|
ASSERT(inUse);
|
|
|
|
//
|
|
// Set the state back to the way we found it.
|
|
//
|
|
|
|
status = AcpiArbDereferenceLinkNode(Arbiter,
|
|
LinkNode);
|
|
|
|
ASSERT(NT_SUCCESS(status));
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
AcpiArbReferenceLinkNode(
|
|
IN PARBITER_INSTANCE Arbiter,
|
|
IN PNSOBJ LinkNode,
|
|
IN ULONG Irq
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine keeps two reference counts. The first
|
|
is a permanent count, representing hardware resources
|
|
that have been committed. The second is a delta
|
|
representing what is currently under consideration.
|
|
|
|
Arguments:
|
|
|
|
Arbiter - current arbiter state
|
|
|
|
LinkNode - link node in question
|
|
|
|
Irq - "Global System Interrupt Vector" that
|
|
the link node is connected to.
|
|
|
|
Permanently - indicates whether this reference is
|
|
for a committed allocation
|
|
|
|
|
|
Return Value:
|
|
|
|
status
|
|
|
|
--*/
|
|
{
|
|
PCM_RESOURCE_LIST resList = NULL;
|
|
PLIST_ENTRY linkNodes;
|
|
PLINK_NODE linkNode;
|
|
BOOLEAN found = FALSE;
|
|
NTSTATUS status;
|
|
UCHAR flags;
|
|
|
|
PAGED_CODE();
|
|
|
|
DEBUG_PRINT(3, ("Referencing link node %p, Irq: %x\n",
|
|
LinkNode,
|
|
Irq));
|
|
|
|
ASSERT(LinkNode);
|
|
|
|
linkNodes = &((PARBITER_EXTENSION)(Arbiter->Extension))->LinkNodeHead;
|
|
linkNode = (PLINK_NODE)linkNodes->Flink;
|
|
|
|
//
|
|
// Search to see if we are already know about this link node.
|
|
//
|
|
while (linkNode != (PLINK_NODE)linkNodes) {
|
|
|
|
if (linkNode->NameSpaceObject == LinkNode) {
|
|
|
|
found = TRUE;
|
|
break;
|
|
}
|
|
|
|
linkNode = (PLINK_NODE)linkNode->List.Flink;
|
|
}
|
|
|
|
//
|
|
// If not, then we need to keep track of it. And
|
|
// the hardware needs to be made to match it.
|
|
//
|
|
if (!found) {
|
|
|
|
//
|
|
// This is the first permanent reference. So
|
|
// program the link node hardware.
|
|
//
|
|
|
|
linkNode = ExAllocatePoolWithTag(NonPagedPool, sizeof(LINK_NODE), ACPI_ARBITER_POOLTAG);
|
|
|
|
if (!linkNode) {
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
RtlZeroMemory(linkNode, sizeof(LINK_NODE));
|
|
|
|
linkNode->NameSpaceObject = LinkNode;
|
|
linkNode->CurrentIrq = Irq;
|
|
linkNode->TempIrq = Irq;
|
|
linkNode->AttachedDevices.Next = (PSINGLE_LIST_ENTRY)&linkNode->AttachedDevices;
|
|
|
|
InsertTailList(linkNodes, ((PLIST_ENTRY)(linkNode)));
|
|
|
|
//
|
|
// Figure out what the flags ought to be.
|
|
//
|
|
|
|
status = AcpiArbGetLinkNodeOptions(LinkNode,
|
|
&resList,
|
|
&flags);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
ExFreePool(resList); // not actually needed here
|
|
|
|
//
|
|
// Record the flags associated with this link node.
|
|
//
|
|
|
|
linkNode->Flags = flags;
|
|
|
|
} else {
|
|
|
|
ASSERT(NT_SUCCESS(status));
|
|
|
|
//
|
|
// Something is wrong. Make up reasonable flags.
|
|
//
|
|
|
|
linkNode->Flags = VECTOR_LEVEL | VECTOR_ACTIVE_LOW;
|
|
}
|
|
|
|
DEBUG_PRINT(3, ("Link node object connected to vector %x\n", Irq));
|
|
|
|
}
|
|
#if DBG
|
|
else {
|
|
|
|
if (!((linkNode->ReferenceCount == 0) &&
|
|
(linkNode->TempRefCount == 0))) {
|
|
|
|
//
|
|
// Make sure that we maintain consistency
|
|
// with the flags.
|
|
//
|
|
|
|
//
|
|
// Check to see that the link node hasn't changed.
|
|
//
|
|
status = AcpiArbGetLinkNodeOptions(LinkNode,
|
|
&resList,
|
|
&flags);
|
|
|
|
if (resList) ExFreePool(resList); // not actually needed here
|
|
ASSERT(NT_SUCCESS(status));
|
|
ASSERT(flags == linkNode->Flags);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
DEBUG_PRINT(3, (" %d:%d\n", linkNode->ReferenceCount, linkNode->TempRefCount));
|
|
|
|
//
|
|
// Increase its reference count.
|
|
//
|
|
|
|
linkNode->TempIrq = Irq;
|
|
linkNode->TempRefCount++;
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
AcpiArbDereferenceLinkNode(
|
|
IN PARBITER_INSTANCE Arbiter,
|
|
IN PNSOBJ LinkNode
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is the converse of the one above.
|
|
|
|
Arguments:
|
|
|
|
Arbiter - current arbiter state
|
|
|
|
LinkNode - link node in question
|
|
|
|
Permanently - indicates whether this reference is
|
|
for a committed allocation
|
|
|
|
Return Value:
|
|
|
|
status
|
|
|
|
--*/
|
|
{
|
|
PSINGLE_LIST_ENTRY attachedDev;
|
|
PLIST_ENTRY linkNodes;
|
|
PLINK_NODE linkNode;
|
|
BOOLEAN found = FALSE;
|
|
|
|
PAGED_CODE();
|
|
|
|
ASSERT(LinkNode);
|
|
|
|
linkNodes = &((PARBITER_EXTENSION)(Arbiter->Extension))->LinkNodeHead;
|
|
linkNode = (PLINK_NODE)linkNodes->Flink;
|
|
|
|
//
|
|
// Search for this link node.
|
|
//
|
|
|
|
while (linkNode != (PLINK_NODE)linkNodes) {
|
|
|
|
if (linkNode->NameSpaceObject == LinkNode) {
|
|
|
|
found = TRUE;
|
|
break;
|
|
}
|
|
|
|
linkNode = (PLINK_NODE)linkNode->List.Flink;
|
|
}
|
|
|
|
ASSERT(found);
|
|
DEBUG_PRINT(3, ("Dereferencing link node %p %d:%d\n", LinkNode, linkNode->ReferenceCount, linkNode->TempRefCount));
|
|
|
|
linkNode->TempRefCount--;
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
ClearTempLinkNodeCounts(
|
|
IN PARBITER_INSTANCE Arbiter
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine resets all the temporary counts (deltas)
|
|
to zero because the allocations being considered are
|
|
being thrown away instead of being committed.
|
|
|
|
Arguments:
|
|
|
|
Arbiter - current arbiter state
|
|
|
|
Return Value:
|
|
|
|
status
|
|
|
|
--*/
|
|
{
|
|
PLIST_ENTRY linkNodes;
|
|
PLINK_NODE linkNode;
|
|
|
|
PAGED_CODE();
|
|
|
|
linkNodes = &((PARBITER_EXTENSION)(Arbiter->Extension))->LinkNodeHead;
|
|
|
|
linkNode = (PLINK_NODE)linkNodes->Flink;
|
|
|
|
//
|
|
// Run through the link nodes.
|
|
//
|
|
|
|
while (linkNode != (PLINK_NODE)linkNodes) {
|
|
|
|
linkNode->TempRefCount = 0;
|
|
linkNode->TempIrq = linkNode->CurrentIrq;
|
|
|
|
linkNode = (PLINK_NODE)linkNode->List.Flink;
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
MakeTempLinkNodeCountsPermanent(
|
|
IN PARBITER_INSTANCE Arbiter
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine reconciles the temporary and
|
|
permanent references because the resources being
|
|
considered are being committed.
|
|
|
|
Arguments:
|
|
|
|
Arbiter - current arbiter state
|
|
|
|
Return Value:
|
|
|
|
status
|
|
|
|
--*/
|
|
{
|
|
CM_PARTIAL_RESOURCE_DESCRIPTOR irqDesc;
|
|
PLIST_ENTRY linkNodes;
|
|
PLINK_NODE linkNode, nextNode;
|
|
UCHAR flags;
|
|
PNSOBJ dis;
|
|
|
|
PAGED_CODE();
|
|
DEBUG_PRINT(3, ("MakeTempLinkNodeCountsPermanent\n"));
|
|
|
|
//
|
|
// Run through the link nodes.
|
|
//
|
|
|
|
linkNodes = &((PARBITER_EXTENSION)(Arbiter->Extension))->LinkNodeHead;
|
|
linkNode = (PLINK_NODE)linkNodes->Flink;
|
|
|
|
while (linkNode != (PLINK_NODE)linkNodes) {
|
|
|
|
nextNode = (PLINK_NODE)linkNode->List.Flink;
|
|
|
|
DEBUG_PRINT(3, ("LinkNode: %p -- Perm: %d, Temp: %d\n",
|
|
linkNode,
|
|
linkNode->ReferenceCount,
|
|
linkNode->TempRefCount));
|
|
|
|
//
|
|
// Attempt to sanity check this link node.
|
|
//
|
|
|
|
ASSERT(linkNode);
|
|
ASSERT(linkNode->List.Flink);
|
|
ASSERT(linkNode->ReferenceCount <= 70);
|
|
ASSERT(linkNode->TempRefCount <= 70);
|
|
ASSERT(linkNode->TempRefCount >= -70);
|
|
ASSERT(linkNode->CurrentIrq < 0x80000000);
|
|
ASSERT((linkNode->Flags & ~(VECTOR_MODE | VECTOR_POLARITY)) == 0);
|
|
|
|
//
|
|
// Program the link node if either the previous reference count
|
|
// was 0 or if the previous IRQ was different. *And* the current
|
|
// reference count is non-zero.
|
|
//
|
|
|
|
if (((linkNode->ReferenceCount == 0) ||
|
|
(linkNode->CurrentIrq != linkNode->TempIrq)) &&
|
|
((linkNode->ReferenceCount + linkNode->TempRefCount) != 0)) {
|
|
|
|
irqDesc.Type = CmResourceTypeInterrupt;
|
|
irqDesc.ShareDisposition = CmResourceShareShared;
|
|
irqDesc.Flags = CM_RESOURCE_INTERRUPT_LEVEL_SENSITIVE;
|
|
irqDesc.u.Interrupt.Level = (ULONG)linkNode->TempIrq;
|
|
irqDesc.u.Interrupt.Vector = (ULONG)linkNode->TempIrq;
|
|
irqDesc.u.Interrupt.Affinity = 0xffffffff;
|
|
|
|
AcpiArbSetLinkNodeIrq(linkNode->NameSpaceObject,
|
|
&irqDesc);
|
|
|
|
}
|
|
|
|
if ((linkNode->ReferenceCount + linkNode->TempRefCount) == 0) {
|
|
|
|
//
|
|
// This link node has no more references. Disable it.
|
|
//
|
|
|
|
dis = ACPIAmliGetNamedChild(linkNode->NameSpaceObject, PACKED_DIS);
|
|
if (dis) {
|
|
AMLIEvalNameSpaceObject(dis, NULL, 0, NULL);
|
|
}
|
|
}
|
|
|
|
|
|
linkNode->ReferenceCount = linkNode->ReferenceCount +
|
|
linkNode->TempRefCount;
|
|
linkNode->TempRefCount = 0;
|
|
linkNode->CurrentIrq = linkNode->TempIrq;
|
|
|
|
linkNode = nextNode;
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
#ifdef DBG
|
|
VOID
|
|
TrackDevicesConnectedToLinkNode(
|
|
IN PNSOBJ LinkNode,
|
|
IN PDEVICE_OBJECT Pdo
|
|
)
|
|
{
|
|
PLINK_NODE_ATTACHED_DEVICES attachedDevs, newPdo;
|
|
PLIST_ENTRY linkNodes;
|
|
PLINK_NODE linkNode, nextNode;
|
|
BOOLEAN found = FALSE;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Run through the link nodes.
|
|
//
|
|
|
|
linkNodes = &((PARBITER_EXTENSION)(AcpiArbiter.ArbiterState.Extension))->LinkNodeHead;
|
|
linkNode = (PLINK_NODE)linkNodes->Flink;
|
|
|
|
while (linkNode != (PLINK_NODE)linkNodes) {
|
|
if (linkNode->NameSpaceObject == LinkNode) {
|
|
|
|
found = TRUE;
|
|
break;
|
|
}
|
|
|
|
linkNode = (PLINK_NODE)linkNode->List.Flink;
|
|
}
|
|
|
|
if (found) {
|
|
|
|
attachedDevs = (PLINK_NODE_ATTACHED_DEVICES)linkNode->AttachedDevices.Next;
|
|
found = FALSE;
|
|
|
|
while (attachedDevs != (PLINK_NODE_ATTACHED_DEVICES)&linkNode->AttachedDevices.Next) {
|
|
|
|
if (attachedDevs->Pdo == Pdo) {
|
|
found = TRUE;
|
|
break;
|
|
}
|
|
|
|
attachedDevs = (PLINK_NODE_ATTACHED_DEVICES)attachedDevs->List.Next;
|
|
}
|
|
|
|
if (!found) {
|
|
|
|
newPdo = ExAllocatePoolWithTag(PagedPool,
|
|
sizeof(LINK_NODE_ATTACHED_DEVICES),
|
|
ACPI_ARBITER_POOLTAG);
|
|
if (!newPdo) {
|
|
return;
|
|
}
|
|
|
|
RtlZeroMemory(newPdo, sizeof(LINK_NODE_ATTACHED_DEVICES));
|
|
|
|
newPdo->Pdo = Pdo;
|
|
|
|
PushEntryList(&linkNode->AttachedDevices,
|
|
(PSINGLE_LIST_ENTRY)newPdo);
|
|
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
typedef enum {
|
|
RestoreStateInitial,
|
|
RestoreStateDisabled,
|
|
RestoreStateEnabled
|
|
} RESTORE_IRQ_STATE, *PRESTORE_IRQ_STATE;
|
|
|
|
typedef struct {
|
|
CM_PARTIAL_RESOURCE_DESCRIPTOR IrqDesc;
|
|
PLIST_ENTRY LinkNodes;
|
|
PLINK_NODE LinkNode;
|
|
RESTORE_IRQ_STATE State;
|
|
KSPIN_LOCK SpinLock;
|
|
KIRQL OldIrql;
|
|
BOOLEAN CompletingSetLink;
|
|
LONG RunCompletion;
|
|
PFNACB CompletionHandler;
|
|
PVOID CompletionContext;
|
|
} RESTORE_ROUTING_STATE, *PRESTORE_ROUTING_STATE;
|
|
|
|
NTSTATUS
|
|
IrqArbRestoreIrqRouting(
|
|
PFNACB CompletionHandler,
|
|
PVOID CompletionContext
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will set all the IRQ router settings
|
|
to whatever is described in the Link Node list.
|
|
This is useful when the machine is coming out
|
|
of hibernation.
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
status
|
|
|
|
Notes:
|
|
|
|
This function is expected to run at DPC level
|
|
during machine wakeup. It is assumed that no
|
|
other part of the arbiter code will be running
|
|
at that time. Since we can't wait for the
|
|
arbiter lock at DPC level, we will have to
|
|
assume it is not taken.
|
|
|
|
--*/
|
|
{
|
|
PRESTORE_ROUTING_STATE state;
|
|
PARBITER_INSTANCE arbiter;
|
|
NTSTATUS status;
|
|
|
|
//
|
|
// First check to see if there is any work to do.
|
|
//
|
|
|
|
if (HalPicStateIntact()) {
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
state = ExAllocatePoolWithTag(NonPagedPool,
|
|
sizeof(RESTORE_ROUTING_STATE),
|
|
ACPI_ARBITER_POOLTAG);
|
|
|
|
if (!state) {
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
RtlZeroMemory(state, sizeof(RESTORE_ROUTING_STATE));
|
|
|
|
state->State = RestoreStateInitial;
|
|
state->RunCompletion = INITIAL_RUN_COMPLETION;
|
|
state->CompletionHandler = CompletionHandler;
|
|
state->CompletionContext = CompletionContext;
|
|
state->IrqDesc.Type = CmResourceTypeInterrupt;
|
|
state->IrqDesc.ShareDisposition = CmResourceShareShared;
|
|
state->IrqDesc.Flags = CM_RESOURCE_INTERRUPT_LEVEL_SENSITIVE;
|
|
state->IrqDesc.u.Interrupt.Affinity = 0xffffffff;
|
|
|
|
arbiter = &AcpiArbiter.ArbiterState;
|
|
state->LinkNodes = &((PARBITER_EXTENSION)(arbiter->Extension))->LinkNodeHead;
|
|
|
|
state->LinkNode = (PLINK_NODE)state->LinkNodes->Flink;
|
|
|
|
KeInitializeSpinLock(&state->SpinLock);
|
|
|
|
return IrqArbRestoreIrqRoutingWorker(state->LinkNode->NameSpaceObject,
|
|
STATUS_SUCCESS,
|
|
NULL,
|
|
(PVOID)state
|
|
);
|
|
}
|
|
|
|
NTSTATUS
|
|
EXPORT
|
|
IrqArbRestoreIrqRoutingWorker(
|
|
IN PNSOBJ AcpiObject,
|
|
IN NTSTATUS Status,
|
|
IN POBJDATA Result,
|
|
IN PVOID Context
|
|
)
|
|
{
|
|
NTSTATUS status = Status;
|
|
PDEVICE_EXTENSION deviceExtension;
|
|
PRESTORE_ROUTING_STATE state;
|
|
|
|
state = (PRESTORE_ROUTING_STATE)Context;
|
|
|
|
//
|
|
// Entering this function twice with the same state
|
|
// means that we need to run the completion routine.
|
|
//
|
|
|
|
InterlockedIncrement(&state->RunCompletion);
|
|
|
|
switch (state->State) {
|
|
case RestoreStateInitial:
|
|
|
|
|
|
state->State = RestoreStateDisabled;
|
|
deviceExtension = ACPIInternalGetDeviceExtension( AcpiArbiter.DeviceObject );
|
|
status = DisableLinkNodesAsync(
|
|
deviceExtension->AcpiObject,
|
|
IrqArbRestoreIrqRoutingWorker,
|
|
(PVOID)state);
|
|
|
|
if (status == STATUS_PENDING) {
|
|
return status;
|
|
}
|
|
|
|
//
|
|
// Fall through
|
|
//
|
|
|
|
case RestoreStateDisabled:
|
|
|
|
KeAcquireSpinLock(&state->SpinLock,
|
|
&state->OldIrql);
|
|
|
|
while (state->LinkNode != (PLINK_NODE)state->LinkNodes) {
|
|
|
|
if (state->LinkNode->ReferenceCount > 0) {
|
|
|
|
//
|
|
// Program the link node.
|
|
//
|
|
state->IrqDesc.u.Interrupt.Level = (ULONG)state->LinkNode->CurrentIrq;
|
|
state->IrqDesc.u.Interrupt.Vector = (ULONG)state->LinkNode->CurrentIrq;
|
|
|
|
if (!state->CompletingSetLink) {
|
|
|
|
status = AcpiArbSetLinkNodeIrqAsync(state->LinkNode->NameSpaceObject,
|
|
&state->IrqDesc,
|
|
IrqArbRestoreIrqRoutingWorker,
|
|
(PVOID)state
|
|
);
|
|
|
|
if (status == STATUS_PENDING) {
|
|
state->CompletingSetLink = TRUE;
|
|
KeReleaseSpinLock(&state->SpinLock,
|
|
state->OldIrql);
|
|
return status;
|
|
}
|
|
}
|
|
}
|
|
|
|
state->CompletingSetLink = FALSE;
|
|
state->LinkNode = (PLINK_NODE)state->LinkNode->List.Flink;
|
|
}
|
|
|
|
state->State = RestoreStateEnabled;
|
|
|
|
KeReleaseSpinLock(&state->SpinLock,
|
|
state->OldIrql);
|
|
|
|
case RestoreStateEnabled:
|
|
|
|
//
|
|
// Now that we are done programming all the link nodes,
|
|
// we need to restore the ELCR and unmask all the
|
|
// device interrupts.
|
|
//
|
|
|
|
HalRestorePicState();
|
|
|
|
if (state->RunCompletion) {
|
|
|
|
state->CompletionHandler(AcpiObject,
|
|
status,
|
|
NULL,
|
|
state->CompletionContext
|
|
);
|
|
}
|
|
|
|
}
|
|
|
|
ExFreePool(state);
|
|
return status;
|
|
}
|
|
|
|
typedef enum {
|
|
DisableStateInitial,
|
|
DisableStateGotHid,
|
|
DisableStateRanDis,
|
|
DisableStateGetChild,
|
|
DisableStateRecursing
|
|
} DISABLE_LINK_NODES_STATE;
|
|
|
|
typedef struct {
|
|
DISABLE_LINK_NODES_STATE State;
|
|
PNSOBJ RootDevice;
|
|
PUCHAR Hid;
|
|
PNSOBJ Dis;
|
|
PNSOBJ Sibling;
|
|
PNSOBJ NextSibling;
|
|
LONG RunCompletionHandler;
|
|
PFNACB CompletionHandler;
|
|
PVOID CompletionContext;
|
|
} DISABLE_LINK_NODES_CONTEXT, *PDISABLE_LINK_NODES_CONTEXT;
|
|
|
|
NTSTATUS
|
|
DisableLinkNodesAsync(
|
|
IN PNSOBJ Root,
|
|
IN PFNACB CompletionHandler,
|
|
IN PVOID CompletionContext
|
|
)
|
|
{
|
|
PDISABLE_LINK_NODES_CONTEXT context;
|
|
NTSTATUS status;
|
|
|
|
context = ExAllocatePoolWithTag(NonPagedPool,
|
|
sizeof(DISABLE_LINK_NODES_CONTEXT),
|
|
ACPI_ARBITER_POOLTAG);
|
|
|
|
if (!context) {
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
RtlZeroMemory(context, sizeof(DISABLE_LINK_NODES_CONTEXT));
|
|
|
|
context->State = DisableStateInitial;
|
|
context->RootDevice = Root;
|
|
context->CompletionHandler = CompletionHandler;
|
|
context->CompletionContext = CompletionContext;
|
|
context->RunCompletionHandler = INITIAL_RUN_COMPLETION;
|
|
|
|
return DisableLinkNodesAsyncWorker(Root,
|
|
STATUS_SUCCESS,
|
|
NULL,
|
|
(PVOID)context
|
|
);
|
|
}
|
|
|
|
NTSTATUS
|
|
EXPORT
|
|
DisableLinkNodesAsyncWorker(
|
|
IN PNSOBJ AcpiObject,
|
|
IN NTSTATUS Status,
|
|
IN POBJDATA Result,
|
|
IN PVOID Context
|
|
)
|
|
{
|
|
PDISABLE_LINK_NODES_CONTEXT context;
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
PNSOBJ sib;
|
|
PNSOBJ dis;
|
|
|
|
context = (PDISABLE_LINK_NODES_CONTEXT)Context;
|
|
ASSERT(context);
|
|
|
|
//
|
|
// Entering this function twice with the same state
|
|
// means that we need to run the completion routine.
|
|
//
|
|
|
|
InterlockedIncrement(&context->RunCompletionHandler);
|
|
|
|
DisableLinkNodeStartState:
|
|
|
|
switch (context->State) {
|
|
case DisableStateInitial:
|
|
|
|
//
|
|
// Get the _HID of this device to see if
|
|
// it is a link node.
|
|
//
|
|
|
|
context->State = DisableStateGotHid;
|
|
status = ACPIGetNSPnpIDAsync(
|
|
context->RootDevice,
|
|
DisableLinkNodesAsyncWorker,
|
|
context,
|
|
&context->Hid,
|
|
NULL);
|
|
|
|
if (status == STATUS_PENDING) {
|
|
return status;
|
|
} else if (!NT_SUCCESS(status)) {
|
|
|
|
context->State = DisableStateGetChild;
|
|
goto DisableLinkNodeStartState;
|
|
}
|
|
|
|
//
|
|
// Fall through to next state.
|
|
//
|
|
|
|
case DisableStateGotHid:
|
|
|
|
context->State = DisableStateGetChild;
|
|
|
|
if (context->Hid) {
|
|
|
|
if (strstr(context->Hid, LINK_NODE_PNP_ID)) {
|
|
|
|
//
|
|
// We found a _HID of PNP0C0F, which is a
|
|
// link node. So disable it.
|
|
//
|
|
|
|
dis = ACPIAmliGetNamedChild(context->RootDevice,
|
|
PACKED_DIS);
|
|
|
|
if (dis) {
|
|
|
|
context->State = DisableStateRanDis;
|
|
status = AMLIAsyncEvalObject(dis,
|
|
NULL,
|
|
0,
|
|
NULL,
|
|
DisableLinkNodesAsyncWorker,
|
|
(PVOID)context
|
|
);
|
|
|
|
if (status == STATUS_PENDING) {
|
|
return status;
|
|
|
|
} else if (NT_SUCCESS(status)) {
|
|
|
|
//
|
|
// We're done. Jump to the cleanup code.
|
|
//
|
|
break;
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// Link nodes must be disablable.
|
|
//
|
|
|
|
KeBugCheckEx(ACPI_BIOS_ERROR,
|
|
ACPI_LINK_NODE_CANNOT_BE_DISABLED,
|
|
(ULONG_PTR)context->RootDevice,
|
|
0,
|
|
0);
|
|
}
|
|
}
|
|
}
|
|
|
|
case DisableStateGetChild:
|
|
|
|
//
|
|
// Recurse to all of the children. Propagate any errors,
|
|
// but don't stop for them.
|
|
//
|
|
|
|
context->Sibling = NSGETFIRSTCHILD(context->RootDevice);
|
|
|
|
if (!context->Sibling) {
|
|
status = STATUS_SUCCESS;
|
|
break;
|
|
}
|
|
|
|
context->State = DisableStateRecursing;
|
|
|
|
case DisableStateRecursing:
|
|
|
|
while (context->Sibling) {
|
|
|
|
//
|
|
// Cycle through all the children (child and its
|
|
// siblings)
|
|
//
|
|
|
|
sib = context->Sibling;
|
|
context->Sibling = NSGETNEXTSIBLING(context->Sibling);
|
|
|
|
switch (NSGETOBJTYPE(sib)) {
|
|
case OBJTYPE_DEVICE:
|
|
|
|
//
|
|
// This name child of Root is also a device.
|
|
// Recurse.
|
|
//
|
|
|
|
status = DisableLinkNodesAsync(sib,
|
|
DisableLinkNodesAsyncWorker,
|
|
(PVOID)context);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (status == STATUS_PENDING) {
|
|
return status;
|
|
}
|
|
}
|
|
|
|
case DisableStateRanDis:
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Done. Clean up and return.
|
|
//
|
|
|
|
if (context->RunCompletionHandler) {
|
|
|
|
context->CompletionHandler(context->RootDevice,
|
|
status,
|
|
NULL,
|
|
context->CompletionContext
|
|
);
|
|
}
|
|
|
|
if (context->Hid) ExFreePool(context->Hid);
|
|
ExFreePool(context);
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
GetIsaVectorFlags(
|
|
IN ULONG Vector,
|
|
IN OUT UCHAR *Flags
|
|
)
|
|
{
|
|
ULONG i;
|
|
ULONG irq;
|
|
UCHAR flags;
|
|
NTSTATUS returnStatus = STATUS_NOT_FOUND;
|
|
NTSTATUS status;
|
|
|
|
PAGED_CODE();
|
|
|
|
for (i = 0; i < ISA_PIC_VECTORS; i++) {
|
|
|
|
status = LookupIsaVectorOverride(i,
|
|
&irq,
|
|
&flags);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
if (irq == Vector) {
|
|
|
|
//
|
|
// This vector's flags have been overriden.
|
|
//
|
|
|
|
*Flags = flags;
|
|
|
|
ASSERT((*Flags & ~(VECTOR_MODE | VECTOR_POLARITY | VECTOR_TYPE)) == 0);
|
|
returnStatus = STATUS_SUCCESS;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return returnStatus;
|
|
}
|
|
|
|
NTSTATUS
|
|
LookupIsaVectorOverride(
|
|
IN ULONG IsaVector,
|
|
IN OUT ULONG *RedirectionVector OPTIONAL,
|
|
IN OUT UCHAR *Flags OPTIONAL
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function looks to see if this vector has
|
|
been overridden in the MAPIC table.
|
|
|
|
Arguments:
|
|
|
|
IsaVector - ISA vector
|
|
|
|
RedirectionVector - vector that the ISA vector
|
|
will actually trigger
|
|
|
|
Flags - flags in the override table
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS if the ISA vector exists in the
|
|
MAPIC table
|
|
|
|
STATUS_NOT_FOUND if it doesn't
|
|
|
|
--*/
|
|
{
|
|
PAPICTABLE ApicEntry;
|
|
PISA_VECTOR IsaEntry;
|
|
PUCHAR TraversePtr;
|
|
PMAPIC ApicTable;
|
|
USHORT entryFlags;
|
|
ULONG_PTR TableEnd;
|
|
|
|
PAGED_CODE();
|
|
|
|
if (InterruptModel == 0) {
|
|
|
|
//
|
|
// This machine is running in PIC mode, so
|
|
// we should ignore anything from an APIC table.
|
|
//
|
|
|
|
return STATUS_NOT_FOUND;
|
|
}
|
|
|
|
if (IsaVector >= ISA_PIC_VECTORS) {
|
|
|
|
//
|
|
// This vector was never an ISA vector.
|
|
//
|
|
|
|
return STATUS_NOT_FOUND;
|
|
}
|
|
|
|
//
|
|
// Walk the MAPIC table.
|
|
//
|
|
|
|
ApicTable = AcpiInformation->MultipleApicTable;
|
|
|
|
if (!ApicTable) {
|
|
|
|
//
|
|
// This machine didn't have an MAPIC table. So it
|
|
// must not be running in APIC mode. So there must
|
|
// not be any overrides.
|
|
//
|
|
|
|
return STATUS_NOT_FOUND;
|
|
}
|
|
|
|
TraversePtr = (PUCHAR)ApicTable->APICTables;
|
|
TableEnd = (ULONG_PTR)ApicTable +ApicTable->Header.Length;
|
|
while ((ULONG_PTR)TraversePtr < TableEnd) {
|
|
|
|
ApicEntry = (PAPICTABLE) TraversePtr;
|
|
if (ApicEntry->Type == ISA_VECTOR_OVERRIDE &&
|
|
ApicEntry->Length == ISA_VECTOR_OVERRIDE_LENGTH) {
|
|
|
|
//
|
|
// Found an ISA vector redirection entry.
|
|
//
|
|
IsaEntry = (PISA_VECTOR) TraversePtr;
|
|
if (IsaEntry->Source == IsaVector) {
|
|
|
|
if (RedirectionVector) {
|
|
|
|
*RedirectionVector = IsaEntry->GlobalSystemInterruptVector;
|
|
}
|
|
|
|
if (Flags) {
|
|
|
|
entryFlags = IsaEntry->Flags;
|
|
|
|
*Flags = 0;
|
|
|
|
if (((entryFlags & PO_BITS) == POLARITY_HIGH) ||
|
|
((entryFlags & PO_BITS) == POLARITY_CONFORMS_WITH_BUS)) {
|
|
|
|
*Flags |= VECTOR_ACTIVE_HIGH;
|
|
|
|
} else {
|
|
|
|
*Flags |= VECTOR_ACTIVE_LOW;
|
|
}
|
|
|
|
if (((entryFlags & EL_BITS) == EL_EDGE_TRIGGERED) ||
|
|
((entryFlags & EL_BITS) == EL_CONFORMS_WITH_BUS)) {
|
|
|
|
*Flags |= VECTOR_EDGE;
|
|
|
|
} else {
|
|
|
|
*Flags |= VECTOR_LEVEL;
|
|
}
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Sanity check to make sure that we abort tables with bogus length
|
|
// entries
|
|
//
|
|
if (ApicEntry->Length == 0) {
|
|
|
|
break;
|
|
|
|
}
|
|
TraversePtr += (ApicEntry->Length);
|
|
|
|
}
|
|
|
|
return STATUS_NOT_FOUND;
|
|
}
|
|
|