]> rtime.felk.cvut.cz Git - l4.git/blob - l4/pkg/io/server/src/vpci.cc
update
[l4.git] / l4 / pkg / io / server / src / vpci.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
11 #include <l4/vbus/vdevice-ops.h>
12
13 #include <cassert>
14 #include <cstring>
15 #include <cstdlib>
16 #include <l4/cxx/exceptions>
17 #include <l4/io/pciids.h>
18 #include <l4/sys/err.h>
19
20 #include "debug.h"
21 #include "pci.h"
22 #include "vpci.h"
23 #include "vbus_factory.h"
24
25 namespace Vi {
26
27 // -----------------------------------------------------------------------
28 // Pci_virtual_dev
29 // -----------------------------------------------------------------------
30
31 Pci_virtual_dev::Pci_virtual_dev()
32 {
33   memset(&_h, 0, sizeof(_h));
34 }
35
36 int
37 Pci_virtual_dev::cfg_read(int reg, l4_uint32_t *v, Cfg_width order)
38 {
39   reg >>= order;
40   if ((unsigned)reg >= (_h_len >> order))
41     return -L4_ERANGE;
42
43 #define RC(x) *v = *((Hw::Pci::Cfg_type<x>::Type const *)_h + reg); break
44   *v = 0;
45   switch (order)
46     {
47     case Hw::Pci::Cfg_byte: RC(Hw::Pci::Cfg_byte);
48     case Hw::Pci::Cfg_short: RC(Hw::Pci::Cfg_short);
49     case Hw::Pci::Cfg_long: RC(Hw::Pci::Cfg_long);
50     }
51
52   return 0;
53 #undef RC
54 }
55
56 int
57 Pci_virtual_dev::cfg_write(int reg, l4_uint32_t v, Cfg_width order)
58 {
59   switch (reg & ~3)
60     {
61     case 0x4: // status is RO
62       v &= 0x0000ffff << (reg & (3 >> order));
63       break;
64
65     default:
66       break;
67     }
68
69   reg >>= order;
70   if ((unsigned)reg >= (_h_len >> order))
71     return -L4_ERANGE;
72
73 #define RC(x) *((Hw::Pci::Cfg_type<x>::Type *)_h + reg) = v; break
74   switch (order)
75     {
76     case Hw::Pci::Cfg_byte: RC(Hw::Pci::Cfg_byte);
77     case Hw::Pci::Cfg_short: RC(Hw::Pci::Cfg_short);
78     case Hw::Pci::Cfg_long: RC(Hw::Pci::Cfg_long);
79     }
80
81   return 0;
82 #undef RC
83 }
84
85
86
87 // -----------------------------------------------------------------------
88 // Pci_proxy_dev
89 // -----------------------------------------------------------------------
90
91 Pci_proxy_dev::Pci_proxy_dev(Hw::Pci::If *hwf)
92 : _hwf(hwf), _rom(0)
93 {
94   assert (hwf);
95   for (int i = 0; i < 6; ++i)
96     {
97       Adr_resource *r = _hwf->bar(i);
98
99       if (!r)
100         {
101           _vbars[i] = 0;
102           continue;
103         }
104
105       if (_hwf->is_64bit_high_bar(i))
106         {
107           _vbars[i] = l4_uint64_t(r->start()) >> 32;
108         }
109       else
110         {
111           _vbars[i] = r->start();
112           if (r->type() == Adr_resource::Io_res)
113             _vbars[i] |= 1;
114
115           if (r->is_64bit())
116             _vbars[i] |= 4;
117
118           if (r->prefetchable())
119             _vbars[i] |= 8;
120
121         }
122
123       //printf("  bar: %d = %08x\n", i, _vbars[i]);
124     }
125
126   if (_hwf->rom())
127     _rom = _hwf->rom()->start();
128 }
129
130 int
131 Pci_proxy_dev::irq_enable(Irq_info *irq)
132 {
133   for (Resource_list::iterator i = host()->resources()->begin();
134       i != host()->resources()->end(); ++i)
135     {
136       Adr_resource *res = dynamic_cast<Adr_resource*>(*i);
137       if (!res)
138         continue;
139
140       if (res->type() == Adr_resource::Irq_res)
141         {
142           irq->irq = res->start();
143           irq->trigger = !(res->flags() & Resource::Irq_info_base);
144           irq->polarity = !!(res->flags() & (Resource::Irq_info_base * 2));
145           d_printf(DBG_DEBUG, "Enable IRQ: %d %x %x\n", irq->irq, irq->trigger, irq->polarity);
146           if (dlevel(DBG_DEBUG2))
147             dump();
148           return 0;
149         }
150     }
151   return -L4_EINVAL;
152 }
153
154
155
156 l4_uint32_t
157 Pci_proxy_dev::read_bar(int bar)
158 {
159   // d_printf(DBG_ALL, "   read bar[%x]: %08x\n", bar, _vbars[bar]);
160   return _vbars[bar];
161 }
162
163 void
164 Pci_proxy_dev::write_bar(int bar, l4_uint32_t v)
165 {
166   Hw::Pci::If *p = _hwf;
167
168   Adr_resource *r = p->bar(bar);
169   if (!r)
170     return;
171
172   // printf("  write bar[%x]: %llx-%llx...\n", bar, r->abs_start(), r->abs_end());
173   l4_uint64_t size_mask = r->alignment();
174
175   if (r->type() == Adr_resource::Io_res)
176     size_mask |= 0xffff0000;
177
178   if (p->is_64bit_high_bar(bar))
179     size_mask >>= 32;
180
181   _vbars[bar] = (_vbars[bar] & size_mask) | (v & ~size_mask);
182
183   // printf("    bar=%lx\n", _vbars[bar]);
184 }
185
186 void
187 Pci_proxy_dev::write_rom(l4_uint32_t v)
188 {
189   Hw::Pci::If *p = _hwf;
190
191   // printf("write rom bar %x %p\n", v, _dev->rom());
192   Adr_resource *r = p->rom();
193   if (!r)
194     return;
195
196   l4_uint64_t size_mask = r->alignment();
197
198   _rom = (_rom & size_mask) | (v & (~size_mask | 1));
199
200   p->cfg_write(0x30, (r->start() & ~1U) | (v & 1), Hw::Pci::Cfg_long);
201 }
202
203 int
204 Pci_proxy_dev::cfg_read(int reg, l4_uint32_t *v, Cfg_width order)
205 {
206   Hw::Pci::If *p = _hwf;
207
208   l4_uint32_t buf;
209   l4_uint32_t const *r = &buf;
210   reg &= ~0U << order;
211   int dw = reg >> 2;
212   switch (dw)
213     {
214     case 0: case 2: case 11:
215       r = p->cfg_word(dw); break;
216     case 1: return p->cfg_read(reg, v, order);
217     case 3: //buf = l4_uint32_t(_dev->hdr_type) << 16; break;
218             p->cfg_read(dw * 4, &buf, Hw::Pci::Cfg_long);
219             buf |= 0x00800000;
220             break;
221     case 4: case 5: case 6: case 7: case 8: case 9:
222       buf = read_bar(dw-4); break;
223     case 12: buf = read_rom(); break;
224     default: return p->cfg_read(reg, v, order); //buf = 0; break;
225     }
226
227   unsigned mask = ~0U >> (32 - (1U << (order + 3)));
228   *v = (*r >> ((reg & 3) *8)) & mask;
229   return L4_EOK;
230 }
231
232 int
233 Pci_proxy_dev::cfg_write(int reg, l4_uint32_t v, Cfg_width order)
234 {
235   Hw::Pci::If *p = _hwf;
236
237   reg &= ~0U << order;
238   int dw = reg >> 2;
239   switch (dw)
240     {
241     case 4: case 5: case 6: case 7: case 8: case 9: case 12: break;
242     default: return p->cfg_write(reg, v, order);
243     }
244
245   l4_uint32_t old;
246   if (order < 2)
247     cfg_read(reg, &old, Hw::Pci::Cfg_long);
248
249   l4_uint32_t mask = ~0U >> (32 - (1U << (order + 3)));
250   l4_uint32_t sh = (reg & 3) * 8;
251   old &= ~(mask << sh);
252   old |= (v & mask) << sh;
253
254   switch (dw)
255     {
256     case 4: case 5: case 6: case 7: case 8: case 9:
257           write_bar(dw-4, old); break;
258     case 12: write_rom(old); break;
259     default: break;
260     }
261   return L4_EOK;
262 }
263
264 void
265 Pci_proxy_dev::dump() const
266 {
267   Hw::Pci::If *p = _hwf;
268
269   printf("       %04x:%02x:%02x.%x:\n",
270          0, p->bus_nr(), _hwf->host()->adr() >> 16, _hwf->host()->adr() & 0xffff);
271 #if 0
272   char buf[130];
273   libpciids_name_device(buf, sizeof(buf), _dev->vendor(), _dev->device());
274   printf("              %s\n", buf);
275 #endif
276 }
277
278
279 // -----------------------------------------------------------------------
280 // Virtual PCI dummy device
281 // -----------------------------------------------------------------------
282
283 class Pci_dummy : public Pci_virtual_dev, public Device
284 {
285 private:
286   unsigned char _cfg_space[4*4];
287
288 public:
289   int irq_enable(Irq_info *irq)
290   {
291     irq->irq = -1;
292     return -1;
293   }
294
295   Pci_dummy()
296   {
297     add_feature(this);
298     _h = &_cfg_space[0];
299     _h_len = sizeof(_cfg_space);
300     cfg_hdr()->hdr_type = 0x80;
301     cfg_hdr()->vendor_device = 0x02000400;
302     cfg_hdr()->status = 0;
303     cfg_hdr()->class_rev = 0x36440000;
304     cfg_hdr()->cmd = 0x0;
305   }
306
307   bool match_hw_feature(const Hw::Dev_feature*) const { return false; }
308   int dispatch(l4_umword_t, l4_uint32_t, L4::Ipc_iostream&)
309   { return -L4_ENOSYS; }
310   void set_host(Device *d) { _host = d; }
311   Device *host() const { return _host; }
312
313 private:
314   Device *_host;
315 };
316
317
318 // ----------------------------------------------------------------------
319 // Basic virtual PCI bridge functionality
320 // ----------------------------------------------------------------------
321
322 Pci_bridge::Dev::Dev()
323 {
324   memset(_fns, 0, sizeof(_fns));
325 }
326
327 void
328 Pci_bridge::Dev::add_fn(Pci_dev *f)
329 {
330   for (unsigned i = 0; i < sizeof(_fns)/sizeof(_fns[0]); ++i)
331     {
332       if (!_fns[i])
333         {
334           _fns[i] = f;
335           return;
336         }
337     }
338 }
339
340 void
341 Pci_bridge::Dev::sort_fns()
342 {
343   // disabled sorting because the relation of two functions is questionable
344 #if 0
345   unsigned n;
346   for (n = 0; n < sizeof(_fns)/sizeof(_fns[0]) && _fns[n]; ++n)
347     ;
348
349   if (n < 2)
350     return;
351
352   bool exchg = false;
353
354   do
355     {
356       exchg = false;
357       for (unsigned i = 0; i < n - 1; ++i)
358         {
359           if (_fns[i]->dev()->function_nr() > _fns[i+1]->dev()->function_nr())
360             {
361               Pci_dev *t = _fns[i];
362               _fns[i] = _fns[i+1];
363               _fns[i+1] = t;
364               exchg = true;
365             }
366         }
367       n -= 1;
368     }
369   while (exchg && n >= 1);
370 #endif
371 }
372
373 void
374 Pci_bridge::Bus::add_fn(Pci_dev *pd, int slot)
375 {
376   if (slot >= 0)
377     {
378       _devs[slot].add_fn(pd);
379       _devs[slot].sort_fns();
380       return;
381     }
382
383   for (unsigned d = 0; d < Devs && !_devs[d].empty(); ++d)
384     if (_devs[d].cmp(pd))
385       {
386         _devs[d].add_fn(pd);
387         _devs[d].sort_fns();
388         return;
389       }
390
391   for (unsigned d = 0; d < Devs; ++d)
392     if (_devs[d].empty())
393       {
394         _devs[d].add_fn(pd);
395         return;
396       }
397 }
398
399 void
400 Pci_bridge::add_child(Device *d)
401 {
402   Pci_dev *vp = d->find_feature<Pci_dev>();
403
404   // hm, we do currently not add non PCI devices.
405   if (!vp)
406     return;
407
408   _bus.add_fn(vp);
409   Device::add_child(d);
410 }
411
412
413 void
414 Pci_bridge::add_child_fixed(Device *d, Pci_dev *vp, unsigned dn, unsigned fn)
415 {
416   _bus.dev(dn)->fn(fn, vp);
417   Device::add_child(d);
418 }
419
420
421 Pci_bridge *
422 Pci_bridge::find_bridge(unsigned bus)
423 {
424   // printf("PCI[%p]: look for bridge for bus %x %02x %02x\n", this, bus, _subordinate, _secondary);
425   if (bus == _secondary)
426     return this;
427
428   if (bus < _secondary || bus > _subordinate)
429     return 0;
430
431   for (unsigned d = 0; d < Bus::Devs; ++d)
432     for (unsigned f = 0; f < Dev::Fns; ++f)
433       {
434         Pci_dev *p = _bus.dev(d)->fn(f);
435         if (!p)
436           break;
437
438         Pci_bridge *b = dynamic_cast<Pci_bridge*>(p);
439         if (b && (b = b->find_bridge(bus)))
440           return b;
441       }
442
443   return 0;
444 }
445
446
447 bool
448 Pci_bridge::child_dev(unsigned bus, unsigned char dev, unsigned char fn,
449                        Pci_dev **rd)
450 {
451   Pci_bridge *b = find_bridge(bus);
452   if (!b)
453     {
454       *rd = 0;
455       return true;
456     }
457
458   if (dev >= Bus::Devs || fn >= Dev::Fns)
459     {
460       *rd = 0;
461       return true;
462     }
463
464   *rd = b->_bus.dev(dev)->fn(fn);
465   return true;
466 }
467
468 void
469 Pci_bridge::setup_bus()
470 {
471   for (unsigned d = 0; d < Bus::Devs; ++d)
472     for (unsigned f = 0; f < Dev::Fns; ++f)
473       {
474         Pci_dev *p = _bus.dev(d)->fn(f);
475         if (!p)
476           break;
477
478         Pci_bridge *b = dynamic_cast<Pci_bridge*>(p);
479         if (b)
480           {
481             b->_primary = _secondary;
482             if (b->_secondary <= _secondary)
483               {
484                 b->_secondary = ++_subordinate;
485                 b->_subordinate = b->_secondary;
486               }
487
488             b->setup_bus();
489
490             if (_subordinate < b->_subordinate)
491               _subordinate = b->_subordinate;
492           }
493       }
494 }
495
496 void
497 Pci_bridge::finalize_setup()
498 {
499   for (unsigned dn = 0; dn < Bus::Devs; ++dn)
500     {
501       if (!_bus.dev(dn)->empty())
502         continue;
503
504       for (unsigned fn = 0; fn < Dev::Fns; ++fn)
505         if (_bus.dev(dn)->fn(fn))
506           {
507             Pci_dummy *dummy = new Pci_dummy();
508             _bus.dev(dn)->fn(0, dummy);
509             Device::add_child(dummy);
510             break;
511           }
512     }
513 #if 0
514   for (VDevice *c = dynamic_cast<VDevice*>(children()); c; c = c->next())
515     c->finalize_setup();
516 #endif
517 }
518
519 namespace {
520   static Feature_factory_t<Pci_proxy_dev, ::Pci_dev> __pci_f_factory1;
521   static Dev_factory_t<Pci_dummy> __pci_dummy_factory("PCI_dummy_device");
522 }
523
524 }
525