]> rtime.felk.cvut.cz Git - l4.git/blob - kernel/fiasco/src/kern/ia32/io_apic.cpp
Update
[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 <cxx/bitfield>
8 #include "irq_mgr.h"
9 #include "pm.h"
10
11 class Acpi_madt;
12
13 class Io_apic_entry
14 {
15   friend class Io_apic;
16 private:
17   Unsigned64 _e;
18
19 public:
20   enum Delivery { Fixed, Lowest_prio, SMI, NMI = 4, INIT, ExtINT = 7 };
21   enum Dest_mode { Physical, Logical };
22   enum Polarity { High_active, Low_active };
23   enum Trigger { Edge, Level };
24
25   Io_apic_entry() {}
26   Io_apic_entry(Unsigned8 vector, Delivery d, Dest_mode dm, Polarity p,
27                 Trigger t, Unsigned32 dest)
28     : _e(  vector_bfm_t::val(vector) | delivery_bfm_t::val(d) | mask_bfm_t::val(1)
29          | dest_mode_bfm_t::val(dm)  | polarity_bfm_t::val(p)
30          | trigger_bfm_t::val(t)     | dest_bfm_t::val(dest >> 24))
31   {}
32
33   CXX_BITFIELD_MEMBER( 0,  7, vector, _e);
34   CXX_BITFIELD_MEMBER( 8, 10, delivery, _e);
35   CXX_BITFIELD_MEMBER(11, 11, dest_mode, _e);
36   CXX_BITFIELD_MEMBER(13, 13, polarity, _e);
37   CXX_BITFIELD_MEMBER(15, 15, trigger, _e);
38   CXX_BITFIELD_MEMBER(16, 16, mask, _e);
39   // support for IRQ remapping
40   CXX_BITFIELD_MEMBER(48, 48, format, _e);
41   // support for IRQ remapping
42   CXX_BITFIELD_MEMBER(49, 63, irt_index, _e);
43   CXX_BITFIELD_MEMBER(56, 63, dest, _e);
44 };
45
46
47 class Io_apic : public Irq_chip_icu, protected Irq_chip_ia32
48 {
49   friend class Jdb_io_apic_module;
50   friend class Irq_chip_ia32;
51 public:
52   unsigned nr_irqs() const override { return Irq_chip_ia32::nr_irqs(); }
53   bool reserve(Mword pin) override { return Irq_chip_ia32::reserve(pin); }
54   Irq_base *irq(Mword pin) const override { return Irq_chip_ia32::irq(pin); }
55
56 private:
57   struct Apic
58   {
59     Unsigned32 volatile adr;
60     Unsigned32 dummy[3];
61     Unsigned32 volatile data;
62
63     unsigned num_entries();
64     Mword read(int reg);
65     void modify(int reg, Mword set_bits, Mword del_bits);
66     void write(int reg, Mword value);
67   } __attribute__((packed));
68
69   Apic *_apic;
70   mutable Spin_lock<> _l;
71   unsigned _offset;
72   Io_apic *_next;
73
74   static unsigned _nr_irqs;
75   static Io_apic *_first;
76   static Acpi_madt const *_madt;
77   static Io_apic_entry *_state_save_area;
78 };
79
80 class Io_apic_mgr : public Irq_mgr, public Pm_object
81 {
82 public:
83   Io_apic_mgr() { register_pm(Cpu_number::boot_cpu()); }
84 };
85
86
87 IMPLEMENTATION:
88
89 #include "acpi.h"
90 #include "apic.h"
91 #include "assert.h"
92 #include "kmem.h"
93 #include "kip.h"
94 #include "lock_guard.h"
95 #include "boot_alloc.h"
96 #include "warn.h"
97
98 enum { Print_info = 0 };
99
100 Acpi_madt const *Io_apic::_madt;
101 unsigned Io_apic::_nr_irqs;
102 Io_apic *Io_apic::_first;
103 Io_apic_entry *Io_apic::_state_save_area;
104
105
106 PUBLIC Irq_mgr::Irq
107 Io_apic_mgr::chip(Mword irq) const
108 {
109   Io_apic *a = Io_apic::find_apic(irq);
110   if (a)
111     return Irq(a, irq - a->gsi_offset());
112
113   return Irq(0, 0);
114 }
115
116 PUBLIC
117 unsigned
118 Io_apic_mgr::nr_irqs() const
119 {
120   return Io_apic::total_irqs();
121 }
122
123 PUBLIC
124 unsigned
125 Io_apic_mgr::nr_msis() const
126 { return 0; }
127
128 PUBLIC unsigned
129 Io_apic_mgr::legacy_override(Mword i)
130 { return Io_apic::legacy_override(i); }
131
132 PUBLIC void
133 Io_apic_mgr::pm_on_suspend(Cpu_number cpu)
134 {
135   (void)cpu;
136   assert (cpu == Cpu_number::boot_cpu());
137   Io_apic::save_state();
138 }
139
140 PUBLIC void
141 Io_apic_mgr::pm_on_resume(Cpu_number cpu)
142 {
143   (void)cpu;
144   assert (cpu == Cpu_number::boot_cpu());
145   Pic::disable_all_save();
146   Io_apic::restore_state(true);
147 }
148
149
150 IMPLEMENT inline
151 Mword
152 Io_apic::Apic::read(int reg)
153 {
154   adr = reg;
155   asm volatile ("": : :"memory");
156   return data;
157 }
158
159 IMPLEMENT inline
160 void
161 Io_apic::Apic::modify(int reg, Mword set_bits, Mword del_bits)
162 {
163   Mword tmp;
164   adr = reg;
165   asm volatile ("": : :"memory");
166   tmp = data;
167   tmp &= ~del_bits;
168   tmp |= set_bits;
169   data = tmp;
170 }
171
172 IMPLEMENT inline
173 void
174 Io_apic::Apic::write(int reg, Mword value)
175 {
176   adr = reg;
177   asm volatile ("": : :"memory");
178   data = value;
179 }
180
181 IMPLEMENT inline
182 unsigned
183 Io_apic::Apic::num_entries()
184 {
185   return (read(1) >> 16) & 0xff;
186 }
187
188 PUBLIC explicit
189 Io_apic::Io_apic(Unsigned64 phys, unsigned gsi_base)
190 : Irq_chip_ia32(0), _l(Spin_lock<>::Unlocked),
191   _offset(gsi_base), _next(0)
192 {
193   if (Print_info)
194     printf("IO-APIC: addr=%lx\n", (Mword)phys);
195
196   Address offs;
197   Address va = Mem_layout::alloc_io_vmem(Config::PAGE_SIZE);
198   assert (va);
199
200   Kmem::map_phys_page(phys, va, false, true, &offs);
201
202   Kip::k()->add_mem_region(Mem_desc(phys, phys + Config::PAGE_SIZE -1, Mem_desc::Reserved));
203
204   Io_apic::Apic *a = (Io_apic::Apic*)(va + offs);
205   a->write(0, 0);
206
207   _apic = a;
208   _irqs = a->num_entries() + 1;
209   _entry = new Irq_entry_code[_irqs];
210
211   if ((_offset + nr_irqs()) > _nr_irqs)
212     _nr_irqs = _offset + nr_irqs();
213
214   Io_apic **c = &_first;
215   while (*c && (*c)->_offset < _offset)
216     c = &((*c)->_next);
217
218   _next = *c;
219   *c = this;
220
221   Mword cpu_phys = ::Apic::apic.cpu(Cpu_number::boot_cpu())->apic_id();
222
223   for (unsigned i = 0; i < _irqs; ++i)
224     {
225       int v = 0x20 + i;
226       Io_apic_entry e(v, Io_apic_entry::Fixed, Io_apic_entry::Physical,
227                       Io_apic_entry::High_active, Io_apic_entry::Edge,
228                       cpu_phys);
229       write_entry(i, e);
230     }
231 }
232
233
234 PUBLIC inline NEEDS["assert.h", "lock_guard.h"]
235 Io_apic_entry
236 Io_apic::read_entry(unsigned i) const
237 {
238   auto g = lock_guard(_l);
239   Io_apic_entry e;
240   //assert(i <= num_entries());
241   e._e = (Unsigned64)_apic->read(0x10+2*i) | (((Unsigned64)_apic->read(0x11+2*i)) << 32);
242   return e;
243 }
244
245
246 PUBLIC inline NEEDS["assert.h", "lock_guard.h"]
247 void
248 Io_apic::write_entry(unsigned i, Io_apic_entry const &e)
249 {
250   auto g = lock_guard(_l);
251   //assert(i <= num_entries());
252   _apic->write(0x10+2*i, e._e);
253   _apic->write(0x11+2*i, e._e >> 32);
254 }
255
256 PROTECTED static FIASCO_INIT
257 void
258 Io_apic::read_overrides()
259 {
260   for (unsigned tmp = 0;; ++tmp)
261     {
262       auto irq = _madt->find<Acpi_madt::Irq_source>(tmp);
263
264       if (!irq)
265         break;
266
267       if (Print_info)
268         printf("IO-APIC: ovr[%2u] %02x -> %x %x\n",
269                tmp, (unsigned)irq->src, irq->irq, (unsigned)irq->flags);
270
271       if (irq->irq >= _nr_irqs)
272         {
273           WARN("IO-APIC: warning override %02x -> %x (flags=%x) points to invalid GSI\n",
274                (unsigned)irq->src, irq->irq, (unsigned)irq->flags);
275           continue;
276         }
277
278       Io_apic *ioapic = find_apic(irq->irq);
279       assert (ioapic);
280       unsigned pin = irq->irq - ioapic->gsi_offset();
281       Irq_chip::Mode mode = ioapic->get_mode(pin);
282
283       unsigned pol = irq->flags & 0x3;
284       unsigned trg = (irq->flags >> 2) & 0x3;
285       switch (pol)
286         {
287         default: break;
288         case 0: break;
289         case 1: mode.polarity() = Mode::Polarity_high; break;
290         case 2: break;
291         case 3: mode.polarity() = Mode::Polarity_low; break;
292         }
293
294       switch (trg)
295         {
296         default: break;
297         case 0: break;
298         case 1: mode.level_triggered() = Mode::Trigger_edge; break;
299         case 2: break;
300         case 3: mode.level_triggered() = Mode::Trigger_level; break;
301         }
302
303       ioapic->set_mode(pin, mode);
304     }
305 }
306
307
308 PUBLIC static FIASCO_INIT
309 Acpi_madt const *
310 Io_apic::lookup_madt()
311 {
312   if (_madt)
313     return _madt;
314
315   _madt = Acpi::find<Acpi_madt const *>("APIC");
316   return _madt;
317 }
318
319 PUBLIC static inline
320 Acpi_madt const *Io_apic::madt() { return _madt; }
321
322 PUBLIC static FIASCO_INIT
323 bool
324 Io_apic::init_scan_apics()
325 {
326   auto madt = lookup_madt();
327
328   if (madt == 0)
329     {
330       WARN("Could not find APIC in RSDT nor XSDT, skipping init\n");
331       return false;
332     }
333
334   int n_apics;
335   for (n_apics = 0;
336        auto ioapic = madt->find<Acpi_madt::Io_apic>(n_apics);
337        ++n_apics)
338     {
339       Io_apic *apic = new Boot_object<Io_apic>(ioapic->adr, ioapic->irq_base);
340
341       if (Print_info)
342         {
343           printf("IO-APIC[%2d]: pins %u\n", n_apics, apic->nr_irqs());
344           apic->dump();
345         }
346     }
347
348   if (!n_apics)
349     {
350       WARN("IO-APIC: Could not find IO-APIC in MADT, skip init\n");
351       return false;
352     }
353
354   if (Print_info)
355     printf("IO-APIC: dual 8259: %s\n", madt->apic_flags & 1 ? "yes" : "no");
356
357   return true;
358 }
359
360
361 PUBLIC static FIASCO_INIT
362 void
363 Io_apic::init(Cpu_number)
364 {
365   if (!Irq_mgr::mgr)
366     Irq_mgr::mgr = new Boot_object<Io_apic_mgr>();
367
368   _state_save_area = new Boot_object<Io_apic_entry>[_nr_irqs];
369   read_overrides();
370
371   // in the case we use the IO-APIC not the PIC we can dynamically use
372   // INT vectors from 0x20 to 0x2f too
373   _vectors.add_free(0x20, 0x30);
374 };
375
376 PUBLIC static
377 void
378 Io_apic::save_state()
379 {
380   for (Io_apic *a = _first; a; a = a->_next)
381     for (unsigned i = 0; i < a->_irqs; ++i)
382       _state_save_area[a->_offset + i] = a->read_entry(i);
383 }
384
385 PUBLIC static
386 void
387 Io_apic::restore_state(bool set_boot_cpu = false)
388 {
389   Mword cpu_phys = 0;
390   if (set_boot_cpu)
391     cpu_phys = ::Apic::apic.cpu(Cpu_number::boot_cpu())->apic_id();
392
393   for (Io_apic *a = _first; a; a = a->_next)
394     for (unsigned i = 0; i < a->_irqs; ++i)
395       {
396         Io_apic_entry e = _state_save_area[a->_offset + i];
397         if (set_boot_cpu && e.format() == 0)
398           e.dest() = cpu_phys;
399         a->write_entry(i, e);
400       }
401 }
402
403 PUBLIC static
404 unsigned
405 Io_apic::total_irqs()
406 { return _nr_irqs; }
407
408 PUBLIC static
409 unsigned
410 Io_apic::legacy_override(unsigned i)
411 {
412   if (!_madt)
413     return i;
414
415   unsigned tmp = 0;
416   for (;;++tmp)
417     {
418       Acpi_madt::Irq_source const *irq
419         = static_cast<Acpi_madt::Irq_source const *>(_madt->find(Acpi_madt::Irq_src_ovr, tmp));
420
421       if (!irq)
422         break;
423
424       if (irq->src == i)
425         return irq->irq;
426     }
427   return i;
428 }
429
430 PUBLIC
431 void
432 Io_apic::dump()
433 {
434   for (unsigned i = 0; i < _irqs; ++i)
435     {
436       Io_apic_entry e = read_entry(i);
437       printf("  PIN[%2u%c]: vector=%2x, del=%u, dm=%s, dest=%u (%s, %s)\n",
438              i, e.mask() ? 'm' : '.',
439              (unsigned)e.vector(), (unsigned)e.delivery(), e.dest_mode() ? "logical" : "physical",
440              (unsigned)e.dest(),
441              e.polarity() ? "low" : "high",
442              e.trigger() ? "level" : "edge");
443     }
444
445 }
446
447 PUBLIC inline
448 bool
449 Io_apic::valid() const { return _apic; }
450
451 PRIVATE inline NEEDS["assert.h", "lock_guard.h"]
452 void
453 Io_apic::_mask(unsigned irq)
454 {
455   auto g = lock_guard(_l);
456   //assert(irq <= _apic->num_entries());
457   _apic->modify(0x10 + irq * 2, 1UL << 16, 0);
458 }
459
460 PRIVATE inline NEEDS["assert.h", "lock_guard.h"]
461 void
462 Io_apic::_unmask(unsigned irq)
463 {
464   auto g = lock_guard(_l);
465   //assert(irq <= _apic->num_entries());
466   _apic->modify(0x10 + irq * 2, 0, 1UL << 16);
467 }
468
469 PUBLIC inline NEEDS["assert.h", "lock_guard.h"]
470 bool
471 Io_apic::masked(unsigned irq)
472 {
473   auto g = lock_guard(_l);
474   //assert(irq <= _apic->num_entries());
475   return _apic->read(0x10 + irq * 2) & (1UL << 16);
476 }
477
478 PUBLIC inline
479 void
480 Io_apic::sync()
481 {
482   (void)_apic->data;
483 }
484
485 PUBLIC inline NEEDS["assert.h", "lock_guard.h"]
486 void
487 Io_apic::set_dest(unsigned irq, Mword dst)
488 {
489   auto g = lock_guard(_l);
490   //assert(irq <= _apic->num_entries());
491   _apic->modify(0x11 + irq * 2, dst & (~0UL << 24), ~0UL << 24);
492 }
493
494 PUBLIC inline
495 unsigned
496 Io_apic::gsi_offset() const { return _offset; }
497
498 PUBLIC static
499 Io_apic *
500 Io_apic::find_apic(unsigned irqnum)
501 {
502   for (Io_apic *a = _first; a; a = a->_next)
503     {
504       if (a->_offset <= irqnum && a->_offset + a->_irqs > irqnum)
505         return a;
506     }
507   return 0;
508 };
509
510 PUBLIC void
511 Io_apic::mask(Mword irq) override
512 {
513   _mask(irq);
514   sync();
515 }
516
517 PUBLIC void
518 Io_apic::ack(Mword) override
519 {
520   ::Apic::irq_ack();
521 }
522
523 PUBLIC void
524 Io_apic::mask_and_ack(Mword irq) override
525 {
526   _mask(irq);
527   sync();
528   ::Apic::irq_ack();
529 }
530
531 PUBLIC void
532 Io_apic::unmask(Mword irq) override
533 {
534   _unmask(irq);
535 }
536
537 PUBLIC void
538 Io_apic::set_cpu(Mword irq, Cpu_number cpu) override
539 {
540   set_dest(irq, ::Apic::apic.cpu(cpu)->apic_id());
541 }
542
543 PROTECTED static inline
544 Mword
545 Io_apic::to_io_apic_trigger(Irq_chip::Mode mode)
546 {
547   return mode.level_triggered()
548          ? Io_apic_entry::Level
549          : Io_apic_entry::Edge;
550 }
551
552 PROTECTED static inline
553 Mword
554 Io_apic::to_io_apic_polarity(Irq_chip::Mode mode)
555 {
556   return mode.polarity() == Irq_chip::Mode::Polarity_high
557          ? Io_apic_entry::High_active
558          : Io_apic_entry::Low_active;
559 }
560
561 PUBLIC int
562 Io_apic::set_mode(Mword pin, Mode mode) override
563 {
564   if (!mode.set_mode())
565     return 0;
566
567   Io_apic_entry e = read_entry(pin);
568   e.polarity() = to_io_apic_polarity(mode);
569   e.trigger() = to_io_apic_trigger(mode);
570   write_entry(pin, e);
571   return 0;
572 }
573
574 PUBLIC inline
575 Irq_chip::Mode
576 Io_apic::get_mode(Mword pin)
577 {
578   Io_apic_entry e = read_entry(pin);
579   Mode m(Mode::Set_irq_mode);
580   m.polarity() = e.polarity() == Io_apic_entry::High_active
581                ? Mode::Polarity_high
582                : Mode::Polarity_low;
583   m.level_triggered() = e.trigger() == Io_apic_entry::Level
584                       ? Mode::Trigger_level
585                       : Mode::Trigger_edge;
586   return m;
587 }
588
589 PUBLIC
590 bool
591 Io_apic::is_edge_triggered(Mword pin) const override
592 {
593   Io_apic_entry e = read_entry(pin);
594   return e.trigger() == Io_apic_entry::Edge;
595 }
596
597 PUBLIC
598 bool
599 Io_apic::alloc(Irq_base *irq, Mword pin) override
600 {
601   unsigned v = valloc<Io_apic>(irq, pin, 0);
602
603   if (!v)
604     return false;
605
606   Io_apic_entry e = read_entry(pin);
607   e.vector() = v;
608   write_entry(pin, e);
609   return true;
610 }
611
612 PUBLIC
613 void
614 Io_apic::unbind(Irq_base *irq) override
615 {
616   extern char entry_int_apic_ignore[];
617   Mword n = irq->pin();
618   mask(n);
619   vfree(irq, &entry_int_apic_ignore);
620   Irq_chip_icu::unbind(irq);
621 }
622
623 PUBLIC static inline
624 bool
625 Io_apic::active()
626 { return _first; }
627
628 IMPLEMENTATION [debug]:
629
630 PUBLIC inline
631 char const *
632 Io_apic::chip_type() const override
633 { return "IO-APIC"; }
634