]> rtime.felk.cvut.cz Git - l4.git/blob - l4/pkg/io/server/src/vicu.cc
8ce9d4f5457fd734cc6e73e0551ded36a957754b
[l4.git] / l4 / pkg / io / server / src / vicu.cc
1 /*
2  * (c) 2010 Alexander Warg <warg@os.inf.tu-dresden.de>
3  *     economic rights: Technische Universität Dresden (Germany)
4  *
5  * This file is part of TUD:OS and distributed under the terms of the
6  * GNU General Public License 2.
7  * Please see the COPYING-GPL-2 file for details.
8  */
9
10 #include <l4/vbus/vdevice-ops.h>
11
12 #include "vicu.h"
13 #include "vbus_factory.h"
14 #include "server.h"
15 #include "main.h"
16
17 #include <l4/re/util/cap_alloc>
18 #include <l4/re/namespace>
19 #include <l4/re/env>
20 #include <l4/re/error_helper>
21 #include <l4/sys/icu>
22 #include <l4/sys/irq>
23 #include <l4/sys/debugger.h>
24 #include <l4/sys/kdebug.h>
25
26 #include <cassert>
27 #include <pthread.h>
28 #include <pthread-l4.h>
29
30 namespace Vi {
31
32 using L4Re::Util::Auto_cap;
33 using L4Re::chksys;
34 using L4Re::chkcap;
35
36
37 Sw_icu::Real_irq_pin Sw_icu::_real_irqs[256];
38
39 Sw_icu::Real_irq_pin *
40 Sw_icu::real_irq(unsigned n)
41 {
42   if (n >= sizeof(_real_irqs)/sizeof(_real_irqs[0]))
43     return 0;
44
45   return &_real_irqs[n];
46 }
47
48
49 Sw_icu::Sw_icu()
50 {
51   add_feature(this);
52   registry->register_obj(this);
53 }
54
55 Sw_icu::~Sw_icu()
56 {
57   registry->unregister_obj(this);
58 }
59
60 int
61 Sw_icu::bind_irq(l4_msgtag_t tag, unsigned irqn, L4::Snd_fpage const &/*irqc*/)
62 {
63   if (tag.items() < 1)
64     return -L4_EINVAL;
65
66
67   // printf("%s[%p]: bind_irq(%d, ...)\n", name(), this, irqn);
68
69   Irq_set::iterator i = _irqs.find(irqn);
70   if (i == _irqs.end())
71     return -L4_ENOENT;
72
73   int err = i->bind(rcv_cap);
74   if (err < 0)
75     printf("ERROR: binding irq %d, result is %d (%s)\n", irqn, err, l4sys_errtostr(err));
76
77   return err;
78 }
79
80 int
81 Sw_icu::unbind_irq(l4_msgtag_t tag, unsigned irqn, L4::Snd_fpage const &/*irqc*/)
82 {
83   if (tag.items() < 1)
84     return -L4_EINVAL;
85   // printf("%s[%p]: unbind_irq(%d, ...)\n", name(), this, irqn);
86
87   Irq_set::iterator i = _irqs.find(irqn);
88   if (i == _irqs.end())
89     return -L4_ENOENT;
90
91   // could check the validity of the cap too, however we just don't care
92   return i->unbind();
93 }
94
95 int
96 Sw_icu::set_mode(l4_msgtag_t /*tag*/, unsigned irqn, l4_umword_t mode)
97 {
98   Irq_set::iterator i = _irqs.find(irqn);
99   if (i == _irqs.end())
100     return -L4_ENOENT;
101
102   if (i->l4_type() != (mode & 0x6))
103     {
104       printf("Changing type of IRQ %d from %x to %lx prohibited\n",
105              irqn, i->l4_type(), mode);
106       return 0;
107     }
108   return 0;
109 }
110
111 int
112 Sw_icu::unmask_irq(l4_msgtag_t /*tag*/, unsigned irqn)
113 {
114   Irq_set::iterator i = _irqs.find(irqn);
115   if (i == _irqs.end())
116     return -L4_ENOENT;
117
118   if (!i->unmask_via_icu())
119     return -L4_EINVAL;
120
121   return i->unmask();
122 }
123
124
125 bool
126 Sw_icu::irqs_allocated(Adr_resource const *r)
127 {
128   for (unsigned n = r->_data().start(); n <= r->_data().end(); ++n)
129     {
130       if (_irqs.find(n) == _irqs.end())
131         return false;
132     }
133
134   return true;
135 }
136
137 bool
138 Sw_icu::add_irqs(Adr_resource const *r)
139 {
140   for (unsigned n = r->_data().start(); n <= r->_data().end(); ++n)
141     {
142       if (_irqs.find(n) != _irqs.end())
143         continue;
144
145       Sw_irq_pin *irq = new Sw_irq_pin(real_irq(n), n, r->flags());
146       _irqs.insert(irq);
147     }
148   return true;
149 }
150
151
152 int
153 Sw_icu::dispatch(l4_umword_t /*obj*/, L4::Ipc_iostream &ios)
154 {
155   l4_umword_t op, irqn;
156   L4::Snd_fpage irqc;
157   l4_msgtag_t tag;
158   ios >> tag >> op >> irqn >> irqc;
159
160   if (tag.label() != L4_PROTO_IRQ)
161     return -L4_EBADPROTO;
162
163   switch (op)
164     {
165     case L4_ICU_OP_BIND:
166       ios >> irqc;
167       return bind_irq(tag, irqn, irqc);
168
169     case L4_ICU_OP_UNBIND:
170       ios >> irqc;
171       return unbind_irq(tag, irqn, irqc);
172
173     case L4_ICU_OP_UNMASK:
174       unmask_irq(tag, irqn);
175       return -L4_ENOREPLY;
176
177     case L4_ICU_OP_SET_MODE:
178         {
179           l4_umword_t mode;
180           ios >> mode;
181           return set_mode(tag, irqn, mode);
182         }
183
184     default: return -L4_ENOSYS;
185     }
186 }
187
188 int
189 Sw_icu::dispatch(l4_umword_t, l4_uint32_t func, L4::Ipc_iostream &ios)
190 {
191   if (func != L4vbus_vicu_get_cap)
192     return -L4_ENOSYS;
193
194   ios << obj_cap();
195   return L4_EOK;
196 }
197
198 //static VBus_factory<Sw_icu> __vicu_factory("Vicu");
199
200 int
201 Sw_icu::Sw_irq_pin::trigger() const
202 {
203   return l4_error(_irq->trigger());
204 }
205
206 int
207 Sw_icu::Real_irq_pin::unbind()
208 {
209   unsigned n = this - real_irq(0);
210   if (n & 0x80)
211     n = (n - 0x80) | L4::Icu::F_msi;
212
213   int err = l4_error(system_icu()->icu->unbind(n, irq()));
214   set_shareable(false);
215   return err;
216 }
217
218 int
219 Sw_icu::Real_irq_pin::bind(L4::Cap<L4::Irq> irq, unsigned mode)
220 {
221   unsigned n = this - real_irq(0);
222   if (n & 0x80)
223     n = (n - 0x80) | L4::Icu::F_msi;
224
225   int err = l4_error(system_icu()->icu->bind(n, irq));
226
227   // allow sharing if IRQ must be acknowledged via the IRQ object 
228   if (err == 0)
229     set_shareable(true);
230
231   if (err < 0)
232     return err;
233
234   // printf(" IRQ[%x]: mode=%x ... ", n, mode);
235   err = l4_error(system_icu()->icu->set_mode(n, mode));
236   // printf("result=%d\n", err);
237
238   return err;
239 }
240
241 int
242 Sw_icu::Real_irq_pin::unmask()
243 {
244   unsigned n = this - real_irq(0);
245   if (n & 0x80)
246     n = (n - 0x80) | L4::Icu::F_msi;
247
248   system_icu()->icu->unmask(n, 0, L4_IPC_NEVER);
249   return -L4_EINVAL;
250 }
251
252 unsigned
253 Sw_icu::Sw_irq_pin::l4_type() const
254 {
255   unsigned m = type();
256   unsigned r = (m & S_irq_type_mask) / Adr_resource::Irq_info_factor;
257   return r;
258 }
259
260 void
261 Sw_icu::Sw_irq_pin::allocate_master_irq()
262 {
263   assert (_master->shared());
264   Auto_cap<L4::Irq>::Cap lirq = chkcap(L4Re::Util::cap_alloc.alloc<L4::Irq>(),
265       "allocating IRQ capability");
266   // printf("IRQ mode = %x -> %x\n", type(), l4_type());
267   chksys(L4Re::Env::env()->factory()->create_irq(lirq.get()), "allocating IRQ");
268   chksys(_master->bind(lirq.get(), l4_type()), "binding IRQ");
269   _master->_irq  = lirq.release();
270   _master->set_chained(true);
271 }
272
273 #if 0
274 int
275 Sw_icu::Sw_irq_pin::share(Auto_cap<L4::Irq>::Cap const &irq)
276 {
277   if (!_master->shareable())
278     {
279       printf("WARNING: IRQ %d cannot be shared\n", irqn());
280       return -L4_EINVAL;
281     }
282
283   try
284     {
285       // the second irq shall be attached to a single hw irq
286       printf("IRQ %d -> proxy -> 2 clients\n", irqn());
287       Auto_cap<L4::Irq>::Cap lirq(L4Re::Util::cap_alloc.alloc<L4::Irq>());
288       if (!lirq.get().is_valid())
289         return -L4_ENOMEM;
290
291       int err = l4_error(L4Re::Env::env()->factory()->create_irq(lirq.get()));
292
293       if (err < 0)
294         return err;
295
296       //enter_kdebug("XX");
297       // XXX: level low is a hack
298       L4Re::chksys(lirq->chain(l4_umword_t(_master), L4::Irq::F_level_low, _master->_irq));
299       L4Re::chksys(lirq->chain(l4_umword_t(_master), L4::Irq::F_level_low, irq.get()));
300       L4Re::chksys(lirq->set_mode(L4::Irq::F_level_low), "set mode");
301       L4Re::chksys(err = _master->bind(lirq.get()));
302       //enter_kdebug("XX");
303
304       // do the internal list enqueuing and resource management
305       _state |= S_bound;
306       _master->_irq = lirq.release();
307       _master->_sw_irqs++;
308       _master->set_chained(true);
309       _irq = irq;
310       return 0;
311     }
312   catch (L4::Runtime_error const &e)
313     {
314       _master->bind(_master->_irq);
315       return e.err_no();
316     }
317 }
318 #endif
319
320 int
321 Sw_icu::Sw_irq_pin::bind(L4::Cap<void> rc)
322 {
323   if (_irq.is_valid())
324     {
325       if (_irq.get().validate(L4Re::This_task).label() > 0)
326         return -L4_EEXIST;
327
328       _unbind();
329     }
330 #if 0
331   Real_irq_pin *ri;
332
333   if (!(ri = real_irq(irqn())))
334     return -L4_EPERM;
335 #endif
336   if (bound())
337     return -L4_EPERM;
338
339   Auto_cap<L4::Irq>::Cap irq =
340     chkcap(L4Re::Util::cap_alloc.alloc<L4::Irq>(), "allocating IRQ capability");
341
342   irq.get().move(L4::cap_cast<L4::Irq>(rc));
343
344   if (_master->shared() && !_master->chained() && _master->_sw_irqs == 0)
345     {
346       allocate_master_irq();
347       assert (_master->chained());
348     }
349
350   if (!_master->chained())
351     {
352       // the first irq shall be attached to a hw irq
353       // printf("IRQ %d -> client\n", irqn());
354       // printf("IRQ mode = %x -> %x\n", type(), l4_type());
355       int err = _master->bind(irq.get(), l4_type());
356       if (err < 0)
357         return err;
358
359       _irq = irq;
360       _master->_irq = _irq.get();
361       ++_master->_sw_irqs;
362       _state |= S_bound;
363       if (err == 1)
364         _state |= S_unmask_via_icu;
365
366       // printf("  bound irq %u -> err=%d\n", irqn(), err);
367       return err;
368     }
369
370   // printf("IRQ %d -> proxy -> %d clients\n", irqn(), _master->_sw_irqs + 1);
371   L4Re::chksys(_master->_irq->chain(l4_umword_t(_master), irq.get()), "attach");
372   _irq = irq;
373   _master->_sw_irqs++;
374
375   return 0;
376 }
377
378 int
379 Sw_icu::Sw_irq_pin::_unbind()
380 {
381   int err = 0;
382   --(_master->_sw_irqs);
383   if (_master->_sw_irqs == 0)
384     {
385       if (_master->chained())
386         L4Re::Util::cap_alloc.free(_master->_irq);
387
388       _master->_irq = L4::Cap<L4::Irq>::Invalid;
389       _master->set_chained(false);
390     }
391
392   _irq = L4::Cap<L4::Irq>::Invalid;
393
394   _state &= ~S_bound;
395   return err;
396 }
397
398 int
399 Sw_icu::Sw_irq_pin::unbind()
400 {
401   if (!_master)
402     return -L4_EINVAL;
403
404   if (!_master->_sw_irqs)
405     return -L4_EINVAL;
406
407   return _unbind();
408 }
409
410 }