/////////////////////////////////////////////////////////////////////////////// // // Microsoft Research Singularity // // Copyright (c) Microsoft Corporation. All rights reserved. // // File: IoApic.cs // // Note: // Intel MP Specification 1.4 // 82093AA I/O Advanced Programmable Interrupt Controller namespace Microsoft.Singularity.Hal { using System; using System.Collections; using System.Diagnostics; using System.Runtime.CompilerServices; using System.Threading; using Microsoft.Singularity.Io; using Microsoft.Singularity.Hal.Acpi; public enum IoBits : ushort { IrqMask = 0x0100, TriggerModeMask = 0x0080, TriggerModeLevel = TriggerModeMask, TriggerModeEdge = 0, RemoteIRR = 0x0040, IntPolMask = 0x0020, IntPolActiveLow = IntPolMask, IntPolActiveHigh = 0, DeliveryStatus = 0x0010, DstMask = 0x0008, // Destination Mask DstLogical = DstMask, DstPhysical = 0, DelModMask = 0x0007, // Delivery Mode Mask DelModFixed = 0x0000, DelModLowest = 0x0001, DelModSMI = 0x0002, DelModReserved1 = 0x0003, DelModNMI = 0x0004, DelModINIT = 0x0005, DelModReserved2 = 0x0006, DelModExtINT = 0x0007, }; [CLSCompliant(false)] public struct RedirectionEntry { public byte Destination; public IoBits IoBits; public byte InterruptVector; [NoHeapAllocation] public RedirectionEntry(byte destination, IoBits ioBits, byte interruptVector) { this.Destination = destination; this.IoBits = ioBits; this.InterruptVector = interruptVector; } [NoHeapAllocation] public RedirectionEntry(byte destination, ushort ioBits, byte interruptVector) { this.Destination = destination; this.IoBits = (IoBits)ioBits; this.InterruptVector = interruptVector; } } [CLSCompliant(false)] public class IoApic { private const uint MaskBit = 1 << 16; private const uint RedHiBase = 0x11; private const uint RedLoBase = 0x10; private const uint IdLimit = 0x0f; private const uint IndexLimit = 24; private const uint RegisterLimit = 0x3f; IoMemory ioRegSel; IoMemory ioWin; uint entries; uint interruptBase; SpinLock ioLock; public IoApic(UIntPtr memoryBase, uint interruptBase) { ioRegSel = IoMemory.MapPhysicalMemory(memoryBase, new UIntPtr(4u), true, true); ioWin = IoMemory.MapPhysicalMemory(memoryBase + new UIntPtr(0x10u), new UIntPtr(4u), true, true); entries = ((ReadRegister(1) & 0xff0000u) >> 16) + 1; for (uint i = 0; i < entries; i++) { SetEntryMask(i, true); } ioLock = new SpinLock(SpinLock.Types.IoApic); this.interruptBase = interruptBase; } [NoHeapAllocation] private bool AcquireLock() { bool irqRestore = Processor.DisableInterrupts(); #if SINGULARITY_MP this.ioLock.Acquire(); #endif // SINGULARITY_MP return irqRestore; } [NoHeapAllocation] private void ReleaseLock(bool irqRestore) { #if SINGULARITY_MP this.ioLock.Release(); #endif // SINGULARITY_MP Processor.RestoreInterrupts(irqRestore); } [ Conditional("DEBUG_APIC") ] [NoHeapAllocation] [HalLock] internal void DumpRedirectionEntries() { bool enabled = AcquireLock(); try { for (uint i = 0; i < entries; i++) { uint hi = ReadRegister(RedHiBase + i * 2); uint lo = ReadRegister(RedLoBase + i * 2); Tracing.Log(Tracing.Debug, "IoApic Entry {0:x2} {1:x8} {2:x8}", i, hi, lo); } } finally { ReleaseLock(enabled); } } [NoHeapAllocation] private void RangeCheck(uint value, uint max, string name) { if (value > max) { DebugStub.Break(); } } [NoHeapAllocation] private uint ReadRegister(uint offset) { RangeCheck(offset, RegisterLimit, "offset"); DebugStub.Assert(Processor.InterruptsDisabled()); IoResult result = ioRegSel.Write32NoThrow(0, offset); DebugStub.Assert(IoResult.Success == result); uint outValue; result = ioWin.Read32NoThrow((byte)0, out outValue); DebugStub.Assert(IoResult.Success == result); return outValue; } [NoHeapAllocation] private void WriteRegister(uint offset, uint value) { RangeCheck(offset, RegisterLimit, "offset"); DebugStub.Assert(Processor.InterruptsDisabled()); IoResult result = ioRegSel.Write32NoThrow(0, offset); DebugStub.Assert(IoResult.Success == result); result = ioWin.Write32NoThrow((byte)0, value); DebugStub.Assert(IoResult.Success == result); } [NoHeapAllocation][HalLock] public byte GetId() { bool enabled = AcquireLock(); try { return (byte)((ReadRegister(0) & 0x0f000000u) >> 24); } finally { ReleaseLock(enabled); } } [NoHeapAllocation][HalLock] public void SetId(byte id) { bool enabled = AcquireLock(); try { RangeCheck(id, IdLimit, "id"); uint reserved = ReadRegister(0) & 0xf0ffffffu; WriteRegister(0, reserved | ((uint)id << 24)); } finally { ReleaseLock(enabled); } } [NoHeapAllocation][HalLock] public byte GetVersion() { bool enabled = AcquireLock(); try { return (byte)ReadRegister(1); } finally { ReleaseLock(enabled); } } [NoHeapAllocation][HalLock] public byte GetArbitrationId() { bool enabled = AcquireLock(); try { return (byte)((ReadRegister(2) & 0x0f000000u) >> 24); } finally { ReleaseLock(enabled); } } [NoHeapAllocation][HalLock] public void SetArbitrationId(byte id) { RangeCheck(id, IdLimit, "id"); bool enabled = AcquireLock(); try { uint reserved = ReadRegister(0) & 0xf0ffffffu; WriteRegister(2, reserved | ((uint)id << 24)); } finally { ReleaseLock(enabled); } } public uint RedirectionEntryCount { [NoHeapAllocation] get { return entries; } } public uint InterruptBase { [NoHeapAllocation] get { return interruptBase; } } [NoHeapAllocation][HalLock] public RedirectionEntry GetRedirectionEntry(uint index) { bool enabled = AcquireLock(); try { RangeCheck(index, IndexLimit, "offset"); uint hi = ReadRegister(RedHiBase + index * 2); uint lo = ReadRegister(RedLoBase + index * 2); return new RedirectionEntry((byte) (hi >> 24), (ushort) ((lo >> 8) & 0x1ff), (byte) (lo & 0xff)); } finally { ReleaseLock(enabled); } } [NoHeapAllocation][HalLock] public void SetRedirectionEntry(uint index, ref RedirectionEntry entry) { bool enabled = AcquireLock(); try { RangeCheck(index, IndexLimit, "offset"); WriteRegister(RedLoBase + index * 2, MaskBit); uint hi = ReadRegister(RedHiBase + index * 2) & 0xffffffu; hi |= ((uint)entry.Destination) << 24; WriteRegister(RedHiBase + index * 2, hi); uint lo = ReadRegister(RedLoBase + index * 2) & 0x1ffffu; lo |= ((uint)entry.IoBits) << 8; lo |= (uint)entry.InterruptVector; WriteRegister(RedLoBase + index * 2, lo); } finally { ReleaseLock(enabled); } } [NoHeapAllocation][HalLock] public void SetEntryMask(uint index, bool masked) { bool enabled = AcquireLock(); try { uint lo = ReadRegister(RedLoBase + index * 2) & ~MaskBit; if (masked) { lo |= MaskBit; } WriteRegister(RedLoBase + index * 2, lo); } finally { ReleaseLock(enabled); } } [NoHeapAllocation][HalLock] public bool GetEntryMask(uint index) { bool enabled = AcquireLock(); try { uint lo = ReadRegister(RedLoBase + index * 2) & MaskBit; return lo == MaskBit; } finally { ReleaseLock(enabled); } } internal static IoApic [] CreateIOApics() { // Prefer to get the list of IoApics from the MADT Madt madt = AcpiTables.GetMadt(); if (madt != null) { ArrayList alist = madt.GetIoApics(); if (alist.Count > 0) { IoApic [] apics = new IoApic[alist.Count]; for (int i = 0; i < alist.Count; i++) { // Have to convert from a ...Hal.Acpi.IoApic into a Hal.IoApic: Microsoft.Singularity.Hal.Acpi.IoApic sourceEntry = (Microsoft.Singularity.Hal.Acpi.IoApic) alist[i]; IoApic destEntry = new IoApic(sourceEntry.Address, sourceEntry.InterruptBase); destEntry.SetId(sourceEntry.Id); apics[i] = destEntry; } DebugStub.Print("Created IoApics from MADT table\n"); return apics; } } DebugStub.Print("Created IoApics from MpResources table table\n"); // Otherwise, create from Mp tables return CreateFromMpResources(); } internal static IoApic [] CreateFromMpResources() { ArrayList ioApicEntries = MpResources.IoApicEntries; if (ioApicEntries == null) { DebugStub.Print("No I/O APICs found\n"); return null; } // Scan IoApic entries and remove those that are marked unusable int usable = ioApicEntries.Count; foreach (MpIoApicEntry entry in ioApicEntries) { if (((int)entry.Flags & 0x1) != 0x1) usable--; } if (usable == 0) { DebugStub.Print("No usable I/O APICs found\n"); return null; } IoApic [] apics = new IoApic[usable]; int i = 0; foreach (MpIoApicEntry entry in ioApicEntries) { if (((int)entry.Flags & 0x1) != 0x1) continue; apics[i] = new IoApic(new UIntPtr(entry.BaseAddress), 0); apics[i].SetId(entry.Id); i++; } return apics; } } public class IoApicInspector { [NoHeapAllocation] public static void DescribeModMask(IoBits ioBits) { ioBits = (IoBits) ((ushort)ioBits & (ushort)IoBits.DelModMask); switch (ioBits) { case IoBits.DelModFixed: DebugStub.Print("Fixed\n"); return; case IoBits.DelModLowest: DebugStub.Print("Lowest\n"); return; case IoBits.DelModSMI: DebugStub.Print("SMI\n"); return; case IoBits.DelModReserved1: DebugStub.Print("Reserved1\n"); return; case IoBits.DelModNMI: DebugStub.Print("NMI\n"); return; case IoBits.DelModINIT: DebugStub.Print("INIT\n"); return; case IoBits.DelModReserved2: DebugStub.Print("Reserved2\n"); return; case IoBits.DelModExtINT: DebugStub.Print("ExtINT\n"); return; default: DebugStub.Print("ERROR\n"); return; } } [NoHeapAllocation] public static void PrintApic(IoApic ioApic) { DebugStub.Print("Io Apic\n"); DebugStub.Print(" Id: {0:x2} Version: {1:x4} ArbitrationId: {2:x2}\n", __arglist( ioApic.GetId(), ioApic.GetVersion(), ioApic.GetArbitrationId())); for (uint i = 0; i < ioApic.RedirectionEntryCount; i++) { RedirectionEntry e = ioApic.GetRedirectionEntry(i); IoBits ib = (IoBits) ((int)e.IoBits & ~(int)IoBits.DelModMask); DebugStub.Print(" IoRedTbl[{0:x2}]" + "Dst: {1:x2} IntVec: {2:x2} Control: {3} {4} {5} {6} {7} {8}" + "Delivery Mode ", __arglist( i, e.Destination, e.InterruptVector, ((ib & IoBits.DstLogical) != 0) ? "Logical" : "Physical", ((ib & IoBits.IrqMask) != 0) ? "Masked" : "Unmasked", ((ib & IoBits.TriggerModeMask) != 0) ? "Level" : "Edge", ((ib & IoBits.RemoteIRR) != 0) ? "Accept" : "Recv", ((ib & IoBits.IntPolMask) != 0) ? "Hi active" : "Lo active", ((ib & IoBits.DeliveryStatus) != 0) ? "Pending" : "Idle")); DescribeModMask(e.IoBits); } } } }