4171 lines
129 KiB
C
4171 lines
129 KiB
C
/*++
|
|
|
|
Copyright (c) 1992, 1993, 1994 Corollary Inc.
|
|
|
|
Module Name:
|
|
|
|
cbus2.c
|
|
|
|
Abstract:
|
|
|
|
This module implements the Corollary Cbus2 specific functions that
|
|
define the Hardware Architecture Layer (HAL) for Windows NT.
|
|
|
|
Cbus2 architecture includes a 400MB/sec processor-memory bus, as well
|
|
as multiple EISA and PCI buses. Up to 10 Pentium processors are supported
|
|
in Cbus2, as well as multiple native Cbus2 I/O cards (ie: SCSI, FDDI, VGA,
|
|
SIO, etc). Cbus2 is fully symmetric: all processors can reach all
|
|
memory and I/O devices, and similarly, all memory and I/O devices can
|
|
reach all processors. The CBC supports fully distributed, lowest in group
|
|
interrupts, as well as broadcast capabilities. Each Cbus2 processor has
|
|
an internal processor write back cache, as well as up to 2MB of L2 direct
|
|
mapped write back cache and a fully associative L3 write back victim cache.
|
|
|
|
Author:
|
|
|
|
Landy Wang (landy@corollary.com) 26-Mar-1992
|
|
|
|
Environment:
|
|
|
|
Kernel mode only.
|
|
|
|
Notes:
|
|
|
|
Open Issues:
|
|
|
|
- Should move the task priority from an fs segment to a ds segment
|
|
now that CBC revision2 will have the identity maps.
|
|
|
|
- The multiple I/O bus code is complete and ready for testing.
|
|
|
|
- The code that supports Cbus2 systems using APICs (local units on
|
|
processors and I/O units on both EISA and CBC I/O cards)
|
|
is complete and ready for testing.
|
|
|
|
- Support for a variable number of Cbus2 CBC hardware interrupt maps
|
|
still needs to be coded.
|
|
|
|
- Cbus2 ECC enable, disable and handler code is not fleshed out.
|
|
when this is done, HandleNMI needs to be fixed for
|
|
xx_PORT_UCHAR multiple bridge issues.
|
|
|
|
Revision History:
|
|
|
|
|
|
--*/
|
|
|
|
#include "halp.h"
|
|
#include "pci.h"
|
|
#include "pcip.h"
|
|
#include "cbus.h" // Cbus1 & Cbus2 max number of elements is here
|
|
#include "cbusrrd.h" // HAL <-> RRD interface definitions
|
|
#include "cbus2.h" // Cbus2 hardware architecture stuff
|
|
#include "cbus_nt.h" // Cbus NT-specific implementation stuff
|
|
#include "cbusnls.h" // Cbus error messages
|
|
#include "cbusapic.h" // Cbus APIC generic definitions
|
|
#include "stdio.h"
|
|
|
|
PULONG
|
|
CbusApicVectorToEoi(
|
|
IN ULONG Vector
|
|
);
|
|
|
|
VOID
|
|
Cbus2BootCPU (
|
|
IN ULONG Processor,
|
|
IN ULONG StartAddress
|
|
);
|
|
|
|
VOID
|
|
Cbus2InitInterruptPolarity(VOID);
|
|
|
|
PVOID
|
|
Cbus2LinkVector(
|
|
IN PBUS_HANDLER RootHandler,
|
|
IN ULONG Vector,
|
|
IN ULONG Irqline
|
|
);
|
|
|
|
ULONG
|
|
Cbus2ReadCSR(PULONG);
|
|
|
|
VOID
|
|
Cbus2WriteCSR(PULONG, ULONG);
|
|
|
|
VOID
|
|
Cbus2InitializeStall(IN ULONG);
|
|
|
|
VOID
|
|
Cbus2InitializeClock(VOID);
|
|
|
|
VOID
|
|
FatalError(
|
|
IN PUCHAR ErrorString
|
|
);
|
|
|
|
VOID
|
|
HalpIpiHandler( VOID );
|
|
|
|
VOID
|
|
Cbus2InitializeCBC(
|
|
IN ULONG Processor
|
|
);
|
|
|
|
VOID
|
|
Cbus2DisableMyInterrupts( ULONG );
|
|
|
|
VOID
|
|
HalpSpuriousInterrupt(VOID);
|
|
|
|
NTSTATUS
|
|
HalpAdjustEisaResourceList (
|
|
IN PVOID BusHandler,
|
|
IN PVOID RootHandler,
|
|
IN OUT PIO_RESOURCE_REQUIREMENTS_LIST *pResourceList
|
|
);
|
|
|
|
ULONG
|
|
HalpGetEisaInterruptVector (
|
|
IN PVOID BusHandler,
|
|
IN PVOID RootHandler,
|
|
IN ULONG BusInterruptLevel,
|
|
IN ULONG BusInterruptVector,
|
|
OUT PKIRQL Irql,
|
|
OUT PKAFFINITY Affinity
|
|
);
|
|
|
|
ULONG
|
|
HalpNoBusData (
|
|
IN PVOID BusHandler,
|
|
IN PVOID RootHandler,
|
|
IN ULONG SlotNumber,
|
|
IN PVOID Buffer,
|
|
IN ULONG Offset,
|
|
IN ULONG Length
|
|
);
|
|
|
|
HalpGetEisaData (
|
|
IN PVOID BusHandler,
|
|
IN PVOID RootHandler,
|
|
IN ULONG SlotNumber,
|
|
IN PVOID Buffer,
|
|
IN ULONG Offset,
|
|
IN ULONG Length
|
|
);
|
|
|
|
BOOLEAN
|
|
HalpTranslateIsaBusAddress (
|
|
IN PVOID BusHandler,
|
|
IN PVOID RootHandler,
|
|
IN PHYSICAL_ADDRESS BusAddress,
|
|
IN OUT PULONG AddressSpace,
|
|
OUT PPHYSICAL_ADDRESS TranslatedAddress
|
|
);
|
|
|
|
BOOLEAN
|
|
HalpTranslateEisaBusAddress (
|
|
IN PVOID BusHandler,
|
|
IN PVOID RootHandler,
|
|
IN PHYSICAL_ADDRESS BusAddress,
|
|
IN OUT PULONG AddressSpace,
|
|
OUT PPHYSICAL_ADDRESS TranslatedAddress
|
|
);
|
|
|
|
VOID
|
|
CbusRebootHandler( VOID );
|
|
|
|
VOID
|
|
Cbus2InitializeApic(
|
|
IN ULONG Processor
|
|
);
|
|
|
|
VOID
|
|
Cbus2CheckBusRanges(VOID);
|
|
|
|
ULONG
|
|
Cbus2MapBusNumberToBridgeIndex(
|
|
ULONG RootBusNumber
|
|
);
|
|
|
|
VOID
|
|
Cbus2SetupPrivateVectors(VOID);
|
|
|
|
VOID
|
|
Cbus2InitializePlatform(VOID);
|
|
|
|
VOID
|
|
Cbus2InitializeCPU(
|
|
IN ULONG Processor
|
|
);
|
|
|
|
VOID
|
|
Cbus2InitOtherBuses(VOID);
|
|
|
|
VOID
|
|
Cbus2ParseRRD(
|
|
IN PEXT_ID_INFO Table,
|
|
IN OUT PULONG Count
|
|
);
|
|
|
|
VOID
|
|
Cbus2InitializeDeviceIntrs(
|
|
IN ULONG Processor
|
|
);
|
|
|
|
UCHAR
|
|
Cbus2CheckForPeleSystem(
|
|
VOID
|
|
);
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(INIT, Cbus2SetupPrivateVectors)
|
|
#pragma alloc_text(INIT, Cbus2BootCPU)
|
|
#pragma alloc_text(INIT, Cbus2InitializePlatform)
|
|
#pragma alloc_text(INIT, Cbus2InitializeCBC)
|
|
#pragma alloc_text(INIT, Cbus2InitializeApic)
|
|
#pragma alloc_text(INIT, Cbus2InitializeCPU)
|
|
#pragma alloc_text(INIT, Cbus2InitOtherBuses)
|
|
#pragma alloc_text(INIT, Cbus2ParseRRD)
|
|
#pragma alloc_text(INIT, Cbus2InitializeDeviceIntrs)
|
|
#pragma alloc_text(INIT, Cbus2InitInterruptPolarity)
|
|
#endif
|
|
|
|
VOID
|
|
Cbus2DisableMyInterrupts(ULONG);
|
|
|
|
extern ADDRESS_USAGE HalpCbusMemoryHole;
|
|
extern ULONG CbusMemoryHoleIndex;
|
|
extern ADDRESS_USAGE HalpCbusMemoryResource;
|
|
extern ULONG CbusMemoryResourceIndex;
|
|
|
|
extern MEMORY_ALLOCATION_DESCRIPTOR HalpMemory[];
|
|
extern ULONG HalpMemoryIndex;
|
|
|
|
extern RRD_CONFIGURATION_T CbusJumpers;
|
|
|
|
extern ULONG CbusRedirVector;
|
|
extern ULONG CbusRebootVector;
|
|
|
|
extern WCHAR rgzMultiFunctionAdapter[];
|
|
extern WCHAR rgzConfigurationData[];
|
|
extern WCHAR rgzIdentifier[];
|
|
|
|
VOID
|
|
CbusPreparePhase0Interrupts(
|
|
IN ULONG,
|
|
IN ULONG,
|
|
IN PVOID
|
|
);
|
|
|
|
ULONG
|
|
Cbus2GetInterruptVector(
|
|
IN PBUS_HANDLER BusHandler,
|
|
IN PBUS_HANDLER RootHandler,
|
|
IN ULONG BusInterruptLevel,
|
|
IN ULONG BusInterruptVector,
|
|
OUT PKIRQL Irql,
|
|
OUT PKAFFINITY Affinity
|
|
);
|
|
|
|
ULONG Cbus2BridgesFound;
|
|
PCSR Cbus2BridgeCSR[CBUS_MAX_BRIDGES];
|
|
UCHAR Cbus2PeleSystemFound;
|
|
|
|
PCSR Cbus2IdentityCSR;
|
|
|
|
PHYSICAL_ADDRESS Cbus2BridgeCSRPaddr[CBUS_MAX_BRIDGES];
|
|
ULONG Cbus2BridgeId[CBUS_MAX_BRIDGES];
|
|
ULONG Cbus2IrqPolarity[CBUS_MAX_BRIDGES];
|
|
ULONG Cbus2BridgePCIBusNumber[CBUS_MAX_BRIDGES];
|
|
ULONG Cbus2BridgeOverride;
|
|
|
|
ULONG Cbus2InterruptController;
|
|
|
|
ULONG Cbus2FixLevelInterrupts;
|
|
ULONG Cbus2CheckSpuriousClock;
|
|
ULONG Cbus2EnableBroadcast;
|
|
ULONG Cbus2ClockVector;
|
|
ULONG Cbus2EoiToHwmap;
|
|
UCHAR Cbus2InterruptPolarity[MAXIMUM_IDTVECTOR + 1];
|
|
PULONG Cbus2TimeStamp0;
|
|
|
|
extern PULONG CbusTimeStamp;
|
|
extern PULONG CbusVectorToEoi[MAXIMUM_IDTVECTOR + 1];
|
|
extern ULONG CbusVectorToHwmap[MAXIMUM_IDTVECTOR + 1];
|
|
|
|
//
|
|
// Created for HalRequestIpi streamlining when operating in CBC mode -
|
|
// otherwise the ipi address could always have been gotten via CbusCSR[].
|
|
//
|
|
PULONG Cbus2SendIPI[MAX_CBUS_ELEMENTS];
|
|
PULONG Cbus2Poke8042;
|
|
|
|
KSPIN_LOCK Cbus2NMILock;
|
|
|
|
//
|
|
// for Cbus2, the CBC interrupt hardware supports all 256 interrupt
|
|
// priorities, and unlike the APIC, doesn't disable receipt of interrupts
|
|
// at granularities of 16-deep buckets. instead the CBC uses the whole byte,
|
|
// instead of 4 bits like the APIC, giving us a granularity of 1. This
|
|
// fine granularity results in us having more available priorities than
|
|
// both NT's 32 IRQL levels _AND_ the total number of EISA irq lines.
|
|
// This distinction is critical because it allows us to associate a unique
|
|
// vector AND PRIORITY to every single interrupt.
|
|
//
|
|
|
|
|
|
//
|
|
// our interrupt prioritization picture from lowest priority
|
|
// to highest priority) thus looks as follows:
|
|
//
|
|
// APC: 0x1F (lowest priority)
|
|
// DPC: 0x2F
|
|
// Lowest Priority EISA: 0x30
|
|
// Highest Priority EISA: 0x4F
|
|
//
|
|
// unused vectors: 0x50->0x60
|
|
//
|
|
// Lowest Priority CBC: 0x61
|
|
// Highest Priority CBC: 0xE9
|
|
// EISA Profile: 0xEB
|
|
// EISA Clock: 0xEC
|
|
// Hal Private Reboot IPI: 0xED
|
|
// IPI Broadcasting: 0xEE->FC
|
|
// IPI: 0xFD
|
|
// Power: 0xFE
|
|
// High: 0xFE (highest priority)
|
|
// Spurious: 0xFF (highest priority)
|
|
//
|
|
//
|
|
|
|
#define CBUS2_PROFILE_TASKPRI 0xEB
|
|
#define CBUS2_CLOCK_TASKPRI 0xEC
|
|
|
|
//
|
|
// all interrupts from this vector up can be enabled by just setting up
|
|
// the calling processor's interrupt configuration register in his CBC.
|
|
//
|
|
|
|
#define CBUS2_REBOOT_TASKPRI 0xED
|
|
#define CBUS2_REDIR_IPI 0xEE
|
|
#define CBUS2_ALTERNATE_IPI 0xEF
|
|
|
|
//
|
|
// It's OK if the redirection IPI shares a vector assignment with the
|
|
// broadcast_low IPI - because only one of them will be used in a given
|
|
// system - the redirection IPI will only be used if we are using APICs to
|
|
// communicate; the broadcast_low IPI will only be used if we are using
|
|
// CBCs to communicate - we will never use both in the same system.
|
|
//
|
|
#define CBUS2_BROADCAST_TASKPRI_LOW 0xEE // only used by Cbus2 CBC
|
|
#define CBUS2_BROADCAST_TASKPRI_HIGH 0xFC // only used by Cbus2 CBC
|
|
|
|
#define CBUS2_IPI_TASKPRI 0xFD
|
|
|
|
#define CBUS2_POWER_TASKPRI 0xFE
|
|
|
|
//
|
|
// we don't really care what this value is for the CBC, but the APIC
|
|
// spec seems to imply that this must be hardcoded at 0xff for future
|
|
// compatibility.
|
|
//
|
|
#define CBUS2_SPURIOUS_TASKPRI 0xFF
|
|
|
|
#define LOWEST_DEVICE_TASKPRI 0x30
|
|
#define LOWEST_CBC_TASKPRI 0x61
|
|
#define HIGHEST_DEVICE_TASKPRI (CBUS2_PROFILE_TASKPRI - 1) // 0xEA
|
|
|
|
//
|
|
// declare an EISA_IRQ2PRI just to flesh out arrays that will be indexed by
|
|
// irq line. the EISA_IRQ2PRI should never actually be used
|
|
//
|
|
|
|
#define EISA_IRQ2PRI HIGHEST_DEVICE_TASKPRI
|
|
|
|
#define EISA_IRQ1PRI (LOWEST_DEVICE_TASKPRI + 0xE * CBUS_MAX_BRIDGES)
|
|
#define EISA_IRQ3PRI (LOWEST_DEVICE_TASKPRI + 0xC * CBUS_MAX_BRIDGES)
|
|
#define EISA_IRQ4PRI (LOWEST_DEVICE_TASKPRI + 0xB * CBUS_MAX_BRIDGES)
|
|
#define EISA_IRQ5PRI (LOWEST_DEVICE_TASKPRI + 0xA * CBUS_MAX_BRIDGES)
|
|
#define EISA_IRQ6PRI (LOWEST_DEVICE_TASKPRI + 0x9 * CBUS_MAX_BRIDGES)
|
|
#define EISA_IRQ7PRI (LOWEST_DEVICE_TASKPRI + 0x8 * CBUS_MAX_BRIDGES)
|
|
#define EISA_IRQ9PRI (LOWEST_DEVICE_TASKPRI + 0x6 * CBUS_MAX_BRIDGES)
|
|
#define EISA_IRQAPRI (LOWEST_DEVICE_TASKPRI + 0x5 * CBUS_MAX_BRIDGES)
|
|
#define EISA_IRQBPRI (LOWEST_DEVICE_TASKPRI + 0x4 * CBUS_MAX_BRIDGES)
|
|
#define EISA_IRQCPRI (LOWEST_DEVICE_TASKPRI + 0x3 * CBUS_MAX_BRIDGES)
|
|
#define EISA_IRQDPRI (LOWEST_DEVICE_TASKPRI + 0x2 * CBUS_MAX_BRIDGES)
|
|
#define EISA_IRQEPRI (LOWEST_DEVICE_TASKPRI + 0x1 * CBUS_MAX_BRIDGES)
|
|
#define EISA_IRQFPRI (LOWEST_DEVICE_TASKPRI)
|
|
|
|
#define EISA_IRQ0PRI2 (LOWEST_DEVICE_TASKPRI + 0xF * CBUS_MAX_BRIDGES + 1)
|
|
#define EISA_IRQ1PRI2 (LOWEST_DEVICE_TASKPRI + 0xE * CBUS_MAX_BRIDGES + 1)
|
|
#define EISA_IRQ2PRI2 (LOWEST_DEVICE_TASKPRI + 0xD * CBUS_MAX_BRIDGES + 1)
|
|
#define EISA_IRQ3PRI2 (LOWEST_DEVICE_TASKPRI + 0xC * CBUS_MAX_BRIDGES + 1)
|
|
#define EISA_IRQ4PRI2 (LOWEST_DEVICE_TASKPRI + 0xB * CBUS_MAX_BRIDGES + 1)
|
|
#define EISA_IRQ5PRI2 (LOWEST_DEVICE_TASKPRI + 0xA * CBUS_MAX_BRIDGES + 1)
|
|
#define EISA_IRQ6PRI2 (LOWEST_DEVICE_TASKPRI + 0x9 * CBUS_MAX_BRIDGES + 1)
|
|
#define EISA_IRQ7PRI2 (LOWEST_DEVICE_TASKPRI + 0x8 * CBUS_MAX_BRIDGES + 1)
|
|
#define EISA_IRQ8PRI2 (LOWEST_DEVICE_TASKPRI + 0x7 * CBUS_MAX_BRIDGES + 1)
|
|
#define EISA_IRQ9PRI2 (LOWEST_DEVICE_TASKPRI + 0x6 * CBUS_MAX_BRIDGES + 1)
|
|
#define EISA_IRQAPRI2 (LOWEST_DEVICE_TASKPRI + 0x5 * CBUS_MAX_BRIDGES + 1)
|
|
#define EISA_IRQBPRI2 (LOWEST_DEVICE_TASKPRI + 0x4 * CBUS_MAX_BRIDGES + 1)
|
|
#define EISA_IRQCPRI2 (LOWEST_DEVICE_TASKPRI + 0x3 * CBUS_MAX_BRIDGES + 1)
|
|
#define EISA_IRQDPRI2 (LOWEST_DEVICE_TASKPRI + 0x2 * CBUS_MAX_BRIDGES + 1)
|
|
#define EISA_IRQEPRI2 (LOWEST_DEVICE_TASKPRI + 0x1 * CBUS_MAX_BRIDGES + 1)
|
|
#define EISA_IRQFPRI2 (LOWEST_DEVICE_TASKPRI + 1)
|
|
|
|
//
|
|
// you would think EISA_IRQ0PRI would be equivalent to
|
|
// (LOWEST_DEVICE_TASKPRI + 0x1 * CBUS_MAX_BRIDGES - 1).
|
|
// however irq0 is the system clock and is instead set to CLOCK2_TASKPRI.
|
|
//
|
|
// EISA_IRQ8PRI (irq8) is the profile interrupt, and is also
|
|
// special-cased in a similar fashion.
|
|
//
|
|
|
|
#define EISA_IRQ0PRI CBUS2_CLOCK_TASKPRI
|
|
#define EISA_IRQ8PRI CBUS2_PROFILE_TASKPRI
|
|
|
|
#define UNUSED_PRI HIGHEST_DEVICE_TASKPRI
|
|
|
|
//
|
|
// 186 priorities (0xE9-0x30+1) are available for device hardware.
|
|
// Vectors and IRQLs are given out in HalGetInterruptVector(). Since
|
|
// drivers can be dynamically linked in at any time, leave
|
|
// space so that they can be given roughly the same priorities as in a
|
|
// PC-environment. ie: IRQL(EISA irq0) > IRQL(EISA irq15), and so on,
|
|
// in order to mimic the standard Microsoft uniprocessor HAL.
|
|
//
|
|
// 23 distinct IRQL levels are left for us (by the kernel) to give to
|
|
// various hardware devices. Multiple drivers will be allowed to share
|
|
// any given IRQL level, although they will be given different VECTORS.
|
|
//
|
|
// All native Cbus2 I/O will be given priorities higher than that
|
|
// of any EISA device. This is because Cbus2 drivers will generally
|
|
// be written by Corollary, and thus guaranteed to have short ISRs, fully
|
|
// utilizing the DPC mechanism.
|
|
//
|
|
// The high 8 IRQL levels are reserved for native Cbus2 drivers.
|
|
// the 16 hardware interrupts for each Cbus2 CBC will be given IRQL
|
|
// priority on a "lower local irq line gets higher priority IRQL" basis.
|
|
//
|
|
// The low 15 IRQLs are for EISA devices, with
|
|
// EISA irql == EISA irq + LOWEST_DEVICE_TASKPRI.
|
|
// EISA irq lines on each bus are assigned identical IRQLs.
|
|
// irq2 (slave 8259), irq0 (clock) and irq8 (profile) are
|
|
// included in these calculations despite the fact that irq2 can't happen,
|
|
// and irq0/irq8 are assigned special (higher) priorities above.
|
|
// They are included because multiple I/O buses are a feature of
|
|
// Cbus2, and the irq0/irq8 of the second I/O bus could be wired
|
|
// up to devices other than 8254 or RTC clocks.
|
|
//
|
|
|
|
#define EISA_IRQLS 15
|
|
|
|
#define EISA_LOW_IRQL (DISPATCH_LEVEL + 1)
|
|
#define EISA_HIGH_IRQL (EISA_LOW_IRQL + EISA_IRQLS - 1)
|
|
#define CBC_HIGH_IRQL (PROFILE_LEVEL - 1)
|
|
|
|
//
|
|
// Limit to 9 I/O CBCs for now: 154 (0xe9 - 0x50 + 1) vectors are available
|
|
// after the dual EISAs and kernel vectors are assigned. Divide this up
|
|
// amongst 16 hardware interrupt map entries per CBC, and 9 is what you get.
|
|
// Note that to allow more than 9 active I/O CBCs, they cannot all
|
|
// be using all their interrupt lines, since there are not enough IDT
|
|
// entries to give them all unique vectors.
|
|
//
|
|
// This limitation should never be a problem, as it is generally
|
|
// unlikely that more than a few interrupt lines per Cbus2 native I/O CBC will
|
|
// be used.
|
|
//
|
|
#define NCBC_BUCKETS 9
|
|
|
|
//
|
|
// each pair of adjacent Cbus2 CBC irqlines on all CBCs will be mapped
|
|
// to the same IRQL. ie: CBC 0..n will have IRQL 27 assigned for hardware
|
|
// interrupt map entries 0 & 1 on these CBCs. This is because after EISA
|
|
// Irql assignments, there are only 8 Irql levels left, and 16
|
|
// CBC hardware interrupt lines can request vectors and levels.
|
|
//
|
|
#define CBC_IRQL_GROUPING 2 // each pair of lines shares an Irql
|
|
|
|
#define HIGHEST_CBC_TASKPRI HIGHEST_DEVICE_TASKPRI // 0xEA
|
|
|
|
#if (HIGH_LEVEL + 1 != 32)
|
|
Cause error to get attention:
|
|
Cbus2IrqlToVector[] must be built and indexed properly
|
|
#endif
|
|
|
|
#if DBG
|
|
#define MAX_ELEMENT_CSRS 15
|
|
|
|
#if (CBUS2_BROADCAST_TASKPRI_HIGH - CBUS2_BROADCAST_TASKPRI_LOW + 1 != MAX_ELEMENT_CSRS)
|
|
cause error to get attention - above task priority assignment must
|
|
leave a broadcast priority for each potential Cbus2 processor in the system.
|
|
this set of priorities MUST be higher than CLOCK2, _AND_ less than IPI,
|
|
so that IPI_IRQL/IPI_TASKPRI will be sufficient to block any of the
|
|
broadcast priorities.
|
|
#endif
|
|
#endif
|
|
|
|
//
|
|
// since each Irql will hold CBUS_MAX_BRIDGES lines at that priority, ensure
|
|
// that the Cbus2 Irql array masks off all lines at a given Irql priority...
|
|
//
|
|
#define MAX_EISA_PRIORITY(eisa_taskpri) (eisa_taskpri + CBUS_MAX_BRIDGES - 1)
|
|
|
|
#define MAX_CBC_PRIORITY(cbc_taskpri) \
|
|
(cbc_taskpri + CBC_IRQL_GROUPING * NCBC_BUCKETS - 1)
|
|
|
|
ULONG Cbus2IrqlToVector[HIGH_LEVEL + 1 ] = {
|
|
|
|
LOW_TASKPRI,
|
|
APC_TASKPRI,
|
|
DPC_TASKPRI,
|
|
MAX_EISA_PRIORITY(EISA_IRQFPRI),
|
|
|
|
MAX_EISA_PRIORITY(EISA_IRQEPRI),
|
|
MAX_EISA_PRIORITY(EISA_IRQDPRI),
|
|
MAX_EISA_PRIORITY(EISA_IRQCPRI),
|
|
MAX_EISA_PRIORITY(EISA_IRQBPRI),
|
|
|
|
MAX_EISA_PRIORITY(EISA_IRQAPRI),
|
|
MAX_EISA_PRIORITY(EISA_IRQ9PRI),
|
|
EISA_IRQ8PRI2, // used on second bridge only
|
|
MAX_EISA_PRIORITY(EISA_IRQ7PRI),
|
|
|
|
MAX_EISA_PRIORITY(EISA_IRQ6PRI),
|
|
MAX_EISA_PRIORITY(EISA_IRQ5PRI),
|
|
MAX_EISA_PRIORITY(EISA_IRQ4PRI),
|
|
MAX_EISA_PRIORITY(EISA_IRQ3PRI),
|
|
|
|
EISA_IRQ2PRI2, // used on second bridge only
|
|
MAX_EISA_PRIORITY(EISA_IRQ1PRI),
|
|
EISA_IRQ0PRI2, // used on second bridge only
|
|
HIGHEST_DEVICE_TASKPRI - 7 * CBC_IRQL_GROUPING * NCBC_BUCKETS,
|
|
|
|
HIGHEST_DEVICE_TASKPRI - 6 * CBC_IRQL_GROUPING * NCBC_BUCKETS,
|
|
HIGHEST_DEVICE_TASKPRI - 5 * CBC_IRQL_GROUPING * NCBC_BUCKETS,
|
|
HIGHEST_DEVICE_TASKPRI - 4 * CBC_IRQL_GROUPING * NCBC_BUCKETS,
|
|
HIGHEST_DEVICE_TASKPRI - 3 * CBC_IRQL_GROUPING * NCBC_BUCKETS,
|
|
|
|
HIGHEST_DEVICE_TASKPRI - 2 * CBC_IRQL_GROUPING * NCBC_BUCKETS,
|
|
HIGHEST_DEVICE_TASKPRI - CBC_IRQL_GROUPING * NCBC_BUCKETS,
|
|
HIGHEST_DEVICE_TASKPRI,
|
|
CBUS2_PROFILE_TASKPRI,
|
|
|
|
CBUS2_CLOCK_TASKPRI,
|
|
CBUS2_IPI_TASKPRI,
|
|
CBUS2_POWER_TASKPRI,
|
|
HIGH_TASKPRI,
|
|
};
|
|
|
|
//
|
|
// A table converting software interrupt Irqls to Cbus2-specific offsets
|
|
// within a given CSR space. Note that all task priorities are shifted
|
|
// by the Cbus2 register width (64 bits) to create the correct hardware
|
|
// offset to poke to cause the interrupt. This table is declared here to
|
|
// optimize the assembly software interrupt request lookup, and is filled
|
|
// in as part of InitializePlatform.
|
|
//
|
|
|
|
#define CBUS2_REGISTER_SHIFT 3
|
|
|
|
//
|
|
// Although the IrqlToAddr table is really used only to speed up software
|
|
// interrupt dispatching, it is filled in for all possible IRQLs.
|
|
//
|
|
ULONG Cbus2IrqlToCbus2Addr[HIGH_LEVEL + 1];
|
|
|
|
#if (MAX_EISA_PRIORITY(EISA_IRQ1PRI) >= \
|
|
HIGHEST_DEVICE_TASKPRI - 8 * CBC_IRQL_GROUPING * NCBC_BUCKETS)
|
|
Cause error to get attention: Cbus2 & EISA vectors must not overlap
|
|
#endif
|
|
|
|
|
|
#define EISA_IRQLINES 16
|
|
|
|
typedef struct _cbus2_irqline_t {
|
|
ULONG Vector;
|
|
KIRQL Irql;
|
|
} CBUS2_IRQLINE_T, *PCBUS2_IRQLINE;
|
|
|
|
//
|
|
// map EISA irqline to Cbus2 programmable vectors & IRQL levels...
|
|
//
|
|
|
|
CBUS2_IRQLINE_T Cbus2EISAIrqlines[CBUS_MAX_BRIDGES][EISA_IRQLINES] =
|
|
{
|
|
EISA_IRQ0PRI, CLOCK2_LEVEL,
|
|
EISA_IRQ1PRI, EISA_LOW_IRQL+0xE, // Irql 11
|
|
EISA_IRQ2PRI, PROFILE_LEVEL -1,
|
|
EISA_IRQ3PRI, EISA_LOW_IRQL+0xC, // Irql F
|
|
|
|
EISA_IRQ4PRI, EISA_LOW_IRQL+0xB,
|
|
EISA_IRQ5PRI, EISA_LOW_IRQL+0xA,
|
|
EISA_IRQ6PRI, EISA_LOW_IRQL+0x9,
|
|
EISA_IRQ7PRI, EISA_LOW_IRQL+0x8, // Irql B
|
|
|
|
EISA_IRQ8PRI, PROFILE_LEVEL,
|
|
EISA_IRQ9PRI, EISA_LOW_IRQL+6,
|
|
EISA_IRQAPRI, EISA_LOW_IRQL+5,
|
|
EISA_IRQBPRI, EISA_LOW_IRQL+4, // Irql 7
|
|
|
|
EISA_IRQCPRI, EISA_LOW_IRQL+3,
|
|
EISA_IRQDPRI, EISA_LOW_IRQL+2,
|
|
EISA_IRQEPRI, EISA_LOW_IRQL+1,
|
|
EISA_IRQFPRI, EISA_LOW_IRQL, // Irql 3
|
|
|
|
EISA_IRQ0PRI2, EISA_LOW_IRQL+0xF, // Irql 13
|
|
EISA_IRQ1PRI2, EISA_LOW_IRQL+0xE,
|
|
EISA_IRQ2PRI2, EISA_LOW_IRQL+0xD,
|
|
EISA_IRQ3PRI2, EISA_LOW_IRQL+0xC, // Irql F
|
|
|
|
EISA_IRQ4PRI2, EISA_LOW_IRQL+0xB,
|
|
EISA_IRQ5PRI2, EISA_LOW_IRQL+0xA,
|
|
EISA_IRQ6PRI2, EISA_LOW_IRQL+0x9,
|
|
EISA_IRQ7PRI2, EISA_LOW_IRQL+0x8, // Irql B
|
|
|
|
EISA_IRQ8PRI2, EISA_LOW_IRQL+7,
|
|
EISA_IRQ9PRI2, EISA_LOW_IRQL+6,
|
|
EISA_IRQAPRI2, EISA_LOW_IRQL+5,
|
|
EISA_IRQBPRI2, EISA_LOW_IRQL+4, // Irql 7
|
|
|
|
EISA_IRQCPRI2, EISA_LOW_IRQL+3,
|
|
EISA_IRQDPRI2, EISA_LOW_IRQL+2,
|
|
EISA_IRQEPRI2, EISA_LOW_IRQL+1,
|
|
EISA_IRQFPRI2, EISA_LOW_IRQL // Irql 3
|
|
};
|
|
|
|
//
|
|
// map Cbus2 hardware interrupt irqlines
|
|
// to Cbus2 programmable vectors & IRQL levels...
|
|
//
|
|
|
|
CBUS2_IRQLINE_T Cbus2CBCIrqlines[REV1_HWINTR_MAP_ENTRIES] =
|
|
{
|
|
HIGHEST_CBC_TASKPRI - NCBC_BUCKETS + 1, CBC_HIGH_IRQL,
|
|
HIGHEST_CBC_TASKPRI - 2 * NCBC_BUCKETS + 1, CBC_HIGH_IRQL,
|
|
HIGHEST_CBC_TASKPRI - 3 * NCBC_BUCKETS + 1, CBC_HIGH_IRQL - 1,
|
|
HIGHEST_CBC_TASKPRI - 4 * NCBC_BUCKETS + 1, CBC_HIGH_IRQL - 1,
|
|
|
|
HIGHEST_CBC_TASKPRI - 5 * NCBC_BUCKETS + 1, CBC_HIGH_IRQL - 2,
|
|
HIGHEST_CBC_TASKPRI - 6 * NCBC_BUCKETS + 1, CBC_HIGH_IRQL - 2,
|
|
HIGHEST_CBC_TASKPRI - 7 * NCBC_BUCKETS + 1, CBC_HIGH_IRQL - 3,
|
|
HIGHEST_CBC_TASKPRI - 8 * NCBC_BUCKETS + 1, CBC_HIGH_IRQL - 3,
|
|
|
|
HIGHEST_CBC_TASKPRI - 9 * NCBC_BUCKETS + 1, CBC_HIGH_IRQL - 4,
|
|
HIGHEST_CBC_TASKPRI -10 * NCBC_BUCKETS + 1, CBC_HIGH_IRQL - 4,
|
|
HIGHEST_CBC_TASKPRI -11 * NCBC_BUCKETS + 1, CBC_HIGH_IRQL - 5,
|
|
HIGHEST_CBC_TASKPRI -12 * NCBC_BUCKETS + 1, CBC_HIGH_IRQL - 5,
|
|
|
|
HIGHEST_CBC_TASKPRI -13 * NCBC_BUCKETS + 1, CBC_HIGH_IRQL - 6,
|
|
HIGHEST_CBC_TASKPRI -14 * NCBC_BUCKETS + 1, CBC_HIGH_IRQL - 6,
|
|
HIGHEST_CBC_TASKPRI -15 * NCBC_BUCKETS + 1, CBC_HIGH_IRQL - 7,
|
|
HIGHEST_CBC_TASKPRI -16 * NCBC_BUCKETS + 1, CBC_HIGH_IRQL - 7,
|
|
};
|
|
|
|
CCHAR HalpFindFirstSetRight[256] = {
|
|
0, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
|
|
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
|
|
5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
|
|
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
|
|
6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
|
|
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
|
|
5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
|
|
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
|
|
7, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
|
|
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
|
|
5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
|
|
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
|
|
6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
|
|
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
|
|
5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
|
|
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0};
|
|
|
|
VOID
|
|
HalpClockInterruptPx( VOID );
|
|
|
|
NTSTATUS
|
|
HalpNoAdjustResourceList (
|
|
IN PVOID BusHandler,
|
|
IN PVOID RootHandler,
|
|
IN OUT PIO_RESOURCE_REQUIREMENTS_LIST *pResourceList
|
|
);
|
|
|
|
NTSTATUS
|
|
HalpNoAssignSlotResources (
|
|
IN PVOID BusHandler,
|
|
IN PVOID RootHandler,
|
|
IN PUNICODE_STRING RegistryPath,
|
|
IN PUNICODE_STRING DriverClassName OPTIONAL,
|
|
IN PDRIVER_OBJECT DriverObject,
|
|
IN PDEVICE_OBJECT DeviceObject OPTIONAL,
|
|
IN ULONG SlotNumber,
|
|
IN OUT PCM_RESOURCE_LIST *AllocatedResources
|
|
);
|
|
|
|
VOID
|
|
HalpProfileInterruptPx( VOID );
|
|
|
|
ULONG
|
|
HalpGetSystemInterruptVector(
|
|
IN PBUS_HANDLER BusHandler,
|
|
IN PBUS_HANDLER RootHandler,
|
|
IN ULONG BusInterruptLevel,
|
|
IN ULONG BusInterruptVector,
|
|
OUT PKIRQL Irql,
|
|
OUT PKAFFINITY Affinity
|
|
);
|
|
|
|
VOID HalpPCISynchronizeType1 (
|
|
IN PBUS_HANDLER BusHandler,
|
|
IN PCI_SLOT_NUMBER Slot,
|
|
IN PKIRQL Irql,
|
|
IN PVOID State
|
|
);
|
|
|
|
VOID HalpPCIReleaseSynchronzationType1 (
|
|
IN PBUS_HANDLER BusHandler,
|
|
IN KIRQL Irql
|
|
);
|
|
|
|
ULONG
|
|
Cbus2GetCbusData(
|
|
IN PBUS_HANDLER BusHandler,
|
|
IN PBUS_HANDLER RootHandler,
|
|
IN ULONG SlotNumber,
|
|
IN PVOID Buffer,
|
|
IN ULONG Offset,
|
|
IN ULONG Length
|
|
);
|
|
|
|
ULONG
|
|
Cbus2SetCbusData(
|
|
IN PBUS_HANDLER BusHandler,
|
|
IN PBUS_HANDLER RootHandler,
|
|
IN ULONG SlotNumber,
|
|
IN PVOID Buffer,
|
|
IN ULONG Offset,
|
|
IN ULONG Length
|
|
);
|
|
|
|
//
|
|
// defines for resetting the keyboard controller used
|
|
// in Cbus2ResetAllOtherProcessors().
|
|
//
|
|
#define RESET 0xfe
|
|
#define KEYBPORT (PUCHAR )0x64
|
|
|
|
#define BRIDGE0 0
|
|
#define IRQ0 0
|
|
#define IRQ8 8
|
|
|
|
extern ULONG ProfileVector;
|
|
extern ULONG CbusClockVector;
|
|
|
|
#define IsPciBridge(a) \
|
|
(a->VendorID != PCI_INVALID_VENDORID && \
|
|
PCI_CONFIG_TYPE(a) == PCI_BRIDGE_TYPE && \
|
|
a->SubClass == 4 && a->BaseClass == 6)
|
|
|
|
typedef struct {
|
|
ULONG BusNo;
|
|
PBUS_HANDLER BusHandler;
|
|
PPCIPBUSDATA BusData;
|
|
PCI_SLOT_NUMBER SlotNumber;
|
|
PPCI_COMMON_CONFIG PciData;
|
|
ULONG IO, Memory, PFMemory;
|
|
UCHAR Buffer[PCI_COMMON_HDR_LENGTH];
|
|
} CONFIGBRIDGE, *PCONFIGBRIDGE;
|
|
|
|
typedef ULONG (*FncConfigIO) (
|
|
IN PPCIPBUSDATA BusData,
|
|
IN PVOID State,
|
|
IN PUCHAR Buffer,
|
|
IN ULONG Offset
|
|
);
|
|
|
|
typedef VOID (*FncSync) (
|
|
IN PBUS_HANDLER BusHandler,
|
|
IN PCI_SLOT_NUMBER Slot,
|
|
IN PKIRQL Irql,
|
|
IN PVOID State
|
|
);
|
|
|
|
typedef VOID (*FncReleaseSync) (
|
|
IN PBUS_HANDLER BusHandler,
|
|
IN KIRQL Irql
|
|
);
|
|
|
|
typedef struct _PCI_CONFIG_HANDLER {
|
|
FncSync Synchronize;
|
|
FncReleaseSync ReleaseSynchronzation;
|
|
FncConfigIO ConfigRead[3];
|
|
FncConfigIO ConfigWrite[3];
|
|
} PCI_CONFIG_HANDLER, *PPCI_CONFIG_HANDLER;
|
|
|
|
PCI_CONFIG_HANDLER PCIConfigHandler;
|
|
|
|
//
|
|
//
|
|
// Cbus2 switch table entry routines begin here
|
|
//
|
|
//
|
|
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called only once from HalInitProcessor() at Phase 0
|
|
by the boot cpu. All other cpus are still in reset.
|
|
|
|
software(APC, DPC, wake) and IPI vectors have already been initialized
|
|
and enabled.
|
|
|
|
all we're doing here is setting up some software structures for two
|
|
EISA interrupts (clock and profile) so they can be enabled later.
|
|
|
|
The bus handler data structures are not initialized until Phase 1,
|
|
so HalGetInterruptVector() may not be called before Phase1.
|
|
|
|
Hence we cannot pass a valid BusHandler parameter to the Tie code.
|
|
That's ok, since it doesn't currently use it.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
VOID
|
|
Cbus2SetupPrivateVectors(VOID)
|
|
{
|
|
PVOID OpaqueClock;
|
|
PVOID OpaqueProfile;
|
|
|
|
//
|
|
// we are defaulting to the EISA (or MCA) bridge 0
|
|
// for all of these interrupts which need enabling during Phase 0.
|
|
//
|
|
|
|
//
|
|
// unfortunately, we had hardcode the vectors onto bridge 0 - this // is because there is no legitimate bus handler structure to pass
|
|
// around at this time during startup.
|
|
//
|
|
if (Cbus2InterruptController == ELEMENT_HAS_CBC) {
|
|
|
|
OpaqueClock = Cbus2LinkVector((PBUS_HANDLER)BRIDGE0,
|
|
CbusClockVector, IRQ0);
|
|
OpaqueProfile = Cbus2LinkVector((PBUS_HANDLER)BRIDGE0,
|
|
ProfileVector, IRQ8);
|
|
}
|
|
else {
|
|
ASSERT (Cbus2InterruptController == ELEMENT_HAS_APIC);
|
|
|
|
OpaqueClock = CbusApicLinkVector((PBUS_HANDLER)BRIDGE0,
|
|
CbusClockVector, IRQ0);
|
|
|
|
OpaqueProfile = CbusApicLinkVector((PBUS_HANDLER)BRIDGE0,
|
|
ProfileVector, IRQ8);
|
|
}
|
|
|
|
CbusPreparePhase0Interrupts(CbusClockVector, IRQ0, OpaqueClock);
|
|
CbusPreparePhase0Interrupts(ProfileVector, IRQ8, OpaqueProfile);
|
|
}
|
|
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function calculates the stall execution, and initializes the
|
|
HAL-specific hardware device (CLOCK & PROFILE) interrupts for the
|
|
Corollary Cbus1 architecture.
|
|
|
|
Arguments:
|
|
|
|
Processor - Supplies a logical processor number to initialize
|
|
|
|
Return Value:
|
|
|
|
VOID
|
|
|
|
--*/
|
|
|
|
VOID
|
|
Cbus2InitializeInterrupts(
|
|
IN ULONG Processor
|
|
)
|
|
{
|
|
//
|
|
// Cbus2: stall uses the RTC irq8 to figure it out (needed in phase0).
|
|
// the clock uses the irq0 (needed in phase0)
|
|
// the perfcounter uses RTC irq8 (not needed till all cpus boot)
|
|
//
|
|
// Cbus1: stall uses the APIC to figure it out (needed in phase0).
|
|
// the clock uses the APIC (needed in phase0)
|
|
// the perfcounter uses irq0 (not needed till all cpus boot)
|
|
// the profile uses RTC irq8 (not needed till all cpus boot)
|
|
//
|
|
|
|
if (Processor == 0) {
|
|
//
|
|
// we must be at Phase0 of system initialization. we need to
|
|
// assign vectors for interrupts needed during Phase0.
|
|
// currently the 8254 clock and the RTC CMOS interrupts are
|
|
// needed during Phase0.
|
|
//
|
|
|
|
Cbus2ClockVector = CbusIrqlToVector[CLOCK2_LEVEL];
|
|
Cbus2SetupPrivateVectors();
|
|
}
|
|
|
|
Cbus2InitializeStall(Processor);
|
|
|
|
//
|
|
// Call the hardware backend handler to generate an
|
|
// interrupt every 10 milliseconds to be used as the system timer.
|
|
//
|
|
|
|
Cbus2InitializeClock();
|
|
|
|
//
|
|
// Set up and enable the irq0 performance counter and irq8 profile
|
|
// interrupts. Also enable the APIC clocks. This also registers
|
|
// the resources being used by these interrupts so other subsystems
|
|
// know the HAL has reserved them.
|
|
//
|
|
|
|
Cbus2InitializeDeviceIntrs(Processor);
|
|
|
|
//
|
|
// APC, DPC and IPI have already been initialized and enabled
|
|
// as part of HalInitializeProcessor.
|
|
//
|
|
}
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Determine if the supplied vector belongs to an EISA device - if so,
|
|
then the corresponding bridge's CBC hardware interrupt map entry
|
|
will need to be modified to enable the line, so return FALSE here.
|
|
|
|
Otherwise, just enable this processor's interrupt configuration register
|
|
for the supplied vector and return TRUE immediately. note that for
|
|
non-device (software) interrupts, generally no CbusVectorTable[] entries
|
|
have been set up.
|
|
|
|
Arguments:
|
|
|
|
Vector - Supplies a vector number to enable
|
|
|
|
Return Value:
|
|
|
|
TRUE if the vector was enabled, FALSE if not.
|
|
|
|
--*/
|
|
BOOLEAN
|
|
Cbus2EnableNonDeviceInterrupt(
|
|
IN ULONG Vector
|
|
)
|
|
{
|
|
PCSR csr;
|
|
|
|
//
|
|
// if pure software interrupt, setting the calling processor's
|
|
// CBC entry to accept the interrupt is sufficient.
|
|
//
|
|
if (Cbus2InterruptController == ELEMENT_HAS_APIC) {
|
|
//
|
|
// If pure software interrupt, no action needed.
|
|
// Note both APIC timer interrupts and IPIs are
|
|
// treated as "software" interrupts. The EOI
|
|
// address still needs to be set up here.
|
|
//
|
|
if (Vector != CBUS2_SPURIOUS_TASKPRI)
|
|
CbusVectorToEoi[Vector] = CbusApicVectorToEoi(Vector);
|
|
|
|
if (Vector < LOWEST_DEVICE_TASKPRI || Vector > CBUS2_CLOCK_TASKPRI) {
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// indicate that Enable_Device_Interrupt will need to be run
|
|
// in order to enable this vector, as it associated with an I/O
|
|
// bus device which will need enabling within a locked critical
|
|
// section.
|
|
//
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// the spurious vector doesn't need any interrupt configuration mucking
|
|
//
|
|
if (Vector == CBUS2_SPURIOUS_TASKPRI) {
|
|
return TRUE;
|
|
}
|
|
|
|
csr = (PCSR)KeGetPcr()->HalReserved[PCR_CSR];
|
|
|
|
if (Vector < LOWEST_DEVICE_TASKPRI) {
|
|
csr->InterruptConfiguration[Vector].csr_register =
|
|
HW_IMODE_ALLINGROUP;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// if just IPI, setting the calling processor's
|
|
// CBC entry to accept the interrupt is sufficient.
|
|
//
|
|
|
|
if (Vector >= CBUS2_REBOOT_TASKPRI && Vector <= CBUS2_IPI_TASKPRI) {
|
|
csr->InterruptConfiguration[Vector].csr_register =
|
|
HW_IMODE_ALLINGROUP;
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// indicate that EnableDeviceInterrupt will need to be run
|
|
// in order to enable this vector, as it is associated with an I/O
|
|
// bus device which will need enabling within a locked critical
|
|
// section.
|
|
//
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Enable the specified interrupt for the calling processor.
|
|
Note that the caller holds the HAL's CbusVectorLock at CLOCK_LEVEL
|
|
on entry.
|
|
|
|
Arguments:
|
|
|
|
Vector - Supplies a vector number to enable
|
|
|
|
HardwarePtr - Supplies a CSR address on some bridge or native Cbus2 I/O
|
|
card which will be generating the specified interrupt vector.
|
|
|
|
FirstAttach - TRUE if this is the first processor to enable the
|
|
specified vector
|
|
|
|
BusNumber - the bus number of the particular bustype.
|
|
|
|
Irqline - the irqline on the particular CBC that will be generating this
|
|
vector
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
VOID
|
|
Cbus2EnableDeviceInterrupt(
|
|
IN ULONG Vector,
|
|
IN PVOID HardwarePtr,
|
|
IN ULONG FirstAttach,
|
|
IN USHORT BusNumber,
|
|
IN USHORT Irqline
|
|
)
|
|
{
|
|
PHWINTRMAP hwentry; // CBC entry generating the intr
|
|
PCSR io_csr; // CBC entry generating the intr
|
|
PCSR csr;
|
|
ULONG IrqPolarity;
|
|
ULONG BusBridge;
|
|
BOOLEAN LowestInGroup;
|
|
BOOLEAN LevelTriggered;
|
|
|
|
//
|
|
// All interrupts are lowest-in-group. However,
|
|
// clocks, profiling & IPIs must stay ALL-IN-GROUP.
|
|
//
|
|
|
|
switch (Vector) {
|
|
|
|
case CBUS2_CLOCK_TASKPRI:
|
|
case CBUS2_PROFILE_TASKPRI:
|
|
case CBUS2_POWER_TASKPRI:
|
|
LowestInGroup = FALSE;
|
|
break;
|
|
|
|
default:
|
|
//
|
|
// Only enable CBC LIG arbitration if we have more than
|
|
// one processor. This is an optimization for speed.
|
|
//
|
|
if (CbusProcessors > 1) {
|
|
LowestInGroup = TRUE;
|
|
}
|
|
else {
|
|
LowestInGroup = FALSE;
|
|
}
|
|
break;
|
|
}
|
|
|
|
//
|
|
// if we are enabling an Irqline on an SPP, then decrement
|
|
// the Irqline first. we artificially set the interrupt range
|
|
// to 1-4 from 0-3. there was generic HAL code that did not
|
|
// allow for allocation of 0.
|
|
//
|
|
BusBridge = Cbus2MapBusNumberToBridgeIndex(BusNumber);
|
|
if (BusBridge) {
|
|
Irqline--;
|
|
}
|
|
|
|
//
|
|
// if this is the _first_ processor to actually enable this
|
|
// interrupt, then we'll need to know what kind of interrupt it is.
|
|
//
|
|
if (Vector >= LOWEST_CBC_TASKPRI && Vector <= HIGHEST_CBC_TASKPRI) {
|
|
|
|
ASSERT ((Vector >= EISA_IRQ1PRI + CBUS_MAX_BRIDGES) &&
|
|
Vector != EISA_IRQ0PRI &&
|
|
Vector != EISA_IRQ8PRI);
|
|
//
|
|
// default for Cbus2 I/O cards is edge
|
|
// triggered (ie rising edge CBC).
|
|
//
|
|
LevelTriggered = FALSE;
|
|
}
|
|
else {
|
|
ASSERT ((Vector >= EISA_IRQFPRI &&
|
|
Vector < EISA_IRQ1PRI + CBUS_MAX_BRIDGES) ||
|
|
Vector == EISA_IRQ0PRI ||
|
|
Vector == EISA_IRQ8PRI);
|
|
//
|
|
// must be an EISA interrupt, so
|
|
// get the irq polarity for the specified bus...
|
|
//
|
|
ASSERT (Irqline < EISA_IRQLINES);
|
|
|
|
IrqPolarity=Cbus2IrqPolarity[BusBridge];
|
|
|
|
//
|
|
// Mark the caller's interrupt as falling
|
|
// (level) or rising (edge) triggered, based
|
|
// on the ELCR register we read earlier.
|
|
//
|
|
if (((IrqPolarity >> Irqline)) & 0x1) {
|
|
//
|
|
// if level triggered, we must program
|
|
// the CBC as falling edge.
|
|
//
|
|
LevelTriggered = TRUE;
|
|
}
|
|
else {
|
|
//
|
|
// if edge triggered, we must program
|
|
// the CBC as rising edge.
|
|
//
|
|
LevelTriggered = FALSE;
|
|
}
|
|
}
|
|
|
|
if (Cbus2InterruptController == ELEMENT_HAS_APIC) {
|
|
//
|
|
// The EOI address is set up here.
|
|
//
|
|
CbusVectorToEoi[Vector] = CbusApicVectorToEoi(Vector);
|
|
|
|
CbusEnableApicInterrupt(BusNumber, Vector, HardwarePtr,
|
|
FirstAttach, LowestInGroup, LevelTriggered);
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Record the interrupt polarity so that the CBUS_EOI macro
|
|
// can more easily determine if the level-triggered hardware
|
|
// workaround for CBC-2 is required.
|
|
//
|
|
if (LevelTriggered == TRUE) {
|
|
Cbus2InterruptPolarity[Vector]=CBUS2_LEVEL_TRIGGERED_INTERRUPT;
|
|
}
|
|
else {
|
|
Cbus2InterruptPolarity[Vector] = CBUS2_EDGE_TRIGGERED_INTERRUPT;
|
|
}
|
|
|
|
//
|
|
// Set up the processor side of the interrupt initialization.
|
|
// this needs to be done for ALL interrupts (ie: software,
|
|
// IPI, etc, as well as for real hardware devices).
|
|
// note that this must precede the I/O side of the initialization
|
|
// because an interrupt could be pending at initialization time
|
|
// (especially with a level-triggered interrupt) and the broadcast
|
|
// could occur before the processor side is set up.
|
|
// this would cause the interrupt to be missed with no way to EOI.
|
|
//
|
|
csr = (PCSR)KeGetPcr()->HalReserved[PCR_CSR];
|
|
|
|
//
|
|
// before allowing LIG interrupts, check to see that
|
|
// RRD has configured the system for LIG's. CBC-1 and
|
|
// CBC-2 did not support LIG.
|
|
//
|
|
if (LowestInGroup == TRUE) {
|
|
//
|
|
// allow OEMs to disable LIG from their ROMs...
|
|
// if they do this, each normally LIG device interrupt
|
|
// will go only to the first processor to enable it.
|
|
//
|
|
if (CbusGlobal.Cbus2Features & CBUS_ENABLED_LIG) {
|
|
csr->InterruptConfiguration[Vector].csr_register =
|
|
HW_IMODE_LIG;
|
|
}
|
|
else if (FirstAttach) {
|
|
csr->InterruptConfiguration[Vector].csr_register =
|
|
HW_IMODE_ALLINGROUP;
|
|
}
|
|
}
|
|
else {
|
|
csr->InterruptConfiguration[Vector].csr_register =
|
|
HW_IMODE_ALLINGROUP;
|
|
}
|
|
|
|
if (FirstAttach) {
|
|
|
|
//
|
|
// initialize the generating bridge or Cbus2 native card's CBC
|
|
// that will be generating this interrupt.
|
|
//
|
|
io_csr = (PCSR)HardwarePtr;
|
|
hwentry = &io_csr->HardwareInterruptMap[Irqline];
|
|
|
|
//
|
|
// Set up the EOI address now that we know which
|
|
// bridge's CBC will need it.
|
|
//
|
|
CbusVectorToEoi[Vector] = (PULONG)
|
|
&io_csr->HardwareInterruptMapEoi[Irqline];
|
|
|
|
//
|
|
// let the generating CBC know in a single dword access...
|
|
//
|
|
|
|
if (LevelTriggered == TRUE) {
|
|
Cbus2EoiToHwmap =
|
|
(int)Cbus2BridgeCSR[BRIDGE0]->HardwareInterruptMapEoi -
|
|
(int)Cbus2BridgeCSR[BRIDGE0]->HardwareInterruptMap;
|
|
CbusVectorToHwmap[Vector] = (HW_LEVEL_LOW | Vector);
|
|
hwentry->csr_register = (HW_LEVEL_LOW | Vector);
|
|
}
|
|
else {
|
|
CbusVectorToHwmap[Vector] = (HW_EDGE_RISING | Vector);
|
|
hwentry->csr_register = (HW_EDGE_RISING | Vector);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Disable the specified interrupt so it can not occur on the calling
|
|
processor upon return from this routine. Note that the caller holds
|
|
the HAL's CbusVectorLock at CLOCK_LEVEL on entry.
|
|
|
|
Arguments:
|
|
|
|
Vector - Supplies a vector number to disable
|
|
|
|
HardwarePtr - Supplies a hardware interrupt map entry address on
|
|
the CBC of the bridge whose Vector is specified
|
|
|
|
LastDetach - TRUE if this is the last processor to detach from the
|
|
specified vector
|
|
|
|
Irqline - the irqline this vector is coming in on
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
VOID
|
|
Cbus2DisableInterrupt(
|
|
IN ULONG Vector,
|
|
IN PVOID HardwarePtr,
|
|
IN ULONG LastDetach,
|
|
IN USHORT BusNumber,
|
|
IN USHORT Irqline
|
|
)
|
|
{
|
|
PHWINTRMAP hwentry; // CBC entry generating the intr
|
|
PCSR csr;
|
|
|
|
if (Cbus2InterruptController == ELEMENT_HAS_APIC) {
|
|
CbusDisableApicInterrupt(BusNumber, Vector, HardwarePtr,
|
|
LastDetach);
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Only the vector matters to us, irql is irrelevant.
|
|
// tell the world that _this processor_ is no longer
|
|
// participating in receipt of this interrupt.
|
|
//
|
|
|
|
csr = (PCSR)(KeGetPcr()->HalReserved[PCR_CSR]);
|
|
csr->InterruptConfiguration[Vector].csr_register = HW_IMODE_DISABLED;
|
|
|
|
//
|
|
// No need to actually reach out to the specific I/O CBC, but
|
|
// if this is the last CPU to detach, turn off the
|
|
// I/O CBC entry which generates the specified interrupt.
|
|
// (this is cleaner from a hardware perspective).
|
|
//
|
|
|
|
if (LastDetach) {
|
|
hwentry = (PHWINTRMAP)HardwarePtr;
|
|
|
|
if (Vector >= LOWEST_DEVICE_TASKPRI &&
|
|
Vector < CBUS2_BROADCAST_TASKPRI_LOW)
|
|
hwentry->csr_register = HW_MODE_DISABLED;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Remove reset from the specified processor, allowing him to boot,
|
|
beginning execution at the specified start address.
|
|
|
|
Arguments:
|
|
|
|
Processor - Supplies a logical processor number to boot
|
|
|
|
StartAddress - Supplies a start address containing real-mode code
|
|
for the processor to execute.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
VOID
|
|
Cbus2BootCPU (
|
|
IN ULONG Processor,
|
|
IN ULONG StartAddress
|
|
)
|
|
{
|
|
PULONG ResetAddress;
|
|
|
|
//
|
|
// For Cbus2, the only hardware dependency when booting
|
|
// additional processors is to put the real-mode starting
|
|
// CS/EIP in 0x467 (the warm reset vector), and clear reset
|
|
// on the specified CPU. The start address has already been
|
|
// set up, so here just remove reset.
|
|
//
|
|
|
|
UNREFERENCED_PARAMETER( StartAddress );
|
|
|
|
ResetAddress = (PULONG)((PUCHAR)CbusCSR[Processor].csr +
|
|
CbusGlobal.smp_creset);
|
|
|
|
*ResetAddress = CbusGlobal.smp_creset_val;
|
|
}
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Overlay the irql-to-vector mappings with the Cbus2
|
|
vector maps. Initialize the broadcast IPI address and the
|
|
"Irql-To-Cbus2-Hardware-Address" translation table.
|
|
|
|
Also read the EISA interrupt edge/level specifications for
|
|
later use when enabling various EISA interrupts.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
VOID
|
|
Cbus2InitializePlatform(VOID)
|
|
{
|
|
ULONG Index;
|
|
LONG Irql;
|
|
ULONG LowerVector;
|
|
ULONG Vector;
|
|
ULONG i;
|
|
extern ULONG CbusVectorToIrql[MAXIMUM_IDTVECTOR + 1];
|
|
|
|
//
|
|
// pick up the the EISA interrupt edge/level requests
|
|
//
|
|
|
|
Cbus2InitInterruptPolarity();
|
|
|
|
//
|
|
// overlay the irql-to-vector mappings with the Cbus2 layout
|
|
//
|
|
|
|
RtlMoveMemory( (PVOID)CbusIrqlToVector,
|
|
(PVOID)Cbus2IrqlToVector,
|
|
(HIGH_LEVEL + 1) * sizeof (ULONG));
|
|
|
|
for (Index = 0; Index < HIGH_LEVEL + 1; Index++) {
|
|
#ifdef CBC_REV1
|
|
Cbus2IrqlToCbus2Addr[Index] =
|
|
(Cbus2IrqlToVector[Index] << CBUS2_REGISTER_SHIFT) +
|
|
FIELD_OFFSET(CSR_T, InterruptRequest);
|
|
#else
|
|
Cbus2IrqlToCbus2Addr[Index] =
|
|
(ULONG)(Cbus2IdentityCSR->InterruptRequest) +
|
|
(Cbus2IrqlToVector[Index] << CBUS2_REGISTER_SHIFT);
|
|
#endif
|
|
}
|
|
|
|
CbusRedirVector = CBUS2_REDIR_IPI;
|
|
CbusRebootVector = CBUS2_REBOOT_TASKPRI;
|
|
|
|
//
|
|
// build the "vector-to-irql" mappings here for fast
|
|
// translation when accepting an interrupt. this is
|
|
// better than continually keeping fs:PcIrql updated,
|
|
// as it allows us to remove instructions from KfRaiseIrql
|
|
// and KfLowerIrql, the hot paths. Although this is done
|
|
// for each interrupt as it is individually enabled, this
|
|
// must also be done here for Cbus2 since multiple vectors
|
|
// can be grouped into a shared irql - this isn't done in
|
|
// Cbus1 because there isn't the concept of supporting multiple
|
|
// different kinds of I/O busses simultaneously.
|
|
//
|
|
for (Irql = HIGH_LEVEL; Irql > 0; Irql--) {
|
|
Vector = Cbus2IrqlToVector[Irql];
|
|
LowerVector = Cbus2IrqlToVector[Irql - 1];
|
|
for (i = Vector; i > LowerVector; i--) {
|
|
CbusVectorToIrql[i] = Irql;
|
|
}
|
|
}
|
|
|
|
//
|
|
// The PCI bus number of the default C-bus II PCI bus bridge
|
|
// must be 0. At initialization time, the additional bus
|
|
// bridges should be set to 0xFF. they will be properly
|
|
// initialized in Cbus2InitiailizeOtherPCIBus().
|
|
//
|
|
for (Index = 1; Index < Cbus2BridgesFound; Index++) {
|
|
Cbus2BridgePCIBusNumber[Index] = 0xFF;
|
|
}
|
|
Cbus2BridgePCIBusNumber[0] = 0;
|
|
|
|
//
|
|
// Check if this is a PELE platform.
|
|
// If so, it uses a different interrupt routing
|
|
// algorithm for the 2nd bus.
|
|
//
|
|
Cbus2PeleSystemFound = Cbus2CheckForPeleSystem();
|
|
}
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Initialize this processor's CSR, interrupts, spurious interrupts & IPI
|
|
vector, using the CBC (not the APIC) for interrupt control.
|
|
|
|
Arguments:
|
|
|
|
Processor - Supplies a logical processor number
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
VOID
|
|
Cbus2InitializeCBC(
|
|
IN ULONG Processor
|
|
)
|
|
{
|
|
PCSR csr;
|
|
PCSR Broadcast;
|
|
ULONG Index, ThisElement, Vector;
|
|
|
|
ASSERT (Cbus2InterruptController == ELEMENT_HAS_CBC);
|
|
//
|
|
// Map this CPU's CSR stuff into his local
|
|
// address space for fast access.
|
|
//
|
|
csr = (PCSR)CbusCSR[Processor].csr;
|
|
|
|
//
|
|
// save the interrupt address for this processor for
|
|
// streamlining the interrupt code
|
|
//
|
|
Cbus2SendIPI[Processor] =
|
|
(PULONG)&(csr->InterruptRequest[CBUS2_IPI_TASKPRI]);
|
|
|
|
//
|
|
// map in the task priority register and start off at
|
|
// IRQL 0 - we are still protected by cli.
|
|
//
|
|
#ifdef CBC_REV1
|
|
(PTASKPRI) KeGetPcr()->HalReserved[PCR_TASKPRI] = &csr->TaskPriority;
|
|
csr->TaskPriority.csr_register = 0;
|
|
#else
|
|
(PTASKPRI) KeGetPcr()->HalReserved[PCR_TASKPRI] =
|
|
&Cbus2IdentityCSR->TaskPriority;
|
|
Cbus2IdentityCSR->TaskPriority.csr_register = 0;
|
|
#endif
|
|
|
|
//
|
|
// Create the spurious interrupt IDT entry for this processor
|
|
//
|
|
KiSetHandlerAddressToIDT(CBUS2_SPURIOUS_TASKPRI, HalpSpuriousInterrupt);
|
|
|
|
//
|
|
// initialize the spurious vector for the CBC
|
|
// to generate when it detects inconsistencies.
|
|
//
|
|
csr->SpuriousVector.csr_register = CBUS2_SPURIOUS_TASKPRI;
|
|
|
|
HalEnableSystemInterrupt(CBUS2_SPURIOUS_TASKPRI, HIGH_LEVEL, Latched);
|
|
|
|
//
|
|
// generate NMIs (trap 2) when we get error interrupts. enabled
|
|
// faults already generate NMI traps by default.
|
|
//
|
|
csr->ErrorVector.LowDword = 2;
|
|
csr->InterruptControl.LowDword = CbusGlobal.InterruptControlMask;
|
|
csr->FaultControl.LowDword = CbusGlobal.FaultControlMask;
|
|
|
|
ThisElement = Processor;
|
|
Broadcast = (PCSR)CbusBroadcastCSR;
|
|
|
|
//
|
|
// This processor participates in all broadcast IPIs
|
|
// except for ones sent by this processor.
|
|
//
|
|
Vector = CBUS2_BROADCAST_TASKPRI_LOW;
|
|
|
|
for (Index = 0; Index < MAX_ELEMENT_CSRS; Index++, Vector++) {
|
|
|
|
if (Index == ThisElement) {
|
|
|
|
//
|
|
// Map this element's broadcast interrupt entry
|
|
// to streamline the IPI sending code, because
|
|
// this is not done easily by the hardware.
|
|
//
|
|
|
|
(ULONG)KeGetPcr()->HalReserved[PCR_BROADCAST] =
|
|
(ULONG)&Broadcast->InterruptRequest[Vector];
|
|
if (Cbus2EnableBroadcast == 0) {
|
|
KiSetHandlerAddressToIDT(Vector,HalpIpiHandler);
|
|
HalEnableSystemInterrupt(Vector,
|
|
IPI_LEVEL, Latched);
|
|
}
|
|
}
|
|
else {
|
|
if (Cbus2EnableBroadcast) {
|
|
KiSetHandlerAddressToIDT(Vector,HalpIpiHandler);
|
|
HalEnableSystemInterrupt(Vector,
|
|
IPI_LEVEL, Latched);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Also initialize the directed IPI entry for this processor.
|
|
//
|
|
KiSetHandlerAddressToIDT(CBUS2_IPI_TASKPRI, HalpIpiHandler);
|
|
HalEnableSystemInterrupt(CBUS2_IPI_TASKPRI, IPI_LEVEL, Latched);
|
|
|
|
//
|
|
// Create an interrupt gate so processors can
|
|
// be stopped cleanly prior to rebooting the system.
|
|
//
|
|
KiSetHandlerAddressToIDT(CBUS2_REBOOT_TASKPRI,
|
|
CbusRebootHandler);
|
|
HalEnableSystemInterrupt(CBUS2_REBOOT_TASKPRI,
|
|
IPI_LEVEL, Latched);
|
|
}
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Initialize all the I/O Apics in the system. This includes I/O APICs
|
|
on the EISA bridges, as well as any that may exist on native Cbus2
|
|
I/O boards.
|
|
|
|
Arguments:
|
|
|
|
Processor - Supplies a logical processor number
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
VOID
|
|
Cbus2InitializeApic(
|
|
IN ULONG Processor
|
|
)
|
|
{
|
|
ULONG BridgeIndex;
|
|
ULONG WindowBaseAddress;
|
|
ULONG WindowBaseAddressShifted;
|
|
ULONG WindowOffsetAddress;
|
|
ULONG CsrPhysicalAddress;
|
|
WINDOWRELOC_T RegisterValue;
|
|
PCSR csr;
|
|
PCBUS2_ELEMENT Cbus2Element;
|
|
ULONG original_bridge;
|
|
ULONG BridgeId;
|
|
|
|
for (BridgeIndex = 0; BridgeIndex < Cbus2BridgesFound; BridgeIndex++) {
|
|
//
|
|
// since each EISA bridge will have the I/O APIC at the
|
|
// same physical address, we must make each one unique so
|
|
// references are guaranteed to get to the desired one.
|
|
// this is done by mapping them into each bridge's window 0
|
|
// relocation register, and thus onto the PCBusWindow0.
|
|
//
|
|
csr = (PCSR)Cbus2BridgeCSR[BridgeIndex];
|
|
CsrPhysicalAddress = Cbus2BridgeCSRPaddr[BridgeIndex].LowPart;
|
|
BridgeId = Cbus2BridgeId[BridgeIndex];
|
|
|
|
//
|
|
// save the original value for restoral after our I/O.
|
|
// repoint our I/O references to the desired bus bridge number.
|
|
//
|
|
original_bridge = csr->BusBridgeSelection.csr_register;
|
|
csr->BusBridgeSelection.csr_register =
|
|
((original_bridge & ~MAX_ELEMENT_CSRS) | BridgeId);
|
|
|
|
//
|
|
// this field must be set to the high 9 bits of the desired
|
|
// address.
|
|
//
|
|
WindowBaseAddressShifted = ((ULONG)CBUS2_IO_APIC_LOCATION >> 23);
|
|
WindowBaseAddress = (WindowBaseAddressShifted << 23);
|
|
WindowOffsetAddress = (ULONG)CBUS2_IO_APIC_LOCATION-WindowBaseAddress;
|
|
|
|
RegisterValue.csr_register = csr->BridgeWindow0.csr_register;
|
|
RegisterValue.ra.WindowBase = WindowBaseAddressShifted;
|
|
csr->BridgeWindow0.csr_register = RegisterValue.csr_register;
|
|
|
|
Cbus2Element = (PCBUS2_ELEMENT)
|
|
(CsrPhysicalAddress - CBUS_CSR_OFFSET);
|
|
|
|
CbusInitializeIOApic(Processor,
|
|
(PVOID)((ULONG)&Cbus2Element->PCBusWindow0 + WindowOffsetAddress),
|
|
CBUS2_REDIR_IPI,
|
|
CBUS2_REBOOT_TASKPRI,
|
|
Cbus2IrqPolarity[BridgeIndex]);
|
|
|
|
//
|
|
// restore our default bridge references to what they were
|
|
// when we started...
|
|
//
|
|
csr->BusBridgeSelection.csr_register = original_bridge;
|
|
}
|
|
|
|
//
|
|
// NOTE:
|
|
// add above for native Cbus2 cards using APICs based on cbdriver.c
|
|
// this will also involve changes to the CbusInitializeIOApic code.
|
|
//
|
|
}
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Initialize this processor's CSR, interrupts, spurious interrupts & IPI
|
|
vector. This routine is also responsible for setting the initial IRQL
|
|
value for this processor to 0 - this is so the first call to KfRaiseIrql
|
|
from the kernel will return a level 0 as the "previous level". note that
|
|
lowering the task priority to irql 0 is harmless at this point because
|
|
we are protected by cli.
|
|
|
|
Arguments:
|
|
|
|
Processor - Supplies a logical processor number
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
VOID
|
|
Cbus2InitializeCPU(
|
|
IN ULONG Processor
|
|
)
|
|
{
|
|
PCSR csr;
|
|
ULONG cbc_config;
|
|
|
|
//
|
|
// Disable all of this processor's incoming interrupts _AND_
|
|
// any generated by his local CBC (otherwise they could go to
|
|
// any processor). This has to be done regardless of whether
|
|
// APIC or CBC mode is being used.
|
|
//
|
|
Cbus2DisableMyInterrupts(Processor);
|
|
|
|
//
|
|
// Setup the interrupt controller as specified by RRD -
|
|
// currently we support either CBC or APIC...
|
|
//
|
|
|
|
if (Cbus2InterruptController == ELEMENT_HAS_CBC) {
|
|
Cbus2InitializeCBC(Processor);
|
|
}
|
|
|
|
else if (Cbus2InterruptController == ELEMENT_HAS_APIC) {
|
|
CbusInitializeLocalApic(Processor, CBUS2_LOCAL_APIC_LOCATION,
|
|
CBUS2_SPURIOUS_TASKPRI);
|
|
|
|
//
|
|
// Only the boot processor initializes all the I/O APICs
|
|
// on all the EISA bridges and native Cbus2 cards.
|
|
//
|
|
|
|
if (Processor == 0) {
|
|
Cbus2InitializeApic(Processor);
|
|
}
|
|
else {
|
|
KiSetHandlerAddressToIDT(CBUS2_REBOOT_TASKPRI,
|
|
CbusRebootHandler);
|
|
HalEnableSystemInterrupt(CBUS2_REBOOT_TASKPRI,
|
|
IPI_LEVEL, Latched);
|
|
}
|
|
}
|
|
else {
|
|
FatalError(MSG_OBSOLETE_PIC);
|
|
}
|
|
|
|
//
|
|
// Set up a pointer to the global system timer for all of the
|
|
// processors. We directly access this from our low-level
|
|
// assembly code.
|
|
//
|
|
// Since we are setting this in global C-bus address space
|
|
// by using the broadcast mechanism, all the processors
|
|
// can provide a uniform synchronized view via
|
|
// KeQueryPerformanceCounter.
|
|
//
|
|
|
|
if (Processor == 0) {
|
|
KeInitializeSpinLock(&Cbus2NMILock);
|
|
|
|
csr = (PCSR)CbusCSR[Processor].csr;
|
|
|
|
Cbus2TimeStamp0 = &csr->SystemTimer.LowDword;
|
|
#ifdef CBC_REV1
|
|
CbusTimeStamp =
|
|
&((PCSR)CbusBroadcastCSR)->SystemTimer.LowDword;
|
|
#else
|
|
CbusTimeStamp =
|
|
&((PCSR)Cbus2IdentityCSR)->SystemTimer.LowDword;
|
|
#endif
|
|
|
|
if (CbusGlobal.Cbus2Features & CBUS_ENABLED_PW) {
|
|
|
|
#if DBG
|
|
DbgPrint("Enabling posted writes\n");
|
|
#endif
|
|
|
|
//
|
|
// if the posted-writes bit is enabled, then
|
|
// allow EISA I/O cycles to use posted writes.
|
|
//
|
|
// call a function here so the compiler won't use byte
|
|
// enables here - we must force a dword access.
|
|
//
|
|
cbc_config = Cbus2ReadCSR(&csr->CbcConfiguration.LowDword);
|
|
Cbus2WriteCSR(&csr->CbcConfiguration.LowDword,
|
|
cbc_config & ~CBC_DISABLE_PW);
|
|
}
|
|
|
|
//
|
|
// C-bus II hardware work-around that is causing less
|
|
// than optimal code in the CBUS_EOI macro.
|
|
//
|
|
if ((CbusGlobal.Cbus2Features &
|
|
CBUS_DISABLE_LEVEL_TRIGGERED_INT_FIX) == 0) {
|
|
Cbus2FixLevelInterrupts = 1;
|
|
}
|
|
|
|
//
|
|
// C-bus II hardware work-around for spurious interrupts
|
|
// on IRQ0 when edge-triggered interrupts are EOI'd
|
|
// and the interrupt is high. work-around consists of
|
|
// detecting spurious clock interrupts in Cbus2ClockInterrupt()
|
|
// and Cbus2ClockInterruptPx().
|
|
//
|
|
if ((CbusGlobal.Cbus2Features &
|
|
CBUS_DISABLE_SPURIOUS_CLOCK_CHECK) == 0) {
|
|
Cbus2CheckSpuriousClock = 1;
|
|
}
|
|
|
|
//
|
|
// C-bus II hardware work-around that prevents Cbus2RequestIpi()
|
|
// from using the CSR broadcast space for sending IPIs.
|
|
//
|
|
if ((CbusGlobal.Cbus2Features & CBUS_ENABLE_BROADCAST) == 1) {
|
|
Cbus2EnableBroadcast = 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function returns the system interrupt vector and IRQL level
|
|
corresponding to the specified bus interrupt level and/or vector. The
|
|
system interrupt vector and IRQL are suitable for use in a subsequent call
|
|
to KeInitializeInterrupt.
|
|
|
|
HalGetInterruptVector() must maintain a "vector to interrupt"
|
|
mapping so when the interrupt is enabled later via
|
|
HalEnableSystemInterrupt(), something intelligent can be done -
|
|
ie: which CBC's hardware interrupt maps to enable!
|
|
this applies both to EISA bridge CBCs and Cbus2 native I/O CBC's.
|
|
|
|
Note that HalEnableSystemInterrupt() will be CALLED by
|
|
EACH processor wishing to participate in the interrupt receipt.
|
|
|
|
Do not detect collisions here because interrupts are allowed to be
|
|
shared at a higher level - the ke\i386\intobj.c will take care of
|
|
the sharing. Just make sure that for any given irq line, only one
|
|
vector is generated, regardless of how many drivers may try to share
|
|
the line.
|
|
|
|
Arguments:
|
|
|
|
BusInterruptLevel - Supplies the bus specific interrupt level.
|
|
|
|
BusInterruptVector - Supplies the bus specific interrupt vector.
|
|
|
|
Irql - Returns the system request priority.
|
|
|
|
Return Value:
|
|
|
|
Returns the system interrupt vector corresponding to the specified device.
|
|
|
|
--*/
|
|
ULONG
|
|
Cbus2MapVector(
|
|
IN PBUS_HANDLER RootHandler,
|
|
IN ULONG BusInterruptLevel,
|
|
IN ULONG BusInterruptVector,
|
|
OUT PKIRQL Irql
|
|
)
|
|
{
|
|
ULONG SystemVector;
|
|
ULONG Index;
|
|
|
|
UNREFERENCED_PARAMETER( BusInterruptVector );
|
|
|
|
if (RootHandler->ConfigurationType == CbusConfiguration) {
|
|
//
|
|
// Each CBC interrupt line on each board
|
|
// gets a different interrupt vector, ie: irq3 on CBC 1
|
|
// gets a different vector from irq3 on CBC2.
|
|
//
|
|
SystemVector = Cbus2CBCIrqlines[BusInterruptLevel].Vector +
|
|
Cbus2MapBusNumberToBridgeIndex(RootHandler->BusNumber);
|
|
|
|
//
|
|
// Group each pair of CBC irqs into a single IRQL.
|
|
//
|
|
*Irql = Cbus2CBCIrqlines[BusInterruptLevel].Irql;
|
|
}
|
|
else {
|
|
|
|
//
|
|
// Must be EISA, ISA, PCI or MCA...
|
|
//
|
|
// For Cbus2, CBUS_MAX_BRIDGES(==2) entries have been allocated per
|
|
// NT IRQL level, with each irq line getting its own _vector_.
|
|
// However, the same irq on each EISA bus shares the same NT IRQL
|
|
// level, since not enough IRQLs are provided to make them
|
|
// unique also.
|
|
//
|
|
|
|
//
|
|
// note that EISA bus 0 gets the lower vector and EISA bus 1 gets
|
|
// the higher vector in each vector pair. this fact is relied
|
|
// upon by Cbus2EnableDeviceInterrupt().
|
|
//
|
|
Index = Cbus2MapBusNumberToBridgeIndex(RootHandler->BusNumber);
|
|
SystemVector = Cbus2EISAIrqlines[Index][BusInterruptLevel].Vector;
|
|
|
|
*Irql = Cbus2EISAIrqlines[Index][BusInterruptLevel].Irql;
|
|
}
|
|
|
|
return SystemVector;
|
|
}
|
|
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
"Link" a given vector to the passed BusNumber/irqline, returning
|
|
a "handle" that can be used to reference it later for operations
|
|
that must access the hardware (ie: Enable & DisableInterrupt).
|
|
|
|
Arguments:
|
|
|
|
InterfaceType - Supplies the type of bus which the vector is for.
|
|
|
|
Vector - Supplies the system interrupt vector corresponding to the
|
|
specified BusNumber/Irqline.
|
|
|
|
BusNumber - Supplies the bus number for the device.
|
|
|
|
Irqline - Supplies the IRQ line of the specified interrupt
|
|
|
|
Return Value:
|
|
|
|
A hardware-specific pointer (actually a CSR hardware interrupt map address)
|
|
that is interpreted only by the Cbus2 backend.
|
|
|
|
--*/
|
|
PVOID
|
|
Cbus2LinkVector(
|
|
IN PBUS_HANDLER RootHandler,
|
|
IN ULONG Vector,
|
|
IN ULONG Irqline
|
|
)
|
|
{
|
|
PCSR csr;
|
|
PVOID Opaque;
|
|
extern PVOID CbusCBCtoCSR(ULONG);
|
|
|
|
if (Cbus2InterruptController == ELEMENT_HAS_APIC) {
|
|
Opaque = CbusApicLinkVector(RootHandler, Vector, Irqline);
|
|
return Opaque;
|
|
}
|
|
|
|
if (RootHandler && RootHandler->ConfigurationType == CbusConfiguration) {
|
|
//
|
|
// map the CBC hardware interrupt map on the Cbus2 native CBC
|
|
// that the hardware is attached to. note that this is
|
|
// purely a physical location issue; the access both to and from
|
|
// the driver's hardware is fully symmetric for ALL processors.
|
|
//
|
|
csr = (PCSR) CbusCBCtoCSR(
|
|
Cbus2MapBusNumberToBridgeIndex(RootHandler->BusNumber));
|
|
}
|
|
else {
|
|
|
|
//
|
|
// For any EISA interrupts, just point the caller at the
|
|
// corresponding bridge entry.
|
|
//
|
|
// Note also that this section of code is called by
|
|
// Cbus2SetupPrivateVectors() to set up CBC
|
|
// mappings at Phase0 for interrupts that must be enabled
|
|
// during Phase0. At that point in startup, the bus
|
|
// enumeration structures don't exist, so we default to
|
|
// EISA bridge 0 for any requests at that point in time.
|
|
//
|
|
|
|
if (!RootHandler)
|
|
csr = (PCSR)Cbus2BridgeCSR[BRIDGE0];
|
|
else
|
|
csr = (PCSR)Cbus2BridgeCSR[
|
|
Cbus2MapBusNumberToBridgeIndex(RootHandler->BusNumber)];
|
|
}
|
|
|
|
return (PVOID)csr;
|
|
}
|
|
|
|
//
|
|
//
|
|
// internal Cbus2 support routines begin here
|
|
//
|
|
//
|
|
//
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
by default, disable all of the calling processor's
|
|
interrupt configuration registers(ICR) so he will take no interrupts.
|
|
also disable all interrupts originating from his CBC, so
|
|
no other processor will get interrupts from any devices
|
|
attached to this CBC.
|
|
|
|
as each interrupt is enabled, it will need to be enabled
|
|
at this CBC, and also in each receiving processors' ICR.
|
|
|
|
all EISA bridges have had their interrupts disabled already.
|
|
as each interrupt is enabled, it will need to be enabled
|
|
at the bridge, and also on each processor participating
|
|
in the reception. this needs to be done for both APIC and CBC
|
|
modes.
|
|
|
|
Arguments:
|
|
|
|
Processor - Supplies the caller's logical processor number whose
|
|
interrupts will be disabled
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
VOID
|
|
Cbus2DisableMyInterrupts(
|
|
IN ULONG Processor
|
|
)
|
|
{
|
|
ULONG Vector;
|
|
PCSR csr;
|
|
|
|
csr = (PCSR)KeGetPcr()->HalReserved[PCR_CSR];
|
|
|
|
for (Vector = 0; Vector < INTR_CONFIG_ENTRIES; Vector++) {
|
|
csr->InterruptConfiguration[Vector].csr_register =
|
|
HW_IMODE_DISABLED;
|
|
}
|
|
|
|
//
|
|
// In the midst of setting up the EISA element CBCs or
|
|
// processor CBCs (for those with native Cbus2 devices attached),
|
|
// a device interrupt that was pending in a bridge's
|
|
// 8259 ISRs may be lost. None should be fatal, even an
|
|
// 8042 keystroke, since the keyboard driver should do a flush
|
|
// on open, and thus recover in the same way the standard
|
|
// uniprocessor NT HAL does when it initializes 8259s.
|
|
//
|
|
// the hardware interrupt map entries for this processor's
|
|
// CBC are disabled by RRD prior to booting each processor,
|
|
// so it doesn't need to be done here.
|
|
//
|
|
}
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Allocate an interrupt vector for a Cbus2 native device.
|
|
|
|
Arguments:
|
|
|
|
BusHandler - Supplies the parent (ie: Internal) pointer
|
|
|
|
RootHandler - Supplies the bus (ie: Cbus2) for the device. For Cbus2
|
|
native I/O devices, the bus number is actually the CBC
|
|
number index in the CbusIoElements[] table.
|
|
|
|
BusInterruptLevel - Supplies the IRQ line of the specified interrupt
|
|
|
|
BusInterruptVector - Unused
|
|
|
|
Irql - Returns the IRQL associated with this interrupt request
|
|
|
|
Affinity - Returns the mask of processors participating in receipt
|
|
of this interrupt
|
|
|
|
Return Value:
|
|
|
|
Returns the system interrupt vector corresponding to the
|
|
specified BusNumber/BusInterruptLevel.
|
|
|
|
--*/
|
|
ULONG
|
|
Cbus2GetInterruptVector(
|
|
IN PBUS_HANDLER BusHandler,
|
|
IN PBUS_HANDLER RootHandler,
|
|
IN ULONG BusInterruptLevel,
|
|
IN ULONG BusInterruptVector,
|
|
OUT PKIRQL Irql,
|
|
OUT PKAFFINITY Affinity
|
|
)
|
|
{
|
|
|
|
extern ULONG CbusIoElementIndex;
|
|
|
|
UNREFERENCED_PARAMETER( BusInterruptVector );
|
|
|
|
#if 0
|
|
//
|
|
// can't do this because we don't include ntddk.h
|
|
//
|
|
ASSERT (RootHandler->InterfaceType == Cbus);
|
|
#endif
|
|
|
|
if (BusInterruptLevel >= REV1_HWINTR_MAP_ENTRIES) {
|
|
|
|
//
|
|
// Illegal BusInterruptVector - do not connect.
|
|
//
|
|
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// Get parent's translation from here...
|
|
//
|
|
return BusHandler->ParentHandler->GetInterruptVector (
|
|
BusHandler->ParentHandler,
|
|
RootHandler,
|
|
BusInterruptLevel,
|
|
BusInterruptVector,
|
|
Irql,
|
|
Affinity
|
|
);
|
|
}
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Convert a PCI interrupt pin to an interrupt line.
|
|
Note that this work has already been done in
|
|
Cbus2HierarchicalPciBusSearch().
|
|
|
|
Arguments:
|
|
|
|
BusHandler - Supplies the parent (ie: Internal) pointer
|
|
|
|
RootHandler - Supplies the bus for the device
|
|
|
|
SlotNumber - Slot number of request
|
|
|
|
PciData - PCI configuration data
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
VOID
|
|
Cbus2PCIPin2CB2Line (
|
|
IN PBUS_HANDLER BusHandler,
|
|
IN PBUS_HANDLER RootHandler,
|
|
IN PCI_SLOT_NUMBER SlotNumber,
|
|
IN PPCI_COMMON_CONFIG PciData
|
|
)
|
|
{
|
|
}
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Convert a PCI interrupt line to an interrupt pin.
|
|
Note that this work has already been done in
|
|
Cbus2HierarchicalPciBusSearch().
|
|
|
|
Arguments:
|
|
|
|
BusHandler - Supplies the parent (ie: Internal) pointer
|
|
|
|
RootHandler - Supplies the bus for the device
|
|
|
|
SlotNumber - Slot number of request
|
|
|
|
PciNewData - New PCI configuration data
|
|
|
|
PciOldData - Old PCI configuration data
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
VOID
|
|
Cbus2PCICB2Line2Pin (
|
|
IN PBUS_HANDLER BusHandler,
|
|
IN PBUS_HANDLER RootHandler,
|
|
IN PCI_SLOT_NUMBER SlotNumber,
|
|
IN PPCI_COMMON_CONFIG PciNewData,
|
|
IN PPCI_COMMON_CONFIG PciOldData
|
|
)
|
|
{
|
|
}
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Returns an interrupt range for the given PciSlot.
|
|
|
|
Arguments:
|
|
|
|
BusHandler - Supplies the parent (ie: Internal) pointer
|
|
|
|
RootHandler - Supplies the bus for the device
|
|
|
|
PciSlot - Slot number of request
|
|
|
|
Interrupt - Pointer to returned Interrupt structure
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
NTSTATUS
|
|
Cbus2GetFixedPCICB2Line (
|
|
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 ||
|
|
PCI_CONFIG_TYPE (PciData) != 0) {
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
*Interrupt = ExAllocatePool (PagedPool, sizeof (SUPPORTED_RANGE));
|
|
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
|
|
Cbus2InitOtherBuses(VOID)
|
|
{
|
|
PBUS_HANDLER Bus;
|
|
ULONG i;
|
|
extern ULONG CBCIndex;
|
|
|
|
//
|
|
// For each Cbus2 CBC present, add a handler
|
|
//
|
|
for (i=0; i < CBCIndex; i++) {
|
|
Bus = HalpAllocateBusHandler (
|
|
CBus, // Interface type
|
|
CbusConfiguration, // Has this configuration space
|
|
i, // bus #
|
|
Internal, // child of this bus
|
|
0, // and number
|
|
0 // sizeof bus specific buffer
|
|
); //(should be sizeof CBUS_IO_ELEMENTS_T
|
|
|
|
//
|
|
// Add Cbus configuration space
|
|
//
|
|
|
|
Bus->GetBusData = (PGETSETBUSDATA) Cbus2GetCbusData;
|
|
Bus->SetBusData = (PGETSETBUSDATA) Cbus2SetCbusData;
|
|
Bus->GetInterruptVector = (PGETINTERRUPTVECTOR) Cbus2GetInterruptVector;
|
|
|
|
#if 0
|
|
//
|
|
// NOT CODED YET: BusSpecific data is a pointer to the info
|
|
// (if possible this structure should be the bus specific info)
|
|
//
|
|
Bus->BusData = (PVOID) &CbusIoElements[i];
|
|
Bus->AdjustResourceList
|
|
Bus->AssignSlotResources
|
|
#endif
|
|
Bus->AdjustResourceList = HalpNoAdjustResourceList;
|
|
Bus->AssignSlotResources = HalpNoAssignSlotResources;
|
|
|
|
}
|
|
}
|
|
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Check for a supported multiprocessor interrupt controller - currently
|
|
this means CBC or APIC only.
|
|
|
|
Check for Cbus2 I/O bridges and disable their incoming interrupts
|
|
here. This cannot be done in HalInitializeProcessor() because generally
|
|
the I/O bridge will not have a CPU on it.
|
|
|
|
Arguments:
|
|
|
|
Table - Supplies a pointer to the RRD extended ID information table
|
|
|
|
Count - Supplies a pointer to the number of valid entries in the
|
|
RRD extended ID information table
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
VOID
|
|
Cbus2ParseRRD(
|
|
IN PEXT_ID_INFO Table,
|
|
IN OUT PULONG Count
|
|
)
|
|
{
|
|
ULONG Index;
|
|
PEXT_ID_INFO Idp;
|
|
PCSR csr;
|
|
UCHAR control_register;
|
|
ULONG original_bridge;
|
|
PCBUS2_ELEMENT Cbus2Element;
|
|
WINDOWRELOC_T RegisterValue;
|
|
PCHAR BusMemoryWindow0;
|
|
PCHAR BusMemoryWindow1;
|
|
extern VOID CbusDisable8259s(USHORT);
|
|
|
|
for (Idp = Table, Index = 0; Index < *Count; Index++, Idp++) {
|
|
|
|
//
|
|
// map the identity range of the CSR space so each CPU
|
|
// can access his own CSR without knowing his slot id.
|
|
// this is useful because the task priority (and other
|
|
// registers) can now be referenced at the same physical
|
|
// address regardless of which processor you're currently
|
|
// executing on. if you couldn't reference it at the same
|
|
// physical address (regardless of processor), then you
|
|
// would need to pushfd/cli/popfd around capturing the
|
|
// current task priority address and setting it. otherwise
|
|
// you could be context switched in between, and you'd
|
|
// corrupt the initial processor's task priority!!!
|
|
//
|
|
if (Idp->id == CbusGlobal.broadcast_id) {
|
|
//
|
|
// unfortunately, the size and offset of the identity
|
|
// map is hardcoded in. we really should get RRD to
|
|
// tell us this offset.
|
|
//
|
|
Cbus2Element = (PCBUS2_ELEMENT)
|
|
(Idp->pel_start - CBUS_CSR_OFFSET);
|
|
|
|
Cbus2IdentityCSR = HalpMapPhysicalMemoryWriteThrough (
|
|
(PVOID)(Cbus2Element->IdentityMappedCsr),
|
|
(ULONG)ADDRESS_AND_SIZE_TO_SPAN_PAGES(
|
|
(ULONG)Cbus2Element->IdentityMappedCsr,
|
|
Idp->pel_size));
|
|
continue;
|
|
}
|
|
|
|
if (Idp->id == LAST_EXT_ID)
|
|
break;
|
|
|
|
// check only processor elements...
|
|
|
|
if (Idp->pm == 0)
|
|
continue;
|
|
|
|
//
|
|
// at least the base bridge must have a
|
|
// distributed interrupt chip (because
|
|
// any CPUs without them will be disabled).
|
|
//
|
|
if (Idp->id != CbusGlobal.bootid)
|
|
continue;
|
|
|
|
//
|
|
// check for CBC first, since if types of interrupt
|
|
// controllers are marked present, we will default to
|
|
// the CBC.
|
|
//
|
|
if (Idp->pel_features & ELEMENT_HAS_CBC) {
|
|
Cbus2InterruptController = ELEMENT_HAS_CBC;
|
|
continue;
|
|
}
|
|
|
|
if (Idp->pel_features & ELEMENT_HAS_APIC) {
|
|
Cbus2InterruptController = ELEMENT_HAS_APIC;
|
|
//
|
|
// patch the ipi vector to one compatible with
|
|
// the cbusapic.asm code.
|
|
//
|
|
Cbus2IrqlToVector[IPI_LEVEL] = CBUS2_ALTERNATE_IPI;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
//
|
|
// there must be at least an APIC or a CBC for us to use
|
|
//
|
|
if ((Cbus2InterruptController &
|
|
(ELEMENT_HAS_APIC | ELEMENT_HAS_CBC)) == 0)
|
|
FatalError(MSG_OBSOLETE_PIC);
|
|
|
|
for (Idp = Table, Index = 0; Index < *Count; Index++, Idp++) {
|
|
|
|
if ((Idp->pel_features & ELEMENT_BRIDGE) == 0) {
|
|
continue;
|
|
}
|
|
|
|
csr = HalpMapPhysicalMemoryWriteThrough (
|
|
(PVOID)Idp->pel_start,
|
|
(ULONG)ADDRESS_AND_SIZE_TO_SPAN_PAGES(
|
|
Idp->pel_start, Idp->pel_size));
|
|
|
|
//
|
|
// to go from 8259 to CBC (or APIC) mode for interrupt handling,
|
|
//
|
|
// a) disable PC compatible interrupts, ie: stop each
|
|
// bridge CBC from asking its 8259 to satisfy INTA
|
|
// pulses to the CPU.
|
|
// b) mask off ALL 8259 interrupt input lines EXCEPT
|
|
// for irq0. since clock interrupts are not external
|
|
// in the EISA chipset, the bridge 8259 must enable
|
|
// them even when the CBC is enabled. putting the
|
|
// 8259 in passthrough mode (ie: the 8259 irq0 input
|
|
// will just be wired straight through) WILL NOT
|
|
// allow the 8259 to actually talk to the CPU; it
|
|
// just allows the interrupt to be seen by the CBC.
|
|
// the CBC is responsible for all the CPU interrupt
|
|
// handshaking.
|
|
// c) enable each participating element's (ie: CPUs only)
|
|
// interrupt configuration register for the vector
|
|
// the HAL has programmed irq0 to actually generate.
|
|
// d) initialize the hardware interrupt map for the irq0
|
|
// entry.
|
|
//
|
|
// IT IS CRITICAL THAT THE ABOVE STEPS HAPPEN IN THE
|
|
// ORDER OUTLINED, OTHERWISE YOU MAY SEE SPURIOUS
|
|
// INTERRUPTS.
|
|
//
|
|
|
|
//
|
|
// now process this I/O bridge:
|
|
//
|
|
// currently assumes that all bridges will be of the same
|
|
// flavor. if this element is a bridge, map it systemwide
|
|
// and disable all incoming interrupts on this bridge.
|
|
// any extra bridges beyond our configuration maximum
|
|
// are just disabled, and not used by NT.
|
|
//
|
|
|
|
if (Cbus2BridgesFound < CBUS_MAX_BRIDGES) {
|
|
Cbus2BridgeCSR[Cbus2BridgesFound] = csr;
|
|
Cbus2BridgeId[Cbus2BridgesFound] = Idp->id;
|
|
Cbus2BridgeCSRPaddr[Cbus2BridgesFound].HighPart = 0;
|
|
Cbus2BridgeCSRPaddr[Cbus2BridgesFound].LowPart =
|
|
Idp->pel_start;
|
|
if ((Idp->id != CBUS2_DEFAULT_BRIDGE_ID) &&
|
|
(csr->IoTypeInformation.LowDword != CBUS2_ELEMENT_TYPE_RAS)) {
|
|
//
|
|
// Use PC Bus Window 0 and 1 of the SPP
|
|
// bus bridge for memory space allocation
|
|
// of PCI devices on the SPP bus.
|
|
//
|
|
Cbus2Element = (PCBUS2_ELEMENT)((PCHAR) Idp->pel_start -
|
|
CBUS_CSR_OFFSET);
|
|
if (Cbus2InterruptController == ELEMENT_HAS_CBC) {
|
|
BusMemoryWindow0 = Cbus2Element->PCBusWindow0;
|
|
RegisterValue.csr_register =
|
|
csr->BridgeWindow0.csr_register;
|
|
RegisterValue.ra.WindowBase =
|
|
(ULONG)BusMemoryWindow0 >>
|
|
CBUS2_WINDOW_REGISTER_SHIFT;
|
|
csr->BridgeWindow0.csr_register =
|
|
RegisterValue.csr_register;
|
|
}
|
|
BusMemoryWindow1 = Cbus2Element->PCBusWindow1;
|
|
RegisterValue.csr_register =
|
|
csr->BridgeWindow1.csr_register;
|
|
RegisterValue.ra.WindowBase =
|
|
(ULONG)BusMemoryWindow1 >>
|
|
CBUS2_WINDOW_REGISTER_SHIFT;
|
|
csr->BridgeWindow1.csr_register =
|
|
RegisterValue.csr_register;
|
|
|
|
//
|
|
// Set Memory hole register of each additional
|
|
// bus bridge.
|
|
//
|
|
csr->PcBusMemoryHoles0.LowDword=CBUS2_NO_MEMORY_HOLES;
|
|
csr->PcBusMemoryHoles1.LowDword=CBUS2_NO_MEMORY_HOLES1;
|
|
csr->PcBusMemoryHoles2.LowDword=CBUS2_NO_MEMORY_HOLES2;
|
|
|
|
//
|
|
// Turn LED of SPP bus bridge on.
|
|
//
|
|
csr->LED.LowDword = CBUS2_LED_ON;
|
|
}
|
|
if (csr->IoTypeInformation.LowDword!=CBUS2_ELEMENT_TYPE_RAS)
|
|
Cbus2BridgesFound++;
|
|
}
|
|
|
|
if (Idp->pel_features & ELEMENT_HAS_8259) {
|
|
|
|
//
|
|
// disable all inputs in the 8259 IMRs except for the
|
|
// irq0. and explicitly force these masks onto the
|
|
// 8259s.
|
|
//
|
|
// if profiling is disabled, we will disable it in
|
|
// the interrupt configuration registers, but still
|
|
// we must leave the 8259 irq0 enabled. not to worry,
|
|
// the processor will not see irq0 interrupts.
|
|
// this way, if profiling is re-enabled later, we
|
|
// only need to change the interrupt configuration
|
|
// registers, and bingo, we provide the desired effect.
|
|
//
|
|
|
|
//
|
|
// save the original value for restoral after our read.
|
|
// repoint our I/O references to the desired bus
|
|
// bridge number.
|
|
//
|
|
original_bridge = csr->BusBridgeSelection.csr_register;
|
|
csr->BusBridgeSelection.csr_register =
|
|
((original_bridge & ~MAX_ELEMENT_CSRS) | Idp->id);
|
|
|
|
control_register =
|
|
READ_PORT_UCHAR((PUCHAR)CbusGlobal.Control8259Mode);
|
|
WRITE_PORT_UCHAR((PUCHAR)CbusGlobal.Control8259Mode,
|
|
(UCHAR)(control_register | (UCHAR)CbusGlobal.Control8259ModeValue));
|
|
|
|
#ifdef MCA
|
|
CbusDisable8259s(0xFFFF);
|
|
#else
|
|
CbusDisable8259s(0xFFFE);
|
|
#endif
|
|
|
|
//
|
|
// restore our default bridge references to what they
|
|
// were when we started...
|
|
//
|
|
csr->BusBridgeSelection.csr_register = original_bridge;
|
|
}
|
|
|
|
//
|
|
// In the midst of setting up the EISA element CBCs or
|
|
// processor CBCs (for those with Cbus2 native devices), a
|
|
// device interrupt that was pending in a bridge's 8259 ISRs
|
|
// may be lost. None should be fatal, even an
|
|
// 8042 keystroke, since the keyboard driver does a flush
|
|
// on open, and will, thus recover in the same way the standard
|
|
// uniprocessor NT HAL does when it initializes 8259s.
|
|
//
|
|
// If RRD instructed us to operate in APIC mode, then we want
|
|
// all the hardware interrupt map registers disabled as well.
|
|
// so this code works for both CBC and APIC modes.
|
|
//
|
|
// the hardware interrupt map entries for this processor's
|
|
// CBC are disabled by RRD prior to booting each processor,
|
|
// so it doesn't need to be done here.
|
|
//
|
|
}
|
|
}
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Called to put all the other processors in reset prior to reboot.
|
|
In order to safely put the other processors in reset, an IPI is
|
|
sent to the other processors to force them to write-invalidate
|
|
their L1 cache and halt. The calling processor will wait 5 seconds
|
|
for the IPI to take affect.
|
|
|
|
Once accomplished, the calling processor then clears all hardware
|
|
interrupt maps on the default bridge. The interrupt controller
|
|
is reset back to the 8259s. The keyboard controller is then
|
|
reset and ThisProcessor spins awaiting the reboot.
|
|
|
|
This routine can be called at any IRQL and can be called
|
|
whilst cli'd at interrupt time.
|
|
|
|
Arguments:
|
|
|
|
ThisProcessor - Supplies the caller's logical processor number
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
VOID
|
|
Cbus2ResetAllOtherProcessors(
|
|
IN ULONG ThisProcessor
|
|
)
|
|
{
|
|
PHWINTRMAP hwentry;
|
|
PCSR io_csr;
|
|
ULONG Index;
|
|
ULONG Irq;
|
|
PCSR csr;
|
|
UCHAR control_register;
|
|
|
|
//
|
|
// repoint our I/O references to the default bus bridge
|
|
// don't bother saving and restoring the current bridge
|
|
// selection since we're about to reboot anyway.
|
|
//
|
|
csr = (PCSR)CbusCSR[ThisProcessor].csr;
|
|
csr->BusBridgeSelection.csr_register = Cbus2BridgeId[0];
|
|
|
|
control_register = READ_PORT_UCHAR((PUCHAR)CbusGlobal.Control8259Mode);
|
|
|
|
//
|
|
// we need to protect ourselves from interrupts as the
|
|
// CBC will be disabled with our next write and the 8259s
|
|
// will be in control...
|
|
//
|
|
_asm {
|
|
cli
|
|
}
|
|
|
|
//
|
|
// IPI the additional processors to get them to flush
|
|
// their internal caches and halt. this will be more
|
|
// conducive for the subsequent reset.
|
|
//
|
|
for (Index = 0; Index < CbusProcessors; Index++) {
|
|
if (Index == ThisProcessor)
|
|
continue;
|
|
csr = (PCSR)CbusCSR[Index].csr;
|
|
Cbus2WriteCSR((PULONG)
|
|
&(csr->InterruptRequest[CBUS2_REBOOT_TASKPRI]), 1);
|
|
}
|
|
|
|
//
|
|
// Delay 5 seconds to give the additional processors
|
|
// a chance to get the previous IPI.
|
|
//
|
|
KeStallExecutionProcessor(5000000);
|
|
|
|
//
|
|
// Disable all the bridge hardware interrupt maps.
|
|
// This will disable all EISA IRQ interrupts.
|
|
//
|
|
for (Index = 0; Index < Cbus2BridgesFound; Index++) {
|
|
io_csr = (PCSR)Cbus2BridgeCSR[Index];
|
|
for (Irq = 0; Irq <= EISA_IRQLS; Irq++) {
|
|
hwentry = &io_csr->HardwareInterruptMap[Irq];
|
|
hwentry->csr_register = 0;
|
|
}
|
|
}
|
|
|
|
WRITE_PORT_UCHAR((PUCHAR)CbusGlobal.Control8259Mode,
|
|
(UCHAR)(control_register & ~(UCHAR)CbusGlobal.Control8259ModeValue));
|
|
|
|
//
|
|
// reset the keyboard controller.
|
|
//
|
|
WRITE_PORT_UCHAR(KEYBPORT, RESET);
|
|
loop:
|
|
goto loop;
|
|
}
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function initializes the HAL-specific hardware device
|
|
(CLOCK & PROFILE) interrupts for the Corollary Cbus2 architecture.
|
|
|
|
Arguments:
|
|
|
|
none.
|
|
|
|
Return Value:
|
|
|
|
VOID
|
|
|
|
--*/
|
|
VOID
|
|
Cbus2InitializeDeviceIntrs(
|
|
IN ULONG Processor
|
|
)
|
|
{
|
|
PCSR csr;
|
|
ULONG TimeStamp;
|
|
extern VOID Cbus2ClockInterrupt(VOID);
|
|
extern VOID Cbus2ClockInterruptPx(VOID);
|
|
|
|
//
|
|
// here we initialize & enable all the device interrupts.
|
|
// this routine is called from HalInitSystem.
|
|
//
|
|
// each processor needs to call KiSetHandlerAddressToIDT()
|
|
// and HalEnableSystemInterrupt() for himself.
|
|
//
|
|
|
|
if (Processor == 0) {
|
|
|
|
//
|
|
// Support the HAL's exported interface to the rest of the
|
|
// system for the IDT configuration. This routine will
|
|
// also set up the IDT entry and enable the actual interrupt.
|
|
//
|
|
// Only one processor needs to do this, especially since
|
|
// the additional processors are vectoring elsewhere for speed.
|
|
//
|
|
|
|
HalpEnableInterruptHandler (
|
|
DeviceUsage, // Mark as device vector
|
|
IRQ0, // Bus interrupt level
|
|
CbusClockVector, // System IDT
|
|
CLOCK2_LEVEL, // System Irql
|
|
Cbus2ClockInterrupt, // ISR
|
|
Latched);
|
|
|
|
HalpEnableInterruptHandler (
|
|
DeviceUsage, // Mark as device vector
|
|
IRQ8, // Bus interrupt level
|
|
ProfileVector, // System IDT
|
|
PROFILE_LEVEL, // System Irql
|
|
HalpProfileInterrupt, // ISR
|
|
Latched);
|
|
|
|
}
|
|
else {
|
|
//
|
|
// if the spurious clock check is enabled, then
|
|
// synchronize this CPU's SystemTimer by capturing CPU0's.
|
|
//
|
|
if (Cbus2CheckSpuriousClock) {
|
|
csr = (PCSR)CbusCSR[Processor].csr;
|
|
TimeStamp = Cbus2ReadCSR(Cbus2TimeStamp0);
|
|
Cbus2WriteCSR(&csr->SystemTimer.LowDword, TimeStamp);
|
|
}
|
|
|
|
KiSetHandlerAddressToIDT(CbusClockVector,Cbus2ClockInterruptPx);
|
|
HalEnableSystemInterrupt(CbusClockVector, CLOCK2_LEVEL, Latched);
|
|
|
|
KiSetHandlerAddressToIDT(ProfileVector, HalpProfileInterruptPx);
|
|
HalEnableSystemInterrupt(ProfileVector, PROFILE_LEVEL, Latched);
|
|
}
|
|
}
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Translate a Root BusNumber to a C-bus II bridge index.
|
|
|
|
Arguments:
|
|
|
|
Root BusNumber - Supplies the child bus.
|
|
|
|
Return Value:
|
|
|
|
Index - The C-bus II bridge index.
|
|
|
|
--*/
|
|
ULONG
|
|
Cbus2MapBusNumberToBridgeIndex(
|
|
ULONG RootBusNumber
|
|
)
|
|
{
|
|
ULONG Index;
|
|
|
|
for (Index = Cbus2BridgesFound - 1; Index >= 0; Index--) {
|
|
if (RootBusNumber >= Cbus2BridgePCIBusNumber[Index])
|
|
break;
|
|
}
|
|
|
|
return Index;
|
|
}
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Translate a bridge ID to an index.
|
|
|
|
Arguments:
|
|
|
|
BridgeId - Supplies the bridge ID.
|
|
|
|
Return Value:
|
|
|
|
Index - The C-bus II bridge index.
|
|
|
|
--*/
|
|
ULONG
|
|
Cbus2MapBridgeIdToIndex(
|
|
ULONG BridgeId
|
|
)
|
|
{
|
|
ULONG Index;
|
|
|
|
for (Index = Cbus2BridgesFound - 1; Index >= 0; Index--) {
|
|
if (BridgeId == Cbus2BridgeId[Index])
|
|
break;
|
|
}
|
|
|
|
return Index;
|
|
}
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Check the passed pRange list for qualifying ranges that
|
|
can be mapped into C-bus space.
|
|
|
|
Arguments:
|
|
|
|
pRange - Supplies the range list.
|
|
|
|
Base - Supplies the base of the C-bus II remappable range.
|
|
|
|
Limit - Supplies the limit of the C-bus II remappable range.
|
|
|
|
SystemBase - Supplies the SystemBase for the translation.
|
|
|
|
Return Value:
|
|
|
|
Number of new ranges added.
|
|
|
|
--*/
|
|
ULONG
|
|
Cbus2CheckRange(
|
|
PSUPPORTED_RANGE pRange,
|
|
ULONG Base,
|
|
ULONG Limit,
|
|
PCHAR SystemBase
|
|
)
|
|
{
|
|
ULONG NewRanges = 0;
|
|
|
|
for (; pRange; pRange = pRange->Next) {
|
|
//
|
|
// Check if in the range by checking for stradlers and fully
|
|
// contained Base/Limit pairs.
|
|
//
|
|
if ((pRange->Base <= Base && pRange->Limit >= Base) ||
|
|
(pRange->Base >= Base && pRange->Base <= Limit)) {
|
|
|
|
//
|
|
// Check for a base stradler.
|
|
//
|
|
if (pRange->Base < Base) {
|
|
HalpAddRange (
|
|
pRange,
|
|
pRange->SystemAddressSpace,
|
|
pRange->SystemBase,
|
|
Base,
|
|
pRange->Limit
|
|
);
|
|
pRange->Limit = Base - 1;
|
|
pRange = pRange->Next;
|
|
//
|
|
// Should still be sorted, but bump the count.
|
|
//
|
|
NewRanges++;
|
|
}
|
|
|
|
//
|
|
// Check for a limit stradler.
|
|
//
|
|
if (pRange->Limit > Limit) {
|
|
HalpAddRange (
|
|
pRange,
|
|
pRange->SystemAddressSpace,
|
|
pRange->SystemBase,
|
|
Limit + 1,
|
|
pRange->Limit
|
|
);
|
|
pRange->Limit = Limit;
|
|
//
|
|
// Should still be sorted, but bump the count.
|
|
//
|
|
NewRanges++;
|
|
}
|
|
|
|
//
|
|
// Set SystemBase for the qualifying pRange.
|
|
// Make sure that SystemAddressSpace is set to 0.
|
|
//
|
|
pRange->SystemBase = (ULONG)SystemBase;
|
|
pRange->SystemAddressSpace = 0;
|
|
}
|
|
}
|
|
|
|
return NewRanges;
|
|
}
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Check the passed pRange list for limits that
|
|
exceed that new limit. If so, lower the limit.
|
|
|
|
Arguments:
|
|
|
|
pRange - Supplies the range list.
|
|
|
|
NewLimit - Supplies the new limit for the given range list.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
VOID
|
|
Cbus2LowerLimit(
|
|
PSUPPORTED_RANGE pRange,
|
|
ULONG NewLimit
|
|
)
|
|
{
|
|
for (; pRange; pRange = pRange->Next) {
|
|
if (pRange->Limit > NewLimit)
|
|
pRange->Limit = NewLimit;
|
|
//
|
|
// Check if the extent is no longer valid.
|
|
//
|
|
if (pRange->Base > pRange->Limit) {
|
|
pRange->Base = 0;
|
|
pRange->Limit = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Set the range for this pRange to the passed base and limit.
|
|
|
|
Arguments:
|
|
|
|
pRange - Supplies the range list.
|
|
|
|
NewBase - Supplies the new base for the given range list.
|
|
|
|
NewLimit - Supplies the new limit for the given range list.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
VOID
|
|
Cbus2SetRange(
|
|
PSUPPORTED_RANGE pRange,
|
|
LONGLONG NewBase,
|
|
LONGLONG NewLimit
|
|
)
|
|
{
|
|
pRange->Base = NewBase;
|
|
pRange->Limit = NewLimit;
|
|
pRange->Next = 0;
|
|
}
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Called once, from HalReportResourceUsage(), to check all the
|
|
Ranges in all the PBUS_HANDLERs. note that it is currently
|
|
assumed that HalpInitializePciBus() is the last in the chain to
|
|
add and configure buses. if this condition changes, then the
|
|
calling of this routine will need to be adjusted.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
VOID
|
|
Cbus2CheckBusRanges(VOID)
|
|
{
|
|
PBUS_HANDLER Bus;
|
|
PSUPPORTED_RANGES BusAddresses;
|
|
ULONG InterfaceType;
|
|
ULONG BridgeIndex;
|
|
ULONG BusNumber;
|
|
|
|
//
|
|
// Check all the buses (InterfaceType, BusNumber) pairs ...
|
|
//
|
|
for (InterfaceType=0; InterfaceType<MaximumInterfaceType; InterfaceType++) {
|
|
for (BusNumber = 0; ; BusNumber++) {
|
|
if ((Bus = HaliHandlerForBus (InterfaceType, BusNumber)) == NULL) {
|
|
break;
|
|
}
|
|
if (Bus->InterfaceType != PCIBus)
|
|
break;
|
|
BusAddresses = Bus->BusAddresses;
|
|
if (BusAddresses) {
|
|
//
|
|
// Get the root bus, or C-bus II bridge index.
|
|
// The SystemBase will be either:
|
|
//
|
|
// IO - offset 0x3c10000 into the CSR space
|
|
// Memory - offset 0x0 into the CSR space
|
|
//
|
|
BridgeIndex = Cbus2MapBusNumberToBridgeIndex(Bus->BusNumber);
|
|
|
|
//
|
|
// For compatibility with non-Microsoft maintained
|
|
// device drivers, don't translate bus 0 addresses.
|
|
//
|
|
if (BridgeIndex == 0) {
|
|
//
|
|
// ensure that the limit of any memory range
|
|
// on the primary bus bridge is below the
|
|
// CSR space.
|
|
//
|
|
Cbus2LowerLimit(&BusAddresses->PrefetchMemory,
|
|
CbusGlobal.cbusio - 1);
|
|
Cbus2LowerLimit(&BusAddresses->Memory,
|
|
CbusGlobal.cbusio - 1);
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Called once, from HalReportResourceUsage(), to check all the
|
|
Ranges in all the PBUS_HANDLERs. note that it is currently
|
|
assumed that HalpInitializePciBus() is the last in the chain to
|
|
add and configure buses. if this condition changes, then the
|
|
calling of this routine will need to be adjusted.
|
|
|
|
Arguments:
|
|
|
|
Bus - Bus Handler to check.
|
|
|
|
BridgeIndex - Logical bridge index.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
VOID
|
|
Cbus2AdjustSPPBusRange(
|
|
PBUS_HANDLER Bus,
|
|
UCHAR BridgeIndex,
|
|
UCHAR RootPciBus
|
|
)
|
|
{
|
|
PSUPPORTED_RANGES BusAddresses;
|
|
PHYSICAL_ADDRESS CsrPhysicalAddress;
|
|
PCBUS2_ELEMENT Cbus2Element;
|
|
PCHAR IOSystemBase;
|
|
LONGLONG MemorySystemBase;
|
|
ULONG MemorySystemLimit;
|
|
|
|
if (!(BusAddresses = Bus->BusAddresses))
|
|
return;
|
|
//
|
|
// check if the range of the IO bus address is within
|
|
// CBUS2_IO_BASE_ADDRESS & CBUS2_IO_LIMIT. if so,
|
|
// add IOSystemBase to uniquely target the I/O to the
|
|
// SPP bus bridge.
|
|
//
|
|
CsrPhysicalAddress = Cbus2BridgeCSRPaddr[BridgeIndex];
|
|
Cbus2Element = (PCBUS2_ELEMENT)
|
|
((PCHAR)CsrPhysicalAddress.LowPart - CBUS_CSR_OFFSET);
|
|
IOSystemBase = Cbus2Element->PCBusIO;
|
|
BusAddresses->NoIO += Cbus2CheckRange(&BusAddresses->IO,
|
|
CBUS2_IO_BASE_ADDRESS, CBUS2_IO_LIMIT, IOSystemBase);
|
|
if (RootPciBus) {
|
|
if (Cbus2InterruptController == ELEMENT_HAS_CBC) {
|
|
MemorySystemBase = (ULONGLONG)
|
|
((ULONG)Cbus2Element->PCBusWindow0);
|
|
MemorySystemLimit = ((ULONG)Cbus2Element->PCBusWindow0 +
|
|
(sizeof (Cbus2Element->PCBusWindow0) * 2) - 1);
|
|
}
|
|
else {
|
|
MemorySystemBase = (ULONGLONG)
|
|
((ULONG)Cbus2Element->PCBusWindow1);
|
|
MemorySystemLimit = ((ULONG)Cbus2Element->PCBusWindow1 +
|
|
sizeof (Cbus2Element->PCBusWindow1) - 1);
|
|
}
|
|
Cbus2SetRange(&BusAddresses->PrefetchMemory,
|
|
MemorySystemBase, MemorySystemLimit);
|
|
Cbus2SetRange(&BusAddresses->Memory,
|
|
MemorySystemBase, MemorySystemLimit);
|
|
}
|
|
}
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Called once, from CbusEstablishMaps(), to find the holes in the
|
|
C-bus memory space that is not useable for device allocation.
|
|
These ranges are entered into a table and this table is then used:
|
|
|
|
1. to remove the hole ranges from the memory descriptor table as
|
|
passed in from the BIOS E820 function (see comment in HalpAddMem()).
|
|
|
|
2. to add to the resource list used by the C-bus HAL.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
VOID
|
|
Cbus2AddMemoryHoles(VOID)
|
|
{
|
|
PCSR csr;
|
|
ULONG Index;
|
|
ULONG Start;
|
|
ULONG TLM;
|
|
|
|
//
|
|
// HalpMemory[] lists all the valid memory ranges reported by RRD.
|
|
// use AddMemoryHole() and AddMemoryResource() to remove gaps
|
|
// between these memory ranges.
|
|
//
|
|
for (Index = 0; Index < HalpMemoryIndex - 1; Index++) {
|
|
|
|
Start = PAGES_TO_BYTES(HalpMemory[Index].BasePage +
|
|
HalpMemory[Index].PageCount),
|
|
AddMemoryHole(Start,
|
|
PAGES_TO_BYTES(HalpMemory[Index+1].BasePage) - Start);
|
|
AddMemoryResource(Start,
|
|
PAGES_TO_BYTES(HalpMemory[Index+1].BasePage) - Start);
|
|
|
|
}
|
|
|
|
//
|
|
// the final memory gap is from the end of the last memory range
|
|
// through the TLM register. the TLM register marks the start
|
|
// of resource allocatable memory. the TLM register is in units
|
|
// of 256MB where 0 indicates 256MB.
|
|
//
|
|
csr = (PCSR)Cbus2BridgeCSR[BRIDGE0];
|
|
TLM = (Cbus2ReadCSR(&csr->TLM.LowDword) + 1) * 256 * 1024 * 1024;
|
|
Start = PAGES_TO_BYTES(HalpMemory[Index].BasePage +
|
|
HalpMemory[Index].PageCount);
|
|
if (TLM - Start) {
|
|
AddMemoryHole(Start, TLM - Start);
|
|
AddMemoryResource(Start, TLM - Start);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Mask for valid bits of edge/level control register (ELCR) in 82357 ISP:
|
|
// ie: ensure irqlines 0, 1, 2, 8 and 13 are always marked edge, as the
|
|
// I/O register will not have them set correctly. All other bits in the
|
|
// I/O register will be valid without us having to poke them.
|
|
//
|
|
// If this is a microchannel machine, then we don't use the mask.
|
|
//
|
|
#define ELCR_MASK 0xDEF8
|
|
|
|
#define PIC1_ELCR_PORT (PUCHAR)0x4D0 // ISP edge/level control regs
|
|
#define PIC2_ELCR_PORT (PUCHAR)0x4D1
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Called once to read the EISA interrupt configuration registers.
|
|
This will tell us which interrupt lines are level-triggered and
|
|
which are edge-triggered. Note that irqlines 0, 1, 2, 8 and 13
|
|
are not valid in the 4D0/4D1 registers and are defaulted to edge.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
The interrupt line polarity of all the EISA irqlines on all the EISA
|
|
buses in the system.
|
|
|
|
--*/
|
|
VOID
|
|
Cbus2InitInterruptPolarity(
|
|
VOID
|
|
)
|
|
{
|
|
ULONG InterruptLines;
|
|
ULONG BridgeIndex;
|
|
ULONG original_bridge;
|
|
ULONG BridgeId;
|
|
PCSR csr;
|
|
|
|
for (BridgeIndex = 0; BridgeIndex < Cbus2BridgesFound; BridgeIndex++) {
|
|
InterruptLines = 0;
|
|
|
|
//
|
|
// Read the edge-level control register (ELCR) so we'll know how
|
|
// to mark each driver's interrupt line (ie: edge or level
|
|
// triggered) in the CBC or APIC I/O unit redirection table
|
|
// entry.
|
|
//
|
|
|
|
BridgeId = Cbus2BridgeId[BridgeIndex];
|
|
csr = Cbus2BridgeCSR[BridgeIndex];
|
|
|
|
//
|
|
// save the original value for restoral after our read.
|
|
// repoint our I/O references to the desired bus bridge number.
|
|
//
|
|
original_bridge = csr->BusBridgeSelection.csr_register;
|
|
csr->BusBridgeSelection.csr_register =
|
|
((original_bridge & ~MAX_ELEMENT_CSRS) | BridgeId);
|
|
|
|
//
|
|
// read the ELCR register from the correct bus bridge
|
|
//
|
|
InterruptLines = ( ((ULONG)READ_PORT_UCHAR(PIC2_ELCR_PORT) << 8) |
|
|
((ULONG)READ_PORT_UCHAR(PIC1_ELCR_PORT)) );
|
|
|
|
//
|
|
// restore our default bridge references to what they were
|
|
// when we started...
|
|
//
|
|
csr->BusBridgeSelection.csr_register = original_bridge;
|
|
|
|
//
|
|
// Explicitly mark irqlines 0, 1, 2, 8 and 13 as edge,
|
|
// unless the system is a microchannel system.
|
|
// Leave all other irqlines at their current register values.
|
|
//
|
|
|
|
#ifndef MCA
|
|
InterruptLines &= ELCR_MASK;
|
|
#endif
|
|
|
|
Cbus2IrqPolarity[BridgeIndex] = InterruptLines;
|
|
}
|
|
}
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Call HalpPCISynchronizeType1() from ixpcibus.c for proper
|
|
configuration space synchronization and then adjust the
|
|
bridge selection csr to point to the desired bus.
|
|
|
|
Arguments:
|
|
|
|
BusHandler - BusHandler for PCI configuration read/write
|
|
|
|
Slot - Desired slot.
|
|
|
|
Irql - Pointer to hold previous Irql.
|
|
|
|
PciCfg1 - Type 1 configuration access.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
VOID
|
|
Cbus2PCISynchronize(
|
|
IN PBUS_HANDLER BusHandler,
|
|
IN PCI_SLOT_NUMBER Slot,
|
|
IN PKIRQL Irql,
|
|
IN PPCI_TYPE1_CFG_BITS PciCfg1
|
|
)
|
|
{
|
|
PCSR csr = Cbus2IdentityCSR;
|
|
ULONG BusNumber;
|
|
ULONG BridgeId;
|
|
|
|
HalpPCISynchronizeType1 (BusHandler, Slot, Irql, PciCfg1);
|
|
BusNumber = BusHandler->BusNumber;
|
|
if (Cbus2BridgeOverride) {
|
|
BusNumber = Cbus2BridgeOverride;
|
|
Cbus2BridgeOverride = 0;
|
|
}
|
|
BridgeId = Cbus2BridgeId[Cbus2MapBusNumberToBridgeIndex(BusNumber)];
|
|
csr->BusBridgeSelection.csr_register = BridgeId;
|
|
}
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Restore the bridge selection csr to bus bridge 0 and
|
|
then call HalpPCIReleaseSynchronzationType1() in ixpcibus.c
|
|
to release the PCI configuration synchronization.
|
|
|
|
Arguments:
|
|
|
|
BusHandler - BusHandler for PCI configuration read/write
|
|
|
|
Irql - Pointer that holds Irql to restore to.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
VOID
|
|
Cbus2PCIReleaseSynchronization(
|
|
IN PBUS_HANDLER BusHandler,
|
|
IN KIRQL Irql
|
|
)
|
|
{
|
|
PCSR csr = Cbus2IdentityCSR;
|
|
|
|
csr->BusBridgeSelection.csr_register = 0;
|
|
HalpPCIReleaseSynchronzationType1 (BusHandler, Irql);
|
|
}
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Read the PCI configuration for the designated PPB.
|
|
Update the bus fields for this bridge device.
|
|
|
|
Arguments:
|
|
|
|
BusNumber - Bus Number of Bridge device.
|
|
|
|
Device - Device Number of Bridge device.
|
|
|
|
Function - Function of Bridge device.
|
|
|
|
PrimaryBus - set Primary bus to this value.
|
|
|
|
SecondaryBus - set Secondary bus to this value.
|
|
|
|
SubordinateBus - set Subordinate bus to this value.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
VOID
|
|
Cbus2SetPPBBridgeBuses(
|
|
ULONG BusNumber,
|
|
PCI_SLOT_NUMBER Slot,
|
|
ULONG Function,
|
|
ULONG PrimaryBus,
|
|
ULONG SecondaryBus,
|
|
ULONG SubordinateBus
|
|
)
|
|
{
|
|
PBUS_HANDLER BusHandler;
|
|
PCI_SLOT_NUMBER SlotNumber;
|
|
PPCI_COMMON_CONFIG PciData;
|
|
UCHAR iBuffer[PCI_COMMON_HDR_LENGTH];
|
|
|
|
PciData = (PPCI_COMMON_CONFIG) iBuffer;
|
|
BusHandler = HalpHandlerForBus (PCIBus, BusNumber);
|
|
SlotNumber.u.bits.DeviceNumber = Slot.u.bits.DeviceNumber;
|
|
SlotNumber.u.bits.FunctionNumber = Function;
|
|
SlotNumber.u.bits.Reserved = 0;
|
|
HalpReadPCIConfig (BusHandler, SlotNumber, PciData,
|
|
0, PCI_COMMON_HDR_LENGTH);
|
|
PciData->u.type1.PrimaryBus = (UCHAR)PrimaryBus;
|
|
PciData->u.type1.SecondaryBus = (UCHAR)SecondaryBus;
|
|
PciData->u.type1.SubordinateBus = (UCHAR)SubordinateBus;
|
|
HalpWritePCIConfig (BusHandler, SlotNumber, PciData,
|
|
0, PCI_COMMON_HDR_LENGTH);
|
|
}
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Read the PCI configuration for the designated host bridge.
|
|
Update the bus fields for this bridge device.
|
|
|
|
Arguments:
|
|
|
|
BusNumber - Bus Number of Bridge device.
|
|
|
|
Device - Device Number of Bridge device.
|
|
|
|
Function - Function of Bridge device.
|
|
|
|
PrimaryBus - set Primary bus to this value.
|
|
|
|
SubordinateBus - set Subordinate bus to this value.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
VOID
|
|
Cbus2SetHostBridgeBuses(
|
|
ULONG BusNumber,
|
|
ULONG Device,
|
|
ULONG Function,
|
|
ULONG PrimaryBus,
|
|
ULONG SubordinateBus
|
|
)
|
|
{
|
|
PBUS_HANDLER BusHandler;
|
|
PCI_SLOT_NUMBER SlotNumber;
|
|
PCBUS2_HOST_BRIDGE_CONFIG PciData;
|
|
UCHAR iBuffer[sizeof (CBUS2_HOST_BRIDGE_CONFIG)];
|
|
ULONG BusOverride = 0;
|
|
static ULONG FirstTry = 1;
|
|
|
|
PciData = (PCBUS2_HOST_BRIDGE_CONFIG) iBuffer;
|
|
BusHandler = HalpHandlerForBus (PCIBus, BusNumber);
|
|
SlotNumber.u.bits.DeviceNumber = Device;
|
|
SlotNumber.u.bits.FunctionNumber = Function;
|
|
SlotNumber.u.bits.Reserved = 0;
|
|
retry:
|
|
HalpReadPCIConfig (BusHandler, SlotNumber, PciData,
|
|
0, sizeof (CBUS2_HOST_BRIDGE_CONFIG));
|
|
if (PciData->VendorID == PCI_INVALID_VENDORID && FirstTry) {
|
|
FirstTry = 0;
|
|
BusHandler->BusNumber = 0;
|
|
Cbus2BridgeOverride = BusNumber;
|
|
BusOverride = BusNumber;
|
|
goto retry;
|
|
}
|
|
PciData->BusNumber = (UCHAR)PrimaryBus;
|
|
PciData->SubordinateBusNumber = (UCHAR)SubordinateBus;
|
|
Cbus2BridgeOverride = BusOverride;
|
|
HalpWritePCIConfig (BusHandler, SlotNumber, PciData,
|
|
0, sizeof (CBUS2_HOST_BRIDGE_CONFIG));
|
|
BusHandler->BusNumber = BusNumber;
|
|
}
|
|
|
|
UCHAR Cbus2SPPInterruptRoute[] = {
|
|
1, 4, 3, 2,
|
|
2, 1, 4, 3,
|
|
3, 2, 1, 4,
|
|
4, 3, 2, 1
|
|
};
|
|
|
|
UCHAR Cbus2SPPInterruptRouteForPele[] = {
|
|
10, 10, 10, 10,
|
|
11, 11, 11, 11,
|
|
12, 12, 12, 12,
|
|
13, 13, 13, 13
|
|
};
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Route the interrupt for the given device and interrupt pin
|
|
for the secondary peer pci bridge.
|
|
|
|
Arguments:
|
|
|
|
Device - Device number for the device to route.
|
|
|
|
InterruptPin - Interrupt Pin requested
|
|
|
|
Return Value:
|
|
|
|
Resulting routed interrupt line.
|
|
|
|
--*/
|
|
UCHAR
|
|
Cbus2RouteSPPInterrupt(
|
|
ULONG Device,
|
|
UCHAR InterruptPin
|
|
)
|
|
{
|
|
if (Device == 0 || InterruptPin == 0) {
|
|
return 0;
|
|
}
|
|
if (Cbus2PeleSystemFound) {
|
|
return Cbus2SPPInterruptRouteForPele[
|
|
((InterruptPin - 1) * 4) + ((Device - 1) % 4)];
|
|
}
|
|
else {
|
|
return Cbus2SPPInterruptRoute[
|
|
((InterruptPin - 1) * 4) + ((Device - 1) % 4)];
|
|
}
|
|
}
|
|
|
|
UCHAR Cbus2PPBInterruptRoute[] = {
|
|
1, 2, 3, 4,
|
|
2, 3, 4, 1,
|
|
3, 4, 1, 2,
|
|
4, 1, 2, 3,
|
|
};
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Route the interrupt for the given device and interrupt pin
|
|
for a PCI-to-PCI bridge.
|
|
|
|
Arguments:
|
|
|
|
BusNumber - Bus number for this device.
|
|
|
|
Device - Device number for the device to route.
|
|
|
|
InterruptPin - Current Interrupt Pin request.
|
|
|
|
Return Value:
|
|
|
|
Resulting routed interrupt line.
|
|
|
|
--*/
|
|
ULONG
|
|
Cbus2RoutePPBInterrupt(
|
|
ULONG BusNumber,
|
|
ULONG Device,
|
|
UCHAR InterruptPin
|
|
)
|
|
{
|
|
PBUS_HANDLER Bus;
|
|
PPCIPBUSDATA BusData;
|
|
|
|
while (TRUE) {
|
|
Bus = HaliHandlerForBus(PCIBus, BusNumber);
|
|
if (Bus->ParentHandler->InterfaceType == Internal ||
|
|
Device == 0 || InterruptPin == 0) {
|
|
break;
|
|
}
|
|
InterruptPin = Cbus2PPBInterruptRoute[
|
|
((Device % 4) * 4) + (InterruptPin - 1)];
|
|
BusNumber = Bus->ParentHandler->BusNumber;
|
|
BusData = (PPCIPBUSDATA) Bus->BusData;
|
|
Device = BusData->CommonData.ParentSlot.u.bits.DeviceNumber;
|
|
}
|
|
|
|
return (Device << 8) | InterruptPin;
|
|
}
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Add an additional PCI bus to the registry.
|
|
|
|
Arguments:
|
|
|
|
PCIRegInfo - Global registry information.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
VOID
|
|
Cbus2AddPciBusToRegistry(
|
|
PPCI_REGISTRY_INFO PCIRegInfo
|
|
)
|
|
{
|
|
UNICODE_STRING unicodeString;
|
|
OBJECT_ATTRIBUTES objectAttributes;
|
|
HANDLE hBus;
|
|
NTSTATUS status;
|
|
UCHAR buf2[150];
|
|
PCONFIGURATION_COMPONENT Component;
|
|
PCM_FULL_RESOURCE_DESCRIPTOR Description;
|
|
ACCESS_MASK DesiredAccess;
|
|
PBUS_HANDLER Bus;
|
|
ULONG mnum;
|
|
ULONG d;
|
|
|
|
mnum = 0;
|
|
//
|
|
// Add another PCI bus in the registry.
|
|
//
|
|
for (; ;) {
|
|
//
|
|
// Find next available MultiFunctionAdapter key.
|
|
//
|
|
DesiredAccess = KEY_READ | KEY_WRITE;
|
|
swprintf ((PWCHAR) buf2, L"%s\\%d",
|
|
rgzMultiFunctionAdapter, mnum);
|
|
RtlInitUnicodeString (&unicodeString, (PWCHAR) buf2);
|
|
InitializeObjectAttributes( &objectAttributes,
|
|
&unicodeString,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
(PSECURITY_DESCRIPTOR) NULL);
|
|
status = ZwOpenKey( &hBus, DesiredAccess,
|
|
&objectAttributes);
|
|
if (!NT_SUCCESS(status)) {
|
|
break;
|
|
}
|
|
// already exists, next
|
|
ZwClose (hBus);
|
|
mnum += 1;
|
|
}
|
|
ZwCreateKey (&hBus,
|
|
DesiredAccess,
|
|
&objectAttributes,
|
|
0,
|
|
NULL,
|
|
REG_OPTION_VOLATILE,
|
|
&d
|
|
);
|
|
//
|
|
// Add needed registry values for this
|
|
// MultifunctionAdapter entry.
|
|
//
|
|
RtlInitUnicodeString (&unicodeString, rgzIdentifier);
|
|
ZwSetValueKey (hBus,
|
|
&unicodeString,
|
|
0L,
|
|
REG_SZ,
|
|
L"PCI",
|
|
sizeof (L"PCI")
|
|
);
|
|
|
|
RtlInitUnicodeString (&unicodeString, rgzConfigurationData);
|
|
Description = (PCM_FULL_RESOURCE_DESCRIPTOR) buf2;
|
|
Description->InterfaceType = PCIBus;
|
|
Description->BusNumber = (ULONG)PCIRegInfo->NoBuses;
|
|
Description->PartialResourceList.Version = 0;
|
|
Description->PartialResourceList.Revision = 0;
|
|
Description->PartialResourceList.Count = 0;
|
|
ZwSetValueKey (hBus,
|
|
&unicodeString,
|
|
0L,
|
|
REG_FULL_RESOURCE_DESCRIPTOR,
|
|
Description,
|
|
sizeof (*Description)
|
|
);
|
|
|
|
RtlInitUnicodeString (&unicodeString, L"Component Information");
|
|
Component = (PCONFIGURATION_COMPONENT) buf2;
|
|
RtlZeroMemory (Component, sizeof (*Component));
|
|
|
|
//
|
|
// Setting the Component structure values using the
|
|
// structure doesn't match the PCI bus 0 setting.
|
|
// Set the values by hand to match what ntloader did
|
|
// for bus 0. Proper code would be:
|
|
//
|
|
// Component->AffinityMask = 0xffffffff;
|
|
//
|
|
buf2[12] = 0xff;
|
|
buf2[13] = 0xff;
|
|
buf2[14] = 0xff;
|
|
buf2[15] = 0xff;
|
|
ZwSetValueKey (hBus,
|
|
&unicodeString,
|
|
0L,
|
|
REG_BINARY,
|
|
Component,
|
|
16
|
|
);
|
|
ZwClose (hBus);
|
|
}
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Search the given PCI bus for device and PPB devices.
|
|
As devices are found, route any needed interrupts pins.
|
|
As PPB devices are found, recursively descend to the lower bus.
|
|
|
|
Arguments:
|
|
|
|
BridgeIndex - Logical index of C-bus II bridge.
|
|
|
|
PCIRegInfo - Global registry information.
|
|
|
|
BusNumber - Bus Number to begin search from.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
VOID
|
|
Cbus2HierarchicalPciBusSearch(
|
|
UCHAR BridgeIndex,
|
|
PPCI_REGISTRY_INFO PCIRegInfo,
|
|
ULONG BusNumber
|
|
)
|
|
{
|
|
PBUS_HANDLER ParentBus;
|
|
PBUS_HANDLER ChildBus;
|
|
PPCIPBUSDATA ChildBusData;
|
|
CONFIGBRIDGE CB;
|
|
PSUPPORTED_RANGES ParentBusAddresses;
|
|
PSUPPORTED_RANGES ChildBusAddresses;
|
|
LONGLONG IOLimit;
|
|
LONGLONG MemoryLimit;
|
|
LONGLONG PrefetchMemoryLimit;
|
|
ULONG Device;
|
|
ULONG Function;
|
|
ULONG SecondaryBus;
|
|
UCHAR InterruptPin;
|
|
ULONG Retval;
|
|
UCHAR Rescan;
|
|
BOOLEAN FoundDisabledBridge;
|
|
|
|
CB.PciData = (PPCI_COMMON_CONFIG) CB.Buffer;
|
|
CB.SlotNumber.u.bits.Reserved = 0;
|
|
|
|
CB.BusHandler = HalpHandlerForBus (PCIBus, BusNumber);
|
|
CB.BusData = (PPCIPBUSDATA) CB.BusHandler->BusData;
|
|
|
|
//
|
|
// Set resource limits for PCI-to-PCI bridges found.
|
|
// Each is allowed 25% of the amount available on the parent.
|
|
//
|
|
ParentBus = CB.BusHandler;
|
|
ParentBusAddresses = ParentBus->BusAddresses;
|
|
IOLimit = ((ParentBusAddresses->IO.Limit -
|
|
ParentBusAddresses->IO.Base) + 1) >> 2;
|
|
MemoryLimit = ((ParentBusAddresses->Memory.Limit -
|
|
ParentBusAddresses->Memory.Base) + 1) >> 2;
|
|
PrefetchMemoryLimit = ((ParentBusAddresses->PrefetchMemory.Limit -
|
|
ParentBusAddresses->PrefetchMemory.Base) + 1) >> 2;
|
|
|
|
//
|
|
// Seach all the devices of this bus.
|
|
//
|
|
for (Device = 0; Device < PCI_MAX_DEVICES; Device++) {
|
|
CB.SlotNumber.u.bits.DeviceNumber = Device;
|
|
|
|
for (Function = 0; Function < PCI_MAX_FUNCTION; Function++) {
|
|
CB.SlotNumber.u.bits.FunctionNumber = Function;
|
|
|
|
//
|
|
// Read PCI configuration information
|
|
//
|
|
HalpReadPCIConfig (
|
|
CB.BusHandler,
|
|
CB.SlotNumber,
|
|
CB.PciData,
|
|
0,
|
|
PCI_COMMON_HDR_LENGTH
|
|
);
|
|
|
|
if (CB.PciData->VendorID == PCI_INVALID_VENDORID) {
|
|
// next device
|
|
break;
|
|
}
|
|
|
|
if (!IsPciBridge (CB.PciData)) {
|
|
Retval = Cbus2RoutePPBInterrupt(BusNumber, Device,
|
|
CB.PciData->u.type0.InterruptPin);
|
|
InterruptPin = (UCHAR)(Retval & 0xff);
|
|
Retval >>= 8;
|
|
CB.PciData->u.type0.InterruptLine =
|
|
Cbus2RouteSPPInterrupt(Retval, InterruptPin);
|
|
CB.PciData->LatencyTimer = CBUS2_PCI_LATENCY_TIMER;
|
|
HalpWritePCIConfig (CB.BusHandler, CB.SlotNumber,
|
|
CB.PciData, 0, PCI_COMMON_HDR_LENGTH);
|
|
continue;
|
|
}
|
|
|
|
CB.PciData->Command |=
|
|
PCI_ENABLE_BUS_MASTER | PCI_ENABLE_MEMORY_SPACE |
|
|
PCI_ENABLE_IO_SPACE | PCI_ENABLE_SERR;
|
|
|
|
//
|
|
// Found a PCI-PCI bridge.
|
|
// Set up its parent child relationships.
|
|
//
|
|
ChildBus = HalpAllocateAndInitPciBusHandler (
|
|
PCIRegInfo->HardwareMechanism & 0xf,
|
|
PCIRegInfo->NoBuses, FALSE);
|
|
|
|
PCIConfigHandler.Synchronize = Cbus2PCISynchronize;
|
|
PCIConfigHandler.ReleaseSynchronzation =
|
|
Cbus2PCIReleaseSynchronization;
|
|
|
|
ChildBusData = (PPCIPBUSDATA) ChildBus->BusData;
|
|
ChildBus->GetInterruptVector =
|
|
(PGETINTERRUPTVECTOR) Cbus2GetInterruptVector;
|
|
ChildBusData->CommonData.Pin2Line =
|
|
(PciPin2Line) Cbus2PCIPin2CB2Line;
|
|
ChildBusData->CommonData.Line2Pin =
|
|
(PciLine2Pin) Cbus2PCICB2Line2Pin;
|
|
ChildBusData->GetIrqRange = Cbus2GetFixedPCICB2Line;
|
|
|
|
//
|
|
// Assign I/O, Memory and Prefetch resources
|
|
// for this PPB.
|
|
//
|
|
// First reduce the parent bus ranges.
|
|
//
|
|
Cbus2SetRange(&ParentBusAddresses->IO,
|
|
ParentBusAddresses->IO.Base,
|
|
ParentBusAddresses->IO.Limit - IOLimit);
|
|
Cbus2SetRange(&ParentBusAddresses->Memory,
|
|
ParentBusAddresses->Memory.Base,
|
|
ParentBusAddresses->Memory.Limit - MemoryLimit);
|
|
Cbus2SetRange(&ParentBusAddresses->PrefetchMemory,
|
|
ParentBusAddresses->PrefetchMemory.Base,
|
|
ParentBusAddresses->PrefetchMemory.Limit -
|
|
PrefetchMemoryLimit);
|
|
//
|
|
// Next, set the ranges in the config space of the PPB.
|
|
//
|
|
CB.PciData->u.type1.IOBase = PCI_IO_TO_CFG(
|
|
ParentBusAddresses->IO.Limit + 1);
|
|
CB.PciData->u.type1.IOLimit = PCI_IO_TO_CFG(
|
|
ParentBusAddresses->IO.Limit + IOLimit);
|
|
CB.PciData->u.type1.MemoryBase = PCI_MEMORY_TO_CFG(
|
|
ParentBusAddresses->Memory.Limit + 1);
|
|
CB.PciData->u.type1.MemoryLimit = PCI_MEMORY_TO_CFG(
|
|
ParentBusAddresses->Memory.Limit + MemoryLimit);
|
|
CB.PciData->u.type1.PrefetchBase = PCI_PREFETCH_TO_CFG(
|
|
ParentBusAddresses->PrefetchMemory.Limit + 1);
|
|
CB.PciData->u.type1.PrefetchLimit = PCI_PREFETCH_TO_CFG(
|
|
ParentBusAddresses->PrefetchMemory.Limit +
|
|
PrefetchMemoryLimit);
|
|
//
|
|
// Lastly, set the ranges in the child bus handler.
|
|
// Some of this code tracks HalpGetPciBridgeConfig().
|
|
//
|
|
ChildBusAddresses = ChildBus->BusAddresses;
|
|
ChildBusData->BridgeConfigRead = TRUE;
|
|
HalpSetBusHandlerParent (ChildBus, ParentBus);
|
|
ChildBusData->ParentBus = (UCHAR) ParentBus->BusNumber;
|
|
ChildBusData->CommonData.ParentSlot = CB.SlotNumber;
|
|
Cbus2SetRange(&ChildBusAddresses->IO,
|
|
ParentBusAddresses->IO.Limit + 1,
|
|
ParentBusAddresses->IO.Limit + IOLimit);
|
|
Cbus2SetRange(&ChildBusAddresses->Memory,
|
|
ParentBusAddresses->Memory.Limit + 1,
|
|
ParentBusAddresses->Memory.Limit + MemoryLimit);
|
|
Cbus2SetRange(&ChildBusAddresses->PrefetchMemory,
|
|
ParentBusAddresses->PrefetchMemory.Limit + 1,
|
|
ParentBusAddresses->PrefetchMemory.Limit +
|
|
PrefetchMemoryLimit);
|
|
|
|
//
|
|
// Update the configuration space.
|
|
//
|
|
HalpWritePCIConfig (CB.BusHandler, CB.SlotNumber,
|
|
CB.PciData, 0, PCI_COMMON_HDR_LENGTH);
|
|
|
|
//
|
|
// Adjust appropriate ranges.
|
|
//
|
|
Cbus2AdjustSPPBusRange(ChildBus, BridgeIndex, 0);
|
|
|
|
//
|
|
// Set the bus numbers for this bridge.
|
|
//
|
|
Cbus2SetPPBBridgeBuses((UCHAR)BusNumber, CB.SlotNumber, 0,
|
|
(UCHAR)BusNumber, (UCHAR)PCIRegInfo->NoBuses, 0xff);
|
|
//
|
|
// Add an additional PCI bus to the registry.
|
|
//
|
|
Cbus2AddPciBusToRegistry(PCIRegInfo);
|
|
SecondaryBus = PCIRegInfo->NoBuses;
|
|
PCIRegInfo->NoBuses++;
|
|
Cbus2HierarchicalPciBusSearch(BridgeIndex, PCIRegInfo,
|
|
SecondaryBus);
|
|
Cbus2SetPPBBridgeBuses((UCHAR)BusNumber, CB.SlotNumber, 0,
|
|
(UCHAR)BusNumber, (UCHAR)SecondaryBus,
|
|
(UCHAR)(PCIRegInfo->NoBuses - 1));
|
|
}
|
|
}
|
|
}
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
If an additional C-bus bridge is present in the system,
|
|
perform a hierarchical scan of the bus, set up the interrupt
|
|
routing for all the devices on the bus and allocate and initialize
|
|
the bus handlers for the buses.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
VOID
|
|
Cbus2InitializeOtherPciBus(
|
|
VOID
|
|
)
|
|
{
|
|
UNICODE_STRING unicodeString, ConfigName, IdentName;
|
|
OBJECT_ATTRIBUTES objectAttributes;
|
|
HANDLE hMFunc, hBus;
|
|
NTSTATUS status;
|
|
UCHAR buffer[sizeof(PPCI_REGISTRY_INFO) + 99];
|
|
PWSTR p;
|
|
WCHAR wstr[8];
|
|
volatile PPCI_REGISTRY_INFO PCIRegInfo;
|
|
PKEY_VALUE_FULL_INFORMATION ValueInfo;
|
|
PCM_FULL_RESOURCE_DESCRIPTOR Desc;
|
|
PCM_PARTIAL_RESOURCE_DESCRIPTOR PDesc;
|
|
PCONFIGURATION_COMPONENT Component;
|
|
PBUS_HANDLER Bus;
|
|
PPCIPBUSDATA BusData;
|
|
UCHAR BridgeIndex;
|
|
ULONG i, junk;
|
|
|
|
//
|
|
// Search the hardware description looking for any reported
|
|
// PCI bus. The first ARC entry for a PCI bus will contain
|
|
// the PCI_REGISTRY_INFO.
|
|
//
|
|
RtlInitUnicodeString (&unicodeString, rgzMultiFunctionAdapter);
|
|
InitializeObjectAttributes (
|
|
&objectAttributes,
|
|
&unicodeString,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL, // handle
|
|
NULL);
|
|
|
|
status = ZwOpenKey (&hMFunc, KEY_READ, &objectAttributes);
|
|
if (!NT_SUCCESS(status)) {
|
|
return ;
|
|
}
|
|
|
|
unicodeString.Buffer = wstr;
|
|
unicodeString.MaximumLength = sizeof (wstr);
|
|
|
|
RtlInitUnicodeString (&ConfigName, rgzConfigurationData);
|
|
RtlInitUnicodeString (&IdentName, rgzIdentifier);
|
|
|
|
ValueInfo = (PKEY_VALUE_FULL_INFORMATION) buffer;
|
|
|
|
for (i=0; TRUE; i++) {
|
|
RtlIntegerToUnicodeString (i, 10, &unicodeString);
|
|
InitializeObjectAttributes (
|
|
&objectAttributes,
|
|
&unicodeString,
|
|
OBJ_CASE_INSENSITIVE,
|
|
hMFunc,
|
|
NULL);
|
|
|
|
status = ZwOpenKey (&hBus, KEY_READ, &objectAttributes);
|
|
if (!NT_SUCCESS(status)) {
|
|
//
|
|
// Out of Multifunction adapter entries...
|
|
//
|
|
|
|
ZwClose (hMFunc);
|
|
return ;
|
|
}
|
|
|
|
//
|
|
// Check the Indentifier to see if this is a PCI entry
|
|
//
|
|
status = ZwQueryValueKey (
|
|
hBus,
|
|
&IdentName,
|
|
KeyValueFullInformation,
|
|
ValueInfo,
|
|
sizeof (buffer),
|
|
&junk
|
|
);
|
|
|
|
if (!NT_SUCCESS (status)) {
|
|
ZwClose (hBus);
|
|
continue;
|
|
}
|
|
|
|
p = (PWSTR) ((PUCHAR) ValueInfo + ValueInfo->DataOffset);
|
|
if (p[0] != L'P' || p[1] != L'C' || p[2] != L'I' || p[3] != 0) {
|
|
ZwClose (hBus);
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// The first PCI entry has the PCI_REGISTRY_INFO structure
|
|
// attached to it.
|
|
//
|
|
|
|
status = ZwQueryValueKey (
|
|
hBus,
|
|
&ConfigName,
|
|
KeyValueFullInformation,
|
|
ValueInfo,
|
|
sizeof (buffer),
|
|
&junk
|
|
);
|
|
|
|
ZwClose (hBus);
|
|
if (!NT_SUCCESS(status)) {
|
|
continue ;
|
|
}
|
|
|
|
Desc = (PCM_FULL_RESOURCE_DESCRIPTOR) ((PUCHAR)
|
|
ValueInfo + ValueInfo->DataOffset);
|
|
PDesc = (PCM_PARTIAL_RESOURCE_DESCRIPTOR) ((PUCHAR)
|
|
Desc->PartialResourceList.PartialDescriptors);
|
|
|
|
if (PDesc->Type == CmResourceTypeDeviceSpecific) {
|
|
// got it..
|
|
PCIRegInfo = (PPCI_REGISTRY_INFO) (PDesc+1);
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Initialize each additional C-bus II PCI bus bridge
|
|
//
|
|
for (BridgeIndex = 1; BridgeIndex < Cbus2BridgesFound; BridgeIndex++) {
|
|
//
|
|
// Record the PCI Bus number for additional
|
|
// C-bus II PCI bus bridges.
|
|
//
|
|
Cbus2BridgePCIBusNumber[BridgeIndex] =
|
|
(ULONG)PCIRegInfo->NoBuses;
|
|
|
|
//
|
|
// Initialize IRQ Polarity table for additional
|
|
// C-bus II PCI bus bridges.
|
|
//
|
|
Cbus2IrqPolarity[BridgeIndex] = 0xffff;
|
|
|
|
Bus = HalpAllocateAndInitPciBusHandler (
|
|
PCIRegInfo->HardwareMechanism & 0xf,
|
|
(ULONG)PCIRegInfo->NoBuses, FALSE);
|
|
|
|
PCIConfigHandler.Synchronize = Cbus2PCISynchronize;
|
|
PCIConfigHandler.ReleaseSynchronzation =
|
|
Cbus2PCIReleaseSynchronization;
|
|
|
|
Bus->GetInterruptVector =
|
|
(PGETINTERRUPTVECTOR) Cbus2GetInterruptVector;
|
|
BusData = (PPCIPBUSDATA) Bus->BusData;
|
|
BusData->CommonData.Pin2Line =
|
|
(PciPin2Line) Cbus2PCIPin2CB2Line;
|
|
BusData->CommonData.Line2Pin =
|
|
(PciLine2Pin) Cbus2PCICB2Line2Pin;
|
|
BusData->GetIrqRange = Cbus2GetFixedPCICB2Line;
|
|
Cbus2AdjustSPPBusRange(Bus, BridgeIndex, 1);
|
|
|
|
//
|
|
// Set the bus numbers for this bridge.
|
|
//
|
|
Cbus2SetHostBridgeBuses((ULONG)PCIRegInfo->NoBuses, 0, 0,
|
|
(ULONG)PCIRegInfo->NoBuses, 0xff);
|
|
|
|
//
|
|
// Add an additional PCI bus to the registry.
|
|
//
|
|
Cbus2AddPciBusToRegistry(PCIRegInfo);
|
|
|
|
PCIRegInfo->NoBuses++;
|
|
|
|
//
|
|
// Search the PCI bus on this bus bridge determining
|
|
// the hierarchy.
|
|
//
|
|
Cbus2HierarchicalPciBusSearch(BridgeIndex, PCIRegInfo,
|
|
(ULONG)(PCIRegInfo->NoBuses - 1));
|
|
|
|
//
|
|
// Set the bus numbers for this bridge.
|
|
//
|
|
Cbus2SetHostBridgeBuses(Cbus2BridgePCIBusNumber[BridgeIndex],
|
|
0, 0, Cbus2BridgePCIBusNumber[BridgeIndex],
|
|
(ULONG)PCIRegInfo->NoBuses - 1);
|
|
}
|
|
}
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Check if this C-bus II system is a PELE system.
|
|
If it is a PELE system, it will use a different interrupt
|
|
routing algorithm than other C-bus II platforms.
|
|
|
|
The identification is done by checking the system board's EISA ID
|
|
which is at I/O addresses 0xc80 - 0xc83:
|
|
|
|
1. Write FFh to 0C80h
|
|
2. Read 0C80h
|
|
If the contents of 0xc80 equals 0xff, discontinue the
|
|
identification process -- the system board does not
|
|
have a readable ID. If the contents of 0xc80 does not
|
|
equal 0xff and the most significant bit is a zero,
|
|
the system board supports a readable ID that can be read
|
|
at 0xc80 - 0xc83.
|
|
|
|
PELE's ID is "FUJC071", so each byte of the System board ID is as follows:
|
|
|
|
1st byte(0xc80) is 0x1a.
|
|
2nd byte(0xc81) is 0xaa.
|
|
3rd byte(0xc82) is 0xc0.
|
|
4th byte(0xc83) is 0x71.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
Boolean whether a PELE system was found.
|
|
|
|
--*/
|
|
UCHAR
|
|
Cbus2CheckForPeleSystem(
|
|
VOID
|
|
)
|
|
{
|
|
WRITE_PORT_UCHAR((PUCHAR)0xc80, (UCHAR)0xff);
|
|
if (READ_PORT_UCHAR((PUCHAR)0xc80) != (UCHAR)0x1a)
|
|
return FALSE;
|
|
if (READ_PORT_UCHAR((PUCHAR)0xc81) != (UCHAR)0xaa)
|
|
return FALSE;
|
|
if (READ_PORT_UCHAR((PUCHAR)0xc82) != (UCHAR)0xc0)
|
|
return FALSE;
|
|
return TRUE;
|
|
}
|