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