]> rtime.felk.cvut.cz Git - l4.git/blob - l4/pkg/io/server/src/virt/vbus.cc
update
[l4.git] / l4 / pkg / io / server / src / virt / vbus.cc
1 /*
2  * (c) 2010 Adam Lackorzynski <adam@os.inf.tu-dresden.de>,
3  *          Alexander Warg <warg@os.inf.tu-dresden.de>
4  *     economic rights: Technische Universität Dresden (Germany)
5  *
6  * This file is part of TUD:OS and distributed under the terms of the
7  * GNU General Public License 2.
8  * Please see the COPYING-GPL-2 file for details.
9  */
10 #include <l4/re/protocols>
11
12 #include <l4/cxx/ipc_server>
13
14 #include <l4/re/env>
15 #include <l4/re/namespace>
16 #include <l4/re/dataspace-sys.h>
17 #include <l4/re/inhibitor-sys.h>
18
19 #include <l4/re/error_helper>
20
21 #include <l4/vbus/vbus_types.h>
22 #include <l4/vbus/vdevice-ops.h>
23
24 #include <cstdio>
25
26 #include "debug.h"
27 #include "hw_msi.h"
28 #include "vbus.h"
29 #include "vmsi.h"
30 #include "vicu.h"
31 #include "server.h"
32 #include "res.h"
33 #include "cfg.h"
34 #include "vbus_factory.h"
35
36 Vi::System_bus::Root_resource_factory::Factory_list
37   Vi::System_bus::Root_resource_factory::_factories;
38
39 namespace {
40
41 class Root_irq_rs : public Resource_space
42 {
43 private:
44   Vi::System_bus *_bus;
45   Vi::Sw_icu *_icu;
46
47 public:
48   Root_irq_rs(Vi::System_bus *bus)
49     : Resource_space(), _bus(bus), _icu(new Vi::Sw_icu())
50   {
51     _bus->add_child(_icu);
52     _bus->sw_icu(_icu);
53     _icu->name("L4ICU");
54   }
55
56   bool request(Resource *parent, Device *, Resource *child, Device *)
57   {
58     if (0)
59       {
60         printf("VBUS: IRQ resource request: ");
61         child->dump();
62       }
63
64     if (!parent)
65       return false;
66
67     if (!_icu)
68       {
69         _icu = new Vi::Sw_icu();
70         _bus->add_child(_icu);
71         _bus->sw_icu(_icu);
72       }
73
74     d_printf(DBG_DEBUG2, "Add IRQ resources to vbus: ");
75     if (dlevel(DBG_DEBUG2))
76       child->dump();
77
78     _icu->add_irqs(child);
79     return _bus->add_resource_to_bus(child);
80   };
81
82   bool alloc(Resource *parent, Device *, Resource *child, Device *, bool)
83   {
84     d_printf(DBG_DEBUG2, "Allocate virtual IRQ resource ...\n");
85     if (dlevel(DBG_DEBUG2))
86       child->dump();
87
88     Vi::Msi_resource *msi = dynamic_cast<Vi::Msi_resource*>(child);
89     if (!msi || !parent)
90       return false;
91
92     d_printf(DBG_DEBUG2, "  Allocate Virtual MSI...\n");
93
94     if (!_icu)
95       {
96         _icu = new Vi::Sw_icu();
97         _bus->add_child(_icu);
98       }
99
100     int nr = _icu->alloc_irq(msi->flags(), msi->hw_msi());
101     if (nr < 0)
102       {
103         d_printf(DBG_ERR, "ERROR: cannot allocate MSI resource\n");
104         return false;
105       }
106
107     msi->start_end(nr, nr);
108     msi->del_flags(Resource::F_disabled);
109
110     if (dlevel(DBG_DEBUG2))
111       {
112         msi->dump(4);
113         msi->hw_msi()->dump(4);
114       }
115
116     return _bus->add_resource_to_bus(msi);
117   }
118
119   ~Root_irq_rs() {}
120 };
121
122 class Root_x_rs : public Resource_space
123 {
124 private:
125   Vi::System_bus *_bus;
126
127 public:
128   Root_x_rs(Vi::System_bus *bus) : Resource_space(), _bus(bus)
129   {}
130
131   bool request(Resource *parent, Device *, Resource *child, Device *)
132   {
133     if (0)
134       {
135         printf("VBUS: X resource request: ");
136         child->dump();
137       }
138
139     if (!parent)
140       return false;
141
142
143     return _bus->add_resource_to_bus(child);
144   }
145
146   bool alloc(Resource *, Device *, Resource *, Device *, bool)
147   { return false; }
148
149   ~Root_x_rs() {}
150 };
151 }
152
153
154
155 namespace Vi {
156
157 System_bus::System_bus(Inhibitor_mux *mux)
158 : Inhibitor_provider(mux), _sw_icu(0)
159 {
160   add_feature(this);
161   add_resource(new Root_resource(Resource::Irq_res, new Root_irq_rs(this)));
162   Resource_space *x = new Root_x_rs(this);
163   add_resource(new Root_resource(Resource::Mmio_res, x));
164   add_resource(new Root_resource(Resource::Mmio_res | Resource::F_prefetchable, x));
165   add_resource(new Root_resource(Resource::Io_res, x));
166   typedef Root_resource_factory RF;
167   for (RF::Factory_list::Const_iterator i = RF::_factories.begin();
168       i != RF::_factories.end();
169       ++i)
170     add_resource((*i)->create(this));
171 }
172
173 System_bus::~System_bus()
174 {
175   registry->unregister_obj(this);
176   // FIXME: must delete all devices
177 }
178
179 /**
180  * \brief Add the region described by \a r to the VBUS resources.
181  *
182  * Adds the resource to the resources available through this V-BUS.
183  * This also supports overlapping resources by merging them together.
184  */
185 bool
186 System_bus::add_resource_to_bus(Resource *r)
187 {
188   auto x = _resources.find(r);
189   if (x == _resources.end())
190     {
191       _resources.insert(r);
192       return true;
193     }
194
195   // overlapping resource entry already found
196   if (typeid (*r) != typeid (**x))
197     {
198       if (dlevel(DBG_ERR))
199         {
200           printf("error: overlapping incompatible resources for vbus\n");
201           printf("       new:   "); r->dump(); puts(" conflicts with");
202           printf("       found: "); (*x)->dump(); puts("");
203         }
204       return false;
205     }
206
207   if ((*x)->contains(*r))
208     // already fully included
209     return true;
210
211   if (r->contains(**x))
212     {
213       _resources.erase(x);
214       _resources.insert(r);
215       return true;
216     }
217
218   if (dlevel(DBG_ERR))
219     {
220       printf("error: oddly overlaping resources for vbus\n");
221       printf("       new:   "); r->dump(); puts(" confilicts with");
222       printf("       found: "); (*x)->dump(); puts("");
223     }
224   return false;
225 }
226
227 bool
228 System_bus::resource_allocated(Resource const *r) const
229 {
230   if (r->disabled())
231     return false;
232
233   auto i = _resources.find(const_cast<Resource *>(r));
234   if (i == _resources.end())
235     return false;
236
237   return (*i)->contains(*r);
238 }
239
240 int
241 System_bus::pm_suspend()
242 {
243   inhibitor_release(L4VBUS_INHIBITOR_SUSPEND);
244
245   return 0;
246 }
247
248 int
249 System_bus::pm_resume()
250 {
251   inhibitor_acquire(L4VBUS_INHIBITOR_SUSPEND, "vbus active");
252
253   return 0;
254 }
255
256 void
257 System_bus::dump_resources() const
258 {
259   for (auto i = _resources.begin();
260        i != _resources.end(); ++i)
261     (*i)->dump();
262 }
263
264 int
265 System_bus::request_resource(L4::Ipc::Iostream &ios)
266 {
267   l4vbus_resource_t res;
268   ios.get(res);
269
270   Resource ires(res.type, res.start, res.end);
271   if (dlevel(DBG_DEBUG2))
272     {
273       printf("request resource: ");
274       ires.dump();
275       puts("");
276     }
277
278   auto i = _resources.find(&ires);
279
280   if (0)
281     dump_resources();
282
283   if (i == _resources.end() || !(*i)->contains(ires))
284     return -L4_ENOENT;
285
286   if (0)
287     if (dlevel(DBG_DEBUG))
288       {
289         printf("  found resource: ");
290         (*i)->dump();
291         puts("");
292       }
293
294   if (res.type == L4VBUS_RESOURCE_PORT)
295     {
296       l4_uint64_t sz = res.end + 1 - res.start;
297
298       int szl2 = 0;
299       while ((1UL << szl2) < sz)
300         ++szl2;
301
302       if ((1UL << szl2) > sz)
303         --szl2;
304
305       ios << L4::Ipc::Snd_fpage::io(res.start, szl2, L4_FPAGE_RWX);
306       return L4_EOK;
307     }
308
309
310   return -L4_ENOENT;
311 }
312
313 int
314 System_bus::request_iomem(L4::Ipc::Iostream &ios)
315 {
316   L4::Opcode op;
317   ios >> op;
318   switch (op)
319     {
320     case L4Re::Dataspace_::Map:
321         {
322           l4_addr_t offset, spot;
323           unsigned long flags;
324           ios >> offset >> spot >> flags;
325
326           Resource pivot(L4VBUS_RESOURCE_MEM, offset, offset);
327           auto r = _resources.find(&pivot);
328
329           if (r == _resources.end())
330             {
331               if (dlevel(DBG_INFO))
332                 {
333                   printf("Request: No resource at %lx\n", offset);
334                   printf("Available resources:\n");
335                   dump_resources();
336                 }
337               return -L4_ERANGE;
338             }
339
340           offset = l4_trunc_page(offset);
341
342           l4_addr_t st = l4_trunc_page((*r)->start());
343           l4_addr_t adr = (*r)->map_iomem();
344
345           if (!adr)
346             return -L4_ENOMEM;
347
348           adr = l4_trunc_page(adr);
349
350           l4_addr_t addr = offset - st + adr;
351           unsigned char order
352             = l4_fpage_max_order(L4_PAGESHIFT,
353                                  addr, addr, addr + (*r)->size(), spot);
354
355           // we also might want to do WB instead of UNCACHED...
356           ios << L4::Ipc::Snd_fpage::mem(l4_trunc_size(addr, order), order,
357                                     L4_FPAGE_RWX, l4_trunc_page(spot),
358                                     L4::Ipc::Snd_fpage::Map,
359                                     L4::Ipc::Snd_fpage::Uncached);
360           return L4_EOK;
361         }
362     }
363   return -L4_ENOSYS;
364 };
365
366 int
367 System_bus::inhibitor_dispatch(L4::Ipc::Iostream &ios)
368 {
369   L4::Opcode op;
370   ios >> op;
371
372   switch (op)
373     {
374     case L4Re::Inhibitor_::Acquire:
375       inhibitor_acquire(ios);
376       return L4_EOK;
377     case L4Re::Inhibitor_::Release:
378         {
379           l4_umword_t id;
380           ios >> id;
381           inhibitor_release(id);
382           return L4_EOK;
383         }
384     case L4Re::Inhibitor_::Next_lock_info:
385         {
386           l4_mword_t id;
387           ios >> id;
388           ++id;
389           if (id >= L4VBUS_INHIBITOR_MAX)
390             return -L4_ENODEV;
391
392           static char const *const names[] =
393           {
394             /* [L4VBUS_INHIBITOR_SUSPEND]  = */ "suspend",
395             /* [L4VBUS_INHIBITOR_SHUTDOWN] = */ "shutdown",
396             /* [L4VBUS_INHIBITOR_WAKEUP]   = */ "wakeup"
397           };
398
399           ios << (l4_umword_t)id << names[id];
400           return L4_EOK;
401         }
402     default:
403       return -L4_ENOSYS;
404     }
405 }
406
407 int
408 System_bus::dispatch(l4_umword_t obj, L4::Ipc::Iostream &ios)
409 {
410   l4_msgtag_t tag;
411   ios >> tag;
412
413   switch (tag.label())
414     {
415     case L4_PROTO_IRQ: // The ICU for event source
416     case L4Re::Protocol::Event:
417       return Vbus_event_source::dispatch(obj, ios);
418     case L4Re::Protocol::Inhibitor:
419       return inhibitor_dispatch(ios);
420
421     case L4Re::Protocol::Dataspace:
422       return request_iomem(ios);
423
424     case 0:
425         {
426           l4vbus_device_handle_t devid;
427           l4_uint32_t func;
428           ios >> devid >> func;
429           Device *dev = get_dev_by_id(devid);
430           if (!dev)
431             return -L4_ENODEV;
432           return dev->vdevice_dispatch(obj, func, ios);
433         }
434
435     default:
436       return -L4_EBADPROTO;
437     }
438
439 }
440
441 int
442 System_bus::dispatch(l4_umword_t, l4_uint32_t func, L4::Ipc::Iostream &ios)
443 {
444   switch (func)
445     {
446     case L4vbus_vbus_request_resource:
447       return request_resource(ios);
448     default:
449       return -L4_ENOSYS;
450     }
451 }
452
453 Vbus_event_source::Vbus_event_source()
454 {
455   using L4Re::Util::Auto_cap;
456   using L4Re::chkcap;
457   using L4Re::chksys;
458
459   Auto_cap<L4Re::Dataspace>::Cap buffer_ds
460     = chkcap(L4Re::Util::cap_alloc.alloc<L4Re::Dataspace>(),
461              "allocate event-buffer data-space capability");
462
463   chksys(L4Re::Env::env()->mem_alloc()->alloc(L4_PAGESIZE, buffer_ds.get()),
464          "allocate event-buffer data-space");
465
466   chksys(buffer.attach(buffer_ds.get(), L4Re::Env::env()->rm()),
467          "attach event-buffer data-space");
468
469   _ds = buffer_ds.release();
470 }
471
472 /**
473  * \brief Send device notification to client.
474  *
475  * \param dev device the notification is originating from.
476  * \param type event type to be sent, see event_enums.h for types.
477  * \param event event ID.
478  * \param value value for the event.
479  * \param syn trigger an IRQ for the event if true.
480  */
481 bool
482 System_bus::dev_notify(Device const *dev, unsigned type,
483                        unsigned event, unsigned value, bool syn)
484 {
485   Vbus_event_source::Event ev;
486   ev.time = l4_kip_clock(l4re_kip());
487   ev.payload.type = type;
488   ev.payload.code = event;
489   ev.payload.value = value;
490   ev.payload.stream_id = (l4vbus_device_handle_t)dev;
491
492   return Vbus_event_source::put(ev, syn);
493 }
494
495 int
496 System_bus::get_stream_info_for_id(l4_umword_t dev_id, L4Re::Event_stream_info *info)
497 {
498   Device *dev = get_dev_by_id(dev_id);
499   if (!dev)
500     return -L4_ENOSYS;
501
502   Io::Event_source_infos const *_info = dev->get_event_infos();
503   if (!_info)
504     return -L4_ENOSYS;
505
506   *info = _info->info;
507   return 0;
508 }
509
510 int
511 System_bus::get_stream_state_for_id(l4_umword_t dev_id, L4Re::Event_stream_state *state)
512 {
513   Device *dev = get_dev_by_id(dev_id);
514   if (!dev)
515     return -L4_ENOSYS;
516
517   Io::Event_source_infos const *_info = dev->get_event_infos();
518   if (!_info)
519     return -L4_ENOSYS;
520
521   *state = _info->state;
522   return 0;
523 }
524
525 L4::Cap<void>
526 Vbus_event_source::rcv_cap()
527 {
528   return ::rcv_cap;
529 }
530
531 void
532 System_bus::inhibitor_signal(l4_umword_t id)
533 {
534   // FIXME: need a subscribed flag!
535   // only signal if this inhibitor is actually acquired
536   if (!inhibitor_acquired(id))
537     return;
538
539   Vbus_event_source::Event ev;
540   ev.time = l4_kip_clock(l4re_kip());
541   ev.payload.type = L4RE_EV_PM;
542   ev.payload.code = id;
543   ev.payload.value = 1;
544   ev.payload.stream_id = (l4vbus_device_handle_t)0;
545
546   put(ev);
547 }
548
549 }