]> rtime.felk.cvut.cz Git - l4.git/blob - kernel/fiasco/src/kern/ia32/io_apic.cpp
dfe94e6b5e8e21ebad47fec4240019f325fc0410
[l4.git] / kernel / fiasco / src / kern / ia32 / io_apic.cpp
1 INTERFACE:
2
3 #include <types.h>
4 #include "initcalls.h"
5 #include <spin_lock.h>
6 #include "irq_chip_ia32.h"
7 #include <bitfield>
8
9 class Acpi_madt;
10
11 class Io_apic_entry
12 {
13   friend class Io_apic;
14 private:
15   Unsigned64 _e;
16
17 public:
18   enum Delivery { Fixed, Lowest_prio, SMI, NMI = 4, INIT, ExtINT = 7 };
19   enum Dest_mode { Physical, Logical };
20   enum Polarity { High_active, Low_active };
21   enum Trigger { Edge, Level };
22
23   Io_apic_entry() {}
24   Io_apic_entry(Unsigned8 vector, Delivery d, Dest_mode dm, Polarity p,
25                 Trigger t, Unsigned32 dest)
26     : _e(  vector_bfm_t::val(vector) | delivery_bfm_t::val(d) | mask_bfm_t::val(1)
27          | dest_mode_bfm_t::val(dm)  | polarity_bfm_t::val(p)
28          | trigger_bfm_t::val(t)     | dest_bfm_t::val(dest >> 24))
29   {}
30
31   CXX_BITFIELD_MEMBER( 0,  7, vector, _e);
32   CXX_BITFIELD_MEMBER( 8, 10, delivery, _e);
33   CXX_BITFIELD_MEMBER(11, 11, dest_mode, _e);
34   CXX_BITFIELD_MEMBER(13, 13, polarity, _e);
35   CXX_BITFIELD_MEMBER(15, 15, trigger, _e);
36   CXX_BITFIELD_MEMBER(16, 16, mask, _e);
37   CXX_BITFIELD_MEMBER(56, 63, dest, _e);
38 };
39
40
41 class Io_apic : public Irq_chip_ia32
42 {
43   friend class Jdb_io_apic_module;
44
45 private:
46   struct Apic
47   {
48     Unsigned32 volatile adr;
49     Unsigned32 dummy[3];
50     Unsigned32 volatile data;
51
52     unsigned num_entries();
53     Mword read(int reg);
54     void modify(int reg, Mword set_bits, Mword del_bits);
55     void write(int reg, Mword value);
56   } __attribute__((packed));
57
58   Apic *_apic;
59   Spin_lock<> _l;
60   unsigned _offset;
61   Io_apic *_next;
62
63   static unsigned _nr_irqs;
64   static Io_apic *_first;
65   static Acpi_madt const *_madt;
66 };
67
68 IMPLEMENTATION:
69
70 #include "acpi.h"
71 #include "apic.h"
72 #include "irq_mgr.h"
73 #include "kmem.h"
74 #include "kdb_ke.h"
75 #include "kip.h"
76 #include "lock_guard.h"
77 #include "boot_alloc.h"
78
79 Acpi_madt const *Io_apic::_madt;
80 unsigned Io_apic::_nr_irqs;
81 Io_apic *Io_apic::_first;
82
83
84 class Io_apic_mgr : public Irq_mgr
85 {
86 };
87
88 PUBLIC Irq_mgr::Irq
89 Io_apic_mgr::chip(Mword irq) const
90 {
91   Io_apic *a = Io_apic::find_apic(irq);
92   if (a)
93     return Irq(a, irq - a->gsi_offset());
94
95   return Irq(0, 0);
96 }
97
98 PUBLIC
99 unsigned
100 Io_apic_mgr::nr_irqs() const
101 {
102   return Io_apic::total_irqs();
103 }
104
105 PUBLIC
106 unsigned
107 Io_apic_mgr::nr_msis() const
108 { return 0; }
109
110 PUBLIC unsigned
111 Io_apic_mgr::legacy_override(Mword i)
112 { return Io_apic::legacy_override(i); }
113
114
115 IMPLEMENT inline
116 Mword
117 Io_apic::Apic::read(int reg)
118 {
119   adr = reg;
120   asm volatile ("": : :"memory");
121   return data;
122 }
123
124 IMPLEMENT inline
125 void
126 Io_apic::Apic::modify(int reg, Mword set_bits, Mword del_bits)
127 {
128   register Mword tmp;
129   adr = reg;
130   asm volatile ("": : :"memory");
131   tmp = data;
132   tmp &= ~del_bits;
133   tmp |= set_bits;
134   data = tmp;
135 }
136
137 IMPLEMENT inline
138 void
139 Io_apic::Apic::write(int reg, Mword value)
140 {
141   adr = reg;
142   asm volatile ("": : :"memory");
143   data = value;
144 }
145
146 IMPLEMENT inline
147 unsigned
148 Io_apic::Apic::num_entries()
149 {
150   return (read(1) >> 16) & 0xff;
151 }
152
153 PUBLIC explicit inline
154 Io_apic::Io_apic(Io_apic::Apic *addr, unsigned irqs, unsigned gsi_base)
155 : Irq_chip_ia32(irqs), _apic(addr), _l(Spin_lock<>::Unlocked),
156   _offset(gsi_base), _next(0)
157 {}
158
159
160 PUBLIC inline NEEDS["kdb_ke.h", "lock_guard.h"]
161 Io_apic_entry
162 Io_apic::read_entry(unsigned i)
163 {
164   auto g = lock_guard(_l);
165   Io_apic_entry e;
166   //assert_kdb(i <= num_entries());
167   e._e = (Unsigned64)_apic->read(0x10+2*i) | (((Unsigned64)_apic->read(0x11+2*i)) << 32);
168   return e;
169 }
170
171
172 PUBLIC inline NEEDS["kdb_ke.h", "lock_guard.h"]
173 void
174 Io_apic::write_entry(unsigned i, Io_apic_entry const &e)
175 {
176   auto g = lock_guard(_l);
177   //assert_kdb(i <= num_entries());
178   _apic->write(0x10+2*i, e._e);
179   _apic->write(0x11+2*i, e._e >> 32);
180 }
181
182 PUBLIC static FIASCO_INIT
183 bool
184 Io_apic::init(unsigned cpu)
185 {
186   _madt = Acpi::find<Acpi_madt const *>("APIC");
187
188   if (_madt == 0)
189     {
190       printf("Could not find APIC in RSDT nor XSDT, skipping init\n");
191       return false;
192     }
193   printf("IO-APIC: MADT = %p\n", _madt);
194
195   int n_apics = 0;
196
197   for (n_apics = 0;
198        Acpi_madt::Io_apic const *ioapic = static_cast<Acpi_madt::Io_apic const *>(_madt->find(Acpi_madt::IOAPIC, n_apics));
199        ++n_apics)
200     {
201       printf("IO-APIC[%2d]: struct: %p adr=%x\n", n_apics, ioapic, ioapic->adr);
202
203       Address offs;
204       Address va = Mem_layout::alloc_io_vmem(Config::PAGE_SIZE);
205       assert (va);
206
207       Kmem::map_phys_page(ioapic->adr, va, false, true, &offs);
208
209       Kip::k()->add_mem_region(Mem_desc(ioapic->adr, ioapic->adr + Config::PAGE_SIZE -1, Mem_desc::Reserved));
210
211       Io_apic::Apic *a = (Io_apic::Apic*)(va + offs);
212       a->write(0, 0);
213
214       unsigned const irqs = a->num_entries() + 1;
215       Io_apic *apic = new Boot_object<Io_apic>(a, irqs, ioapic->irq_base);
216
217       if ((apic->_offset + irqs) > _nr_irqs)
218         _nr_irqs = apic->_offset + irqs;
219
220       for (unsigned i = 0; i < irqs; ++i)
221         {
222           int v = 0x20+i;
223           Io_apic_entry e(v, Io_apic_entry::Fixed, Io_apic_entry::Physical,
224               Io_apic_entry::High_active, Io_apic_entry::Edge,
225               ::Apic::apic.cpu(cpu)->apic_id());
226           apic->write_entry(i, e);
227         }
228
229       Io_apic **c = &_first;
230       while (*c && (*c)->_offset < apic->_offset)
231         c = &((*c)->_next);
232
233       apic->_next = *c;
234       *c = apic;
235
236       printf("IO-APIC[%2d]: pins %u\n", n_apics, irqs);
237       apic->dump();
238     }
239
240   if (!n_apics)
241     {
242       printf("IO-APIC: Could not find IO-APIC in MADT, skip init\n");
243       return false;
244     }
245
246
247   printf("IO-APIC: dual 8259: %s\n", _madt->apic_flags & 1 ? "yes" : "no");
248
249   for (unsigned tmp = 0;;++tmp)
250     {
251       Acpi_madt::Irq_source const *irq
252         = static_cast<Acpi_madt::Irq_source const *>(_madt->find(Acpi_madt::Irq_src_ovr, tmp));
253
254       if (!irq)
255         break;
256
257       printf("IO-APIC: ovr[%2u] %02x -> %x\n", tmp, irq->src, irq->irq);
258     }
259
260   Irq_mgr::mgr = new Boot_object<Io_apic_mgr>();
261
262   // in the case we use the IO-APIC not the PIC we can dynamically use
263   // INT vectors from 0x20 to 0x2f too
264   _vectors.add_free(0x20, 0x30);
265   return true;
266 };
267
268 PUBLIC static
269 unsigned
270 Io_apic::total_irqs()
271 { return _nr_irqs; }
272
273 PUBLIC static
274 unsigned
275 Io_apic::legacy_override(unsigned i)
276 {
277   if (!_madt)
278     return i;
279
280   unsigned tmp = 0;
281   for (;;++tmp)
282     {
283       Acpi_madt::Irq_source const *irq
284         = static_cast<Acpi_madt::Irq_source const *>(_madt->find(Acpi_madt::Irq_src_ovr, tmp));
285
286       if (!irq)
287         break;
288
289       if (irq->src == i)
290         return irq->irq;
291     }
292   return i;
293 }
294
295 PUBLIC
296 void
297 Io_apic::dump()
298 {
299   for (unsigned i = 0; i < _irqs; ++i)
300     {
301       Io_apic_entry e = read_entry(i);
302       printf("  PIN[%2u%c]: vector=%2x, del=%u, dm=%s, dest=%u (%s, %s)\n",
303              i, e.mask() ? 'm' : '.',
304              (unsigned)e.vector(), (unsigned)e.delivery(), e.dest_mode() ? "logical" : "physical",
305              (unsigned)e.dest(),
306              e.polarity() ? "low" : "high",
307              e.trigger() ? "level" : "edge");
308     }
309
310 }
311
312 PUBLIC inline
313 bool
314 Io_apic::valid() const { return _apic; }
315
316 PRIVATE inline NEEDS["kdb_ke.h", "lock_guard.h"]
317 void
318 Io_apic::_mask(unsigned irq)
319 {
320   auto g = lock_guard(_l);
321   //assert_kdb(irq <= _apic->num_entries());
322   _apic->modify(0x10 + irq * 2, 1UL << 16, 0);
323 }
324
325 PRIVATE inline NEEDS["kdb_ke.h", "lock_guard.h"]
326 void
327 Io_apic::_unmask(unsigned irq)
328 {
329   auto g = lock_guard(_l);
330   //assert_kdb(irq <= _apic->num_entries());
331   _apic->modify(0x10 + irq * 2, 0, 1UL << 16);
332 }
333
334 PUBLIC inline NEEDS["kdb_ke.h", "lock_guard.h"]
335 bool
336 Io_apic::masked(unsigned irq)
337 {
338   auto g = lock_guard(_l);
339   //assert_kdb(irq <= _apic->num_entries());
340   return _apic->read(0x10 + irq * 2) & (1UL << 16);
341 }
342
343 PUBLIC inline
344 void
345 Io_apic::sync()
346 {
347   (void)_apic->data;
348 }
349
350 PUBLIC inline NEEDS["kdb_ke.h", "lock_guard.h"]
351 void
352 Io_apic::set_dest(unsigned irq, Mword dst)
353 {
354   auto g = lock_guard(_l);
355   //assert_kdb(irq <= _apic->num_entries());
356   _apic->modify(0x11 + irq * 2, dst & (~0UL << 24), ~0UL << 24);
357 }
358
359 PUBLIC inline
360 unsigned
361 Io_apic::gsi_offset() const { return _offset; }
362
363 PUBLIC static
364 Io_apic *
365 Io_apic::find_apic(unsigned irqnum)
366 {
367   for (Io_apic *a = _first; a; a = a->_next)
368     {
369       if (a->_offset <= irqnum && a->_offset + a->_irqs > irqnum)
370         return a;
371     }
372   return 0;
373 };
374
375 PUBLIC void
376 Io_apic::mask(Mword irq)
377 {
378   _mask(irq);
379   sync();
380 }
381
382 PUBLIC void
383 Io_apic::ack(Mword)
384 {
385   ::Apic::irq_ack();
386 }
387
388 PUBLIC void
389 Io_apic::mask_and_ack(Mword irq)
390 {
391   _mask(irq);
392   sync();
393   ::Apic::irq_ack();
394 }
395
396 PUBLIC void
397 Io_apic::unmask(Mword irq)
398 {
399   _unmask(irq);
400 }
401
402 PUBLIC void
403 Io_apic::set_cpu(Mword irq, unsigned cpu)
404 {
405   set_dest(irq, ::Apic::apic.cpu(cpu)->apic_id());
406 }
407
408 static inline
409 Mword to_io_apic_trigger(unsigned mode)
410 {
411   return (mode & Irq_base::Trigger_level)
412             ? Io_apic_entry::Level
413             : Io_apic_entry::Edge;
414 }
415
416 static inline
417 Mword to_io_apic_polarity(unsigned mode)
418 {
419   return (mode & Irq_base::Polarity_low)
420              ? Io_apic_entry::Low_active
421              : Io_apic_entry::High_active;
422 }
423
424 PUBLIC unsigned
425 Io_apic::set_mode(Mword pin, unsigned mode)
426 {
427   if ((mode & Irq_base::Polarity_mask) == Irq_base::Polarity_both)
428     mode = Irq_base::Polarity_low;
429
430   Io_apic_entry e = read_entry(pin);
431   e.polarity() = to_io_apic_polarity(mode);
432   e.trigger() = to_io_apic_trigger(mode);
433   write_entry(pin, e);
434   return mode;
435 }
436
437 PUBLIC
438 bool
439 Io_apic::alloc(Irq_base *irq, Mword pin)
440 {
441   unsigned v = valloc(irq, pin, 0);
442
443   if (!v)
444     return false;
445
446   Io_apic_entry e = read_entry(pin);
447   e.vector() = v;
448   write_entry(pin, e);
449   return true;
450 }
451
452 PUBLIC
453 void
454 Io_apic::unbind(Irq_base *irq)
455 {
456   extern char entry_int_apic_ignore[];
457   Mword n = irq->pin();
458   mask(n);
459   vfree(irq, &entry_int_apic_ignore);
460   Irq_chip_icu::unbind(irq);
461 }
462
463 PUBLIC inline
464 char const *
465 Io_apic::chip_type() const
466 { return "IO-APIC"; }
467
468 PUBLIC static inline
469 bool
470 Io_apic::active()
471 { return _first; }