]> rtime.felk.cvut.cz Git - l4.git/blob - kernel/fiasco/src/kern/irq.cpp
7079fdc988f8350dc3e0a462c8e4551ba3aa3424
[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 Receiver;
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   Receiver *_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   unsigned set_mode(Mword, unsigned mode) { return mode; }
76   void switch_mode(unsigned)
77   {
78     // the irq object is assumed to be always handled as
79     // level triggered
80   }
81
82   void set_cpu(Mword, unsigned)
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 };
95
96 //-----------------------------------------------------------------------------
97 IMPLEMENTATION:
98
99 #include "assert_opt.h"
100 #include "atomic.h"
101 #include "config.h"
102 #include "cpu_lock.h"
103 #include "entry_frame.h"
104 #include "globals.h"
105 #include "ipc_sender.h"
106 #include "kdb_ke.h"
107 #include "kmem_slab.h"
108 #include "lock_guard.h"
109 #include "minmax.h"
110 #include "receiver.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   unsigned mode = utcb->values[0] >> 16;
242
243   if (tag.items() == 0)
244     return commit_result(-L4_err::EInval);
245
246   if (tag.items() && snd_items.next())
247     {
248       L4_fpage bind_irq(snd_items.get()->d);
249       if (EXPECT_FALSE(!bind_irq.is_objpage()))
250         return commit_error(utcb, L4_error::Overflow);
251
252       irq = Kobject::dcast<Irq*>(o_space->lookup_local(bind_irq.obj_index()));
253     }
254
255   if (!irq)
256     return commit_result(-L4_err::EInval);
257
258   if (mode & Set_irq_mode)
259     printf("DEPRECATED SET IRQ MODE\n");
260     //pin()->set_mode(mode);
261
262   irq->unbind();
263
264   if (!irq->masked())
265     {
266       Smword old;
267       do
268         old = _mask_cnt;
269       while (!mp_cas(&_mask_cnt, old, old + 1));
270     }
271
272   bind(irq, 0);
273
274   irq->Irq_base::_next = Irq_base::_next;
275   Irq_base::_next = irq;
276
277   return commit_result(0);
278 }
279
280 PUBLIC
281 L4_msg_tag
282 Irq_muxer::kinvoke(L4_obj_ref, Mword /*rights*/, Syscall_frame *f,
283                    Utcb const *utcb, Utcb *)
284 {
285   register Context *const c_thread = ::current();
286   assert_opt (c_thread);
287   register Space *const c_space = c_thread->space();
288   assert_opt (c_space);
289
290   L4_msg_tag tag = f->tag();
291
292   if (EXPECT_FALSE(tag.proto() != L4_msg_tag::Label_irq))
293     return commit_result(-L4_err::EBadproto);
294
295   if (EXPECT_FALSE(tag.words() < 1))
296     return commit_result(-L4_err::EInval);
297
298   switch ((utcb->values[0] & 0xffff))
299     {
300     case Op_chain:
301       return sys_attach(tag, utcb, f, c_space);
302     case Op_trigger:
303       log();
304       hit(0);
305       return no_reply();
306     default:
307       return commit_result(-L4_err::EInval);
308     }
309 }
310
311
312
313 /** Bind a receiver to this device interrupt.
314     @param t the receiver that wants to receive IPC messages for this IRQ
315     @return true if the binding could be established
316  */
317 PUBLIC inline NEEDS ["atomic.h", "cpu_lock.h", "lock_guard.h"]
318 bool
319 Irq_sender::alloc(Receiver *t)
320 {
321   bool ret = mp_cas(&_irq_thread, reinterpret_cast<Receiver*>(0), t);
322
323   if (ret)
324     {
325       if (EXPECT_TRUE(t != 0))
326         {
327           t->inc_ref();
328           _chip->set_cpu(pin(), t->cpu());
329         }
330
331       _queued = 0;
332     }
333
334   return ret;
335 }
336
337 PUBLIC
338 Receiver *
339 Irq_sender::owner() const { return _irq_thread; }
340
341 /** Release an device interrupt.
342     @param t the receiver that ownes the IRQ
343     @return true if t really was the owner of the IRQ and operation was 
344             successful
345  */
346 PUBLIC
347 bool
348 Irq_sender::free(Receiver *t)
349 {
350   bool ret = mp_cas(&_irq_thread, t, reinterpret_cast<Receiver*>(0));
351
352   if (ret)
353     {
354       auto guard = lock_guard(cpu_lock);
355       mask();
356
357       if (EXPECT_TRUE(t != 0))
358         {
359           t->abort_send(this);
360
361           // release cpu-lock early, actually before delete
362           guard.reset();
363
364           if (t->dec_ref() == 0)
365             delete t;
366         }
367     }
368
369   return ret;
370 }
371
372 PUBLIC explicit
373 Irq_sender::Irq_sender(Ram_quota *q = 0)
374 : Kobject_h<Irq_sender, Irq>(q), _queued(0), _irq_thread(0), _irq_id(~0UL)
375 {
376   hit_func = &hit_level_irq;
377 }
378
379 PUBLIC
380 void
381 Irq_sender::switch_mode(unsigned mode)
382 {
383   if ((mode & Trigger_mask) == Trigger_edge)
384     hit_func = &hit_edge_irq;
385   else
386     hit_func = &hit_level_irq;
387 }
388
389 PUBLIC
390 void
391 Irq_sender::destroy(Kobject ***rl)
392 {
393   auto g = lock_guard(cpu_lock);
394   if (_irq_thread)
395     free(_irq_thread);
396
397   Irq::destroy(rl);
398 }
399
400
401 /** Consume one interrupt.
402     @return number of IRQs that are still pending.
403  */
404 PRIVATE inline NEEDS ["atomic.h"]
405 Smword
406 Irq_sender::consume()
407 {
408   Smword old;
409
410   do
411     {
412       old = _queued;
413     }
414   while (!mp_cas (&_queued, old, old - 1));
415
416   if (old == 2 && hit_func == &hit_edge_irq)
417     unmask();
418
419   return old - 1;
420 }
421
422 PUBLIC inline
423 int
424 Irq_sender::queued()
425 {
426   return _queued;
427 }
428
429
430 /**
431  * Predicate used to figure out if the sender shall be enqueued
432  * for sending a second message after sending the first.
433  */
434 PUBLIC inline NEEDS[Irq_sender::consume]
435 bool
436 Irq_sender::requeue_sender()
437 { return consume() > 0; }
438
439 /**
440  * Predicate used to figure out if the sender shall be deqeued after
441  * sending the request.
442  */
443 PUBLIC inline NEEDS[Irq_sender::consume]
444 bool
445 Irq_sender::dequeue_sender()
446 { return consume() < 1; }
447
448 PUBLIC inline
449 Syscall_frame *
450 Irq_sender::transfer_msg(Receiver *recv)
451 {
452   Syscall_frame* dst_regs = recv->rcv_regs();
453
454   // set ipc return value: OK
455   dst_regs->tag(L4_msg_tag(0));
456
457   // set ipc source thread id
458   dst_regs->from(_irq_id);
459
460   return dst_regs;
461 }
462
463 PUBLIC void
464 Irq_sender::modify_label(Mword const *todo, int cnt)
465 {
466   for (int i = 0; i < cnt*4; i += 4)
467     {
468       Mword const test_mask = todo[i];
469       Mword const test      = todo[i+1];
470       if ((_irq_id & test_mask) == test)
471         {
472           Mword const set_mask = todo[i+2];
473           Mword const set      = todo[i+3];
474
475           _irq_id = (_irq_id & ~set_mask) | set;
476           return;
477         }
478     }
479 }
480
481
482 PRIVATE static
483 unsigned
484 Irq_sender::handle_remote_hit(Context::Drq *, Context *, void *arg)
485 {
486   Irq_sender *irq = (Irq_sender*)arg;
487   irq->set_cpu(current_cpu());
488   irq->send_msg(irq->_irq_thread);
489   return Context::Drq::No_answer;
490 }
491
492 PRIVATE inline
493 Smword
494 Irq_sender::queue()
495 {
496   Smword old;
497   do
498     old = _queued;
499   while (!mp_cas(&_queued, old, old + 1));
500   return old;
501 }
502
503
504 PRIVATE inline
505 void
506 Irq_sender::count_and_send(Smword queued)
507 {
508   if (EXPECT_TRUE (queued == 0) && EXPECT_TRUE(_irq_thread != 0))       // increase hit counter
509     {
510       if (EXPECT_FALSE(_irq_thread->cpu() != current_cpu()))
511         _irq_thread->drq(&_drq, handle_remote_hit, this, 0,
512                          Context::Drq::Target_ctxt, Context::Drq::No_wait);
513       else
514         send_msg(_irq_thread);
515     }
516 }
517
518
519 PUBLIC inline NEEDS[Irq_sender::count_and_send, Irq_sender::queue]
520 void
521 Irq_sender::_hit_level_irq(Upstream_irq const *ui)
522 {
523   // We're entered holding the kernel lock, which also means irqs are
524   // disabled on this CPU (XXX always correct?).  We never enable irqs
525   // in this stack frame (except maybe in a nonnested invocation of
526   // switch_exec() -> switchin_context()) -- they will be re-enabled
527   // once we return from it (iret in entry.S:all_irqs) or we switch to
528   // a different thread.
529
530   // LOG_MSG_3VAL(current(), "IRQ", dbg_id(), 0, _queued);
531
532   assert (cpu_lock.test());
533   mask_and_ack();
534   ui->ack();
535   count_and_send(queue());
536 }
537
538 PRIVATE static
539 void
540 Irq_sender::hit_level_irq(Irq_base *i, Upstream_irq const *ui)
541 { nonull_static_cast<Irq_sender*>(i)->_hit_level_irq(ui); }
542
543 PUBLIC inline NEEDS[Irq_sender::count_and_send, Irq_sender::queue]
544 void
545 Irq_sender::_hit_edge_irq(Upstream_irq const *ui)
546 {
547   // We're entered holding the kernel lock, which also means irqs are
548   // disabled on this CPU (XXX always correct?).  We never enable irqs
549   // in this stack frame (except maybe in a nonnested invocation of
550   // switch_exec() -> switchin_context()) -- they will be re-enabled
551   // once we return from it (iret in entry.S:all_irqs) or we switch to
552   // a different thread.
553
554   // LOG_MSG_3VAL(current(), "IRQ", dbg_id(), 0, _queued);
555
556   assert (cpu_lock.test());
557   Smword q = queue();
558
559   // if we get a second edge triggered IRQ before the first is
560   // handled we can mask the IRQ.  The consume function will
561   // unmask the IRQ when the last IRQ is dequeued.
562   if (!q)
563     ack();
564   else
565     mask_and_ack();
566
567   ui->ack();
568   count_and_send(q);
569 }
570
571 PRIVATE static
572 void
573 Irq_sender::hit_edge_irq(Irq_base *i, Upstream_irq const *ui)
574 { nonull_static_cast<Irq_sender*>(i)->_hit_edge_irq(ui); }
575
576
577 PRIVATE
578 L4_msg_tag
579 Irq_sender::sys_attach(L4_msg_tag const &tag, Utcb const *utcb, Syscall_frame * /*f*/,
580                 Obj_space *o_space)
581 {
582   L4_snd_item_iter snd_items(utcb, tag.words());
583
584   Receiver *thread = 0;
585   unsigned mode = utcb->values[0] >> 16;
586
587   if (tag.items() == 0)
588     {
589       // detach
590       if (mode & Set_irq_mode)
591         printf("DEPRECATED SET IRQ MODE\n");
592         //pin()->set_mode(mode);
593       else
594         {
595           free(_irq_thread);
596           _irq_id = ~0UL;
597         }
598       return commit_result(0);
599     }
600
601   if (tag.items() && snd_items.next())
602     {
603       L4_fpage bind_thread(snd_items.get()->d);
604       if (EXPECT_FALSE(!bind_thread.is_objpage()))
605         return commit_error(utcb, L4_error::Overflow);
606
607       thread = Kobject::dcast<Thread_object*>(o_space->lookup_local(bind_thread.obj_index()));
608     }
609
610   if (!thread)
611     thread = current_thread();
612
613   if (alloc(thread))
614     {
615       if (mode & Set_irq_mode)
616         printf("DEPRECATED SET IRQ MODE\n");
617       _irq_id = utcb->values[1];
618       return commit_result(0);
619     }
620
621   return commit_result(-L4_err::EInval);
622 }
623
624
625 PUBLIC
626 L4_msg_tag
627 Irq_sender::kinvoke(L4_obj_ref, Mword /*rights*/, Syscall_frame *f,
628                     Utcb const *utcb, Utcb *)
629 {
630   register Context *const c_thread = ::current();
631   assert_opt (c_thread);
632   register Space *const c_space = c_thread->space();
633   assert_opt (c_space);
634
635   L4_msg_tag tag = f->tag();
636
637   if (EXPECT_FALSE(tag.proto() != L4_msg_tag::Label_irq))
638     return commit_result(-L4_err::EBadproto);
639
640   if (EXPECT_FALSE(tag.words() < 1))
641     return commit_result(-L4_err::EInval);
642
643   switch ((utcb->values[0] & 0xffff))
644     {
645     case Op_eoi_1:
646     case Op_eoi_2:
647       if (_queued < 1)
648         unmask();
649
650       return no_reply();
651     case Op_attach: /* ATTACH, DETACH */
652       return sys_attach(tag, utcb, f, c_space);
653     case Op_trigger:
654       log();
655       hit(0);
656       return no_reply();
657     default:
658       return commit_result(-L4_err::EInval);
659     }
660 }
661
662 PUBLIC
663 Mword
664 Irq_sender::obj_id() const
665 { return _irq_id; }
666
667
668
669  // Irq implementation
670
671 static Kmem_slab _irq_allocator(max(sizeof (Irq_sender), sizeof(Irq_muxer)),
672                                 __alignof__ (Irq), "Irq");
673
674 PRIVATE static
675 Irq::Allocator *
676 Irq::allocator()
677 { return &_irq_allocator; }
678
679 PUBLIC inline
680 void *
681 Irq::operator new (size_t, void *p)
682 { return p; }
683
684 PUBLIC
685 void
686 Irq::operator delete (void *_l)
687 {
688   Irq *l = reinterpret_cast<Irq*>(_l);
689   if (l->_q)
690     l->_q->free(sizeof(Irq));
691
692   allocator()->free(l);
693 }
694
695 PUBLIC template<typename T> inline NEEDS[Irq::allocator, Irq::operator new]
696 static
697 T*
698 Irq::allocate(Ram_quota *q)
699 {
700   void *nq =allocator()->q_alloc(q);
701   if (nq)
702     return new (nq) T(q);
703
704   return 0;
705 }
706
707
708 PUBLIC explicit inline
709 Irq::Irq(Ram_quota *q = 0) : _q(q) {}
710
711 PUBLIC
712 void
713 Irq::destroy(Kobject ***rl)
714 {
715   Irq_base::destroy();
716   Kobject::destroy(rl);
717 }