]> rtime.felk.cvut.cz Git - l4.git/blob - kernel/fiasco/src/kern/irq.cpp
5a55192bfcadcab433529345f355451fac425176
[l4.git] / kernel / fiasco / src / kern / irq.cpp
1 INTERFACE:
2
3 #include "ipc_sender.h"
4 #include "irq_pin.h"
5 #include "kobject_helper.h"
6 #include "member_offs.h"
7 #include "sender.h"
8 #include "context.h"
9
10 class Ram_quota;
11 class Receiver;
12
13
14
15 /** Hardware interrupts.  This class encapsulates handware IRQs.  Also,
16     it provides a registry that ensures that only one receiver can sign up
17     to receive interrupt IPC messages.
18  */
19 class Irq : public Irq_base, public Kobject
20 {
21   MEMBER_OFFSET();
22   FIASCO_DECLARE_KOBJ();
23
24 private:
25   typedef slab_cache_anon Allocator;
26
27 public:
28   enum Mode
29   {
30     Set_irq_mode  = 1,
31     Trigger_edge  = 0,
32     Trigger_level = 2,
33     Polarity_high = 0,
34     Polarity_low  = 4,
35   };
36
37   enum Op
38   {
39     Op_eoi_1      = 0,
40     Op_attach     = 1,
41     Op_trigger    = 2,
42     Op_chain      = 3,
43     Op_eoi_2      = 4,
44   };
45
46 private:
47   Irq(Irq&);
48
49 protected:
50   Ram_quota *_q;
51   Context::Drq _drq;
52
53 public:
54   virtual ~Irq() {}
55
56 };
57
58 class Irq_sender
59 : public Kobject_h<Irq_sender, Irq>,
60   public Ipc_sender<Irq_sender>
61 {
62 public:
63   Mword kobject_size() const { return sizeof(*this); }
64
65 private:
66   Irq_sender(Irq_sender &);
67
68 protected:
69   Smword _queued;
70   Receiver *_irq_thread;
71
72 private:
73   Mword _irq_id;
74 };
75
76 class Irq_muxer : public Kobject_h<Irq_muxer, Irq>
77 {
78   friend class Chain_irq_pin;
79
80 private:
81   Smword _mask_cnt;
82 };
83
84 class Irq_debugger : public Irq_base
85 {
86 };
87
88
89 class Chain_irq_pin : public Sw_irq_pin
90 {};
91
92 //-----------------------------------------------------------------------------
93 INTERFACE [debug]:
94
95 EXTENSION class Irq
96 {
97 public:
98   struct Irq_log
99   {
100     Mword   irq_obj;
101     Address user_ip;
102     int     irq_number;
103   };
104
105   static unsigned irq_log_fmt(Tb_entry *, int, char *)
106   asm ("__irq_log_fmt");
107
108 };
109
110
111 //-----------------------------------------------------------------------------
112 IMPLEMENTATION:
113
114 #include "atomic.h"
115 #include "config.h"
116 #include "cpu_lock.h"
117 #include "entry_frame.h"
118 #include "globals.h"
119 #include "ipc_sender.h"
120 #include "kdb_ke.h"
121 #include "kmem_slab.h"
122 #include "lock_guard.h"
123 #include "minmax.h"
124 #include "receiver.h"
125 #include "std_macros.h"
126 #include "thread_object.h"
127 #include "thread_state.h"
128 #include "l4_buf_iter.h"
129
130 FIASCO_DEFINE_KOBJ(Irq);
131
132 namespace {
133 static Irq_base *irq_base_dcast(Kobject_iface *o)
134 { return Kobject::dcast<Irq*>(o); }
135
136 struct Irq_base_cast
137 {
138   Irq_base_cast()
139   { Irq_base::dcast = &irq_base_dcast; }
140 };
141
142 static Irq_base_cast register_irq_base_cast;
143 }
144
145 PUBLIC static inline
146 Irq *
147 Irq::self(Irq_pin const *pin)
148 {
149 #define MYoffsetof(TYPE, MEMBER) (((size_t) &((TYPE *)10)->MEMBER) - 10)
150   return reinterpret_cast<Irq*>(reinterpret_cast<Mword>(pin)
151       - MYoffsetof(Irq, _pin));
152 #undef MYoffsetof
153 }
154
155
156
157 PUBLIC inline explicit
158 Chain_irq_pin::Chain_irq_pin(Irq_muxer *i)
159 { payload()[0] = Mword(i); }
160
161 PUBLIC inline
162 Irq_muxer *
163 Chain_irq_pin::irq() const
164 { return (Irq_muxer*)payload()[0]; }
165
166 PUBLIC
167 void
168 Chain_irq_pin::do_unmask()
169 {
170   Smword old;
171   do
172     old = irq()->_mask_cnt;
173   while (!mp_cas(&irq()->_mask_cnt, old, old - 1));
174
175   if (old == 1)
176     irq()->pin()->unmask();
177 }
178
179
180 PUBLIC
181 void
182 Chain_irq_pin::do_mask()
183 {
184   Smword old;
185   do
186     old = irq()->_mask_cnt;
187   while (!mp_cas(&irq()->_mask_cnt, old, old + 1));
188
189   if (old == 0)
190     irq()->pin()->mask();
191 }
192
193
194 PUBLIC
195 void
196 Chain_irq_pin::unbind_irq()
197 {
198   Irq_base *self = Irq::self(this);
199   Irq_base *n;
200   for (n = irq(); n->_next && n->_next != self; n = n->_next)
201     ;
202
203   assert (n->_next == self);
204   n->_next = n->_next->_next;
205   if (masked())
206     do_unmask();
207
208   replace<Sw_irq_pin>();
209 }
210
211
212 PUBLIC
213 void
214 Chain_irq_pin::do_mask_and_ack()
215 {
216 }
217
218
219 PUBLIC inline
220 void *
221 Irq::operator new (size_t, void *p)
222 { return p; }
223
224 PUBLIC
225 void
226 Irq::operator delete (void *_l)
227 {
228   Irq *l = reinterpret_cast<Irq*>(_l);
229   if (l->_q)
230     l->_q->free(sizeof(Irq));
231
232   allocator()->free(l);
233 }
234
235 PUBLIC template<typename T> inline NEEDS[Irq::allocator, Irq::operator new]
236 static
237 T*
238 Irq::allocate(Ram_quota *q)
239 {
240   void *nq =allocator()->q_alloc(q);
241   if (nq)
242     return new (nq) T(q);
243
244   return 0;
245 }
246
247 static Kmem_slab _irq_allocator(max(sizeof (Irq_sender), sizeof(Irq_muxer)),
248                                 __alignof__ (Irq), "Irq");
249
250 PRIVATE static
251 Irq::Allocator *
252 Irq::allocator()
253 { return &_irq_allocator; }
254
255
256 /** Bind a receiver to this device interrupt.
257     @param t the receiver that wants to receive IPC messages for this IRQ
258     @return true if the binding could be established
259  */
260 PUBLIC inline NEEDS ["atomic.h", "cpu_lock.h", "lock_guard.h"]
261 bool
262 Irq_sender::alloc(Receiver *t)
263 {
264   bool ret = mp_cas(&_irq_thread, reinterpret_cast<Receiver*>(0), t);
265
266   if (ret)
267     {
268       if (EXPECT_TRUE(t != 0))
269         {
270           t->inc_ref();
271           pin()->set_cpu(t->cpu());
272         }
273
274       _queued = 0;
275     }
276
277   return ret;
278 }
279
280 PUBLIC
281 Receiver *
282 Irq_sender::owner() const { return _irq_thread; }
283
284 /** Release an device interrupt.
285     @param t the receiver that ownes the IRQ
286     @return true if t really was the owner of the IRQ and operation was 
287             successful
288  */
289 PUBLIC
290 bool
291 Irq_sender::free(Receiver *t)
292 {
293   bool ret = mp_cas(&_irq_thread, t, reinterpret_cast<Receiver*>(0));
294
295   if (ret)
296     {
297       Lock_guard<Cpu_lock> guard(&cpu_lock);
298       pin()->mask();
299
300       if (EXPECT_TRUE(t != 0))
301         {
302           t->abort_send(this);
303
304           // release cpu-lock early, actually before delete
305           guard.release();
306
307           if (t->dec_ref() == 0)
308             delete t;
309         }
310     }
311
312   return ret;
313 }
314
315
316 PUBLIC explicit inline
317 Irq::Irq(Ram_quota *q = 0)
318 : _q(q)
319 {
320   new (pin()) Sw_irq_pin();
321   pin()->mask();
322 }
323
324 PUBLIC
325 void
326 Irq::destroy(Kobject ***rl)
327 {
328   Irq_base::destroy();
329   Kobject::destroy(rl);
330 }
331
332 PUBLIC explicit
333 Irq_sender::Irq_sender(Ram_quota *q = 0)
334 : Kobject_h<Irq_sender, Irq>(q), _queued(0), _irq_thread(0), _irq_id(~0UL)
335 {}
336
337 PUBLIC
338 void
339 Irq_sender::destroy(Kobject ***rl)
340 {
341   if (_irq_thread)
342     free(_irq_thread);
343
344   Irq::destroy(rl);
345 }
346
347 PUBLIC inline
348 unsigned long
349 Irq::irq() const
350 { return pin()->payload()[0]; }
351
352 /** Consume one interrupt.
353     @return number of IRQs that are still pending.
354  */
355 PRIVATE inline NEEDS ["atomic.h"]
356 Smword
357 Irq_sender::consume()
358 {
359   Smword old;
360
361   do
362     {
363       old = _queued;
364     }
365   while (!mp_cas (&_queued, old, old - 1));
366
367   return old - 1;
368 }
369
370 PUBLIC inline
371 int
372 Irq_sender::queued()
373 {
374   return _queued;
375 }
376
377
378 /**
379  * Predicate used to figure out if the sender shall be enqueued
380  * for sending a second message after sending the first.
381  */
382 PUBLIC inline NEEDS[Irq_sender::consume]
383 bool
384 Irq_sender::requeue_sender()
385 { return consume() > 0; }
386
387 /**
388  * Predicate used to figure out if the sender shall be deqeued after
389  * sending the request.
390  */
391 PUBLIC inline NEEDS[Irq_sender::consume]
392 bool
393 Irq_sender::dequeue_sender()
394 { return consume() < 1; }
395
396 PUBLIC inline
397 Syscall_frame *
398 Irq_sender::transfer_msg(Receiver *recv)
399 {
400   Syscall_frame* dst_regs = recv->rcv_regs();
401
402   // set ipc return value: OK
403   dst_regs->tag(L4_msg_tag(0, 0, 0, L4_msg_tag::Label_irq));
404
405   // set ipc source thread id
406   dst_regs->from(_irq_id);
407
408   return dst_regs;
409 }
410
411 PUBLIC void
412 Irq_sender::modify_label(Mword const *todo, int cnt)
413 {
414   for (int i = 0; i < cnt*4; i += 4)
415     {
416       Mword const test_mask = todo[i];
417       Mword const test      = todo[i+1];
418       if ((_irq_id & test_mask) == test)
419         {
420           Mword const set_mask = todo[i+2];
421           Mword const set      = todo[i+3];
422
423           _irq_id = (_irq_id & ~set_mask) | set;
424           return;
425         }
426     }
427 }
428
429
430 PRIVATE static
431 unsigned
432 Irq_sender::handle_remote_hit(Context::Drq *, Context *, void *arg)
433 {
434   Irq_sender *irq = (Irq_sender*)arg;
435   irq->pin()->set_cpu(current_cpu());
436   irq->send_msg(irq->_irq_thread);
437   return Context::Drq::No_answer;
438 }
439
440 PRIVATE
441 void
442 Irq_muxer::hit()
443 {
444   assert (cpu_lock.test());
445   pin()->mask_and_ack();
446
447   if (EXPECT_FALSE (!Irq_base::_next))
448     return;
449
450   int irqs = 0;
451   for (Irq_base *n = Irq_base::_next; n;)
452     {
453       Irq *i = nonull_static_cast<Irq*>(n);
454       ++irqs;
455       i->pin()->__mask();
456       n = i->Irq_base::_next;
457     }
458
459     {
460       Smword old;
461       do
462         old = _mask_cnt;
463       while (!mp_cas(&_mask_cnt, old, old + irqs));
464     }
465
466   for (Irq_base *n = Irq_base::_next; n;)
467     {
468       Irq *i = nonull_static_cast<Irq*>(n);
469       i->hit();
470       n = i->Irq_base::_next;
471     }
472 }
473
474
475 PUBLIC inline
476 void
477 Irq_debugger::hit()
478 {
479   assert (cpu_lock.test());
480   pin()->mask_and_ack();
481
482 #if defined(CONFIG_KDB) || defined(CONFIG_JDB)
483   if (pin()->check_debug_irq())
484     kdb_ke("IRQ ENTRY");
485 #endif
486   pin()->unmask();
487 }
488
489
490 PUBLIC inline
491 void
492 Irq_sender::hit()
493 {
494   // We're entered holding the kernel lock, which also means irqs are
495   // disabled on this CPU (XXX always correct?).  We never enable irqs
496   // in this stack frame (except maybe in a nonnested invocation of
497   // switch_exec() -> switchin_context()) -- they will be re-enabled
498   // once we return from it (iret in entry.S:all_irqs) or we switch to
499   // a different thread.
500
501   // LOG_MSG_3VAL(current(), "IRQ", dbg_id(), 0, _queued);
502
503   assert (cpu_lock.test());
504   pin()->mask_and_ack();
505
506   if (EXPECT_FALSE (!_irq_thread))
507     return;
508   else if (EXPECT_FALSE (_irq_thread == (void*)-1))
509     {
510       // debugger attached to IRQ
511 #if defined(CONFIG_KDB) || defined(CONFIG_JDB)
512       if (pin()->check_debug_irq())
513         kdb_ke("IRQ ENTRY");
514 #endif
515       pin()->unmask();
516       return;
517     }
518
519
520   Smword old;
521   do
522     old = _queued;
523   while (!mp_cas(&_queued, old, old + 1));
524
525   if (EXPECT_TRUE (old == 0))   // increase hit counter
526     {
527       if (EXPECT_FALSE(_irq_thread->cpu() != current_cpu()))
528         _irq_thread->drq(&_drq, handle_remote_hit, this, 0,
529                          Context::Drq::Target_ctxt, Context::Drq::No_wait);
530       else
531         send_msg(_irq_thread);
532     }
533 }
534
535
536 PRIVATE
537 L4_msg_tag
538 Irq_sender::sys_attach(L4_msg_tag const &tag, Utcb const *utcb, Syscall_frame * /*f*/,
539                 Obj_space *o_space)
540 {
541   L4_snd_item_iter snd_items(utcb, tag.words());
542
543   Receiver *thread = 0;
544   unsigned mode = utcb->values[0] >> 16;
545
546   if (tag.items() == 0)
547     {
548       // detach
549       if (mode & Set_irq_mode)
550         printf("DEPRECATED SET IRQ MODE\n");
551         //pin()->set_mode(mode);
552       else
553         {
554           free(_irq_thread);
555           _irq_id = ~0UL;
556         }
557       return commit_result(0);
558     }
559
560   if (tag.items() && snd_items.next())
561     {
562       L4_fpage bind_thread(snd_items.get()->d);
563       if (EXPECT_FALSE(!bind_thread.is_objpage()))
564         return commit_error(utcb, L4_error::Overflow);
565
566       thread = Kobject::dcast<Thread_object*>(o_space->lookup_local(bind_thread.obj_index()));
567     }
568
569   if (!thread)
570     thread = current_thread();
571
572   if (alloc(thread))
573     {
574       if (mode & Set_irq_mode)
575         printf("DEPRECATED SET IRQ MODE\n");
576       _irq_id = utcb->values[1];
577       return commit_result(0);
578     }
579
580   return commit_result(-L4_err::EInval);
581 }
582
583 PUBLIC explicit
584 Irq_muxer::Irq_muxer(Ram_quota *q = 0)
585 : Kobject_h<Irq_muxer, Irq>(q), _mask_cnt(0)
586 {}
587
588 PUBLIC
589 void
590 Irq_muxer::destroy(Kobject ***rl)
591 {
592   // FIXME: unchain IRQs
593
594   Irq::destroy(rl);
595 }
596
597 PRIVATE
598 L4_msg_tag
599 Irq_muxer::sys_attach(L4_msg_tag const &tag, Utcb const *utcb, Syscall_frame * /*f*/,
600                 Obj_space *o_space)
601 {
602   L4_snd_item_iter snd_items(utcb, tag.words());
603
604   Irq *irq = 0;
605   unsigned mode = utcb->values[0] >> 16;
606
607   if (tag.items() == 0)
608     return commit_result(-L4_err::EInval);
609
610   if (tag.items() && snd_items.next())
611     {
612       L4_fpage bind_irq(snd_items.get()->d);
613       if (EXPECT_FALSE(!bind_irq.is_objpage()))
614         return commit_error(utcb, L4_error::Overflow);
615
616       irq = Kobject::dcast<Irq*>(o_space->lookup_local(bind_irq.obj_index()));
617     }
618
619   if (!irq)
620     return commit_result(-L4_err::EInval);
621
622   if (mode & Set_irq_mode)
623     printf("DEPRECATED SET IRQ MODE\n");
624     //pin()->set_mode(mode);
625
626   irq->pin()->unbind_irq();
627
628   if (!irq->pin()->masked())
629     {
630       Smword old;
631       do
632         old = _mask_cnt;
633       while (!mp_cas(&_mask_cnt, old, old + 1));
634     }
635
636   irq->pin()->replace<Chain_irq_pin>(this);
637
638   irq->Irq_base::_next = Irq_base::_next;
639   Irq_base::_next = irq;
640
641   return commit_result(0);
642 }
643
644 PUBLIC
645 L4_msg_tag
646 Irq_muxer::kinvoke(L4_obj_ref, Mword /*rights*/, Syscall_frame *f,
647                    Utcb const *utcb, Utcb *)
648 {
649   register Context *const c_thread = ::current();
650   register Space *const c_space = c_thread->space();
651   register Obj_space *const o_space = c_space->obj_space();
652
653   L4_msg_tag tag = f->tag();
654
655   if (EXPECT_FALSE(tag.proto() != L4_msg_tag::Label_irq))
656     return commit_result(-L4_err::EBadproto);
657
658   if (EXPECT_FALSE(tag.words() < 1))
659     return commit_result(-L4_err::EInval);
660
661   switch ((utcb->values[0] & 0xffff))
662     {
663     case Op_chain:
664       return sys_attach(tag, utcb, f, o_space);
665     case Op_trigger:
666       hit();
667       Irq::log_irq(this, 0);
668       return no_reply();
669     default:
670       return commit_result(-L4_err::EInval);
671     }
672 }
673
674 PUBLIC
675 L4_msg_tag
676 Irq_sender::kinvoke(L4_obj_ref, Mword /*rights*/, Syscall_frame *f,
677                     Utcb const *utcb, Utcb *)
678 {
679   register Context *const c_thread = ::current();
680   register Space *const c_space = c_thread->space();
681   register Obj_space *const o_space = c_space->obj_space();
682
683   L4_msg_tag tag = f->tag();
684
685   if (EXPECT_FALSE(tag.proto() != L4_msg_tag::Label_irq))
686     return commit_result(-L4_err::EBadproto);
687
688   if (EXPECT_FALSE(tag.words() < 1))
689     return commit_result(-L4_err::EInval);
690
691   switch ((utcb->values[0] & 0xffff))
692     {
693     case Op_eoi_1:
694     case Op_eoi_2:
695       pin()->unmask();
696       return no_reply();
697     case Op_attach: /* ATTACH, DETACH */
698       return sys_attach(tag, utcb, f, o_space);
699     case Op_trigger:
700       hit();
701       Irq::log_irq(this, 0);
702       return no_reply();
703     default:
704       return commit_result(-L4_err::EInval);
705     }
706 }
707
708 PUBLIC
709 Mword
710 Irq_sender::obj_id() const
711 { return _irq_id; }
712
713
714 // --------------------------------------------------------------------------
715 IMPLEMENTATION [debug]:
716
717 #include "logdefs.h"
718
719 PUBLIC
720 char const *
721 Chain_irq_pin::pin_type() const
722 { return "CHAIN IRQ"; }
723
724 IMPLEMENT
725 unsigned
726 Irq::irq_log_fmt(Tb_entry *e, int maxlen, char *buf)
727 {
728   Irq_log *l = e->payload<Irq_log>();
729   return snprintf(buf, maxlen, "0x%x/%u D:%lx userip=%lx",
730                   l->irq_number, l->irq_number,
731                   l->irq_obj, l->user_ip);
732 }
733
734 PUBLIC static inline
735 void
736 Irq::log_irq(Irq *irq, int nr)
737 {
738   Context *c = current();
739   LOG_TRACE("IRQ-Object triggers", "irq", c, __irq_log_fmt,
740       Irq::Irq_log *l = tbe->payload<Irq::Irq_log>();
741       l->irq_number = nr;
742       l->user_ip    = c->regs()->ip();
743       l->irq_obj    = irq ? irq->dbg_id() : ~0UL;
744   );
745 }
746
747 PUBLIC static inline NEEDS["config.h"]
748 void
749 Irq::log_timer_irq(int nr)
750 {
751   Context *c = current();
752   LOG_TRACE("Kernel Timer Events", "timer", c, __irq_log_fmt,
753       Irq::Irq_log *l = tbe->payload<Irq::Irq_log>();
754       l->irq_number = nr;
755       l->user_ip    = c->regs()->ip(),
756       l->irq_obj    = ~0UL;
757   );
758 }
759
760 // --------------------------------------------------------------------------
761 IMPLEMENTATION [!debug]:
762
763 PUBLIC static inline
764 void
765 Irq::log_irq(Irq *, int)
766 {}
767
768 PUBLIC static inline
769 void
770 Irq::log_timer_irq(int)
771 {}