1166 lines
27 KiB
C
1166 lines
27 KiB
C
/*++
|
||
|
||
Module Name:
|
||
|
||
mpsyssup.c
|
||
|
||
Abstract:
|
||
|
||
This file contains APIC-related funtions that are
|
||
specific to halmps. The functions that can be
|
||
shared with the APIC version of the ACPI HAL are
|
||
still in mpsys.c.
|
||
|
||
Author:
|
||
|
||
Ron Mosgrove (Intel)
|
||
|
||
Environment:
|
||
|
||
Kernel mode only.
|
||
|
||
Revision History:
|
||
|
||
Jake Oshins - 10-20-97 - split off from mpsys.c
|
||
|
||
|
||
*/
|
||
|
||
#include "halp.h"
|
||
#include "apic.inc"
|
||
#include "pcmp_nt.inc"
|
||
|
||
VOID
|
||
HalpMpsPCIPhysicalWorkaround (
|
||
VOID
|
||
);
|
||
|
||
NTSTATUS
|
||
HalpSearchBusForVector(
|
||
IN INTERFACE_TYPE BusType,
|
||
IN ULONG BusNo,
|
||
IN ULONG Vector,
|
||
IN OUT PBUS_HANDLER *BusHandler
|
||
);
|
||
|
||
BOOLEAN
|
||
HalpMPSBusId2NtBusId (
|
||
IN UCHAR ApicBusId,
|
||
OUT PPCMPBUSTRANS *ppBusType,
|
||
OUT PULONG BusNo
|
||
);
|
||
|
||
//
|
||
// Packed, somewhat arbitrary representation of an interrupt source.
|
||
// This array, when taken with the next one, allows you to figure
|
||
// out which bus-relative source maps to which APIC-relative source.
|
||
//
|
||
ULONG HalpSourceIrqIds[MAX_SOURCE_IRQS];
|
||
|
||
//
|
||
// Linear mapping of interrupt input on array of I/O APICs, where all the
|
||
// APICs have an ordering. (Used as index into HalpIntiInfo. Paired with
|
||
// HalpSourceIrqIds.)
|
||
//
|
||
USHORT HalpSourceIrqMapping[MAX_SOURCE_IRQS];
|
||
|
||
//
|
||
// HalpLastEnumeratedActualProcessor - Number of the last processor
|
||
// enumerated and returned to the OS. (Reset on resume from hibernate).
|
||
//
|
||
// This variable is incremented independently of the processor number
|
||
// NT uses.
|
||
//
|
||
|
||
UCHAR HalpLastEnumeratedActualProcessor = 0;
|
||
|
||
extern USHORT HalpEisaIrqMask;
|
||
extern USHORT HalpEisaIrqIgnore;
|
||
|
||
#ifdef ALLOC_PRAGMA
|
||
#pragma alloc_text(INIT,HalpInitIntiInfo)
|
||
#pragma alloc_text(INIT,HalpMpsPCIPhysicalWorkaround)
|
||
#pragma alloc_text(PAGELK,HalpGetApicInterruptDesc)
|
||
#pragma alloc_text(PAGELK, HalpEnableLocalNmiSources)
|
||
#pragma alloc_text(PAGE, HalpMPSBusId2NtBusId)
|
||
#pragma alloc_text(PAGE, HalpFindIdeBus)
|
||
#pragma alloc_text(PAGE, HalpSearchBusForVector)
|
||
#pragma alloc_text(PAGE, HalpInterruptsDescribedByMpsTable)
|
||
#pragma alloc_text(PAGE, HalpPci2MpsBusNumber)
|
||
#endif
|
||
|
||
VOID
|
||
HalpInitIntiInfo (
|
||
VOID
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function is called at initialization time before any interrupts
|
||
are connected. It reads the PC+MP Inti table and builds internal
|
||
information needed to route each Inti.
|
||
|
||
Return Value:
|
||
|
||
The following structures are filled in:
|
||
HalpIntiInfo
|
||
HalpSourceIrqIds
|
||
HalpSourceIrqMapping
|
||
HalpISAIqpToVector
|
||
|
||
--*/
|
||
{
|
||
ULONG ApicNo, BusNo, InterruptInput, IdIndex;
|
||
PPCMPINTI pInti;
|
||
PPCMPIOAPIC pIoApic;
|
||
PPCMPPROCESSOR pProc;
|
||
PPCMPBUSTRANS pBusType;
|
||
ULONG i, id;
|
||
UCHAR Level, Polarity;
|
||
|
||
//
|
||
// Clear IntiInfo table
|
||
//
|
||
|
||
for (i=0; i < MAX_INTI; i++) {
|
||
HalpIntiInfo[i].Type = 0xf;
|
||
HalpIntiInfo[i].Level = 0;
|
||
HalpIntiInfo[i].Polarity = 0;
|
||
}
|
||
|
||
//
|
||
// Check for MPS bios work-around
|
||
//
|
||
|
||
HalpMpsPCIPhysicalWorkaround();
|
||
|
||
//
|
||
// Initialize HalpMaxApicInti table
|
||
//
|
||
|
||
for (pInti = HalpMpInfoTable.IntiEntryPtr;
|
||
pInti->EntryType == ENTRY_INTI;
|
||
pInti++) {
|
||
|
||
|
||
//
|
||
// Which IoApic number is this?
|
||
//
|
||
|
||
for (pIoApic = HalpMpInfoTable.IoApicEntryPtr, ApicNo = 0;
|
||
pIoApic->EntryType == ENTRY_IOAPIC;
|
||
pIoApic++, ApicNo++) {
|
||
|
||
if ( (pInti->IoApicId == pIoApic->IoApicId) ||
|
||
(pInti->IoApicId == 0xff) ) {
|
||
break;
|
||
}
|
||
}
|
||
|
||
if ( (pInti->IoApicId != pIoApic->IoApicId) &&
|
||
(pInti->IoApicId != 0xff) ) {
|
||
DBGMSG ("PCMP table corrupt - IoApic not found for Inti\n");
|
||
continue;
|
||
}
|
||
|
||
if (!(pIoApic->IoApicFlag & IO_APIC_ENABLED)) {
|
||
DBGMSG ("PCMP IoApic for Inti is disabled\n");
|
||
continue;
|
||
}
|
||
|
||
//
|
||
// Make sure we are below the max # of IOApic which
|
||
// are supported
|
||
//
|
||
|
||
ASSERT (ApicNo < MAX_IOAPICS);
|
||
|
||
//
|
||
// Track Max Inti line per IOApic
|
||
//
|
||
|
||
if (pInti->IoApicInti >= HalpMaxApicInti[ApicNo]) {
|
||
HalpMaxApicInti[ApicNo] = pInti->IoApicInti+1;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Make sure there aren't more Inti lines then we can support
|
||
//
|
||
|
||
InterruptInput = 0;
|
||
for (i=0; i < MAX_IOAPICS; i++) {
|
||
InterruptInput += HalpMaxApicInti[i];
|
||
}
|
||
ASSERT (InterruptInput < MAX_INTI);
|
||
|
||
//
|
||
// Look at each Inti and record it's type in it's
|
||
// corresponding array entry
|
||
//
|
||
|
||
IdIndex = 0;
|
||
for (pInti = HalpMpInfoTable.IntiEntryPtr;
|
||
pInti->EntryType == ENTRY_INTI;
|
||
pInti++) {
|
||
|
||
//
|
||
// Which IoApic number is this?
|
||
//
|
||
|
||
for (pIoApic = HalpMpInfoTable.IoApicEntryPtr, ApicNo = 0;
|
||
pIoApic->EntryType == ENTRY_IOAPIC;
|
||
pIoApic++, ApicNo++) {
|
||
|
||
if ( (pInti->IoApicId == pIoApic->IoApicId) ||
|
||
(pInti->IoApicId == 0xff) ) {
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (!(pIoApic->IoApicFlag & IO_APIC_ENABLED)) {
|
||
continue;
|
||
}
|
||
|
||
//
|
||
// Determine the NT bus this INTI is on
|
||
//
|
||
|
||
if (!HalpMPSBusId2NtBusId (pInti->SourceBusId, &pBusType, &BusNo)) {
|
||
DBGMSG ("HAL: Initialize INTI - unkown MPS bus type\n");
|
||
continue;
|
||
}
|
||
|
||
//
|
||
// Calulcate InterruptInput value for this APIC Inti
|
||
//
|
||
|
||
InterruptInput = pInti->IoApicInti;
|
||
for (i = 0; i < ApicNo; i++) {
|
||
InterruptInput += HalpMaxApicInti[i];
|
||
}
|
||
|
||
//
|
||
// Get IntiInfo for this vector.
|
||
//
|
||
|
||
Polarity = (UCHAR) pInti->Signal.Polarity;
|
||
Level = HalpInitLevel[pInti->Signal.Level][pBusType->Level];
|
||
|
||
//
|
||
// Verify Level & Polarity mappings made sense
|
||
//
|
||
|
||
#if DBG
|
||
if (!(pBusType->NtType == MicroChannel || !(Level & CFG_ERROR))) {
|
||
|
||
DbgPrint("\n\n\n MPS BIOS problem! WHQL, fail this machine!\n");
|
||
DbgPrint("Intin: BusType %s BusNo: %x\n",
|
||
pBusType->PcMpType,
|
||
pInti->SourceBusId);
|
||
DbgPrint(" SrcBusIRQ: %x EL: %x PO: %x\n",
|
||
pInti->SourceBusIrq,
|
||
pInti->Signal.Level,
|
||
pInti->Signal.Polarity);
|
||
|
||
if (pBusType->NtType == PCIBus) {
|
||
|
||
DbgPrint("This entry is for PCI device %x on bus %x, PIN %x\n",
|
||
pInti->SourceBusIrq >> 2,
|
||
pInti->SourceBusId,
|
||
(pInti->SourceBusIrq & 0x3) + 1);
|
||
}
|
||
}
|
||
#endif
|
||
Level &= ~CFG_ERROR;
|
||
|
||
//
|
||
// See if this inti should go into the mask of inti that
|
||
// we won't assign to ISA devices.
|
||
//
|
||
// The last part of the test here guarantees that we are not
|
||
// picky about any devices that are in the HalpEisaIrqIgnore
|
||
// mask. This keep the mouse (and possibly other weird devices
|
||
// alive.)
|
||
//
|
||
|
||
if ((pBusType->NtType == Isa) &&
|
||
((Level & ~CFG_MUST_BE) == CFG_LEVEL) &&
|
||
!((1 << pInti->SourceBusIrq) & HalpEisaIrqIgnore)) {
|
||
|
||
HalpPciIrqMask |= (1 << pInti->SourceBusIrq);
|
||
}
|
||
|
||
if ((pBusType->NtType == Eisa) &&
|
||
((Level & ~CFG_MUST_BE) == CFG_LEVEL)) {
|
||
|
||
HalpEisaIrqMask |= (1 << pInti->SourceBusIrq);
|
||
|
||
if (HalpBusType != MACHINE_TYPE_EISA) {
|
||
|
||
//
|
||
// The BIOS thinks that this is an EISA
|
||
// inti. But we don't think that this
|
||
// is an EISA machine. So put this on the
|
||
// list of PCI inti, too.
|
||
//
|
||
|
||
HalpPciIrqMask |= (1 << pInti->SourceBusIrq);
|
||
}
|
||
}
|
||
|
||
#if DBG
|
||
if (HalpIntiInfo[InterruptInput].Type != 0xf) {
|
||
//
|
||
// Multiple irqs are connected to the Inti line. Make
|
||
// sure Type, Level, and Polarity are all the same.
|
||
//
|
||
|
||
ASSERT (HalpIntiInfo[InterruptInput].Type == pInti->IntType);
|
||
ASSERT (HalpIntiInfo[InterruptInput].Level == Level);
|
||
ASSERT (HalpIntiInfo[InterruptInput].Polarity == Polarity);
|
||
}
|
||
#endif
|
||
//
|
||
// Remember this Inti's configuration info
|
||
//
|
||
|
||
HalpIntiInfo[InterruptInput].Type = pInti->IntType;
|
||
HalpIntiInfo[InterruptInput].Level = Level;
|
||
HalpIntiInfo[InterruptInput].Polarity = Polarity;
|
||
|
||
//
|
||
// Get IRQs encoding for translations
|
||
//
|
||
|
||
ASSERT (pBusType->NtType < 16);
|
||
ASSERT (BusNo < 256);
|
||
|
||
if ( (pBusType->NtType == PCIBus) &&
|
||
(pInti->SourceBusIrq == 0) ) {
|
||
id = BusIrq2Id(pBusType->NtType, BusNo, 0x80);
|
||
} else {
|
||
id = BusIrq2Id(pBusType->NtType, BusNo, pInti->SourceBusIrq);
|
||
}
|
||
|
||
//
|
||
// Addinti mapping to translation table, do it now
|
||
//
|
||
|
||
HalpSourceIrqIds[IdIndex] = id;
|
||
HalpSourceIrqMapping[IdIndex] = (USHORT) InterruptInput;
|
||
IdIndex++;
|
||
|
||
//
|
||
// Lots of source IRQs are supported; however, the PC+MP table
|
||
// allows for an aribtrary number even beyond the APIC limit.
|
||
//
|
||
|
||
if (IdIndex >= MAX_SOURCE_IRQS) {
|
||
DBGMSG ("MAX_SOURCE_IRQS exceeded\n");
|
||
break;
|
||
}
|
||
|
||
}
|
||
|
||
//
|
||
// Fill in the boot processors PCMP Apic ID.
|
||
//
|
||
|
||
pProc = HalpMpInfoTable.ProcessorEntryPtr;
|
||
for (i=0; i < HalpMpInfoTable.ProcessorCount; i++, pProc++) {
|
||
if (pProc->CpuFlags & BSP_CPU) {
|
||
((PHALPRCB)KeGetCurrentPrcb()->HalReserved)->PCMPApicID = pProc->LocalApicId;
|
||
}
|
||
}
|
||
|
||
//
|
||
// If this is an EISA machine check the ELCR
|
||
//
|
||
|
||
if (HalpBusType == MACHINE_TYPE_EISA) {
|
||
HalpCheckELCR ();
|
||
}
|
||
}
|
||
|
||
BOOLEAN
|
||
HalpMPSBusId2NtBusId (
|
||
IN UCHAR MPSBusId,
|
||
OUT PPCMPBUSTRANS *ppBusType,
|
||
OUT PULONG BusNo
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Lookup MPS Table BusId into PCMPBUSTRANS (NtType) and instance #.
|
||
|
||
Arguments:
|
||
|
||
MPSBusId - Bus ID # in MPS table
|
||
ppBusType - Returned pointer to PPCMPBUSTRANS for this bus type
|
||
BusNo - Returned instance # of given bus
|
||
|
||
Return Value:
|
||
|
||
TRUE if MPSBusId was cross referenced into an NT id.
|
||
|
||
--*/
|
||
{
|
||
PPCMPBUS pBus, piBus;
|
||
PPCMPBUSTRANS pBusType;
|
||
NTSTATUS status;
|
||
UCHAR parentBusNo;
|
||
BOOLEAN foundFirstRootBus = FALSE;
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// What Bus is this?
|
||
//
|
||
|
||
for (pBus = HalpMpInfoTable.BusEntryPtr;
|
||
pBus->EntryType == ENTRY_BUS;
|
||
pBus++) {
|
||
|
||
if (MPSBusId == pBus->BusId) {
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (MPSBusId != pBus->BusId) {
|
||
DBGMSG ("PCMP table corrupt - Bus not found for Inti\n");
|
||
return FALSE;
|
||
}
|
||
|
||
//
|
||
// What InterfaceType is this Bus?
|
||
//
|
||
|
||
for (pBusType = HalpTypeTranslation;
|
||
pBusType->NtType != MaximumInterfaceType;
|
||
pBusType++) {
|
||
|
||
if (pBus->BusType[0] == pBusType->PcMpType[0] &&
|
||
pBus->BusType[1] == pBusType->PcMpType[1] &&
|
||
pBus->BusType[2] == pBusType->PcMpType[2] &&
|
||
pBus->BusType[3] == pBusType->PcMpType[3] &&
|
||
pBus->BusType[4] == pBusType->PcMpType[4] &&
|
||
pBus->BusType[5] == pBusType->PcMpType[5]) {
|
||
break;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Which instance of this BusType?
|
||
//
|
||
|
||
if (!pBusType->PhysicalInstance) {
|
||
|
||
//
|
||
// This algorithm originally just counted the number
|
||
// of busses of this type. The newer algorithm works
|
||
// around bugs in the MPS tables. The rules are listed.
|
||
//
|
||
// 1) The first PCI bus of a given type is always bus
|
||
// number 0.
|
||
//
|
||
// 2) For busses that are secondary root PCI busses, the
|
||
// bus number count is incremented to equal the MPS bus
|
||
// number.
|
||
//
|
||
// 3) For busses that are generated by PCI to PCI bridges,
|
||
// the bus number is incremented by one.
|
||
//
|
||
// N.B. Rule #3 implies that if one bus under a bridge
|
||
// is described, all must be.
|
||
//
|
||
|
||
for (piBus = HalpMpInfoTable.BusEntryPtr, *BusNo = 0;
|
||
piBus < pBus;
|
||
piBus++) {
|
||
|
||
if (pBus->BusType[0] == piBus->BusType[0] &&
|
||
pBus->BusType[1] == piBus->BusType[1] &&
|
||
pBus->BusType[2] == piBus->BusType[2] &&
|
||
pBus->BusType[3] == piBus->BusType[3] &&
|
||
pBus->BusType[4] == piBus->BusType[4] &&
|
||
pBus->BusType[5] == piBus->BusType[5]) {
|
||
|
||
status = HalpMpsGetParentBus(piBus->BusId,
|
||
&parentBusNo);
|
||
|
||
if (NT_SUCCESS(status)) {
|
||
|
||
//
|
||
// This is a child bus.
|
||
//
|
||
|
||
*BusNo += 1;
|
||
|
||
} else {
|
||
|
||
//
|
||
// This is a root bus.
|
||
//
|
||
|
||
if (!foundFirstRootBus) {
|
||
|
||
//
|
||
// This is the first root bus.
|
||
// To work around buggy MPS BIOSes, this
|
||
// root is always numbered 0.
|
||
//
|
||
|
||
*BusNo = 0;
|
||
foundFirstRootBus = TRUE;
|
||
|
||
} else {
|
||
|
||
//
|
||
// This is a secondary root of this type. Believe
|
||
// the MPS tables.
|
||
//
|
||
|
||
*BusNo = piBus->BusId;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
} else {
|
||
*BusNo = pBus->BusId;
|
||
}
|
||
|
||
if (pBusType->NtType == MaximumInterfaceType) {
|
||
return FALSE;
|
||
}
|
||
|
||
*ppBusType = pBusType;
|
||
return TRUE;
|
||
}
|
||
|
||
VOID
|
||
HalpMpsPCIPhysicalWorkaround (
|
||
VOID
|
||
)
|
||
{
|
||
PPCMPBUS pBus;
|
||
PPCMPBUSTRANS pBusType;
|
||
|
||
//
|
||
// The MPS specification has a subtle comment that PCI bus IDs are
|
||
// suppose to match their physical PCI bus number. Many BIOSes don't
|
||
// do this, so unless there's a PCI bus #0 listed in the MPS table
|
||
// assume that the BIOS is broken
|
||
//
|
||
|
||
//
|
||
// Find the PCI interface type
|
||
//
|
||
|
||
for (pBusType = HalpTypeTranslation;
|
||
pBusType->NtType != MaximumInterfaceType;
|
||
pBusType++) {
|
||
|
||
if (pBusType->PcMpType[0] == 'P' &&
|
||
pBusType->PcMpType[1] == 'C' &&
|
||
pBusType->PcMpType[2] == 'I' &&
|
||
pBusType->PcMpType[3] == ' ' &&
|
||
pBusType->PcMpType[4] == ' ' &&
|
||
pBusType->PcMpType[5] == ' ' ) {
|
||
break;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Find the bus with ID == 0
|
||
//
|
||
|
||
pBus = HalpMpInfoTable.BusEntryPtr;
|
||
while (pBus->EntryType == ENTRY_BUS) {
|
||
|
||
if (pBus->BusId == 0) {
|
||
|
||
//
|
||
// If it's a PCI bus, assume physical bus IDs
|
||
//
|
||
|
||
if (pBus->BusType[0] != 'P' ||
|
||
pBus->BusType[1] != 'C' ||
|
||
pBus->BusType[2] != 'I' ||
|
||
pBus->BusType[3] != ' ' ||
|
||
pBus->BusType[4] != ' ' ||
|
||
pBus->BusType[5] != ' ' ) {
|
||
|
||
//
|
||
// Change default behaviour of PCI type
|
||
// from physical to virtual
|
||
//
|
||
|
||
pBusType->PhysicalInstance = FALSE;
|
||
}
|
||
|
||
break;
|
||
}
|
||
|
||
pBus += 1;
|
||
}
|
||
}
|
||
|
||
|
||
|
||
BOOLEAN
|
||
HalpGetApicInterruptDesc (
|
||
IN INTERFACE_TYPE BusType,
|
||
IN ULONG BusNumber,
|
||
IN ULONG BusInterruptLevel,
|
||
OUT PUSHORT PcMpInti
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This procedure gets a "Inti" describing the requested interrupt
|
||
|
||
Arguments:
|
||
|
||
BusType - The Bus type as known to the IO subsystem
|
||
|
||
BusNumber - The number of the Bus we care for
|
||
|
||
BusInterruptLevel - IRQ on the Bus
|
||
|
||
Return Value:
|
||
|
||
TRUE if PcMpInti found; otherwise FALSE.
|
||
|
||
PcMpInti - A number that describes the interrupt to the HAL.
|
||
|
||
--*/
|
||
{
|
||
ULONG i, id;
|
||
|
||
if (BusType < 16 && BusNumber < 256 && BusInterruptLevel < 256) {
|
||
|
||
//
|
||
// get unique BusType,BusNumber,BusInterrupt ID
|
||
//
|
||
|
||
id = BusIrq2Id(BusType, BusNumber, BusInterruptLevel);
|
||
|
||
//
|
||
// Search for ID of Bus Irq mapping, and return the corresponding
|
||
// InterruptLine.
|
||
//
|
||
|
||
for (i=0; i < MAX_SOURCE_IRQS; i++) {
|
||
if (HalpSourceIrqIds[i] == id) {
|
||
*PcMpInti = HalpSourceIrqMapping[i];
|
||
return TRUE;
|
||
}
|
||
}
|
||
}
|
||
|
||
//
|
||
// Not found or search out of range
|
||
//
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
PBUS_HANDLER
|
||
HalpFindIdeBus(
|
||
IN ULONG Vector
|
||
)
|
||
{
|
||
PBUS_HANDLER ideBus;
|
||
NTSTATUS status;
|
||
ULONG pciNo;
|
||
|
||
PAGED_CODE();
|
||
|
||
status = HalpSearchBusForVector(Isa, 0, Vector, &ideBus);
|
||
|
||
if (NT_SUCCESS(status)) {
|
||
return ideBus;
|
||
}
|
||
|
||
status = HalpSearchBusForVector(Eisa, 0, Vector, &ideBus);
|
||
|
||
if (NT_SUCCESS(status)) {
|
||
return ideBus;
|
||
}
|
||
|
||
status = HalpSearchBusForVector(MicroChannel, 0, Vector, &ideBus);
|
||
|
||
if (NT_SUCCESS(status)) {
|
||
return ideBus;
|
||
}
|
||
|
||
for (pciNo = 0; pciNo <= 255; pciNo++) {
|
||
|
||
status = HalpSearchBusForVector(PCIBus, pciNo, Vector, &ideBus);
|
||
|
||
if (NT_SUCCESS(status)) {
|
||
return ideBus;
|
||
}
|
||
|
||
if (status == STATUS_NO_SUCH_DEVICE) {
|
||
break;
|
||
}
|
||
}
|
||
|
||
return NULL;
|
||
}
|
||
|
||
NTSTATUS
|
||
HalpSearchBusForVector(
|
||
IN INTERFACE_TYPE BusType,
|
||
IN ULONG BusNo,
|
||
IN ULONG Vector,
|
||
IN OUT PBUS_HANDLER *BusHandler
|
||
)
|
||
{
|
||
PBUS_HANDLER ideBus;
|
||
NTSTATUS status;
|
||
BOOLEAN found;
|
||
USHORT inti;
|
||
|
||
PAGED_CODE();
|
||
|
||
ideBus = HaliHandlerForBus(BusType, BusNo);
|
||
|
||
if (!ideBus) {
|
||
return STATUS_NO_SUCH_DEVICE;
|
||
}
|
||
|
||
found = HalpGetApicInterruptDesc(BusType,
|
||
BusNo,
|
||
Vector,
|
||
&inti);
|
||
|
||
if (!found) {
|
||
return STATUS_NOT_FOUND;
|
||
}
|
||
|
||
*BusHandler = ideBus;
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
ULONG
|
||
HalpGetIoApicId(
|
||
ULONG ApicNo
|
||
)
|
||
{
|
||
return (ULONG) HalpMpInfoTable.IoApicEntryPtr[ApicNo].IoApicId;
|
||
}
|
||
|
||
VOID
|
||
HalpMarkProcessorStarted(
|
||
ULONG ApicID,
|
||
ULONG NtNumber
|
||
)
|
||
{
|
||
return;
|
||
}
|
||
|
||
ULONG
|
||
HalpInti2BusInterruptLevel(
|
||
ULONG Inti
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This procedure does a lookup to find a bus-relative
|
||
interrupt vector associated with an Inti.
|
||
|
||
Note: If two different devices are sharing an interrupt,
|
||
this function will return the answer for the first one
|
||
that it finds. Fortunately, the only devices that use
|
||
their bus-relative vectors for anything (ISA devices)
|
||
can't share interrupts.
|
||
|
||
Arguments:
|
||
|
||
Inti - Interrupt Input on an I/O APIC
|
||
|
||
Return Value:
|
||
|
||
A bus-relative interrupt vector.
|
||
|
||
--*/
|
||
{
|
||
ULONG i;
|
||
|
||
for (i=0; i < MAX_SOURCE_IRQS; i++) {
|
||
|
||
if (HalpSourceIrqMapping[i] == Inti) {
|
||
|
||
return Id2BusIrq(HalpSourceIrqIds[i]);
|
||
}
|
||
}
|
||
|
||
//
|
||
// We should never fail to find a mapping.
|
||
//
|
||
|
||
#if DBG
|
||
KeBugCheckEx(HAL_INITIALIZATION_FAILED, 5, Inti, 0, 0);
|
||
#endif
|
||
|
||
return 0;
|
||
}
|
||
|
||
NTSTATUS
|
||
HalpGetNextProcessorApicId(
|
||
IN ULONG PrcbProcessorNumber,
|
||
IN OUT UCHAR *ApicId
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function returns an APIC ID of a non-started processor,
|
||
which will be started by HalpStartProcessor.
|
||
|
||
Arguments:
|
||
|
||
PrcbProcessorNumber - The logical processor number that will
|
||
be associated with this APIC ID.
|
||
|
||
ApicId - pointer to a value to fill in with the APIC ID.
|
||
|
||
Return Value:
|
||
|
||
status
|
||
|
||
--*/
|
||
{
|
||
PPCMPPROCESSOR ApPtr;
|
||
ULONG ProcessorNumber;
|
||
|
||
if (PrcbProcessorNumber == 0) {
|
||
|
||
//
|
||
// I don't believe anyone ever askes for 0 and I plan not
|
||
// to handle it. peterj 12/5/00.
|
||
//
|
||
|
||
KeBugCheckEx(HAL_INITIALIZATION_FAILED,
|
||
6,
|
||
HalpLastEnumeratedActualProcessor,
|
||
0,
|
||
0);
|
||
}
|
||
|
||
if (HalpLastEnumeratedActualProcessor >= HalpMpInfoTable.ProcessorCount) {
|
||
|
||
//
|
||
// Sorry, no more processors.
|
||
//
|
||
|
||
return STATUS_NOT_FOUND;
|
||
}
|
||
|
||
++HalpLastEnumeratedActualProcessor;
|
||
ProcessorNumber = HalpLastEnumeratedActualProcessor;
|
||
|
||
//
|
||
// Get the MP Table entry for this processor
|
||
//
|
||
|
||
ApPtr = HalpMpInfoTable.ProcessorEntryPtr;
|
||
|
||
|
||
#if 0
|
||
if (ProcessorNumber == 0) {
|
||
|
||
//
|
||
// Return the ID of the boot processor (BSP).
|
||
//
|
||
|
||
while (ApPtr->EntryType == ENTRY_PROCESSOR) {
|
||
if ((ApPtr->CpuFlags & CPU_ENABLED) &&
|
||
(ApPtr->CpuFlags & BSP_CPU)) {
|
||
*ApicId = (UCHAR)ApPtr->LocalApicId;
|
||
return STATUS_SUCCESS;
|
||
}
|
||
++ApPtr;
|
||
}
|
||
|
||
//
|
||
// Boot processor not found.
|
||
//
|
||
|
||
return STATUS_NOT_FOUND;
|
||
}
|
||
#endif
|
||
|
||
//
|
||
// Skip 'ProcessorNumber' enabled processors. The next enabled
|
||
// processor entry (after those) will be the "next" processor.
|
||
//
|
||
// Note: The BSP may not be amongst the first 'ProcessorNumber'
|
||
// processors so we must skip 'ProcessorNumber' - 1, and check
|
||
// for the and skip the BSP.
|
||
//
|
||
|
||
--ProcessorNumber;
|
||
|
||
while ((ProcessorNumber) && (ApPtr->EntryType == ENTRY_PROCESSOR)) {
|
||
if ((ApPtr->CpuFlags & CPU_ENABLED) &&
|
||
!(ApPtr->CpuFlags & BSP_CPU)) {
|
||
|
||
//
|
||
// Account for this entry (we have already started it) if this
|
||
// processor is enabled and not the BSP (we decremented for the
|
||
// BSP before entering the loop).
|
||
//
|
||
--ProcessorNumber;
|
||
}
|
||
++ApPtr;
|
||
}
|
||
|
||
//
|
||
// Find the first remaining enabled processor.
|
||
//
|
||
|
||
while(ApPtr->EntryType == ENTRY_PROCESSOR) {
|
||
if ((ApPtr->CpuFlags & CPU_ENABLED) &&
|
||
!(ApPtr->CpuFlags & BSP_CPU)) {
|
||
*ApicId = (UCHAR)ApPtr->LocalApicId;
|
||
return STATUS_SUCCESS;
|
||
}
|
||
++ApPtr;
|
||
}
|
||
|
||
//
|
||
// We did not find another processor.
|
||
//
|
||
|
||
return STATUS_NOT_FOUND;
|
||
}
|
||
|
||
NTSTATUS
|
||
HalpGetApicIdByProcessorNumber(
|
||
IN UCHAR Processor,
|
||
IN OUT USHORT *ApicId
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function returns an APIC ID for a given processor.
|
||
It is intended this routine be able to produce the same
|
||
APIC ID order as HalpGetNextProcessorApicId.
|
||
|
||
Note: This won't actually work in the presence of skipped
|
||
procesors.
|
||
|
||
Arguments:
|
||
|
||
Processor - The logical processor number that is
|
||
associated with this APIC ID.
|
||
|
||
ApicId - pointer to a value to fill in with the APIC ID.
|
||
|
||
Return Value:
|
||
|
||
status
|
||
|
||
--*/
|
||
{
|
||
PPCMPPROCESSOR ApPtr;
|
||
|
||
//
|
||
// Get the MP Table entry for this processor
|
||
//
|
||
|
||
ApPtr = HalpMpInfoTable.ProcessorEntryPtr;
|
||
|
||
if (Processor == 0) {
|
||
|
||
//
|
||
// Return the ID of the boot processor (BSP).
|
||
//
|
||
|
||
while (ApPtr->EntryType == ENTRY_PROCESSOR) {
|
||
if ((ApPtr->CpuFlags & CPU_ENABLED) &&
|
||
(ApPtr->CpuFlags & BSP_CPU)) {
|
||
*ApicId = (UCHAR)ApPtr->LocalApicId;
|
||
return STATUS_SUCCESS;
|
||
}
|
||
++ApPtr;
|
||
}
|
||
|
||
//
|
||
// Boot processor not found.
|
||
//
|
||
|
||
return STATUS_NOT_FOUND;
|
||
}
|
||
|
||
for ( ; TRUE ; ApPtr++) {
|
||
|
||
if (ApPtr->EntryType != ENTRY_PROCESSOR) {
|
||
|
||
//
|
||
// Out of processor entries, fail.
|
||
//
|
||
|
||
return STATUS_NOT_FOUND;
|
||
}
|
||
|
||
if (ApPtr->CpuFlags & BSP_CPU) {
|
||
|
||
//
|
||
// BSP is processor 0 and is not considered in the
|
||
// search for processors other than 0.
|
||
//
|
||
|
||
continue;
|
||
}
|
||
|
||
if (ApPtr->CpuFlags & CPU_ENABLED) {
|
||
|
||
//
|
||
// Count this processor.
|
||
//
|
||
|
||
Processor--;
|
||
|
||
if (Processor == 0) {
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
ASSERT(ApPtr->EntryType == ENTRY_PROCESSOR);
|
||
|
||
*ApicId = ApPtr->LocalApicId;
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
BOOLEAN
|
||
HalpInterruptsDescribedByMpsTable(
|
||
IN UCHAR MpsBusNumber
|
||
)
|
||
{
|
||
PPCMPINTI busInterrupt;
|
||
|
||
PAGED_CODE();
|
||
|
||
for (busInterrupt = HalpMpInfoTable.IntiEntryPtr;
|
||
busInterrupt->EntryType == ENTRY_INTI;
|
||
busInterrupt++) {
|
||
|
||
//
|
||
// The MPS spec requires that, if one interrupt
|
||
// on a bus is described, all interrupts on that
|
||
// bus must be described. So finding one match
|
||
// is enough.
|
||
//
|
||
|
||
if (busInterrupt->SourceBusId == MpsBusNumber) {
|
||
|
||
return TRUE;
|
||
}
|
||
}
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
NTSTATUS
|
||
HalpPci2MpsBusNumber(
|
||
IN UCHAR PciBusNumber,
|
||
OUT UCHAR *MpsBusNumber
|
||
)
|
||
{
|
||
PPCMPBUSTRANS busType;
|
||
ULONG mpsBusNumber = 0;
|
||
ULONG busNumber;
|
||
|
||
PAGED_CODE();
|
||
|
||
for (mpsBusNumber = 0;
|
||
mpsBusNumber < 0x100;
|
||
mpsBusNumber++) {
|
||
|
||
if (HalpMPSBusId2NtBusId((UCHAR)mpsBusNumber,
|
||
&busType,
|
||
&busNumber)) {
|
||
|
||
if ((busType->NtType == PCIBus) &&
|
||
(PciBusNumber == (UCHAR)busNumber)) {
|
||
|
||
*MpsBusNumber = (UCHAR)mpsBusNumber;
|
||
return STATUS_SUCCESS;
|
||
}
|
||
}
|
||
}
|
||
|
||
return STATUS_NOT_FOUND;
|
||
}
|
||
|
||
VOID
|
||
HalpEnableLocalNmiSources(
|
||
VOID
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine parses the information from the MP table and
|
||
enables any NMI sources in the local APIC of the processor
|
||
that it is running on.
|
||
|
||
Callers of this function must be holding HalpAccountingLock.
|
||
|
||
Arguments:
|
||
|
||
Return Value:
|
||
|
||
--*/
|
||
{
|
||
PKPCR pPCR;
|
||
UCHAR ThisCpu;
|
||
UCHAR LocalApicId;
|
||
PPCMPLINTI pEntry;
|
||
ULONG NumEntries;
|
||
|
||
pPCR = KeGetPcr();
|
||
ThisCpu = pPCR->Prcb->Number;
|
||
|
||
//
|
||
// Enable local processor NMI source
|
||
//
|
||
|
||
LocalApicId = ((PHALPRCB)pPCR->Prcb->HalReserved)->PCMPApicID;
|
||
NumEntries = HalpMpInfoTable.LintiCount;
|
||
|
||
for (pEntry = HalpMpInfoTable.LintiEntryPtr;
|
||
((pEntry) && (NumEntries > 0));
|
||
pEntry++, --NumEntries) {
|
||
|
||
if ( ( (pEntry->DestLocalApicId == LocalApicId) ||
|
||
(pEntry->DestLocalApicId == 0xff)) &&
|
||
(pEntry->IntType == INT_TYPE_NMI) ) {
|
||
|
||
//
|
||
// Found local NMI source, enable it
|
||
//
|
||
|
||
if (pEntry->DestLocalApicInti == 0) {
|
||
pLocalApic[LU_INT_VECTOR_0/4] = ( LEVEL_TRIGGERED |
|
||
ACTIVE_HIGH | DELIVER_NMI | NMI_VECTOR);
|
||
} else {
|
||
pLocalApic[LU_INT_VECTOR_1/4] = ( LEVEL_TRIGGERED |
|
||
ACTIVE_HIGH | DELIVER_NMI | NMI_VECTOR);
|
||
}
|
||
}
|
||
}
|
||
}
|