/*++ Copyright (c) 1998 Microsoft Corporation Module Name: i64sapic.c Abstract: Implements I/O Sapic functionality Author: Todd Kjos (HP) (v-tkjos) 1-Jun-1998 Environment: Kernel mode only. Revision History: --*/ #include "halp.h" #include "iosapic.h" #include VOID IoSapicMaskEntry( PIO_INTR_CONTROL IoUnit, ULONG RteNumber ); VOID IoSapicSetEntry( PIO_INTR_CONTROL IoUnit, ULONG RteNumber ); VOID IoSapicEnableEntry( PIO_INTR_CONTROL IoUnit, ULONG RteNumber ); // // Method structure for control of IO Sapic Hardware // INTR_METHODS HalpIoSapicMethods = { IoSapicMaskEntry, IoSapicSetEntry, IoSapicEnableEntry }; VOID HalpInti2InterruptController ( IN ULONG InterruptInput, OUT PIO_INTR_CONTROL *InterruptController, OUT PULONG ControllerInti ) /*++ Routine Description: Convert InterruptInput to an interrupt controller structure and input number Arguments: InterruptInput - System Global Interrupt Input InterruptController - Pointer to Interupt controller structure ControllerInti - Redirection Table Entry on this interrupt controller Return Value: --*/ { PIO_INTR_CONTROL IoUnit; for (IoUnit=HalpIoSapicList; IoUnit; IoUnit=IoUnit->flink) { if (InterruptInput <= IoUnit->IntiMax) { if (IoUnit->IntiBase > InterruptInput) { // // If there are holes in the list of Global System Vectors AND // someone specifies one of the non-existant GSVs, make sure we // return an error rather than getting horribly confused about // which IOAPIC contains the Inti. // IoUnit = NULL; } break; } } *InterruptController = IoUnit; if (IoUnit) { *ControllerInti = InterruptInput-IoUnit->IntiBase; } } BOOLEAN HalpGetSapicInterruptDesc ( IN INTERFACE_TYPE BusType, IN ULONG BusNumber, IN ULONG BusInterruptLevel, OUT PULONG Inti, OUT PKAFFINITY InterruptAffinity ) /*++ Routine Description: This procedure gets a "Inti" describing the requested interrupt Arguments: BusType - The Bus type as known to the IO subsystem BusNumber - The number of the Bus we care for BusInterruptLevel - IRQ on the Bus Return Value: TRUE if AcpiInti found; otherwise FALSE. Inti - Global system interrupt input --*/ { PIO_INTR_CONTROL IoUnit; ULONG RteNumber; HalpInti2InterruptController(BusInterruptLevel, &IoUnit, &RteNumber); // Make sure Inti is not out of range if (IoUnit == NULL) { return FALSE; } // It's in range, just give back the same value as was passed in *Inti = BusInterruptLevel; // // The Interrupt affinity is the intersection of the global affinity mask // (HalpDefaultInterruptAffinity) and any additional restrictions due to the // location of the Io Sapic (IoUnit->InterruptAffinity). // *InterruptAffinity = IoUnit->InterruptAffinity & HalpDefaultInterruptAffinity; return(TRUE); } ULONG HalpINTItoVector( ULONG Inti ) // Returns the Vector associated with this global interrupt input // Vector is node and IDT entry { PIO_INTR_CONTROL IoUnit; ULONG RteNumber; HalpInti2InterruptController( Inti, &IoUnit, &RteNumber ); ASSERT(IoUnit); return (IoUnit->Inti[RteNumber].GlobalVector); } VOID HalpSetINTItoVector( ULONG Inti, ULONG Vector ) // Sets the vector for this global interrupt input // Vector is node and IDT entry { PIO_INTR_CONTROL IoUnit; ULONG RteNumber; HalpInti2InterruptController( Inti, &IoUnit, &RteNumber ); ASSERT(IoUnit); // .Vector (IDTEntry) is set in SetRedirEntry IoUnit->Inti[RteNumber].GlobalVector = Vector; } VOID HalpSetRedirEntry ( IN ULONG InterruptInput, IN ULONG Entry, IN USHORT ThisCpuApicID ) /*++ Routine Description: This procedure sets a IO Unit Redirection Table Entry Must be called with the HalpAccountingLock held Arguments: Return Value: None. --*/ { PIO_INTR_CONTROL IoUnit; ULONG RteNumber; HalpInti2InterruptController( InterruptInput, &IoUnit, &RteNumber ); ASSERT(IoUnit); ASSERT(IoUnit->Inti[RteNumber].GlobalVector); ASSERT((UCHAR)(IoUnit->Inti[RteNumber].GlobalVector) == (UCHAR)Entry); IoUnit->Inti[RteNumber].Vector = Entry; IoUnit->Inti[RteNumber].Destination = ThisCpuApicID << 16; IoUnit->FreeVectors[Entry >> 4] &= ~(1 << (Entry & 0x0F)); IoUnit->IntrMethods->SetEntry(IoUnit, RteNumber); } VOID HalpWriteRedirEntry ( IN ULONG GlobalInterrupt, IN UCHAR SapicVector, IN USHORT DestinationCPU, IN ULONG Flags, IN ULONG InterruptType ) { ULONG rteNumber; PIO_INTR_CONTROL ioUnit; HalpInti2InterruptController( GlobalInterrupt, &ioUnit, &rteNumber ); ASSERT(ioUnit); ioUnit->Inti[rteNumber].Vector = SapicVector; // // Set the delivery mode // switch (InterruptType) { case PLATFORM_INT_PMI: ioUnit->Inti[rteNumber].Vector &= ~INT_TYPE_MASK; // first clear the field ioUnit->Inti[rteNumber].Vector |= DELIVER_SMI; break; case PLATFORM_INT_CPE: ioUnit->Inti[rteNumber].Vector &= ~INT_TYPE_MASK; // first clear the field ioUnit->Inti[rteNumber].Vector |= DELIVER_LOW_PRIORITY; break; case PLATFORM_INT_INIT: ioUnit->Inti[rteNumber].Vector &= ~INT_TYPE_MASK; // first clear the field ioUnit->Inti[rteNumber].Vector |= DELIVER_INIT; break; } // // So we honor the flags passed into this function. // if (IS_LEVEL_TRIGGERED_MPS(Flags)) { ioUnit->Inti[rteNumber].Vector |= LEVEL_TRIGGERED; } else { ioUnit->Inti[rteNumber].Vector &= ~LEVEL_TRIGGERED; } if (IS_ACTIVE_LOW_MPS(Flags)) { ioUnit->Inti[rteNumber].Vector |= ACTIVE_LOW; } else { ioUnit->Inti[rteNumber].Vector &= ~ACTIVE_LOW; } ioUnit->Inti[rteNumber].Destination = DestinationCPU << 16; ioUnit->FreeVectors[SapicVector >> 4] &= ~(1 << (SapicVector & 0x0F)); ioUnit->IntrMethods->SetEntry(ioUnit, rteNumber); } // HalpWriteRedirEntry() VOID HalpGetRedirEntry ( IN ULONG InterruptInput, IN PULONG Entry, IN PULONG Destination ) /*++ Routine Description: Arguments: Return Value: None. --*/ { PIO_INTR_CONTROL IoUnit; ULONG RteNumber; HalpInti2InterruptController( InterruptInput,&IoUnit,&RteNumber ); ASSERT(IoUnit); *Entry = IoUnit->Inti[RteNumber].Vector; *Destination = IoUnit->Inti[RteNumber].Destination; } VOID HalpGetFreeVectors( IN ULONG InterruptInput, OUT PUSHORT *FreeVectors ) { ULONG rteNumber; PIO_INTR_CONTROL ioUnit; HalpInti2InterruptController( InterruptInput, &ioUnit, &rteNumber ); ASSERT(ioUnit); *FreeVectors = ioUnit->FreeVectors; } VOID HalpSetVectorAllocated( IN ULONG InterruptInput, IN UCHAR Vector ) { ULONG rteNumber; PIO_INTR_CONTROL ioUnit; HalpInti2InterruptController( InterruptInput, &ioUnit, &rteNumber ); ASSERT(ioUnit); ioUnit->FreeVectors[Vector >> 4] &= ~(1 << (Vector & 0xF)); } VOID HalpEnableRedirEntry( IN ULONG InterruptInput ) /*++ Routine Description: This procedure enables a IO Unit Redirection Table Entry by setting the mask bit in the Redir Entry. Arguments: InterruptInput - The input line we're interested in Return Value: None. --*/ { PIO_INTR_CONTROL IoUnit; ULONG RteNumber; HalpInti2InterruptController( InterruptInput, &IoUnit, &RteNumber ); ASSERT(IoUnit); IoUnit->IntrMethods->EnableEntry(IoUnit, RteNumber); } VOID HalpDisableRedirEntry( IN ULONG InterruptInput ) /*++ Routine Description: This procedure disables a IO Unit Redirection Table Entry by setting the mask bit in the Redir Entry. Arguments: InterruptInput - The input line we're interested in Return Value: None. --*/ { PIO_INTR_CONTROL IoUnit; ULONG RteNumber; HalpInti2InterruptController( InterruptInput, &IoUnit, &RteNumber ); ASSERT(IoUnit); IoUnit->IntrMethods->MaskEntry(IoUnit, RteNumber); } VOID IoSapicMaskEntry( PIO_INTR_CONTROL IoUnit, ULONG RteNumber ) { PIO_SAPIC_REGS IoSapicPtr = IoUnit->RegBaseVirtual; IoSapicPtr->RegisterSelect = RteNumber * 2 + IO_REDIR_00_LOW; IoSapicPtr->RegisterWindow |= INTERRUPT_MASKED; HalDebugPrint(( HAL_VERBOSE, "HAL: IoSapicMaskEntry - %d [%#p]: Dest=%#x Vec=%#x\n", RteNumber,IoSapicPtr, IoUnit->Inti[RteNumber].Destination, IoUnit->Inti[RteNumber].Vector )); } VOID IoSapicEnableEntry( PIO_INTR_CONTROL IoUnit, ULONG RteNumber ) { PIO_SAPIC_REGS IoSapicPtr = IoUnit->RegBaseVirtual; IoSapicPtr->RegisterSelect = RteNumber * 2 + IO_REDIR_00_LOW; IoSapicPtr->RegisterWindow &= (~INTERRUPT_MASKED); HalDebugPrint(( HAL_VERBOSE, "HAL: IoSapicEnableEntry: %d [%#p]: Dest=%#x Vec=%#x\n", RteNumber, IoSapicPtr, IoUnit->Inti[RteNumber].Destination, IoUnit->Inti[RteNumber].Vector )); } VOID IoSapicSetEntry( PIO_INTR_CONTROL IoUnit, ULONG RteNumber ) { PIO_SAPIC_REGS IoSapicPtr = IoUnit->RegBaseVirtual; ULONG RedirRegister; PULONG_PTR EoiValue; USHORT ApicId; // Only SetEntry sets the eoi table because set entry is the only // one that sets the destination CPU. EoiValue = (PULONG_PTR)(IoUnit->Inti[RteNumber].Vector & LEVEL_TRIGGERED ? &IoSapicPtr->Eoi : 0 ); ApicId = (USHORT)((IoUnit->Inti[RteNumber].Destination & SAPIC_XID_MASK) >> SAPIC_XID_SHIFT); HalpWriteEOITable( IoUnit->Inti[RteNumber].Vector & INT_VECTOR_MASK, EoiValue, HalpGetProcessorNumberByApicId(ApicId)); RedirRegister = RteNumber * 2 + IO_REDIR_00_LOW; IoSapicPtr->RegisterSelect = RedirRegister+1; IoSapicPtr->RegisterWindow = IoUnit->Inti[RteNumber].Destination; IoSapicPtr->RegisterSelect = RedirRegister; IoSapicPtr->RegisterWindow = IoUnit->Inti[RteNumber].Vector; // Enable HalDebugPrint(( HAL_VERBOSE, "HAL: IoSapicSetEntry: %d [%#p]: Dest=%#x Vec=%#x Eoi=%#p\n", RteNumber, IoSapicPtr, IoUnit->Inti[RteNumber].Destination, IoUnit->Inti[RteNumber].Vector, EoiValue )); } BOOLEAN HalpIsActiveLow( ULONG Inti ) { PIO_INTR_CONTROL IoUnit; ULONG RteNumber; HalpInti2InterruptController ( Inti,&IoUnit,&RteNumber ); return( (IoUnit->Inti[RteNumber].Vector & ACTIVE_LOW) == ACTIVE_LOW); } BOOLEAN HalpIsLevelTriggered( ULONG Inti ) { PIO_INTR_CONTROL IoUnit; ULONG RteNumber; HalpInti2InterruptController ( Inti,&IoUnit,&RteNumber ); ASSERT(IoUnit); return( (IoUnit->Inti[RteNumber].Vector & LEVEL_TRIGGERED) == LEVEL_TRIGGERED); } VOID HalpSetPolarity( ULONG Inti, BOOLEAN ActiveLow ) { PIO_INTR_CONTROL IoUnit; ULONG RteNumber; HalpInti2InterruptController ( Inti,&IoUnit,&RteNumber ); ASSERT(IoUnit); if (ActiveLow) { IoUnit->Inti[RteNumber].Vector |= ACTIVE_LOW; } else { IoUnit->Inti[RteNumber].Vector &= ~ACTIVE_LOW; } } VOID HalpSetLevel( ULONG Inti, BOOLEAN LevelTriggered ) { PIO_INTR_CONTROL IoUnit; ULONG RteNumber; HalpInti2InterruptController ( Inti,&IoUnit,&RteNumber ); ASSERT(IoUnit); if (LevelTriggered) { IoUnit->Inti[RteNumber].Vector |= LEVEL_TRIGGERED; } else { IoUnit->Inti[RteNumber].Vector &= ~LEVEL_TRIGGERED; } } VOID HalpSpuriousHandler ( IN PKINTERRUPT_ROUTINE Interrupt, IN PKTRAP_FRAME TrapFrame ) /*++ Routine Description: Spurious Interrupt handler. Dummy return or we can count number of occurance of spurious interrupts. Right now, we will do a dummy return. Arguements: Return Parameters: --*/ { }