The Pedigree Project  0.1
mach_pc/Pic.cc
1 /*
2  * Copyright (c) 2008-2014, Pedigree Developers
3  *
4  * Please see the CONTRIB file in the root of the source tree for a full
5  * list of contributors.
6  *
7  * Permission to use, copy, modify, and distribute this software for any
8  * purpose with or without fee is hereby granted, provided that the above
9  * copyright notice and this permission notice appear in all copies.
10  *
11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18  */
19 
20 #include "Pic.h"
21 #include "pedigree/kernel/LockGuard.h"
22 #include "pedigree/kernel/Log.h"
23 #include "pedigree/kernel/compiler.h"
24 #include "pedigree/kernel/machine/Device.h"
25 #include "pedigree/kernel/machine/IrqHandler.h"
26 #include "pedigree/kernel/processor/InterruptManager.h"
27 #include "pedigree/kernel/utilities/Iterator.h"
28 #include "pedigree/kernel/utilities/utility.h"
29 
30 // TODO: Needs locking
31 
32 #define BASE_INTERRUPT_VECTOR 0x20
33 
34 // Number of IRQs in a single millisecond before an IRQ source is blocked.
35 // A value of 10, for example, would mean if an IRQ matches the threshold
36 // and sustained its output for a second, 10,000 IRQs would be triggered.
37 #define DEFAULT_IRQ_MITIGATE_THRESHOLD 10
38 
40 
41 void Pic::tick()
42 {
43 }
44 
45 bool Pic::control(uint8_t irq, ControlCode code, size_t argument)
46 {
47  if (UNLIKELY(irq > 16))
48  return false;
49 
50  Spinlock lock;
51  LockGuard<Spinlock> guard(lock);
52 
53  switch (code)
54  {
55  case MitigationThreshold:
56  if (LIKELY(argument))
57  {
58  if (UNLIKELY(m_Handler[irq].count() > 1))
59  m_MitigationThreshold[irq] += argument;
60  else
61  m_MitigationThreshold[irq] = argument;
62  }
63  else
64  m_MitigationThreshold[irq] = DEFAULT_IRQ_MITIGATE_THRESHOLD;
65  return true;
66  }
67 
68  return false;
69 }
70 
71 irq_id_t
72 Pic::registerIsaIrqHandler(uint8_t irq, IrqHandler *handler, bool bEdge)
73 {
74  if (UNLIKELY(irq >= 16))
75  return 0;
76 
77  // Save the IrqHandler
78  m_Handler[irq].pushBack(handler);
79  m_HandlerEdge[irq] = bEdge;
80 
81  // Enable/Unmask the IRQ
82  enable(irq, true);
83 
84  return irq + BASE_INTERRUPT_VECTOR;
85 }
86 irq_id_t Pic::registerPciIrqHandler(IrqHandler *handler, Device *pDevice)
87 {
88  if (UNLIKELY(!pDevice))
89  return 0;
90  irq_id_t irq = pDevice->getInterruptNumber();
91  if (UNLIKELY(irq >= 16))
92  return 0;
93 
94  // Save the IrqHandler
95  m_Handler[irq].pushBack(handler);
96  m_HandlerEdge[irq] = false; // PCI bus uses level triggered IRQs
97 
98  // Enable/Unmask the IRQ
99  enable(irq, true);
100 
101  return irq + BASE_INTERRUPT_VECTOR;
102 }
103 void Pic::acknowledgeIrq(irq_id_t Id)
104 {
105  uint8_t irq = Id - BASE_INTERRUPT_VECTOR;
106 
107  // Enable the irq again (the interrupt reason got removed)
108  enable(irq, true);
109  eoi(irq);
110 }
111 void Pic::unregisterHandler(irq_id_t Id, IrqHandler *handler)
112 {
113  uint8_t irq = Id - BASE_INTERRUPT_VECTOR;
114 
115  // Disable the IRQ
116  if (!m_Handler[irq].count())
117  enable(irq, false);
118 
119  // Remove the handler
120  for (List<IrqHandler *>::Iterator it = m_Handler[irq].begin();
121  it != m_Handler[irq].end(); it++)
122  {
123  if (*it == handler)
124  {
125  m_Handler[irq].erase(it);
126  return;
127  }
128  }
129 }
130 
132 {
133  // Allocate the I/O ports
134  if (m_SlavePort.allocate(0xA0, 4) == false)
135  return false;
136  if (m_MasterPort.allocate(0x20, 4) == false)
137  return false;
138 
139  // Initialise the slave and master PIC
140  m_MasterPort.write8(0x11, 0);
141  m_SlavePort.write8(0x11, 0);
142  m_MasterPort.write8(BASE_INTERRUPT_VECTOR, 1);
143  m_SlavePort.write8(BASE_INTERRUPT_VECTOR + 0x08, 1);
144  m_MasterPort.write8(0x04, 1);
145  m_SlavePort.write8(0x02, 1);
146  m_MasterPort.write8(0x01, 1);
147  m_SlavePort.write8(0x01, 1);
148  m_MasterPort.write8(0x00, 1);
149  m_SlavePort.write8(0x00, 1);
150 
151  // Register the interrupts
153  for (size_t i = 0; i < 16; i++)
154  if (IntManager.registerInterruptHandler(
155  i + BASE_INTERRUPT_VECTOR, this) == false)
156  return false;
157 
158  for (size_t i = 0; i < 16; i++)
159  {
160  m_IrqCount[i] = 0;
161  m_MitigatedIrqs[i] = false;
162  m_MitigationThreshold[i] = DEFAULT_IRQ_MITIGATE_THRESHOLD;
163  }
164 
165  // Disable all IRQ's (exept IRQ2)
166  enableAll(false);
167 
168  return true;
169 }
170 
172  : m_SlavePort("PIC #2"), m_MasterPort("PIC #1"), m_InterruptMask(0),
173  m_Lock(false)
174 {
175  for (size_t i = 0; i < 16; i++)
176  {
177  m_Handler[i].clear();
178  m_HandlerEdge[i] = false;
179  }
180 }
181 
182 bool Pic::spurious(size_t irq)
183 {
184  if (irq > 7)
185  {
186  // Get ISR for slave.
187  uint8_t mask = 1 << (irq - 8);
188  m_SlavePort.write8(0x0B, 0);
189  uint8_t isr = m_SlavePort.read8(0);
190  m_SlavePort.write8(0x0A, 0);
191  return (isr & mask) == 0;
192  }
193  else
194  {
195  // Get ISR for master.
196  uint8_t mask = 1 << irq;
197  m_MasterPort.write8(0x0B, 0);
198  uint8_t isr = m_MasterPort.read8(0);
199  m_SlavePort.write8(0x0A, 0);
200  return (isr & mask) == 0;
201  }
202 }
203 
204 void Pic::interrupt(size_t interruptNumber, InterruptState &state)
205 {
206  size_t irq = (interruptNumber - BASE_INTERRUPT_VECTOR);
207  m_IrqCount[irq]++;
208 
209  // If disable() has been called for this IRQ, we need to do spurious IRQ
210  // detection.
211  if (m_InterruptMask & (1 << irq))
212  {
213  if (spurious(irq))
214  {
215  ERROR("PIC: spurious IRQ" << Dec << irq << Hex);
216  return;
217  }
218  }
219 
220  // Call the irq handler, if any
221  if (LIKELY(m_Handler[irq].count() != 0))
222  {
223  if (m_HandlerEdge[irq])
224  eoi(irq);
225 
226  bool bHandled = false;
227  for (List<IrqHandler *>::Iterator it = m_Handler[irq].begin();
228  it != m_Handler[irq].end(); it++)
229  {
230  bool tmp = (*it)->irq(irq, state);
231  if ((!bHandled) && tmp)
232  bHandled = true;
233  }
234 
235  if (!bHandled)
236  {
237  // Disable/Mask the IRQ line (the handler did not remove
238  // the interrupt reason, yet)
239  enable(irq, false);
240  }
241 
242  if (!m_HandlerEdge[irq])
243  eoi(irq);
244  }
245  else
246  {
247  NOTICE("PIC: unhandled irq #" << irq << " occurred");
248  }
249 }
250 
251 void Pic::eoi(uint8_t irq)
252 {
253  if (irq > 7)
254  {
255  m_SlavePort.write8(0x60 + (irq - 8), 0);
256 
257  // ACK the cascade IRQ (IRQ2).
258  m_MasterPort.write8(0x62, 0);
259  }
260  else
261  {
262  m_MasterPort.write8(0x60 + irq, 0);
263  }
264 }
265 
266 void Pic::enable(uint8_t irq, bool enable)
267 {
268  if (enable)
269  {
270  m_InterruptMask &= ~(1 << irq);
271  }
272  else
273  {
274  m_InterruptMask |= 1 << irq;
275  }
276 
277  if (irq <= 7)
278  {
279  uint8_t mask = m_MasterPort.read8(1);
280  if (enable == true)
281  mask = mask & ~(1 << irq);
282  else
283  mask = mask | (1 << irq);
284 
285  m_MasterPort.write8(mask, 1);
286  }
287  else
288  {
289  uint8_t mask = m_SlavePort.read8(1);
290  if (enable == true)
291  mask = mask & ~(1 << (irq - 8));
292  else
293  mask = mask | (1 << (irq - 8));
294 
295  m_SlavePort.write8(mask, 1);
296  }
297 }
298 void Pic::enableAll(bool enable)
299 {
300  if (enable == false)
301  {
302  m_InterruptMask = 0xFB;
303  m_MasterPort.write8(0xFB, 1);
304  m_SlavePort.write8(0xFB, 1);
305  }
306  else
307  {
308  m_InterruptMask = 0;
309  m_MasterPort.write8(0x00, 1);
310  m_SlavePort.write8(0x00, 1);
311  }
312 }
IoPort m_MasterPort
Definition: mach_pc/Pic.h:101
virtual bool registerInterruptHandler(size_t nInterruptNumber, InterruptHandler *pHandler)=0
virtual irq_id_t registerPciIrqHandler(IrqHandler *handler, Device *pDevice)
Definition: mach_pc/Pic.cc:86
void clear()
Definition: List.h:386
void pushBack(const T &value)
Definition: List.h:232
uint8_t m_InterruptMask
Definition: mach_pc/Pic.h:114
virtual void tick()
Definition: mach_pc/Pic.cc:41
virtual void acknowledgeIrq(irq_id_t Id)
Definition: mach_pc/Pic.cc:103
Iterator erase(Iterator &Iter)
Definition: List.h:343
size_t m_IrqCount[16]
Definition: mach_pc/Pic.h:108
Handles interrupts and interrupt registrations from kernel components.
virtual uint8_t read8(size_t offset=0)
virtual bool control(uint8_t irq, ControlCode code, size_t argument)
Definition: mach_pc/Pic.cc:45
virtual uintptr_t getInterruptNumber()
Definition: Device.h:262
Definition: Device.h:43
IoPort m_SlavePort
Definition: mach_pc/Pic.h:99
virtual void unregisterHandler(irq_id_t Id, IrqHandler *handler)
Definition: mach_pc/Pic.cc:111
List< IrqHandler * > m_Handler[16]
Definition: mach_pc/Pic.h:104
Definition: List.h:64
bool m_HandlerEdge[16]
Definition: mach_pc/Pic.h:106
static Pic m_Instance
Definition: mach_pc/Pic.h:120
#define NOTICE(text)
Definition: Log.h:74
bool m_MitigatedIrqs[16]
Definition: mach_pc/Pic.h:110
Definition: Log.h:136
bool spurious(size_t irq)
Definition: mach_pc/Pic.cc:182
bool allocate(io_port_t ioPort, size_t size)
Definition: IoPort.cc:34
Pic() INITIALISATION_ONLY
Definition: mach_pc/Pic.cc:171
virtual void interrupt(size_t interruptNumber, InterruptState &state)
Definition: mach_pc/Pic.cc:204
#define ERROR(text)
Definition: Log.h:82
Definition: Log.h:138
static InterruptManager & instance()
Iterator end()
Definition: List.h:135
virtual irq_id_t registerIsaIrqHandler(uint8_t irq, IrqHandler *handler, bool bEdge=false)
Definition: mach_pc/Pic.cc:72
bool initialise() INITIALISATION_ONLY
Definition: mach_pc/Pic.cc:131
size_t m_MitigationThreshold[16]
Definition: mach_pc/Pic.h:112
virtual void write8(uint8_t value, size_t offset=0)
Spinlock m_Lock
Definition: mach_pc/Pic.h:117