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