//////////////////////////////////////////////////////////////////////////////// // // Microsoft Research Singularity // // Copyright (c) Microsoft Corporation. All rights reserved. // // File: Pic.cs // // Note: // http://home.ozonline.com.au/davmac/davpage/PIC.html // //#define DEBUG_INTERRUPTS //#define DEBUG_DISPATCH_IO using Microsoft.Singularity.Io; using System; using System.Diagnostics; using System.Runtime.CompilerServices; using System.Threading; using Microsoft.Singularity.Configuration; namespace Microsoft.Singularity.Hal { // declare resources for the kernel manifest [DriverCategory] [Signature("/pnp/PNP0000")] public sealed class PicResources : DriverCategoryDeclaration { [IoPortRange(0, Default = 0x20, Length = 0x02)] public IoPortRange port1; [IoPortRange(1, Default = 0xa0, Length = 0x02)] public IoPortRange port2; } [CLSCompliant(false)] public sealed class Pic { // Constants for PIC interrupt vectors. public const byte PIC0_BIOS_VECTOR = 0x08; // IRQ0..IRQ7 public const byte PIC1_BIOS_VECTOR = 0x70; // IRQ8..IRQF public const byte PIC_NS_EOI = 0x20; // OCW1: End of Interrupt. public const ushort PIC_I_PIC1 = 0x04; // IRQ 2 / INT 0x0a 10 public const byte PIC_VECTORS = 16; private ushort irqMask; private byte baseVector; private IoPort pic0CtrlPort; private IoPort pic0MaskPort; private IoPort pic1CtrlPort; private IoPort pic1MaskPort; internal Pic(PnpConfig config) { // /pnp/08/00/01/PNP0000 0003 cfg dis // I/O Port inf=01 min=0020 max=0020 aln=01 len=02 0020..0021 // I/O Port inf=01 min=00a0 max=00a0 aln=01 len=02 00a0..00a1 // IRQ mask=0004 type=79 // Range[0]: 0x20; // PIC0 // Range[1]: 0xA0; // PIC1 pic0CtrlPort = ((IoPortRange)config.DynamicRanges[0]) .PortAtOffset(0, 1, Access.ReadWrite); pic0MaskPort = ((IoPortRange)config.DynamicRanges[0]) .PortAtOffset(1, 1, Access.ReadWrite); pic1CtrlPort = ((IoPortRange)config.DynamicRanges[1]) .PortAtOffset(0, 1, Access.ReadWrite); pic1MaskPort = ((IoPortRange)config.DynamicRanges[1]) .PortAtOffset(1, 1, Access.ReadWrite); } public byte Initialize(byte baseVector) { DebugStub.Print("Pic.Initializing IRQs at interrupt {0:x2}\n", __arglist(baseVector)); this.baseVector = baseVector; irqMask = unchecked((ushort)~PIC_I_PIC1); // Set ICW1 (must be followed by ICW2, ICW3, and ICW4). // 00010001b - 8-byte,cascaded,triggered,w/ICW4 pic1CtrlPort.Write8(0x11); pic0CtrlPort.Write8(0x11); // Set ICW2..ICW3 (must follow ICW1) pic1MaskPort.Write8((byte)(baseVector + 8)); pic0MaskPort.Write8(baseVector); pic1MaskPort.Write8(0x02); // Cascaded interrupt number pic0MaskPort.Write8(0x04); // Cascaded interrupt bit. pic1MaskPort.Write8(1); // End of Interrupt pic0MaskPort.Write8(1); pic1CtrlPort.Write8(0x20); // EOI pic0CtrlPort.Write8(0x20); // EOI MaskAll(); return PIC_VECTORS; } public void Initialize() { this.Initialize(0x70); } public void Finalize() { UnmaskAll(); } [NoHeapAllocation] public byte InterruptToIrq(byte interrupt) { return (byte)(interrupt - baseVector); } [NoHeapAllocation] public byte IrqToInterrupt(byte irq) { return (byte)(baseVector + irq); } public byte MaximumIrq { [NoHeapAllocation] get { return PIC_VECTORS - 1; } } [System.Diagnostics.Conditional("PIC_DEBUG")] private void DumpRegisters() { pic0CtrlPort.Write8(0x04 | 0x02); // OCW3 - read IRR byte irr0 = pic0CtrlPort.Read8(); pic0CtrlPort.Write8(0x04 | 0x03); // OCW3 - read ISR byte isr0 = pic0CtrlPort.Read8(); pic1CtrlPort.Write8(0x04 | 0x02); // OCW3 - read IRR byte irr1 = pic1CtrlPort.Read8(); pic1CtrlPort.Write8(0x04 | 0x03); // OCW3 - read ISR byte isr1 = pic1CtrlPort.Read8(); byte mask0 = pic0MaskPort.Read8(); byte mask1 = pic1MaskPort.Read8(); DebugStub.Print("PIC IRR: {0:x2}{1:x2} ISR: {2:x2}{3:x2} Mask:{4:x2}{5:x2}\n", __arglist(irr1, irr0, isr1, isr0, mask1, mask0)); } [NoHeapAllocation] public void ClearInterrupt(byte interrupt) { byte irq = InterruptToIrq(interrupt); Mask(irq); AckIrq(irq); #if DEBUG_INTERRUPTS DebugStub.Print("pic.ClearInterrupt({0:x2})\n", __arglist(irqMask)); #endif } [NoHeapAllocation] public void AckIrq(byte irq) { DebugStub.Assert(Processor.InterruptsDisabled()); #if PIC_DEBUG DumpRegisters(); #endif // Mark the IRQ as activated and mask it #if DEBUG_INTERRUPTS DebugStub.Print("Int{0:x2} Acked, Mask={1:x4}\n", __arglist(irq + baseVector, irqMask)); #endif // Quite the interrupt controller. IoResult result; if (irq >= 8) { result = pic1CtrlPort.Write8NoThrow(PIC_NS_EOI); DebugStub.Assert(IoResult.Success == result); } result = pic0CtrlPort.Write8NoThrow(PIC_NS_EOI); DebugStub.Assert(IoResult.Success == result); #if PIC_DEBUG DumpRegisters(); #endif } [NoHeapAllocation] public ushort Pending() { byte irr0; byte irr1; IoResult result = pic0CtrlPort.Write8NoThrow(0x04 | 0x02); // OCW3 - read IRR DebugStub.Assert(IoResult.Success == result); result = pic0CtrlPort.Read8NoThrow(out irr0); DebugStub.Assert(IoResult.Success == result); result = pic1CtrlPort.Write8NoThrow(0x04 | 0x02); // OCW3 - read IRR DebugStub.Assert(IoResult.Success == result); result = pic1CtrlPort.Read8NoThrow(out irr1); DebugStub.Assert(IoResult.Success == result); return (ushort) (irr0 | (irr1<<8)); } [NoHeapAllocation] public void EnableIrq(byte irq) { if (irq == 2 || irq >= PIC_VECTORS) { DebugStub.Break(); // throw new OverflowException // (String.Format("irq {0} out of range.", irq)); } #if DEBUG_INTERRUPTS DebugStub.Print("Int{0:x2} Enable, Mask={1:x4}\n", __arglist(irq + baseVector, irqMask)); #endif bool saved = Processor.DisableInterrupts(); try { Unmask(irq); DumpRegisters(); } finally { Processor.RestoreInterrupts(saved); } } [NoHeapAllocation] public void DisableIrq(byte irq) { if (irq == 2 || irq >= PIC_VECTORS) { DebugStub.Break(); // throw new OverflowException // (String.Format("irq {0} out of range.", irq)); } #if DEBUG_INTERRUPTS DebugStub.Print("Int{0:x2} Disable\n", __arglist(irq + baseVector)); #endif bool saved = Processor.DisableInterrupts(); try { Mask(irq); DumpRegisters(); } finally { Processor.RestoreInterrupts(saved); } } ////////////////////////////////////////////////////////////////////// // // [NoHeapAllocation] private void Mask(byte irq) { DebugStub.Assert(Processor.InterruptsDisabled()); ushort newMask = (ushort)(irqMask | (1 << irq)); if (newMask != irqMask) { #if DEBUG_DISPATCH_IO DebugStub.WriteLine("-- Mask IRQs: old={0:x4} new={1:x4}", __arglist(irqMask, newMask)); #endif irqMask = newMask; IoResult result; result = pic1MaskPort.Write8NoThrow((byte)(irqMask >> 8)); DebugStub.Assert(IoResult.Success == result); result = pic0MaskPort.Write8NoThrow((byte)(irqMask & 0xff)); DebugStub.Assert(IoResult.Success == result); #if PIC_DEBUG byte mask0; pic0MaskPort.Read8NoThrow(out mask0); byte mask1; pic1MaskPort.Read8NoThrow(out mask1); DebugStub.Print("PIC Mask: {0:x2}{1:x2}\n", __arglist(mask1, mask0)); #endif } } [NoHeapAllocation] private void Unmask(byte irq) { DebugStub.Assert(Processor.InterruptsDisabled()); ushort newMask = (ushort)(irqMask & ~(1 << irq)); if (newMask != irqMask) { #if DEBUG_DISPATCH_IO DebugStub.WriteLine("-- Unmask IRQs: old={0:x4} new={1:x4}", __arglist(irqMask, newMask)); #endif irqMask = newMask; IoResult result; result = pic1MaskPort.Write8NoThrow((byte)(irqMask >> 8)); DebugStub.Assert(IoResult.Success == result); result = pic0MaskPort.Write8NoThrow((byte)(irqMask & 0xff)); DebugStub.Assert(IoResult.Success == result); #if PIC_DEBUG byte mask0; result = pic0MaskPort.Read8(out mask0); DebugStub.Assert(IoResult.Success == result); byte mask1; result = pic1MaskPort.Read8(out mask1); DebugStub.Assert(IoResult.Success == result); DebugStub.Print("PIC Mask: {0:x2}{1:x2}\n", __arglist(mask1, mask0)); #endif } } [NoHeapAllocation] public bool IrqMasked(byte irq) { if (irq >= PIC_VECTORS) { return false; } return (irqMask & (1u << irq)) != 0; } private void MaskAll() { DebugStub.Assert(Processor.InterruptsDisabled()); pic1MaskPort.Write8(0xff); pic0MaskPort.Write8(0xff); } private void UnmaskAll() { DebugStub.Assert(Processor.InterruptsDisabled()); pic1MaskPort.Write8((byte)(irqMask >> 8)); pic0MaskPort.Write8((byte)(irqMask & 0xff)); } } }