952 lines
26 KiB
C
952 lines
26 KiB
C
/*++
|
||
|
||
|
||
Copyright (c) 1989 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
ixpciint.c
|
||
|
||
Abstract:
|
||
|
||
All PCI bus interrupt mapping is in this module, so that a real
|
||
system which doesn't have all the limitations which PC PCI
|
||
systems have can replaced this code easly.
|
||
(bus memory & i/o address mappings can also be fix here)
|
||
|
||
Author:
|
||
|
||
Ken Reneris
|
||
|
||
Environment:
|
||
|
||
Kernel mode
|
||
|
||
Revision History:
|
||
|
||
|
||
--*/
|
||
|
||
#include "halp.h"
|
||
#include "pci.h"
|
||
#include "pcip.h"
|
||
#include "pcmp_nt.inc"
|
||
|
||
volatile ULONG PCIType2Stall;
|
||
extern struct HalpMpInfo HalpMpInfoTable;
|
||
extern BOOLEAN HalpHackNoPciMotion;
|
||
extern BOOLEAN HalpDoingCrashDump;
|
||
|
||
VOID
|
||
HalpPCIPin2MPSLine (
|
||
IN PBUS_HANDLER BusHandler,
|
||
IN PBUS_HANDLER RootHandler,
|
||
IN PCI_SLOT_NUMBER SlotNumber,
|
||
IN PPCI_COMMON_CONFIG PciData
|
||
);
|
||
|
||
VOID
|
||
HalpPCIBridgedPin2Line (
|
||
IN PBUS_HANDLER BusHandler,
|
||
IN PBUS_HANDLER RootHandler,
|
||
IN PCI_SLOT_NUMBER SlotNumber,
|
||
IN PPCI_COMMON_CONFIG PciData
|
||
);
|
||
|
||
VOID
|
||
HalpPCIMPSLine2Pin (
|
||
IN PBUS_HANDLER BusHandler,
|
||
IN PBUS_HANDLER RootHandler,
|
||
IN PCI_SLOT_NUMBER SlotNumber,
|
||
IN PPCI_COMMON_CONFIG PciNewData,
|
||
IN PPCI_COMMON_CONFIG PciOldData
|
||
);
|
||
|
||
NTSTATUS
|
||
HalpGetFixedPCIMPSLine (
|
||
IN PBUS_HANDLER BusHandler,
|
||
IN PBUS_HANDLER RootHandler,
|
||
IN PCI_SLOT_NUMBER PciSlot,
|
||
OUT PSUPPORTED_RANGE *Interrupt
|
||
);
|
||
|
||
BOOLEAN
|
||
HalpMPSBusId2NtBusId (
|
||
IN UCHAR ApicBusId,
|
||
OUT PPCMPBUSTRANS *ppBusType,
|
||
OUT PULONG BusNo
|
||
);
|
||
|
||
ULONG
|
||
HalpGetPCIBridgedInterruptVector (
|
||
IN PBUS_HANDLER BusHandler,
|
||
IN PBUS_HANDLER RootHandler,
|
||
IN ULONG InterruptLevel,
|
||
IN ULONG InterruptVector,
|
||
OUT PKIRQL Irql,
|
||
OUT PKAFFINITY Affinity
|
||
);
|
||
|
||
#ifdef ALLOC_PRAGMA
|
||
#pragma alloc_text(INIT, HalpSubclassPCISupport)
|
||
#pragma alloc_text(INIT, HalpMPSPCIChildren)
|
||
#pragma alloc_text(PAGE, HalpGetFixedPCIMPSLine)
|
||
#pragma alloc_text(PAGE, HalpGetPCIBridgedInterruptVector)
|
||
#pragma alloc_text(PAGE, HalpIrqTranslateRequirementsPci)
|
||
#pragma alloc_text(PAGE, HalpIrqTranslateResourcesPci)
|
||
#endif
|
||
|
||
|
||
//
|
||
// Turn PCI pin to inti via the MPS spec
|
||
// (note: pin must be non-zero)
|
||
//
|
||
|
||
#define PCIPin2Int(Slot,Pin) \
|
||
((((Slot.u.bits.DeviceNumber << 2) | (Pin-1)) != 0) ? \
|
||
(Slot.u.bits.DeviceNumber << 2) | (Pin-1) : 0x80);
|
||
|
||
#define PCIInt2Pin(interrupt) \
|
||
((interrupt & 0x3) + 1)
|
||
|
||
#define PCIInt2Slot(interrupt) \
|
||
((interrupt & 0x7f) >> 2)
|
||
|
||
|
||
VOID
|
||
HalpSubclassPCISupport (
|
||
PBUS_HANDLER Handler,
|
||
ULONG HwType
|
||
)
|
||
{
|
||
ULONG d, pin, i, MaxDeviceFound;
|
||
PPCIPBUSDATA BusData;
|
||
PCI_SLOT_NUMBER SlotNumber;
|
||
BOOLEAN DeviceFound;
|
||
|
||
|
||
BusData = (PPCIPBUSDATA) Handler->BusData;
|
||
SlotNumber.u.bits.Reserved = 0;
|
||
MaxDeviceFound = 0;
|
||
DeviceFound = FALSE;
|
||
|
||
#ifdef P6_WORKAROUNDS
|
||
BusData->MaxDevice = 0x10;
|
||
#endif
|
||
|
||
//
|
||
// Find any PCI bus which has MPS inti information, and provide
|
||
// MPS handlers for dealing with it.
|
||
//
|
||
// Note: we assume that any PCI bus with any MPS information
|
||
// is totally defined. (Ie, it's not possible to connect some PCI
|
||
// interrupts on a given PCI bus via the MPS table without connecting
|
||
// them all).
|
||
//
|
||
// Note2: we assume that PCI buses are listed in the MPS table in
|
||
// the same order the BUS declares them. (Ie, the first listed
|
||
// PCI bus in the MPS table is assumed to match physical PCI bus 0, etc).
|
||
//
|
||
//
|
||
|
||
for (d=0; d < PCI_MAX_DEVICES; d++) {
|
||
|
||
SlotNumber.u.bits.DeviceNumber = d;
|
||
SlotNumber.u.bits.FunctionNumber = 0;
|
||
|
||
for (pin=1; pin <= 4; pin++) {
|
||
i = PCIPin2Int (SlotNumber, pin);
|
||
if (HalpGetApicInterruptDesc(PCIBus, Handler->BusNumber, i, (PUSHORT)&i)) {
|
||
MaxDeviceFound = d;
|
||
DeviceFound = TRUE;
|
||
}
|
||
}
|
||
}
|
||
|
||
if (DeviceFound) {
|
||
|
||
//
|
||
// There are Inti mapping for interrupts on this PCI bus
|
||
// Change handlers for this bus to MPS versions
|
||
//
|
||
|
||
Handler->GetInterruptVector = HalpGetSystemInterruptVector;
|
||
BusData->CommonData.Pin2Line = (PciPin2Line) HalpPCIPin2MPSLine;
|
||
BusData->CommonData.Line2Pin = (PciLine2Pin) HalpPCIMPSLine2Pin;
|
||
BusData->GetIrqRange = HalpGetFixedPCIMPSLine;
|
||
|
||
if (BusData->MaxDevice < MaxDeviceFound) {
|
||
BusData->MaxDevice = MaxDeviceFound;
|
||
}
|
||
|
||
} else {
|
||
|
||
//
|
||
// Not all PCI machines are eisa machine, since the PCI interrupts
|
||
// aren't coming into IoApics go check the Eisa ELCR for broken
|
||
// behaviour.
|
||
//
|
||
|
||
HalpCheckELCR ();
|
||
}
|
||
}
|
||
|
||
|
||
VOID
|
||
HalpMPSPCIChildren (
|
||
VOID
|
||
)
|
||
/*++
|
||
|
||
Any PCI buses which don't have declared interrupt mappings and
|
||
are children of parent buses that have MPS interrupt mappings
|
||
need to inherit interrupts from parents via PCI barbar pole
|
||
algorithm
|
||
|
||
--*/
|
||
{
|
||
PBUS_HANDLER Handler, Parent;
|
||
PPCIPBUSDATA BusData, ParentData;
|
||
ULONG b, cnt, i, id;
|
||
PCI_SLOT_NUMBER SlotNumber;
|
||
struct {
|
||
union {
|
||
UCHAR map[4];
|
||
ULONG all;
|
||
} u;
|
||
} Interrupt, Hold;
|
||
|
||
//
|
||
// Lookup each PCI bus in the system
|
||
//
|
||
|
||
for (b=0; Handler = HaliHandlerForBus(PCIBus, b); b++) {
|
||
|
||
BusData = (PPCIPBUSDATA) Handler->BusData;
|
||
|
||
if (BusData->CommonData.Pin2Line == (PciPin2Line) HalpPCIPin2MPSLine) {
|
||
|
||
//
|
||
// This bus already has mappings
|
||
//
|
||
|
||
continue;
|
||
}
|
||
|
||
|
||
//
|
||
// Check if any parent has PCI MPS interrupt mappings
|
||
//
|
||
|
||
Interrupt.u.map[0] = 1;
|
||
Interrupt.u.map[1] = 2;
|
||
Interrupt.u.map[2] = 3;
|
||
Interrupt.u.map[3] = 4;
|
||
|
||
Parent = Handler;
|
||
SlotNumber = BusData->CommonData.ParentSlot;
|
||
|
||
while (Parent = Parent->ParentHandler) {
|
||
|
||
if (Parent->InterfaceType != PCIBus) {
|
||
break;
|
||
}
|
||
|
||
//
|
||
// Check if parent has MPS interrupt mappings
|
||
//
|
||
|
||
ParentData = (PPCIPBUSDATA) Parent->BusData;
|
||
if (ParentData->CommonData.Pin2Line == (PciPin2Line) HalpPCIPin2MPSLine) {
|
||
|
||
//
|
||
// This parent has MPS interrupt mappings. Set the device
|
||
// to get its InterruptLine values from the buses SwizzleIn table
|
||
//
|
||
|
||
Handler->GetInterruptVector = HalpGetPCIBridgedInterruptVector;
|
||
BusData->CommonData.Pin2Line = (PciPin2Line) HalpPCIBridgedPin2Line;
|
||
BusData->CommonData.Line2Pin = (PciLine2Pin) HalpPCIMPSLine2Pin;
|
||
|
||
for (i=0; i < 4; i++) {
|
||
id = PCIPin2Int (SlotNumber, Interrupt.u.map[i]);
|
||
BusData->SwizzleIn[i] = (UCHAR) id;
|
||
}
|
||
break;
|
||
}
|
||
|
||
//
|
||
// Apply interrupt mapping
|
||
//
|
||
|
||
i = SlotNumber.u.bits.DeviceNumber;
|
||
Hold.u.map[0] = Interrupt.u.map[(i + 0) & 3];
|
||
Hold.u.map[1] = Interrupt.u.map[(i + 1) & 3];
|
||
Hold.u.map[2] = Interrupt.u.map[(i + 2) & 3];
|
||
Hold.u.map[3] = Interrupt.u.map[(i + 3) & 3];
|
||
Interrupt.u.all = Hold.u.all;
|
||
|
||
SlotNumber = ParentData->CommonData.ParentSlot;
|
||
}
|
||
|
||
}
|
||
}
|
||
|
||
|
||
VOID
|
||
HalpPCIPin2MPSLine (
|
||
IN PBUS_HANDLER BusHandler,
|
||
IN PBUS_HANDLER RootHandler,
|
||
IN PCI_SLOT_NUMBER SlotNumber,
|
||
IN PPCI_COMMON_CONFIG PciData
|
||
)
|
||
/*++
|
||
--*/
|
||
{
|
||
if (!PciData->u.type0.InterruptPin) {
|
||
return ;
|
||
}
|
||
|
||
PciData->u.type0.InterruptLine = (UCHAR)
|
||
PCIPin2Int (SlotNumber, PciData->u.type0.InterruptPin);
|
||
}
|
||
|
||
VOID
|
||
HalpPCIBridgedPin2Line (
|
||
IN PBUS_HANDLER BusHandler,
|
||
IN PBUS_HANDLER RootHandler,
|
||
IN PCI_SLOT_NUMBER SlotNumber,
|
||
IN PPCI_COMMON_CONFIG PciData
|
||
)
|
||
/*++
|
||
|
||
This function maps the device's InterruptPin to an InterruptLine
|
||
value.
|
||
|
||
test function particular to dec pci-pci bridge card
|
||
|
||
--*/
|
||
{
|
||
PPCIPBUSDATA BusData;
|
||
ULONG i;
|
||
|
||
if (!PciData->u.type0.InterruptPin) {
|
||
return ;
|
||
}
|
||
|
||
//
|
||
// Convert slot Pin into Bus INTA-D.
|
||
//
|
||
|
||
BusData = (PPCIPBUSDATA) BusHandler->BusData;
|
||
|
||
i = (PciData->u.type0.InterruptPin +
|
||
SlotNumber.u.bits.DeviceNumber - 1) & 3;
|
||
|
||
PciData->u.type0.InterruptLine = BusData->SwizzleIn[i];
|
||
}
|
||
|
||
|
||
VOID
|
||
HalpPCIMPSLine2Pin (
|
||
IN PBUS_HANDLER BusHandler,
|
||
IN PBUS_HANDLER RootHandler,
|
||
IN PCI_SLOT_NUMBER SlotNumber,
|
||
IN PPCI_COMMON_CONFIG PciNewData,
|
||
IN PPCI_COMMON_CONFIG PciOldData
|
||
)
|
||
/*++
|
||
--*/
|
||
{
|
||
//
|
||
// PCI interrupts described in the MPS table are directly
|
||
// connected to APIC Inti pins.
|
||
// Do nothing...
|
||
//
|
||
}
|
||
|
||
ULONG
|
||
HalpGetPCIBridgedInterruptVector (
|
||
IN PBUS_HANDLER BusHandler,
|
||
IN PBUS_HANDLER RootHandler,
|
||
IN ULONG InterruptLevel,
|
||
IN ULONG InterruptVector,
|
||
OUT PKIRQL Irql,
|
||
OUT PKAFFINITY Affinity
|
||
)
|
||
{
|
||
//
|
||
// Get parent's translation
|
||
//
|
||
|
||
return BusHandler->ParentHandler->GetInterruptVector (
|
||
BusHandler->ParentHandler,
|
||
BusHandler->ParentHandler,
|
||
InterruptLevel,
|
||
InterruptVector,
|
||
Irql,
|
||
Affinity
|
||
);
|
||
|
||
}
|
||
|
||
|
||
|
||
NTSTATUS
|
||
HalpGetFixedPCIMPSLine (
|
||
IN PBUS_HANDLER BusHandler,
|
||
IN PBUS_HANDLER RootHandler,
|
||
IN PCI_SLOT_NUMBER PciSlot,
|
||
OUT PSUPPORTED_RANGE *Interrupt
|
||
)
|
||
{
|
||
UCHAR buffer[PCI_COMMON_HDR_LENGTH];
|
||
PPCI_COMMON_CONFIG PciData;
|
||
|
||
PciData = (PPCI_COMMON_CONFIG) buffer;
|
||
HalGetBusData (
|
||
PCIConfiguration,
|
||
BusHandler->BusNumber,
|
||
PciSlot.u.AsULONG,
|
||
PciData,
|
||
PCI_COMMON_HDR_LENGTH
|
||
);
|
||
|
||
if (PciData->VendorID == PCI_INVALID_VENDORID) {
|
||
return STATUS_UNSUCCESSFUL;
|
||
}
|
||
|
||
*Interrupt = ExAllocatePoolWithTag(PagedPool,
|
||
sizeof(SUPPORTED_RANGE),
|
||
HAL_POOL_TAG);
|
||
if (!*Interrupt) {
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
RtlZeroMemory (*Interrupt, sizeof (SUPPORTED_RANGE));
|
||
(*Interrupt)->Base = 1; // base = 1, limit = 0
|
||
|
||
|
||
if (!PciData->u.type0.InterruptPin) {
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
(*Interrupt)->Base = PciData->u.type0.InterruptLine;
|
||
(*Interrupt)->Limit = PciData->u.type0.InterruptLine;
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
VOID
|
||
HalpPCIType2TruelyBogus (
|
||
ULONG Context
|
||
)
|
||
/*++
|
||
|
||
This is a piece of work.
|
||
|
||
Type 2 of the PCI configuration space is bad. Bad as in to
|
||
access it one needs to block out 4K of I/O space.
|
||
|
||
Video cards are bad. The only decode the bits in an I/O address
|
||
they feel like. Which means one can't block out a 4K range
|
||
or these video cards don't work.
|
||
|
||
Combinding all these bad things onto an MP machine is even
|
||
more (sic) bad. The I/O ports can't be mapped out unless
|
||
all processors stop accessing I/O space.
|
||
|
||
Allowing access to device specific PCI control space during
|
||
an interrupt isn't bad, (although accessing it on every interrupt
|
||
is ineficent) but this cause the added grief that all processors
|
||
need to obtained at above all device interrupts.
|
||
|
||
And... naturally we have an MP machine with a wired down
|
||
bad video controller, stuck in the bad Type 2 configuration
|
||
space (when we told everyone about type 1!). So the "fix"
|
||
is to HALT ALL processors for the duration of reading/writing
|
||
ANY part of PCI configuration space such that we can be sure
|
||
no processor is touching the 4k I/O ports which get mapped out
|
||
of existance when type2 accesses occur.
|
||
|
||
----
|
||
|
||
While I'm flaming. Hooking PCI interrupts ontop of ISA interrupts
|
||
in a machine which has the potential to have 240+ interrupts
|
||
sources (read APIC) is bad.
|
||
|
||
--*/
|
||
{
|
||
// oh - let's just wait here and not pay attention to that other processor
|
||
// guy whom is punching holes into the I/O space
|
||
while (PCIType2Stall == Context) {
|
||
HalpPollForBroadcast ();
|
||
}
|
||
}
|
||
|
||
|
||
VOID
|
||
HalpPCIAcquireType2Lock (
|
||
PKSPIN_LOCK SpinLock,
|
||
PKIRQL OldIrql
|
||
)
|
||
{
|
||
if (!HalpDoingCrashDump) {
|
||
*OldIrql = KfRaiseIrql (CLOCK2_LEVEL-1);
|
||
KiAcquireSpinLock (SpinLock);
|
||
|
||
//
|
||
// Interrupt all other processors and have them wait until the
|
||
// barrier is cleared. (HalpGenericCall waits until the target
|
||
// processors have been interrupted before returning)
|
||
//
|
||
|
||
HalpGenericCall (
|
||
HalpPCIType2TruelyBogus,
|
||
PCIType2Stall,
|
||
HalpActiveProcessors & ~KeGetCurrentPrcb()->SetMember
|
||
);
|
||
} else {
|
||
*OldIrql = HIGH_LEVEL;
|
||
}
|
||
}
|
||
|
||
VOID
|
||
HalpPCIReleaseType2Lock (
|
||
PKSPIN_LOCK SpinLock,
|
||
KIRQL Irql
|
||
)
|
||
{
|
||
if (!HalpDoingCrashDump) {
|
||
PCIType2Stall++; // clear barrier
|
||
KiReleaseSpinLock (SpinLock);
|
||
KfLowerIrql (Irql);
|
||
}
|
||
}
|
||
|
||
NTSTATUS
|
||
HalpIrqTranslateRequirementsPci(
|
||
IN PVOID Context,
|
||
IN PIO_RESOURCE_DESCRIPTOR Source,
|
||
IN PDEVICE_OBJECT PhysicalDeviceObject,
|
||
OUT PULONG TargetCount,
|
||
OUT PIO_RESOURCE_DESCRIPTOR *Target
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function translates IRQ resource requirements from
|
||
a PCI bus that is described in the MPS table to the
|
||
root.
|
||
|
||
Arguments:
|
||
|
||
Context - must hold the MPS bus number of this PCI bus
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS, so long as we can allocate the necessary
|
||
memory
|
||
|
||
--*/
|
||
#define USE_INT_LINE_REGISTER_TOKEN 0xffffffff
|
||
{
|
||
PIO_RESOURCE_DESCRIPTOR target;
|
||
PPCMPBUSTRANS busType;
|
||
PBUS_HANDLER busHandler;
|
||
NTSTATUS status;
|
||
UCHAR mpsBusNumber;
|
||
ULONG devPciBus, bridgePciBus;
|
||
PCI_SLOT_NUMBER pciSlot;
|
||
UCHAR interruptLine, interruptPin;
|
||
UCHAR dummy;
|
||
PDEVICE_OBJECT parentPdo;
|
||
ROUTING_TOKEN routingToken;
|
||
KIRQL irql;
|
||
KAFFINITY affinity;
|
||
ULONG busVector;
|
||
ULONG vector;
|
||
BOOLEAN success;
|
||
|
||
PAGED_CODE();
|
||
|
||
ASSERT(Source->Type == CmResourceTypeInterrupt);
|
||
ASSERT(PciIrqRoutingInterface.GetInterruptRouting);
|
||
|
||
devPciBus = (ULONG)-1;
|
||
pciSlot.u.AsULONG = (ULONG)-1;
|
||
status = PciIrqRoutingInterface.GetInterruptRouting(
|
||
PhysicalDeviceObject,
|
||
&devPciBus,
|
||
&pciSlot.u.AsULONG,
|
||
&interruptLine,
|
||
&interruptPin,
|
||
&dummy,
|
||
&dummy,
|
||
&parentPdo,
|
||
&routingToken,
|
||
&dummy
|
||
);
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
|
||
//
|
||
// We should never get here. If we do, we have a bug.
|
||
// It means that we're trying to arbitrate PCI IRQs for
|
||
// a non-PCI device.
|
||
//
|
||
|
||
#if DBG
|
||
DbgPrint("HAL: The PnP manager passed a non-PCI PDO to the PCI IRQ translator (%x)\n",
|
||
PhysicalDeviceObject);
|
||
#endif
|
||
*TargetCount = 0;
|
||
return STATUS_INVALID_PARAMETER_3;
|
||
}
|
||
|
||
target = ExAllocatePoolWithTag(PagedPool,
|
||
sizeof(IO_RESOURCE_DESCRIPTOR),
|
||
HAL_POOL_TAG);
|
||
|
||
if (!target) {
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
//
|
||
// Copy the source to fill in all the relevant fields.
|
||
//
|
||
|
||
*target = *Source;
|
||
|
||
if (Context == (PVOID)USE_INT_LINE_REGISTER_TOKEN) {
|
||
|
||
//
|
||
// This bus's vectors aren't described in
|
||
// the MPS table. So just use the Int Line
|
||
// register.
|
||
//
|
||
|
||
busVector = interruptLine;
|
||
|
||
busHandler = HaliHandlerForBus(Isa, 0);
|
||
|
||
} else {
|
||
|
||
mpsBusNumber = (UCHAR)Context;
|
||
success = HalpMPSBusId2NtBusId(mpsBusNumber,
|
||
&busType,
|
||
&bridgePciBus);
|
||
|
||
if (!success) {
|
||
ExFreePool(target);
|
||
return STATUS_UNSUCCESSFUL;
|
||
}
|
||
|
||
//
|
||
// Start with the assumption that the incoming
|
||
// resources will contain the proper MPS-style
|
||
// interrupt vector. This will be guaranteed
|
||
// to be true if some previous translation has
|
||
// been done on these resources. And it might
|
||
// be true otherwise.
|
||
//
|
||
|
||
busVector = Source->u.Interrupt.MinimumVector;
|
||
|
||
if (bridgePciBus == devPciBus) {
|
||
|
||
//
|
||
// If this device sits on the bus for which
|
||
// this translator has been ejected, we can
|
||
// do better than to assume the incoming
|
||
// resources are clever.
|
||
//
|
||
|
||
busVector = PCIPin2Int(pciSlot, interruptPin);
|
||
}
|
||
|
||
//
|
||
// Find the PCI bus that corresponds to this MPS bus.
|
||
//
|
||
|
||
ASSERT(busType->NtType == PCIBus);
|
||
|
||
//
|
||
// TEMPTEMP Use bus handlers for now.
|
||
//
|
||
|
||
busHandler = HaliHandlerForBus(PCIBus, devPciBus);
|
||
|
||
}
|
||
|
||
|
||
vector = busHandler->GetInterruptVector(busHandler,
|
||
busHandler,
|
||
busVector,
|
||
busVector,
|
||
&irql,
|
||
&affinity);
|
||
|
||
if (vector == 0) {
|
||
|
||
#if DBG
|
||
DbgPrint("\nHAL: PCI Device 0x%02x, Func. 0x%x on bus 0x%x is not in the MPS table.\n *** Note to WHQL: Fail this machine. ***\n\n",
|
||
pciSlot.u.bits.DeviceNumber,
|
||
pciSlot.u.bits.FunctionNumber,
|
||
devPciBus);
|
||
#endif
|
||
ExFreePool(target);
|
||
*TargetCount = 0;
|
||
|
||
return STATUS_PNP_BAD_MPS_TABLE;
|
||
|
||
} else {
|
||
|
||
target->u.Interrupt.MinimumVector = vector;
|
||
target->u.Interrupt.MaximumVector = vector;
|
||
|
||
*TargetCount = 1;
|
||
*Target = target;
|
||
}
|
||
|
||
return STATUS_TRANSLATION_COMPLETE;
|
||
}
|
||
|
||
NTSTATUS
|
||
HalpIrqTranslateResourcesPci(
|
||
IN PVOID Context,
|
||
IN PCM_PARTIAL_RESOURCE_DESCRIPTOR Source,
|
||
IN RESOURCE_TRANSLATION_DIRECTION Direction,
|
||
IN ULONG AlternativesCount, OPTIONAL
|
||
IN IO_RESOURCE_DESCRIPTOR Alternatives[], OPTIONAL
|
||
IN PDEVICE_OBJECT PhysicalDeviceObject,
|
||
OUT PCM_PARTIAL_RESOURCE_DESCRIPTOR Target
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function translates IRQ resources between the
|
||
IDT and PCI busses that are described in the MPS
|
||
tables. The translation can go in either direction.
|
||
|
||
Arguments:
|
||
|
||
Context - Must hold the slot number of the bridge in
|
||
the lower sixteen bits. Must hold the
|
||
the bridge's primary bus number in the
|
||
upper sixteen bits.
|
||
|
||
Return Value:
|
||
|
||
status
|
||
|
||
--*/
|
||
{
|
||
PPCMPBUSTRANS busType;
|
||
PBUS_HANDLER busHandler;
|
||
UCHAR mpsBusNumber = (UCHAR)Context;
|
||
ULONG devPciBus, bridgePciBus;
|
||
KIRQL irql;
|
||
KAFFINITY affinity;
|
||
ULONG vector;
|
||
ULONG busVector;
|
||
NTSTATUS status;
|
||
PCI_SLOT_NUMBER pciSlot;
|
||
UCHAR interruptLine;
|
||
UCHAR interruptPin;
|
||
UCHAR dummy;
|
||
PDEVICE_OBJECT parentPdo;
|
||
ROUTING_TOKEN routingToken;
|
||
BOOLEAN useAlternatives = FALSE;
|
||
BOOLEAN foundBus = FALSE;
|
||
|
||
ASSERT(Source->Type = CmResourceTypeInterrupt);
|
||
ASSERT(PciIrqRoutingInterface.GetInterruptRouting);
|
||
|
||
*Target = *Source;
|
||
|
||
devPciBus = (ULONG)-1;
|
||
pciSlot.u.AsULONG = (ULONG)-1;
|
||
status = PciIrqRoutingInterface.GetInterruptRouting(
|
||
PhysicalDeviceObject,
|
||
&devPciBus,
|
||
&pciSlot.u.AsULONG,
|
||
&interruptLine,
|
||
&interruptPin,
|
||
&dummy,
|
||
&dummy,
|
||
&parentPdo,
|
||
&routingToken,
|
||
&dummy
|
||
);
|
||
|
||
ASSERT(NT_SUCCESS(status));
|
||
|
||
switch (Direction) {
|
||
case TranslateChildToParent:
|
||
|
||
if (Context == (PVOID)USE_INT_LINE_REGISTER_TOKEN) {
|
||
|
||
//
|
||
// This bus's vectors aren't described in
|
||
// the MPS table. So just use the Int Line
|
||
// register.
|
||
//
|
||
|
||
interruptLine = (UCHAR)Source->u.Interrupt.Vector;
|
||
|
||
busVector = interruptLine;
|
||
|
||
busHandler = HaliHandlerForBus(Isa, 0);
|
||
|
||
} else {
|
||
|
||
//
|
||
// Find the PCI bus that corresponds to this MPS bus.
|
||
//
|
||
|
||
mpsBusNumber = (UCHAR)Context;
|
||
foundBus = HalpMPSBusId2NtBusId(mpsBusNumber,
|
||
&busType,
|
||
&bridgePciBus);
|
||
|
||
if (!foundBus) {
|
||
return STATUS_INVALID_PARAMETER_1;
|
||
}
|
||
|
||
ASSERT(busType->NtType == PCIBus);
|
||
|
||
//
|
||
// Start with the assumption that the incoming
|
||
// resources will contain the proper MPS-style
|
||
// interrupt vector. This will be guaranteed
|
||
// to be true if some previous translation has
|
||
// been done on these resources. And it might
|
||
// be true otherwise.
|
||
//
|
||
|
||
busVector = Source->u.Interrupt.Vector;
|
||
|
||
if (devPciBus == bridgePciBus) {
|
||
|
||
//
|
||
// If this device sits on the bus for which
|
||
// this translator has been ejected, we can
|
||
// do better than to assume the incoming
|
||
// resources are clever.
|
||
//
|
||
|
||
busVector = PCIPin2Int(pciSlot, interruptPin);
|
||
}
|
||
|
||
//
|
||
// TEMPTEMP Use bus handlers for now.
|
||
//
|
||
|
||
busHandler = HaliHandlerForBus(PCIBus, devPciBus);
|
||
|
||
}
|
||
|
||
vector = busHandler->GetInterruptVector(busHandler,
|
||
busHandler,
|
||
busVector,
|
||
busVector,
|
||
&irql,
|
||
&affinity);
|
||
|
||
ASSERT(vector != 0);
|
||
|
||
Target->u.Interrupt.Vector = vector;
|
||
Target->u.Interrupt.Level = irql;
|
||
Target->u.Interrupt.Affinity = affinity;
|
||
|
||
return STATUS_TRANSLATION_COMPLETE;
|
||
|
||
case TranslateParentToChild:
|
||
|
||
//
|
||
// There is a problem here. We are translating from the
|
||
// context of the IDT down to the context of a specific
|
||
// PCI bus. (One decribed in the MPS tables.) This may
|
||
// not, however, be the bus that PhysicalDeviceObject's
|
||
// hardware lives on. There may be plug-in PCI to PCI
|
||
// bridges between this bus and the device.
|
||
//
|
||
// But we are not being asked the question "What is the
|
||
// bus-relative interrupt with respect to the bus that
|
||
// the device lives on?" We are being asked "What is the
|
||
// bus-relative interrupt once that interrupt passes through
|
||
// all those briges and makes it up to this bus?" This
|
||
// turns out to be a much harder question.
|
||
//
|
||
// There are really two cases:
|
||
//
|
||
// 1) There are no bridges between this bus and the device.
|
||
//
|
||
// This is easy. We answer the first question above and
|
||
// we're done. (This information will actually get used.
|
||
// it will appear in the start device IRP and the device
|
||
// manager.)
|
||
//
|
||
// 2) There are bridges.
|
||
//
|
||
// This is the hard case. And the information, were we
|
||
// actually going to bother to dig it up, would get thrown
|
||
// away. Nobody actually cares what the answer is. The
|
||
// only place it is going is the "Source" argument to
|
||
// the next translator. And the translator for PCI to PCI
|
||
// bridges won't end up using it.
|
||
//
|
||
// So we punt here and just answer the first question.
|
||
//
|
||
|
||
if (Context == (PVOID)USE_INT_LINE_REGISTER_TOKEN) {
|
||
|
||
Target->u.Interrupt.Vector = interruptLine;
|
||
|
||
} else {
|
||
|
||
mpsBusNumber = (UCHAR)Context;
|
||
if (HalpMPSBusId2NtBusId(mpsBusNumber,
|
||
&busType,
|
||
&bridgePciBus)) {
|
||
|
||
if (devPciBus == bridgePciBus) {
|
||
|
||
Target->u.Interrupt.Vector = PCIPin2Int(pciSlot, interruptPin);
|
||
|
||
} else {
|
||
|
||
useAlternatives = TRUE;
|
||
}
|
||
|
||
} else {
|
||
|
||
useAlternatives = TRUE;
|
||
}
|
||
}
|
||
|
||
if (useAlternatives) {
|
||
|
||
//
|
||
// Setup the default case. We assume that the I/O
|
||
// res list had the right answer.
|
||
//
|
||
|
||
ASSERT(AlternativesCount == 1);
|
||
ASSERT(Alternatives[0].Type == CmResourceTypeInterrupt);
|
||
|
||
Target->u.Interrupt.Vector = Alternatives[0].u.Interrupt.MinimumVector;
|
||
}
|
||
|
||
Target->u.Interrupt.Level = Target->u.Interrupt.Vector;
|
||
Target->u.Interrupt.Affinity = 0xffffffff;
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
return STATUS_INVALID_PARAMETER_3;
|
||
}
|
||
|