1 // 2 // Copyright (c) 2010-2018 Antmicro 3 // 4 // This file is licensed under the MIT License. 5 // Full license text is available in 'licenses/MIT.txt'. 6 // 7 using System; 8 using Antmicro.Renode.Logging; 9 using Antmicro.Renode.Peripherals.Bus; 10 using Antmicro.Renode.Utilities; 11 using Antmicro.Renode.Core; 12 using System.Collections.Generic; 13 using System.Collections.ObjectModel; 14 using System.Linq; 15 16 namespace Antmicro.Renode.Peripherals.IRQControllers 17 { 18 [AllowedTranslations(AllowedTranslation.ByteToDoubleWord)] 19 public class IOAPIC: IDoubleWordPeripheral, IIRQController, IKnownSize, INumberedGPIOOutput 20 { IOAPIC(LAPIC lapic)21 public IOAPIC(LAPIC lapic) 22 { 23 this.lapic = lapic; 24 var irqs = new Dictionary<int, IGPIO>(); 25 mask = new bool[MaxRedirectionTableEntries]; 26 internalLock = new object(); 27 for(int i = 0; i < NumberOfOutgoingInterrupts; i++) 28 { 29 irqs[i] = new GPIO(); 30 } 31 Connections = new ReadOnlyDictionary<int, IGPIO>(irqs); 32 externalIrqToVectorMapping = new Dictionary<int, int>(); 33 Reset(); 34 } 35 OnGPIO(int number, bool value)36 public void OnGPIO(int number, bool value) 37 { 38 lock(internalLock) 39 { 40 if(number < 0 || number > MaxRedirectionTableEntries) 41 { 42 throw new ArgumentOutOfRangeException(string.Format("IOAPIC has {0} interrupts, but {1} was triggered", MaxRedirectionTableEntries, number)); 43 } 44 45 if(!mask[number]) 46 { 47 Connections[externalIrqToVectorMapping[number]].Set(value); 48 } 49 } 50 } 51 ReadDoubleWord(long offset)52 public uint ReadDoubleWord(long offset) 53 { 54 lock(internalLock) 55 { 56 if(offset == (long)Registers.Index) 57 { 58 return lastIndex; 59 } 60 if(offset == (long)Registers.Data) 61 { 62 if(lastIndex >= (uint)IndirectRegisters.IoRedirectionTable0 && lastIndex <= (uint)IndirectRegisters.IoRedirectionTable23 + 1) 63 { 64 if(lastIndex % 2 == 0) 65 { 66 var tableIndex = (int)((lastIndex - (uint)IndirectRegisters.IoRedirectionTable0) / 2); 67 return (uint)((mask[tableIndex] ? MaskedBitMask : 0x0) | externalIrqToVectorMapping[tableIndex]); 68 } 69 return 0; 70 } 71 } 72 this.LogUnhandledRead(offset); 73 return 0; 74 } 75 } 76 WriteDoubleWord(long offset, uint value)77 public void WriteDoubleWord(long offset, uint value) 78 { 79 lock(internalLock) 80 { 81 switch((Registers)offset) 82 { 83 case Registers.Index: 84 lastIndex = value; 85 break; 86 case Registers.Data: 87 if(lastIndex >= (uint)IndirectRegisters.IoRedirectionTable0 && lastIndex <= (uint)IndirectRegisters.IoRedirectionTable23 + 1) 88 { 89 var tableIndex = (int)((lastIndex - (uint)IndirectRegisters.IoRedirectionTable0) / 2); 90 if (lastIndex % 2 != 0) 91 { 92 // high bits 93 this.Log(LogLevel.Noisy, "Write to high bits of {0} table index: {0}. It contains physical/logical destination address (APIC ID or set o processors) that is not supported right now.", tableIndex, value); 94 } 95 else 96 { 97 // low bits 98 externalIrqToVectorMapping[tableIndex] = (int)(value & 0xFF); 99 mask[tableIndex] = (value & MaskedBitMask) != 0; 100 101 this.Log(LogLevel.Info, "Setting {0} table index: interrupt vector=0x{1:X}, mask={2}", tableIndex, externalIrqToVectorMapping[tableIndex], mask[tableIndex]); 102 } 103 } 104 break; 105 case Registers.EndOfInterrupt: 106 // value here means irq vector 107 var externalIrqIds = externalIrqToVectorMapping.Where(x => x.Value == (int)value).Select(x => x.Key).ToArray(); 108 if(externalIrqIds.Length == 0) 109 { 110 //We filter out lapic internal timer vector. Due to a bug in HW the software clears all interrupts on ioapic, although this one is only handled by lapic. 111 if(value != lapic.InternalTimerVector) 112 { 113 this.Log(LogLevel.Warning, "Calling end of interrupt on unmapped vector: {0}", value); 114 } 115 return; 116 } 117 118 foreach(var id in externalIrqIds.Where(x => Connections[x].IsSet)) 119 { 120 Connections[id].Unset(); 121 this.Log(LogLevel.Debug, "Ending interrupt #{0} (vector 0x{1:X})", id, value); 122 } 123 break; 124 default: 125 this.LogUnhandledWrite(offset, value); 126 break; 127 } 128 } 129 } 130 Reset()131 public void Reset() 132 { 133 lock(internalLock) 134 { 135 for(int i = 0; i < Connections.Count; i++) 136 { 137 Connections[i].Unset(); 138 } 139 for(int i = 0; i < mask.Length; i++) 140 { 141 mask[i] = true; 142 } 143 externalIrqToVectorMapping.Clear(); 144 lastIndex = 0; 145 } 146 } 147 148 public long Size 149 { 150 get 151 { 152 return 1.MB(); 153 } 154 } 155 156 public IReadOnlyDictionary<int, IGPIO> Connections { get; private set; } 157 158 private bool[] mask; 159 private uint lastIndex; 160 private object internalLock; 161 private readonly Dictionary<int, int> externalIrqToVectorMapping; 162 private readonly LAPIC lapic; 163 164 private const int MaxRedirectionTableEntries = 24; 165 private const int NumberOfOutgoingInterrupts = 256; 166 private const int MaskedBitMask = 0x10000; 167 168 public enum Registers 169 { 170 Index = 0x0, 171 Data = 0x10, 172 IRQPinAssertion = 0x20, 173 EndOfInterrupt = 0x40 174 } 175 176 public enum IndirectRegisters 177 { 178 IoRedirectionTable0 = 0x10, 179 IoRedirectionTable23 = 0x3E 180 } 181 } 182 } 183