NT4/private/ntos/nthals/halcbus/i386/cbus1.c
2020-09-30 17:12:29 +02:00

1871 lines
52 KiB
C

/*++
Copyright (c) 1992, 1993, 1994 Corollary Inc.
Module Name:
cbus1.c
Abstract:
This module implements the initialization of the system dependent
functions that define the Hardware Architecture Layer (HAL) for the
Corollary Cbus1 MP machines under Windows NT.
Author:
Landy Wang (landy@corollary.com) 05-Oct-1992
Environment:
Kernel mode only.
Revision History:
--*/
#include "halp.h"
#include "cbusrrd.h" // HAL <-> RRD interface definitions
#include "cbus.h" // Cbus1 & Cbus2 max number of elements is here
#include "cbus1.h" // Cbus1 hardware architecture stuff
#include "cbus_nt.h" // C-bus NT-specific implementation stuff
#include "bugcodes.h"
#include "stdio.h"
#include "cbusnls.h"
#include "cbusapic.h" // Cbus APIC generic definitions
PULONG
CbusApicVectorToEoi(
IN ULONG Vector
);
VOID
Cbus1PerfInterrupt(VOID);
PVOID
Cbus1LinkVector(
IN PBUS_HANDLER Bus,
IN ULONG Vector,
IN ULONG Irqline
);
VOID
Cbus1InitializeStall(IN ULONG);
VOID
Cbus1InitializeClock(VOID);
VOID
Cbus1InitializePerf(VOID);
ULONG
Cbus1QueryInterruptPolarity(VOID);
VOID
Cbus1BootCPU(
IN ULONG Processor,
IN ULONG RealModeSegOff
);
VOID
Cbus1InitializePlatform(VOID);
VOID
CbusPreparePhase0Interrupts(
IN ULONG,
IN ULONG,
IN PVOID
);
VOID
Cbus1InitializeCPU(
IN ULONG Processor
);
VOID
Cbus1ECCEnable(VOID);
VOID
Cbus1ECCDisable(VOID);
PUCHAR
CbusIDtoCSR(
IN ULONG ArbID
);
VOID
Cbus1Arbitrate(VOID);
VOID
Cbus1HandleJumpers();
VOID
Cbus1ParseRRD(
IN PEXT_ID_INFO Table,
IN OUT PULONG Count
);
PVOID
Cbus1FindDeadID(
IN ULONG ArbID
);
VOID
Cbus1InitializePlatform();
VOID
Cbus1InitializeDeviceIntrs(
IN ULONG Processor
);
VOID
Cbus1SetupPrivateVectors(VOID);
VOID
CbusRebootHandler( VOID );
#ifdef ALLOC_PRAGMA
#pragma alloc_text(INIT, Cbus1SetupPrivateVectors)
#pragma alloc_text(INIT, Cbus1BootCPU)
#pragma alloc_text(INIT, Cbus1InitializePlatform)
#pragma alloc_text(INIT, Cbus1InitializeCPU)
#pragma alloc_text(INIT, Cbus1ECCEnable)
#pragma alloc_text(INIT, Cbus1ECCDisable)
#pragma alloc_text(INIT, CbusIDtoCSR)
#pragma alloc_text(INIT, Cbus1Arbitrate)
#pragma alloc_text(INIT, Cbus1HandleJumpers)
#pragma alloc_text(INIT, Cbus1ParseRRD)
#pragma alloc_text(INIT, Cbus1FindDeadID)
#pragma alloc_text(INIT, Cbus1InitializeDeviceIntrs)
#pragma alloc_text(INIT, Cbus1QueryInterruptPolarity)
#endif
extern PULONG CbusVectorToEoi[MAXIMUM_IDTVECTOR + 1];
PUCHAR Cbus1ID0CSR;
//
// Used for holding IDs of disabled Cbus1 processors. This is
// done to detect whether the user has inserted any obsolete
// boards, since they can still win arbitrations. Thus, we can
// detect disabled arbitration winners from just plain broken systems.
//
PEXT_ID_INFO Cbus1DeadIDs[MAX_ELEMENT_CSRS];
ULONG Cbus1DeadIDsIndex;
//
// The limited priority scheme employed by the Intel APIC is as follows:
//
// there are 256 vectors, but since the APIC ignores the least
// significant 4 bits, we really only have 16 distinct priority levels
// to work with.
//
// processor traps, NT system calls and APC/DPC use up the first 0x30 vectors.
// after the required Power, IPI, Clock and Profiling levels,
// there really isn't much left. see the picture below:
//
// APC: 0x1F (lowest priority)
// DPC: 0x2F
//
// Lowest Priority EISA: 0x30 real devices here
// Highest Priority EISA: 0xBF real devices here
//
// EISA Profile: 0xCF
// EISA Clock(Perf Counter): 0xDD
// APIC Clock (System Timer): 0xDF
// Spurious Vector: 0xEC
// CBUS1_REBOOT_IPI: 0xED
// CBUS1_REDIR_IPI: 0xEE
// IPI: 0xEF
// Power: 0xFF
// High: 0xFF (highest priority)
//
// we have only 9 distinct priority levels left!!!
//
// due to this APIC shortcoming, we are forced to share levels between
// various EISA irq lines. each line will be assigned a different vector,
// and the IRQL mappings will work, but they will not be optimal.
//
// note that the Cbus2 CBC suffers from none of these shortcomings.
//
//
// set unused irql levels to the priority of the irql level above it
// so that if they are inadvertently called from the kernel,
// executive or drivers, nothing bad will happen.
//
#define UNUSED_PRI CBUS1_PROFILE_TASKPRI
#define CBUS1_DEVICELOW_TASKPRI 0x30
#define CBUS1_DEVICEHI_TASKPRI 0xBF
#define CBUS1_IRQ2_TASKPRI 0xCE // not expected to happen
#define CBUS1_PROFILE_TASKPRI 0xCF
#define CBUS1_PERF_TASKPRI 0xDD // also in cb1stall.asm
#define CBUS1_CLOCK_TASKPRI 0xDF
#define CBUS1_REBOOT_IPI 0xED
#define CBUS1_REDIR_IPI 0xEE
#define CBUS1_IPI_TASKPRI 0xEF
#define CBUS1_POWER_TASKPRI 0xFE
//
// we don't really care what the spurious value is, but the APIC
// spec seems to imply that this must be hardcoded at 0xff for future
// compatibility.
//
#define CBUS1_SPURIOUS_TASKPRI 0xFF
//
// since we have 9 priority levels and 13 irq lines, priorities are ordered
// in buckets as follows, from high priority to low priority:
//
// highest priority to keyboard: (usually irq1)
//
// next highest priority to serial port/mouse/network card. (usually irq3)
//
// next highest priority to serial port/network card. (usually irq4)
//
// next highest priority to SCSI/hard disks/networks. (usually irq9)
//
// next highest priority to SCSI/hard disks/networks. (usually irqA)
//
// next highest priority to SCSI/hard disks/networks. (usually irqB or C)
//
// next highest priority to SCSI/hard disks/networks. (usually irqD or E)
//
// next highest priority to SCSI/disks/networks/printers. (usually irqF or 5)
//
// lowest device priority to floppy, printers. (usually irq6 or 7)
//
#define EISA_IRQ0PRI CBUS1_PERF_TASKPRI // 8254 line (perf ctr) & pri
#define EISA_IRQ1PRI (CBUS1_DEVICELOW_TASKPRI + 0x80)
#define EISA_IRQ2PRI CBUS1_IRQ2_TASKPRI
#define EISA_IRQ3PRI (CBUS1_DEVICELOW_TASKPRI + 0x70)
#define EISA_IRQ4PRI (CBUS1_DEVICELOW_TASKPRI + 0x60)
#define EISA_IRQ5PRI (CBUS1_DEVICELOW_TASKPRI + 0x10)
#define EISA_IRQ6PRI (CBUS1_DEVICELOW_TASKPRI + 0x01)
#define EISA_IRQ7PRI (CBUS1_DEVICELOW_TASKPRI + 0x00)
#define EISA_IRQ8PRI CBUS1_PROFILE_TASKPRI // profile line & priority
#define EISA_IRQ9PRI (CBUS1_DEVICELOW_TASKPRI + 0x50)
#define EISA_IRQAPRI (CBUS1_DEVICELOW_TASKPRI + 0x40)
#define EISA_IRQBPRI (CBUS1_DEVICELOW_TASKPRI + 0x31)
#define EISA_IRQCPRI (CBUS1_DEVICELOW_TASKPRI + 0x30)
#define EISA_IRQDPRI (CBUS1_DEVICELOW_TASKPRI + 0x21)
#define EISA_IRQEPRI (CBUS1_DEVICELOW_TASKPRI + 0x20)
#define EISA_IRQFPRI (CBUS1_DEVICELOW_TASKPRI + 0x11)
//
// an EISA_IRQ2PRI is declared above just to flesh out arrays that will
// be indexed by irq line. the EISA_IRQ2PRI should never actually be used
//
#if (HIGH_LEVEL + 1 != 32)
Cbus1IrqlToVector[] NOT dimensioned and indexed properly
#endif
ULONG Cbus1IrqlToVector[HIGH_LEVEL + 1 ] = {
LOW_TASKPRI, APC_TASKPRI, DPC_TASKPRI, EISA_IRQ7PRI,
EISA_IRQ6PRI, EISA_IRQ5PRI, EISA_IRQFPRI, EISA_IRQEPRI,
EISA_IRQDPRI, EISA_IRQCPRI, EISA_IRQBPRI, EISA_IRQAPRI,
EISA_IRQ9PRI, EISA_IRQ4PRI, EISA_IRQ3PRI, EISA_IRQ1PRI,
EISA_IRQ2PRI, UNUSED_PRI, UNUSED_PRI, UNUSED_PRI,
UNUSED_PRI, UNUSED_PRI, UNUSED_PRI, UNUSED_PRI,
UNUSED_PRI, UNUSED_PRI, UNUSED_PRI, CBUS1_PROFILE_TASKPRI,
CBUS1_CLOCK_TASKPRI, CBUS1_IPI_TASKPRI, CBUS1_POWER_TASKPRI, HIGH_TASKPRI,
};
ULONG Cbus1IrqPolarity;
#define IRQPERFLINE 0
#define EISA_IRQLINES 16
typedef struct _cbus1_irqline_t {
ULONG Vector;
KIRQL Irql;
} CBUS1_IRQLINE_T, *PCBUS1_IRQLINE;
//
// map EISA irqline to Cbus1 programmable vectors & IRQL levels,
// as defined above.
//
#define FIRST_DEVICE_LEVEL (DISPATCH_LEVEL+1)
CBUS1_IRQLINE_T Cbus1Irqlines[EISA_IRQLINES] =
{
EISA_IRQ0PRI, CLOCK2_LEVEL,
EISA_IRQ1PRI, FIRST_DEVICE_LEVEL+12,
EISA_IRQ2PRI, FIRST_DEVICE_LEVEL+13,
EISA_IRQ3PRI, FIRST_DEVICE_LEVEL+11,
EISA_IRQ4PRI, FIRST_DEVICE_LEVEL+10,
EISA_IRQ5PRI, FIRST_DEVICE_LEVEL+2,
EISA_IRQ6PRI, FIRST_DEVICE_LEVEL+1,
EISA_IRQ7PRI, FIRST_DEVICE_LEVEL,
EISA_IRQ8PRI, PROFILE_LEVEL,
EISA_IRQ9PRI, FIRST_DEVICE_LEVEL+9,
EISA_IRQAPRI, FIRST_DEVICE_LEVEL+8,
EISA_IRQBPRI, FIRST_DEVICE_LEVEL+7,
EISA_IRQCPRI, FIRST_DEVICE_LEVEL+6,
EISA_IRQDPRI, FIRST_DEVICE_LEVEL+5,
EISA_IRQEPRI, FIRST_DEVICE_LEVEL+4,
EISA_IRQFPRI, FIRST_DEVICE_LEVEL+3
};
PUCHAR Cbus1ResetVector;
extern ULONG CbusRedirVector;
extern ULONG CbusRebootVector;
extern ADDRESS_USAGE HalpCbusMemoryResource;
extern ULONG CbusMemoryResourceIndex;
//
// defines for the Cbus1 ECC syndrome
//
#define MULTIBIT 3
#define DOUBLEBIT 2
#define SINGLEBIT 1
#define NOECCERROR 0x7f
//
// defines for the Cbus1 ECC error register
//
typedef struct _extmear_t {
ULONG offset:24;
ULONG SyndromeHigh:5;
ULONG SyndromeLow:2;
ULONG simmtype:1;
} EXTMEAR_T, *PEXTMEAR;
UCHAR Cbus1EdacSyndrome[] = {
MULTIBIT, /* 00 */ DOUBLEBIT, /* 01 */ DOUBLEBIT, /* 02 */ MULTIBIT, /* 03 */
DOUBLEBIT, /* 04 */ MULTIBIT, /* 05 */ MULTIBIT, /* 06 */ DOUBLEBIT, /* 07 */
DOUBLEBIT, /* 08 */ MULTIBIT, /* 09 */ SINGLEBIT, /* 0A */ DOUBLEBIT, /* 0B */
MULTIBIT, /* 0C */ DOUBLEBIT, /* 0D */ DOUBLEBIT, /* 0E */ SINGLEBIT, /* 0F */
DOUBLEBIT, /* 10 */ MULTIBIT, /* 11 */ SINGLEBIT, /* 12 */ DOUBLEBIT, /* 13 */
SINGLEBIT, /* 14 */ DOUBLEBIT, /* 15 */ DOUBLEBIT, /* 16 */ SINGLEBIT, /* 17 */
SINGLEBIT, /* 18 */ DOUBLEBIT, /* 19 */ DOUBLEBIT, /* 1A */ SINGLEBIT, /* 1B */
DOUBLEBIT, /* 1C */ SINGLEBIT, /* 1D */ MULTIBIT, /* 1E */ DOUBLEBIT, /* 1F */
DOUBLEBIT, /* 20 */ MULTIBIT, /* 21 */ SINGLEBIT, /* 22 */ DOUBLEBIT, /* 23 */
MULTIBIT, /* 24 */ DOUBLEBIT, /* 25 */ DOUBLEBIT, /* 26 */ SINGLEBIT, /* 27 */
SINGLEBIT, /* 28 */ DOUBLEBIT, /* 29 */ DOUBLEBIT, /* 2A */ SINGLEBIT, /* 2B */
DOUBLEBIT, /* 2C */ SINGLEBIT, /* 2D */ MULTIBIT, /* 2E */ DOUBLEBIT, /* 2F */
SINGLEBIT, /* 30 */ DOUBLEBIT, /* 31 */ DOUBLEBIT, /* 32 */ MULTIBIT, /* 33 */
DOUBLEBIT, /* 34 */ SINGLEBIT, /* 35 */ MULTIBIT, /* 36 */ DOUBLEBIT, /* 37 */
DOUBLEBIT, /* 38 */ MULTIBIT, /* 39 */ MULTIBIT, /* 3A */ DOUBLEBIT, /* 3B */
MULTIBIT, /* 3C */ DOUBLEBIT, /* 3D */ DOUBLEBIT, /* 3E */ SINGLEBIT, /* 3F */
DOUBLEBIT, /* 40 */ MULTIBIT, /* 41 */ MULTIBIT, /* 42 */ DOUBLEBIT, /* 43 */
MULTIBIT, /* 44 */ DOUBLEBIT, /* 45 */ DOUBLEBIT, /* 46 */ MULTIBIT, /* 47 */
MULTIBIT, /* 48 */ DOUBLEBIT, /* 49 */ DOUBLEBIT, /* 4A */ SINGLEBIT, /* 4B */
DOUBLEBIT, /* 4C */ MULTIBIT, /* 4D */ SINGLEBIT, /* 4E */ DOUBLEBIT, /* 4F */
MULTIBIT, /* 50 */ DOUBLEBIT, /* 51 */ DOUBLEBIT, /* 52 */ SINGLEBIT, /* 53 */
DOUBLEBIT, /* 54 */ SINGLEBIT, /* 55 */ SINGLEBIT, /* 56 */ DOUBLEBIT, /* 57 */
DOUBLEBIT, /* 58 */ SINGLEBIT, /* 59 */ SINGLEBIT, /* 5A */ DOUBLEBIT, /* 5B */
SINGLEBIT, /* 5C */ DOUBLEBIT, /* 5D */ DOUBLEBIT, /* 5E */ SINGLEBIT, /* 5F */
MULTIBIT, /* 60 */ DOUBLEBIT, /* 61 */ DOUBLEBIT, /* 62 */ SINGLEBIT, /* 63 */
DOUBLEBIT, /* 64 */ SINGLEBIT, /* 65 */ SINGLEBIT, /* 66 */ DOUBLEBIT, /* 67 */
DOUBLEBIT, /* 68 */ SINGLEBIT, /* 69 */ SINGLEBIT, /* 6A */ DOUBLEBIT, /* 6B */
SINGLEBIT, /* 6C */ DOUBLEBIT, /* 6D */ DOUBLEBIT, /* 6E */ SINGLEBIT, /* 6F */
DOUBLEBIT, /* 70 */ SINGLEBIT, /* 71 */ MULTIBIT, /* 72 */ DOUBLEBIT, /* 73 */
SINGLEBIT, /* 74 */ MULTIBIT, /* 75 */ DOUBLEBIT, /* 76 */ SINGLEBIT, /* 77 */
MULTIBIT, /* 78 */ DOUBLEBIT, /* 79 */ DOUBLEBIT, /* 7A */ SINGLEBIT, /* 7B */
DOUBLEBIT, /* 7C */ SINGLEBIT, /* 7D */ SINGLEBIT, /* 7E */ NOECCERROR,/* 7F */
};
VOID
FatalError(
IN PUCHAR ErrorString
);
VOID
CbusHardwareFailure(
IN PUCHAR HardwareMessage
);
VOID
CbusClockInterruptPx( VOID );
VOID
HalpProfileInterruptPx( VOID );
VOID
Cbus1Boot1(
IN ULONG Processor,
IN PQUAD Dest,
IN PQUAD Code,
IN ULONG ResetAddress,
IN ULONG ResetValue
);
extern ULONG Cbus1Boot1End;
VOID
Cbus1Boot2(
IN ULONG Processor,
IN PQUAD Dest,
IN PQUAD Code,
IN ULONG ResetAddress,
IN ULONG ResetValue
);
//
// defines for resetting the keyboard controller used
// in Cbus1ResetAllOtherProcessors().
//
#define RESET 0xfe
#define KEYBPORT (PUCHAR )0x64
#define IRQ0 0
#define IRQ8 8
/*++
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 (8254 perfctr and RTC 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 Link code.
That's ok, since it doesn't currently use it.
Arguments:
None.
Return Value:
None.
--*/
VOID
Cbus1SetupPrivateVectors(VOID)
{
PVOID Opaque;
extern ULONG ProfileVector;
//
// we are defaulting to the EISA (or MCA) bridge 0
// for all of these interrupts which need enabling during Phase 0.
//
Opaque = Cbus1LinkVector((PBUS_HANDLER)0, CBUS1_PERF_TASKPRI, IRQ0);
CbusPreparePhase0Interrupts(CBUS1_PERF_TASKPRI, IRQ0, Opaque);
Opaque = Cbus1LinkVector((PBUS_HANDLER)0, ProfileVector, IRQ8);
CbusPreparePhase0Interrupts(ProfileVector, IRQ8, Opaque);
}
VOID
Cbus1BootCPU(
IN ULONG Processor,
IN ULONG RealModeSegOff
)
/*++
Routine Description:
Remove reset from the specified processor, allowing him to boot,
beginning execution at the specified start address. This ends up
being tricky due to cache flushing concepts generally hidden in ROMs,
but in the HAL for the Cbus1 platform.
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.
--*/
{
PHYSICAL_ADDRESS Bootcode;
ULONG BeginAddress, EndAddress;
ULONG BeginCacheLineIndex;
ULONG EndCacheLineIndex;
UCHAR StartVectorCode[5];
PVOID PhysicalResetVector;
ULONG ip = RealModeSegOff & 0xFFFF;
ULONG cs = ((RealModeSegOff >> 16) & 0xFFFF);
ULONG CacheFlush = CBUS1_CACHE_SIZE;
ULONG LastCacheLineIndex;
//
// if we haven't yet mapped in the reset vector, do so now.
//
if (!Cbus1ResetVector) {
PhysicalResetVector =
(PUCHAR)CbusGlobal.resetvec - CBUS1_CACHE_LINE;
Cbus1ResetVector = (PUCHAR)HalpMapPhysicalMemory (
PhysicalResetVector, 1);
}
//
// build start vector (entry offset followed by cs load base)
//
StartVectorCode[0] = (UCHAR)0xea;
StartVectorCode[1] = (UCHAR)ip & 0xff;
StartVectorCode[2] = (UCHAR)(ip >> 8) & 0xff;
StartVectorCode[3] = (UCHAR)cs & 0xff;
StartVectorCode[4] = (UCHAR)(cs >> 8) & 0xff;
//
// determine which low-level boot function to use. although
// the 2 functions are exactly the same, we must make sure that
// we don't call one which would cause the cache line containing
// the reset vector to be evicted prematurely.
//
LastCacheLineIndex = ((CacheFlush - 1) >> CBUS1_CACHE_SHIFT);
Bootcode = MmGetPhysicalAddress(&Cbus1Boot1);
BeginAddress = Bootcode.LowPart;
BeginCacheLineIndex =
(BeginAddress & (CacheFlush - 1)) >> CBUS1_CACHE_SHIFT;
Bootcode = MmGetPhysicalAddress((PVOID)&Cbus1Boot1End);
EndAddress = Bootcode.LowPart;
EndCacheLineIndex =
(EndAddress & (CacheFlush - 1)) >> CBUS1_CACHE_SHIFT;
//
// in order to startup additional CPUs, we need to write its
// startup vector in the last 16 bytes of the memory
// address space. this memory may or may not be
// present. more specifically, a partially populated
// memory card which doesn't have memory at the end of
// the space, ie: a memory card at 48Mb->56Mb in a
// system with a 64Mb upper limit) will cause an ecc
// error if ecc is enabled.
//
// thus we must disable ecc to prevent such boards from
// generating ecc errors. note that the errors are only
// generated on the read when we initially fill the
// startvector. it's ok to enable ecc after startup
// even though the cacheline containing the StartVectorCode
// has not necessarily been flushed. when it is flushed,
// it will go in the bit bucket and ecc errors are
// generated only on reads (not writes).
//
Cbus1ECCDisable();
//
// call Cbus1Boot1 if there is no wrap...
// see the detailed comment at the top of Cbus1Boot1 for more details.
//
if ((EndCacheLineIndex != LastCacheLineIndex) && EndAddress > BeginAddress) {
Cbus1Boot1(Processor,
(PQUAD) Cbus1ResetVector,
(PQUAD) StartVectorCode,
(ULONG)CbusCSR[Processor].csr +
CbusGlobal.smp_creset,
CbusGlobal.smp_creset_val);
}
else {
Cbus1Boot2(Processor,
(PQUAD) Cbus1ResetVector,
(PQUAD) StartVectorCode,
(ULONG)CbusCSR[Processor].csr +
CbusGlobal.smp_creset,
CbusGlobal.smp_creset_val);
}
Cbus1ECCEnable();
}
VOID
Cbus1InitializePlatform(VOID)
/*++
Routine Description:
Overlay the irql-to-vector mappings with the Cbus1 vector maps.
Give all additional processors proper bus arbitration IDs.
Arguments:
None.
Return Value:
None.
--*/
{
RtlMoveMemory( (PVOID)CbusIrqlToVector,
(PVOID)Cbus1IrqlToVector,
(HIGH_LEVEL + 1) * sizeof (ULONG));
Cbus1IrqPolarity = Cbus1QueryInterruptPolarity();
//
// if there are any Cbus1 additional processors present,
// put them into reset and arbitrate them so they get
// branded with proper IDs.
//
if (CbusProcessors > 1)
Cbus1Arbitrate();
CbusRedirVector = CBUS1_REDIR_IPI;
CbusRebootVector = CBUS1_REBOOT_IPI;
}
VOID
Cbus1InitializeCPU(
IN ULONG Processor
)
/*++
Routine Description:
Initialize this processor's local and I/O APIC units.
Arguments:
Processor - Supplies a logical processor number
Return Value:
None.
--*/
{
CbusInitializeLocalApic(Processor, CBUS1_LOCAL_APIC_LOCATION,
CBUS1_SPURIOUS_TASKPRI);
//
// Only the boot processor needs to initialize the I/O APIC
// on the EISA bridge. Each additional processor just needs
// to give his I/O APIC (since it won't be used) an arbitration ID.
//
if (Processor == 0) {
CbusInitializeIOApic(Processor, CBUS1_IO_APIC_LOCATION,
CBUS1_REDIR_IPI, CBUS1_REBOOT_IPI, Cbus1IrqPolarity);
}
else {
KiSetHandlerAddressToIDT(CBUS1_REBOOT_IPI, CbusRebootHandler);
HalEnableSystemInterrupt(CBUS1_REBOOT_IPI, IPI_LEVEL, Latched);
CbusApicBrandIOUnitID(Processor);
}
}
BOOLEAN
Cbus1EnableNonDeviceInterrupt(
IN ULONG Vector
)
/*++
Routine Description:
Determine if the supplied vector belongs to an EISA device - if so,
then the corresponding I/O APIC entry will need to be modified to
enable the line, so return FALSE here. Otherwise, no work is needed,
so just return TRUE immediately.
Arguments:
Vector - Supplies a vector number to enable
Return Value:
TRUE if the vector was enabled, FALSE if not.
--*/
{
//
// If pure software interrupt, no action needed.
// Note both APIC timer interrupts and IPIs are
// treated as "software" interrupts. Note that
// an EOI will still be needed, so set that up here.
//
if (Vector != CBUS1_SPURIOUS_TASKPRI)
CbusVectorToEoi[Vector] = CbusApicVectorToEoi(Vector);
if (Vector < CBUS1_DEVICELOW_TASKPRI || Vector >= CBUS1_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;
}
VOID
Cbus1EnableDeviceInterrupt(
IN ULONG Vector,
IN PVOID HardwarePtr,
IN ULONG FirstAttach,
IN USHORT BusNumber,
IN USHORT Irqline
)
/*++
Routine Description:
Enable the specified interrupt for the calling processor.
Remember only the boot processor can add/remove processors from
the I/O APIC's redirection entries.
This operation is trivial for the boot processor. However, additional
processors must interrupt the boot processor with an "enable interrupt"
request and then spin waiting for the boot processor to acknowledge that
the entry has been modified. 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 redirection entry address
FirstAttach - TRUE if this is the first processor to enable the
specified vector
Return Value:
None.
--*/
{
BOOLEAN LowestInGroup;
BOOLEAN LevelTriggered;
//
// Only 1 I/O bus in Cbus1 systems, only Cbus2 can have more
//
ASSERT(BusNumber == 0);
//
// Set up the EOI address
//
CbusVectorToEoi[Vector] = CbusApicVectorToEoi(Vector);
//
// Enable APIC LIG arbitration delay (at least 4 cycles on
// our 10Mhz APIC bus, which is 0.4 microseconds).
//
switch (Vector) {
case CBUS1_PERF_TASKPRI:
case CBUS1_CLOCK_TASKPRI:
case CBUS1_PROFILE_TASKPRI:
case CBUS1_POWER_TASKPRI:
// These stay in APIC "FIXED" mode
LowestInGroup = FALSE;
break;
default:
// All others utilize APIC "Lowest-In-Group"
LowestInGroup = TRUE;
break;
}
//
// Note that non-EISA interrupts (ie: APIC clocks or IPI) are
// correctly handled in the EnableNonDeviceInterrupt case, and
// hence calls to enable those will never enter this routine.
// If this changes, the code below needs to be modified because
// these interrupts do NOT have an ELCR entry (there are only
// 16 of those).
//
if (((Cbus1IrqPolarity >> Irqline)) & 0x1) {
LevelTriggered = TRUE;
}
else {
LevelTriggered = FALSE;
}
CbusEnableApicInterrupt(BusNumber, Vector, HardwarePtr, FirstAttach,
LowestInGroup, LevelTriggered);
}
VOID
Cbus1DisableInterrupt(
IN ULONG Vector,
IN PVOID HardwarePtr,
IN ULONG LastDetach,
IN USHORT BusNumber,
IN USHORT Irqline
)
/*++
Routine Description:
Disable the specified interrupt so it can not occur on the calling
processor upon return from this routine. Remember only the boot processor
can add/remove processors from his I/O APIC's redirection entries.
This operation is trivial for the boot processor. However, additional
processors must interrupt the boot processor with a "disable interrupt"
request and then spin waiting for the boot processor to acknowledge that
the entry has been modified. 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 redirection entry address
LastDetach - TRUE if this is the last processor to detach from the
specified vector
Return Value:
None.
--*/
{
UNREFERENCED_PARAMETER( Irqline );
//
// Only 1 I/O bus in Cbus1 systems, only Cbus2 can have more
//
ASSERT(BusNumber == 0);
CbusDisableApicInterrupt(BusNumber, Vector, HardwarePtr, LastDetach);
}
ULONG
Cbus1MapVector(
PBUS_HANDLER Bus,
IN ULONG BusInterruptLevel,
IN ULONG BusInterruptVector,
OUT PKIRQL Irql
)
/*++
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 C-bus II half-card 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:
BusHandler - per bus specific structure
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 SystemVector;
UNREFERENCED_PARAMETER( BusInterruptVector );
SystemVector = Cbus1Irqlines[BusInterruptLevel].Vector;
*Irql = Cbus1Irqlines[BusInterruptLevel].Irql;
return SystemVector;
}
PVOID
Cbus1LinkVector(
IN PBUS_HANDLER Bus,
IN ULONG Vector,
IN ULONG Irqline
)
/*++
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 need to access the hardware (ie: Enable & DisableInterrupt).
Arguments:
Vector - Supplies the system interrupt vector corresponding to the
specified BusNumber/Irqline.
Irqline - Supplies the IRQ line of the specified interrupt
Return Value:
A hardware-specific pointer (actually a redirection entry address)
that is interpreted only by the Cbus1 backend.
--*/
{
return CbusApicLinkVector(Bus, Vector, Irqline);
}
#if DBG
#define NMI_BUTTON_PRESSED() 1
#else
#define NMI_BUTTON_PRESSED() 0
#endif
NTSTATUS
Cbus1ResolveNMI(
IN PVOID NmiInfo
)
/*++
Routine Description:
This function determines the cause of the NMI so that the user can
replace any offending SIMMs.
Arguments:
NmiInfo - pointer to the NMI information structure
Return Value:
Returns the byte address which caused the NMI, 0 if indeterminate
--*/
{
ULONG Board;
PMEMORY_CARD pm;
ULONG Syndrome;
PEXTMEAR Mear;
PHYSICAL_ADDRESS FaultAddress;
UCHAR NmiMessage[80];
BOOLEAN founderror = FALSE;
extern VOID CbusClearEISANMI(VOID);
extern NTSTATUS DefaultHalHandleNMI( IN OUT PVOID);
if (NMI_BUTTON_PRESSED()) {
//
// NMI button was pressed, so go to the debugger
//
_asm {
int 3
}
//
// Clear the NMI in hardware so the system can continue
//
CbusClearEISANMI();
return STATUS_SUCCESS; // ignore this NMI
}
if (CbusGlobal.nonstdecc)
return DefaultHalHandleNMI(NmiInfo);
FaultAddress.LowPart = 0;
FaultAddress.HighPart = 0;
pm = CbusMemoryBoards;
for (Board = 0; Board < CbusMemoryBoardIndex; Board++, pm++) {
if (pm->io_attr == 0)
continue;
//
// the syndrome/page routines below are for
// the Corollary Cbus1 smp/XM architecture.
// this architecture currently supports 256Mb of RAM.
//
// this method is the defacto standard for Corollary
// Cbus1 licensees with > 64Mb.
//
Mear = (PEXTMEAR)pm->regmap;
//
// check whether this memory board generated the ecc error
// double check all the byte vs. clicks conversions and
// also the baseram (for jumpered megabytes)
//
Syndrome =
Cbus1EdacSyndrome[(Mear->SyndromeHigh<<2)|Mear->SyndromeLow];
if (Syndrome == NOECCERROR || Syndrome == SINGLEBIT)
continue;
//
// Cbus1 will always have less than 4Gig of physical memory,
// so just calculate LowPart here. no need to add in
// CbusGlobal.baseram, because that will always be zero.
//
// the Mear->offset will always be zero based, regardless of
// whether the memory was reclaimed above 256MB or not.
//
FaultAddress.LowPart = (Mear->offset << 4);
founderror = TRUE;
//
// Disable ecc so the system can be safely taken down
// without getting another double-bit error (because the
// kernel may iret later).
//
Cbus1ECCDisable();
break;
}
if (founderror == TRUE) {
sprintf(NmiMessage, MSG_CBUS1NMI_ECC, 0, FaultAddress.LowPart);
CbusHardwareFailure (NmiMessage);
}
//
// No errors found in Cbus RAM, so check for EISA errors
//
DefaultHalHandleNMI(NmiInfo);
}
VOID
Cbus1ResetAllOtherProcessors(
IN ULONG Processor
)
/*++
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.
Shadowing is then disabled. The keyboard controller is then
reset and ThisProcessor spins awaiting the reboot.
This routine can be called at any IRQL. The boot processor can
call whilst cli'd at interrupt time.
Arguments:
Processor - Supplies the caller's logical processor number
Return Value:
None.
--*/
{
PUCHAR BiosProcessorCSR;
extern VOID Cbus1RebootRequest(IN ULONG);
_asm {
cli
}
//
// IPI the other processors to get them to flush
// their internal caches and halt. this will be more
// conducive for the subsequent reset.
//
Cbus1RebootRequest(Processor);
//
// Delay 5 seconds to give the additional processors
// a chance to get the previous IPI.
//
KeStallExecutionProcessor(5000000);
//
// issue a global reset to the broadcast address
//
*(PULONG)((PUCHAR)CbusBroadcastCSR + CbusGlobal.smp_sreset) =
CbusGlobal.smp_sreset_val;
//
// Disable BIOS shadowing until RRD recopies the BIOS ROM
// into shadow RAM again. This is because we "reclaim" the
// BIOS hole up at 256MB + 640K, to use it as general purpose
// (shadowed) RAM for NT. Referring to the ROM at 640K (NOT
// 256MB + 640K) you will see a pristine BIOS copy, but
// accessing it at the high address is like using any other
// area of general-purpose DRAM.
//
// thus, during NT, the shadowed copy will be overwritten with
// random data as the pages are used, and we must warn the BIOS
// to recopy the ROM into the shadowed RAM on bootup. Here's
// the magic to instruct the BIOS accordingly:
//
// Note that once RRD regains control, he will again
// shadow the BIOS as long as the EISA Config CMOS bits
// instruct him to. The shadowing is only being disabled
// here because the BIOS will initially get control _before_
// the Corollary RRD ROM does.
//
// if this code wasn't here, the user would have to cycle
// power to boot up successfully.
//
BiosProcessorCSR = (PUCHAR)(KeGetPcr()->HalReserved[PCR_CSR]);
*(BiosProcessorCSR + CBUS1_SHADOW_REGISTER) =
DISABLE_BIOS_SHADOWING;
//
// reset the keyboard controller.
//
WRITE_PORT_UCHAR(KEYBPORT, RESET);
loop:
goto loop;
}
//
//
// most internal Cbus1 support routines begin here.
//
// the exception is the internal routines needed for booting,
// which must be adjacent to the externally-visible boot
// routine above. see the above comments for more details.
//
//
//
VOID
Cbus1ECCEnable(VOID)
/*++
Routine Description:
Enable Cbus1 ECC detection as ordered by the RRD.
Arguments:
None.
Return Value:
None.
--*/
{
ULONG Index;
PMEMORY_CARD pm;
if (CbusGlobal.nonstdecc)
return;
pm = CbusMemoryBoards;
for (Index = 0; Index < CbusMemoryBoardIndex; Index++, pm++) {
if (pm->io_attr)
COUTB(pm->regmap, 0, pm->io_attr);
}
}
VOID
Cbus1ECCDisable(VOID)
/*++
Routine Description:
Disable Cbus1 ECC
Arguments:
None.
Return Value:
None.
--*/
{
ULONG Index;
PMEMORY_CARD pm;
if (CbusGlobal.nonstdecc)
return;
pm = CbusMemoryBoards;
for (Index = 0; Index < CbusMemoryBoardIndex; Index++, pm++) {
if (pm->io_attr)
COUTB(pm->regmap, 0, pm->io_attr & ~CBUS1_EDAC_EN);
}
}
PUCHAR
CbusIDtoCSR(
IN ULONG ArbID
)
/*++
Routine Description:
Search for the given arbitration ID in the
global extended ID table, then return its CSR.
Arguments:
ArbID - Supplies an arbitration ID (that just won the contention cycle)
Return Value:
CSR of the specified arbitration ID, 0 if none found
--*/
{
ULONG Index;
PEXT_ID_INFO Idp;
for ( Index = 0; Index < CbusProcessors; Index++) {
Idp = (PEXT_ID_INFO)CbusCSR[Index].idp;
if (Idp->id == ArbID) {
return (PUCHAR)CbusCSR[Index].csr;
}
}
return (PUCHAR)0;
}
VOID
Cbus1Arbitrate(VOID)
/*++
Routine Description:
Called by the boot processor to arbitrate the other processors
out of reset, and brand them with IDs.
Arguments:
None.
Return Value:
None.
--*/
{
ULONG Index, ArbID;
PUCHAR csr, ArbCSR;
ULONG AdditionalProcessors = 0;
extern PVOID Cbus1FindDeadID( ULONG );
//
// issue a global broadcast to put all additional processors into reset.
//
csr = (PUCHAR)CbusBroadcastCSR + CbusGlobal.smp_sreset;
*((PULONG)csr) = CbusGlobal.smp_sreset_val;
//
// access ID 0's C-bus I/O space - this is different from the boot CPU.
//
for (Index = 0; Index < CbusGlobal.broadcast_id; Index++) {
COUTB(Cbus1ID0CSR, CbusGlobal.smp_contend,
CbusGlobal.smp_contend_val);
COUTB(CbusBroadcastCSR, CbusGlobal.smp_contend,
CbusGlobal.smp_contend_val);
ArbID = READ_PORT_UCHAR(ATB_STATREG) & BS_ARBVALUE;
ArbCSR = CbusIDtoCSR(ArbID);
if (ArbCSR) {
COUTB(ArbCSR, CbusGlobal.smp_setida,
CbusGlobal.smp_setida_val);
if (ArbID >= LOWCPUID && ArbID <= HICPUID)
AdditionalProcessors++;
}
else {
if (!Cbus1FindDeadID(ArbID)) {
CbusHardwareFailure(MSG_ARBITRATE_ID_ERR);
}
}
ArbID = READ_PORT_UCHAR(ATB_STATREG) & BS_ARBVALUE;
if (ArbID == 0) {
ASSERT(AdditionalProcessors == CbusProcessors - 1);
return;
}
};
CbusHardwareFailure(MSG_ARBITRATE_FAILED);
}
VOID
Cbus1HandleJumpers()
/*++
Routine Description:
"Recover" any low memory which has been repointed at the EISA bus
via EISA config. This typically happens when ISA/EISA cards with
dual-ported memory are in the machine, and memory accesses need
to be pointed at the card, as opposed to C-bus general purpose memory.
The Corollary architecture provides a way to still gain use of the
general purpose memory, as well as be able to use the I/O dual-port
memory, by double mapping the C-bus memory at a high memory address.
note that memory accesses in the 640K-1MB hole are by default, pointed
at the EISA bus, and don't need to be repointed via EISA config.
note this is where jumper decoding and memory_holes set up
happens, but the actual memory will not be released (and
hence used), until HalInitSystem Phase 0.
Arguments:
None.
Return Value:
None.
--*/
{
ULONG i, j;
ULONG DoublyMapped;
ULONG Address;
extern RRD_CONFIGURATION_T CbusJumpers;
extern VOID CbusMemoryFree(ULONG, ULONG);
if (CbusGlobal.useholes == 0) {
return;
}
//
// if the base of RAM is _zero_ (ie: XM or later), then we
// will recover the holes up above the "top of RAM", so any
// I/O device dual-port RAM at the low address will continue to work.
//
// if the base of RAM is NON-ZERO, then we are on an older
// Corollary system which is not supported by this HAL - the
// standard Windows NT uniprocessor HAL should be used instead.
//
if (CbusGlobal.baseram != 0)
return;
DoublyMapped = CbusGlobal.memory_ceiling;
//
// reclaim the 640K->1MB hole first
//
CbusMemoryFree(DoublyMapped + 640 * 1024, 384 * 1024);
//
// see if this memory span has been dipswitch
// disabled. if this memory exists (in C-bus
// space and it has been jumpered, add it to
// the list so it will be freed later.
//
for (i = 0; i < ATMB; i++) {
if (CbusJumpers.jmp[i] && CbusJumpers.mem[i]) {
Address = MB(i) + DoublyMapped;
j = 1;
for (i++; i < ATMB && CbusJumpers.jmp[i] && CbusJumpers.mem[i]; i++)
j++;
CbusMemoryFree(Address, MB(j));
}
}
}
VOID
Cbus1ParseRRD(
IN PEXT_ID_INFO Table,
IN OUT PULONG Count
)
/*++
Routine Description:
Check for Cbus1 system boards being up to date for running NT.
Obsolete additional CPU boards are removed from the configuration
structure, and are not booted later. If the boot CPU is obsolete,
the system is halted.
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:
Shuffles the input table, and modifies the count of valid entries,
in order to remove any obsolete processors.
None.
--*/
{
ULONG Index;
ULONG Length;
ULONG j;
PEXT_ID_INFO p;
PEXT_ID_INFO s;
UCHAR HalObsoleteBoardsMsg[80];
Cbus1DeadIDsIndex = 0;
p = Table;
for (Index = 0; Index < *Count; Index++, p++) {
//
// check for the ID 0 entry, which must be the first one.
// although this is NOT a processor, it is needed to
// arbitrate all the Cbus1 additional processors and set their
// IDs. so capture it now.
//
if (Index == 0) {
//
// RRD specifies how much to map in per processor -
// for Cbus1, RRD must give us a size which includes any
// processor-specific registers the HAL may access,
// generally indicated via the CbusGlobal structure.
//
Cbus1ID0CSR = (PUCHAR)HalpMapPhysicalMemoryWriteThrough (
(PVOID)p->pel_start,
(ULONG)ADDRESS_AND_SIZE_TO_SPAN_PAGES(
p->pel_start, p->pel_size));
continue;
}
if (p->pm == 0)
continue;
if ((p->pel_features & ELEMENT_HAS_APIC) == 0) {
//
// the boot processor MUST have an APIC
// so that we can support distributed
// interrupts on Cbus1 platforms.
//
if ((UCHAR)p->id == CbusGlobal.bootid) {
FatalError(MSG_OBSOLETE_PIC);
}
*(PEXT_ID_INFO)&Cbus1DeadIDs[Cbus1DeadIDsIndex] =
*p;
Cbus1DeadIDsIndex++;
//
// remove old board from "found" array
//
s = p;
for (j = Index+1; j < *Count; j++, s++) {
*s = *(s + 1);
}
}
}
if (Cbus1DeadIDsIndex) {
//
// can't let any disabled processors be found,
// warn the user that we are disabling some number
// of processor boards because they cannot access
// the EISA bus.
//
*Count -= Cbus1DeadIDsIndex;
sprintf(HalObsoleteBoardsMsg, MSG_OBSOLETE_PROC, Cbus1DeadIDsIndex);
HalDisplayString(HalObsoleteBoardsMsg);
}
Cbus1HandleJumpers();
//
// Inform the rest of the system of the resources the HAL has reserved
// from the base of C-bus I/O to 4GB.
//
Length = 0xffffffff - CbusGlobal.cbusio;
AddMemoryResource(CbusGlobal.cbusio, Length + 1);
}
PVOID
Cbus1FindDeadID(
IN ULONG ArbID
)
/*++
Routine Description:
Check for a given arbitration ID (that just won) is a "dead" Cbus1
system board - ie: one that we have software disabled because it is
a processor incapable of symmetric I/O.
Arguments:
ArbID - Supplies an arbitration ID to match
Return Value:
An opaque pointer to the disabled processor's CSR space, 0
if the ID was not found.
--*/
{
ULONG Dead;
PUCHAR csr = (PUCHAR)0;
PEXT_ID_INFO Idp = (PEXT_ID_INFO)Cbus1DeadIDs;
extern VOID CbusIOPresent(ULONG, ULONG, ULONG, ULONG, ULONG, PVOID);
for (Dead = 0; Dead < Cbus1DeadIDsIndex; Dead++, Idp++) {
if (Idp->id != ArbID) {
continue;
}
csr = (PUCHAR)HalpMapPhysicalMemoryWriteThrough (
(PVOID)Idp->pel_start,
(ULONG)ADDRESS_AND_SIZE_TO_SPAN_PAGES(
Idp->pel_start, Idp->pel_size));
COUTB(csr, CbusGlobal.smp_setida,
CbusGlobal.smp_setida_val);
switch (Idp->io_function) {
case IOF_INVALID_ENTRY:
case IOF_NO_IO:
case IOF_MEMORY:
break;
default:
//
// Add this I/O functionality to our table
// to make available to Cbus hardware drivers.
//
// pel_features wasn't set for the old boards,
// so check io_function instead.
//
if (Idp->io_function == IOF_CBUS1_SIO ||
(Idp->io_function == IOF_CBUS1_SCSI)) {
//
// Add this I/O card to our list of
// available I/O peripherals.
//
CbusIOPresent(
(ULONG)Idp->id,
(ULONG)Idp->io_function,
(ULONG)Idp->io_attr,
Idp->pel_start,
Idp->pel_size,
(PVOID)csr);
}
break;
}
}
return (PVOID)csr;
}
/*++
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
Cbus1InitializeInterrupts(
IN ULONG Processor
)
{
//
// 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)
//
// 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)
//
if (Processor == 0) {
//
// we must be at Phase0 of system initialization. we need to
// assign vectors for interrupts needed during Phase0.
// currently only the APIC clock is needed during Phase0.
//
CbusVectorToEoi[CBUS1_CLOCK_TASKPRI] =
CbusApicVectorToEoi(CBUS1_CLOCK_TASKPRI);
}
//
// Note that for Cbus1, InitializeClock MUST be called after
// HalpInitializeStallExecution, since both program the APIC timer.
// Note that stall execution sets up his own IDT entry for handling
// the incoming interrupt, so only the above EOI setup is needed.
//
Cbus1InitializeStall(Processor);
//
// Call the hardware backend handler to generate an
// interrupt every 10 milliseconds to be used as the system timer.
//
Cbus1InitializeClock();
//
// Set up the irq0 performance counter and irq8 profile interrupts.
//
if (Processor == 0) {
Cbus1SetupPrivateVectors();
Cbus1InitializePerf();
}
//
// enable both the irq0 and irq8 interrupts as well as the APIC clocks.
// This also registers the resources being used by these interrupts
// so other subsystems know the HAL has reserved them.
//
Cbus1InitializeDeviceIntrs(Processor);
//
// APC, DPC and IPI have already been initialized and enabled
// as part of HalInitializeProcessor.
//
}
/*++
Routine Description:
This function initializes the HAL-specific hardware device
(CLOCK & PROFILE) interrupts for the Corollary Cbus1 architecture.
Arguments:
none.
Return Value:
VOID
--*/
VOID
Cbus1InitializeDeviceIntrs(
IN ULONG Processor
)
{
extern VOID Cbus1ClockInterrupt(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
CBUS1_CLOCK_TASKPRI, // Bus interrupt level
CBUS1_CLOCK_TASKPRI, // System IDT
CLOCK2_LEVEL, // System Irql
Cbus1ClockInterrupt, // ISR
Latched);
HalpEnableInterruptHandler (
DeviceUsage, // Mark as device vector
IRQ8, // Bus interrupt level
CBUS1_PROFILE_TASKPRI,
PROFILE_LEVEL, // System Irql
HalpProfileInterrupt, // ISR
Latched);
HalpEnableInterruptHandler (
DeviceUsage, // Mark as device vector
IRQ0, // Bus interrupt level
CBUS1_PERF_TASKPRI, // System IDT
CLOCK2_LEVEL, // System Irql
Cbus1PerfInterrupt, // ISR
Latched);
}
else {
KiSetHandlerAddressToIDT(CBUS1_CLOCK_TASKPRI, CbusClockInterruptPx);
HalEnableSystemInterrupt(CBUS1_CLOCK_TASKPRI, CLOCK2_LEVEL, Latched);
KiSetHandlerAddressToIDT(CBUS1_PROFILE_TASKPRI, HalpProfileInterruptPx);
HalEnableSystemInterrupt(CBUS1_PROFILE_TASKPRI, PROFILE_LEVEL, Latched);
}
}
//
// 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.
//
#define ELCR_MASK 0xDEF8
#define PIC1_ELCR_PORT (PUCHAR)0x4D0 // ISP edge/level control regs
#define PIC2_ELCR_PORT (PUCHAR)0x4D1
ULONG
Cbus1QueryInterruptPolarity(
VOID
)
/*++
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 in the system.
--*/
{
ULONG 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 APIC I/O unit redirection table entry.
//
InterruptLines = ( ((ULONG)READ_PORT_UCHAR(PIC2_ELCR_PORT) << 8) |
((ULONG)READ_PORT_UCHAR(PIC1_ELCR_PORT)) );
//
// Explicitly mark irqlines 0, 1, 2, 8 and 13 as edge. Leave all
// other irqlines at their current register values.
//
InterruptLines &= ELCR_MASK;
return InterruptLines;
}