]> rtime.felk.cvut.cz Git - l4.git/blob - kernel/fiasco/src/kern/irq.cpp
update
[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   Lock_guard<Cpu_lock> g(&cpu_lock);
342   if (_irq_thread)
343     free(_irq_thread);
344
345   Irq::destroy(rl);
346 }
347
348 PUBLIC inline
349 unsigned long
350 Irq::irq() const
351 { return pin()->payload()[0]; }
352
353 /** Consume one interrupt.
354     @return number of IRQs that are still pending.
355  */
356 PRIVATE inline NEEDS ["atomic.h"]
357 Smword
358 Irq_sender::consume()
359 {
360   Smword old;
361
362   do
363     {
364       old = _queued;
365     }
366   while (!mp_cas (&_queued, old, old - 1));
367
368   return old - 1;
369 }
370
371 PUBLIC inline
372 int
373 Irq_sender::queued()
374 {
375   return _queued;
376 }
377
378
379 /**
380  * Predicate used to figure out if the sender shall be enqueued
381  * for sending a second message after sending the first.
382  */
383 PUBLIC inline NEEDS[Irq_sender::consume]
384 bool
385 Irq_sender::requeue_sender()
386 { return consume() > 0; }
387
388 /**
389  * Predicate used to figure out if the sender shall be deqeued after
390  * sending the request.
391  */
392 PUBLIC inline NEEDS[Irq_sender::consume]
393 bool
394 Irq_sender::dequeue_sender()
395 { return consume() < 1; }
396
397 PUBLIC inline
398 Syscall_frame *
399 Irq_sender::transfer_msg(Receiver *recv)
400 {
401   Syscall_frame* dst_regs = recv->rcv_regs();
402
403   // set ipc return value: OK
404   dst_regs->tag(L4_msg_tag(0, 0, 0, L4_msg_tag::Label_irq));
405
406   // set ipc source thread id
407   dst_regs->from(_irq_id);
408
409   return dst_regs;
410 }
411
412 PUBLIC void
413 Irq_sender::modify_label(Mword const *todo, int cnt)
414 {
415   for (int i = 0; i < cnt*4; i += 4)
416     {
417       Mword const test_mask = todo[i];
418       Mword const test      = todo[i+1];
419       if ((_irq_id & test_mask) == test)
420         {
421           Mword const set_mask = todo[i+2];
422           Mword const set      = todo[i+3];
423
424           _irq_id = (_irq_id & ~set_mask) | set;
425           return;
426         }
427     }
428 }
429
430
431 PRIVATE static
432 unsigned
433 Irq_sender::handle_remote_hit(Context::Drq *, Context *, void *arg)
434 {
435   Irq_sender *irq = (Irq_sender*)arg;
436   irq->pin()->set_cpu(current_cpu());
437   irq->send_msg(irq->_irq_thread);
438   return Context::Drq::No_answer;
439 }
440
441 PRIVATE
442 void
443 Irq_muxer::hit()
444 {
445   assert (cpu_lock.test());
446   pin()->mask_and_ack();
447
448   if (EXPECT_FALSE (!Irq_base::_next))
449     return;
450
451   int irqs = 0;
452   for (Irq_base *n = Irq_base::_next; n;)
453     {
454       Irq *i = nonull_static_cast<Irq*>(n);
455       ++irqs;
456       i->pin()->__mask();
457       n = i->Irq_base::_next;
458     }
459
460     {
461       Smword old;
462       do
463         old = _mask_cnt;
464       while (!mp_cas(&_mask_cnt, old, old + irqs));
465     }
466
467   for (Irq_base *n = Irq_base::_next; n;)
468     {
469       Irq *i = nonull_static_cast<Irq*>(n);
470       i->hit();
471       n = i->Irq_base::_next;
472     }
473 }
474
475
476 PUBLIC inline
477 void
478 Irq_debugger::hit()
479 {
480   assert (cpu_lock.test());
481   pin()->mask_and_ack();
482
483 #if defined(CONFIG_KDB) || defined(CONFIG_JDB)
484   if (pin()->check_debug_irq())
485     kdb_ke("IRQ ENTRY");
486 #endif
487   pin()->unmask();
488 }
489
490
491 PUBLIC inline
492 void
493 Irq_sender::hit()
494 {
495   // We're entered holding the kernel lock, which also means irqs are
496   // disabled on this CPU (XXX always correct?).  We never enable irqs
497   // in this stack frame (except maybe in a nonnested invocation of
498   // switch_exec() -> switchin_context()) -- they will be re-enabled
499   // once we return from it (iret in entry.S:all_irqs) or we switch to
500   // a different thread.
501
502   // LOG_MSG_3VAL(current(), "IRQ", dbg_id(), 0, _queued);
503
504   assert (cpu_lock.test());
505   pin()->mask_and_ack();
506
507   if (EXPECT_FALSE (!_irq_thread))
508     return;
509   else if (EXPECT_FALSE (_irq_thread == (void*)-1))
510     {
511       // debugger attached to IRQ
512 #if defined(CONFIG_KDB) || defined(CONFIG_JDB)
513       if (pin()->check_debug_irq())
514         kdb_ke("IRQ ENTRY");
515 #endif
516       pin()->unmask();
517       return;
518     }
519
520
521   Smword old;
522   do
523     old = _queued;
524   while (!mp_cas(&_queued, old, old + 1));
525
526   if (EXPECT_TRUE (old == 0))   // increase hit counter
527     {
528       if (EXPECT_FALSE(_irq_thread->cpu() != current_cpu()))
529         _irq_thread->drq(&_drq, handle_remote_hit, this, 0,
530                          Context::Drq::Target_ctxt, Context::Drq::No_wait);
531       else
532         send_msg(_irq_thread);
533     }
534 }
535
536
537 PRIVATE
538 L4_msg_tag
539 Irq_sender::sys_attach(L4_msg_tag const &tag, Utcb const *utcb, Syscall_frame * /*f*/,
540                 Obj_space *o_space)
541 {
542   L4_snd_item_iter snd_items(utcb, tag.words());
543
544   Receiver *thread = 0;
545   unsigned mode = utcb->values[0] >> 16;
546
547   if (tag.items() == 0)
548     {
549       // detach
550       if (mode & Set_irq_mode)
551         printf("DEPRECATED SET IRQ MODE\n");
552         //pin()->set_mode(mode);
553       else
554         {
555           free(_irq_thread);
556           _irq_id = ~0UL;
557         }
558       return commit_result(0);
559     }
560
561   if (tag.items() && snd_items.next())
562     {
563       L4_fpage bind_thread(snd_items.get()->d);
564       if (EXPECT_FALSE(!bind_thread.is_objpage()))
565         return commit_error(utcb, L4_error::Overflow);
566
567       thread = Kobject::dcast<Thread_object*>(o_space->lookup_local(bind_thread.obj_index()));
568     }
569
570   if (!thread)
571     thread = current_thread();
572
573   if (alloc(thread))
574     {
575       if (mode & Set_irq_mode)
576         printf("DEPRECATED SET IRQ MODE\n");
577       _irq_id = utcb->values[1];
578       return commit_result(0);
579     }
580
581   return commit_result(-L4_err::EInval);
582 }
583
584 PUBLIC explicit
585 Irq_muxer::Irq_muxer(Ram_quota *q = 0)
586 : Kobject_h<Irq_muxer, Irq>(q), _mask_cnt(0)
587 {}
588
589 PUBLIC
590 void
591 Irq_muxer::destroy(Kobject ***rl)
592 {
593   // FIXME: unchain IRQs
594
595   Irq::destroy(rl);
596 }
597
598 PRIVATE
599 L4_msg_tag
600 Irq_muxer::sys_attach(L4_msg_tag const &tag, Utcb const *utcb, Syscall_frame * /*f*/,
601                 Obj_space *o_space)
602 {
603   L4_snd_item_iter snd_items(utcb, tag.words());
604
605   Irq *irq = 0;
606   unsigned mode = utcb->values[0] >> 16;
607
608   if (tag.items() == 0)
609     return commit_result(-L4_err::EInval);
610
611   if (tag.items() && snd_items.next())
612     {
613       L4_fpage bind_irq(snd_items.get()->d);
614       if (EXPECT_FALSE(!bind_irq.is_objpage()))
615         return commit_error(utcb, L4_error::Overflow);
616
617       irq = Kobject::dcast<Irq*>(o_space->lookup_local(bind_irq.obj_index()));
618     }
619
620   if (!irq)
621     return commit_result(-L4_err::EInval);
622
623   if (mode & Set_irq_mode)
624     printf("DEPRECATED SET IRQ MODE\n");
625     //pin()->set_mode(mode);
626
627   irq->pin()->unbind_irq();
628
629   if (!irq->pin()->masked())
630     {
631       Smword old;
632       do
633         old = _mask_cnt;
634       while (!mp_cas(&_mask_cnt, old, old + 1));
635     }
636
637   irq->pin()->replace<Chain_irq_pin>(this);
638
639   irq->Irq_base::_next = Irq_base::_next;
640   Irq_base::_next = irq;
641
642   return commit_result(0);
643 }
644
645 PUBLIC
646 L4_msg_tag
647 Irq_muxer::kinvoke(L4_obj_ref, Mword /*rights*/, Syscall_frame *f,
648                    Utcb const *utcb, Utcb *)
649 {
650   register Context *const c_thread = ::current();
651   register Space *const c_space = c_thread->space();
652   register Obj_space *const o_space = c_space->obj_space();
653
654   L4_msg_tag tag = f->tag();
655
656   if (EXPECT_FALSE(tag.proto() != L4_msg_tag::Label_irq))
657     return commit_result(-L4_err::EBadproto);
658
659   if (EXPECT_FALSE(tag.words() < 1))
660     return commit_result(-L4_err::EInval);
661
662   switch ((utcb->values[0] & 0xffff))
663     {
664     case Op_chain:
665       return sys_attach(tag, utcb, f, o_space);
666     case Op_trigger:
667       hit();
668       Irq::log_irq(this, 0);
669       return no_reply();
670     default:
671       return commit_result(-L4_err::EInval);
672     }
673 }
674
675 PUBLIC
676 L4_msg_tag
677 Irq_sender::kinvoke(L4_obj_ref, Mword /*rights*/, Syscall_frame *f,
678                     Utcb const *utcb, Utcb *)
679 {
680   register Context *const c_thread = ::current();
681   register Space *const c_space = c_thread->space();
682   register Obj_space *const o_space = c_space->obj_space();
683
684   L4_msg_tag tag = f->tag();
685
686   if (EXPECT_FALSE(tag.proto() != L4_msg_tag::Label_irq))
687     return commit_result(-L4_err::EBadproto);
688
689   if (EXPECT_FALSE(tag.words() < 1))
690     return commit_result(-L4_err::EInval);
691
692   switch ((utcb->values[0] & 0xffff))
693     {
694     case Op_eoi_1:
695     case Op_eoi_2:
696       pin()->unmask();
697       return no_reply();
698     case Op_attach: /* ATTACH, DETACH */
699       return sys_attach(tag, utcb, f, o_space);
700     case Op_trigger:
701       hit();
702       Irq::log_irq(this, 0);
703       return no_reply();
704     default:
705       return commit_result(-L4_err::EInval);
706     }
707 }
708
709 PUBLIC
710 Mword
711 Irq_sender::obj_id() const
712 { return _irq_id; }
713
714
715 // --------------------------------------------------------------------------
716 IMPLEMENTATION [debug]:
717
718 #include "logdefs.h"
719
720 PUBLIC
721 char const *
722 Chain_irq_pin::pin_type() const
723 { return "CHAIN IRQ"; }
724
725 IMPLEMENT
726 unsigned
727 Irq::irq_log_fmt(Tb_entry *e, int maxlen, char *buf)
728 {
729   Irq_log *l = e->payload<Irq_log>();
730   return snprintf(buf, maxlen, "0x%x/%u D:%lx userip=%lx",
731                   l->irq_number, l->irq_number,
732                   l->irq_obj, l->user_ip);
733 }
734
735 PUBLIC static inline
736 void
737 Irq::log_irq(Irq *irq, int nr)
738 {
739   Context *c = current();
740   LOG_TRACE("IRQ-Object triggers", "irq", c, __irq_log_fmt,
741       Irq::Irq_log *l = tbe->payload<Irq::Irq_log>();
742       l->irq_number = nr;
743       l->user_ip    = c->regs()->ip();
744       l->irq_obj    = irq ? irq->dbg_id() : ~0UL;
745   );
746 }
747
748 PUBLIC static inline NEEDS["config.h"]
749 void
750 Irq::log_timer_irq(int nr)
751 {
752   Context *c = current();
753   LOG_TRACE("Timer IRQs (kernel scheduling)", "timer", c, __irq_log_fmt,
754       Irq::Irq_log *l = tbe->payload<Irq::Irq_log>();
755       l->irq_number = nr;
756       l->user_ip    = c->regs()->ip(),
757       l->irq_obj    = ~0UL;
758   );
759 }
760
761 // --------------------------------------------------------------------------
762 IMPLEMENTATION [!debug]:
763
764 PUBLIC static inline
765 void
766 Irq::log_irq(Irq *, int)
767 {}
768
769 PUBLIC static inline
770 void
771 Irq::log_timer_irq(int)
772 {}