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