/*++ 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; }