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