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