]> rtime.felk.cvut.cz Git - l4.git/blob - l4/pkg/io/io/server/src/virt/pci/vpci.cc
Update
[l4.git] / l4 / pkg / io / io / server / src / virt / pci / 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 #include <l4/vbus/vbus_pci-ops.h>
13
14 #include <cassert>
15 #include <cstring>
16 #include <cstdlib>
17 #include <l4/cxx/exceptions>
18 #include <l4/io/pciids.h>
19 #include <l4/sys/err.h>
20
21 #include "debug.h"
22 #include "pci.h"
23 #include "vpci.h"
24 #include "virt/vbus_factory.h"
25
26 namespace Vi {
27
28 // -----------------------------------------------------------------------
29 // Pci_virtual_dev
30 // -----------------------------------------------------------------------
31
32 Pci_virtual_dev::Pci_virtual_dev()
33 {
34   memset(&_h, 0, sizeof(_h));
35 }
36
37 int
38 Pci_virtual_dev::cfg_read(int reg, l4_uint32_t *v, Cfg_width order)
39 {
40   reg >>= order;
41   if ((unsigned)reg >= (_h_len >> order))
42     return -L4_ERANGE;
43
44 #define RC(x) *v = *((Hw::Pci::Cfg_type<x>::Type const *)_h + reg); break
45   *v = 0;
46   switch (order)
47     {
48     case Hw::Pci::Cfg_byte: RC(Hw::Pci::Cfg_byte);
49     case Hw::Pci::Cfg_short: RC(Hw::Pci::Cfg_short);
50     case Hw::Pci::Cfg_long: RC(Hw::Pci::Cfg_long);
51     }
52
53   return 0;
54 #undef RC
55 }
56
57 int
58 Pci_virtual_dev::cfg_write(int reg, l4_uint32_t v, Cfg_width order)
59 {
60   switch (reg & ~3)
61     {
62     case 0x4: // status is RO
63       v &= 0x0000ffff << (reg & (3 >> order));
64       break;
65
66     default:
67       break;
68     }
69
70   reg >>= order;
71   if ((unsigned)reg >= (_h_len >> order))
72     return -L4_ERANGE;
73
74 #define RC(x) *((Hw::Pci::Cfg_type<x>::Type *)_h + reg) = v; break
75   switch (order)
76     {
77     case Hw::Pci::Cfg_byte: RC(Hw::Pci::Cfg_byte);
78     case Hw::Pci::Cfg_short: RC(Hw::Pci::Cfg_short);
79     case Hw::Pci::Cfg_long: RC(Hw::Pci::Cfg_long);
80     }
81
82   return 0;
83 #undef RC
84 }
85
86 // -----------------------------------------------------------------------
87 // Pci_dev_feature
88 // -----------------------------------------------------------------------
89
90
91
92 int
93 Pci_dev_feature::dispatch(l4_umword_t, l4_uint32_t func, L4::Ipc::Iostream& ios)
94 {
95   if (l4vbus_subinterface(func) != L4VBUS_INTERFACE_PCIDEV)
96     return -L4_ENOSYS;
97
98   l4_uint32_t reg;
99   l4_uint32_t value = 0;
100   l4_uint32_t width;
101   Pci_dev::Irq_info info;
102   int res;
103
104   switch (func)
105     {
106     case L4vbus_pcidev_cfg_read:
107       ios >> reg >> width;
108       res = cfg_read(reg, &value, Hw::Pci::cfg_w_to_o(width));
109       if (res < 0)
110         return res;
111       ios << value;
112       return L4_EOK;
113     case L4vbus_pcidev_cfg_write:
114       ios >> reg >> value >> width;
115       return cfg_write(reg, value, Hw::Pci::cfg_w_to_o(width));
116     case L4vbus_pcidev_cfg_irq_enable:
117       res = irq_enable(&info);
118       if (res < 0)
119         return res;
120       ios << info.irq << info.trigger << info.polarity;
121       return L4_EOK;
122     default: return -L4_ENOSYS;
123     }
124 }
125
126
127
128
129 // -----------------------------------------------------------------------
130 // Pci_proxy_dev
131 // -----------------------------------------------------------------------
132
133 bool
134 Pci_proxy_dev::scan_pci_caps()
135 {
136   l4_uint8_t pci_cap;
137   _hwf->cfg_read(Hw::Pci::Config::Capability_ptr, &pci_cap);
138   bool is_pci_express = false;
139   while (pci_cap)
140     {
141       l4_uint16_t cap;
142       _hwf->cfg_read(pci_cap, &cap);
143       switch (cap & 0xff)
144         {
145         case Hw::Pci::Cap::Pcie:
146           is_pci_express = true;
147           /* fall through */
148         case Hw::Pci::Cap::Pcix:
149         case Hw::Pci::Cap::Msi:
150         case Hw::Pci::Cap::Msi_x:
151         case Hw::Pci::Cap::Vndr:
152         default:
153           add_pci_cap(new Pci_proxy_cap(_hwf, pci_cap));
154           break;
155         }
156
157       pci_cap = cap >> 8;
158     }
159   return is_pci_express;
160 }
161
162 void
163 Pci_proxy_dev::scan_pcie_caps()
164 {
165   l4_uint16_t offset = 0x100;
166   l4_uint32_t c;
167   for (;;)
168     {
169       _hwf->cfg_read(offset, &c);
170       if (offset == 0x100 && ((c & 0xffff) == 0xffff))
171         return;
172
173       add_pcie_cap(new Pcie_proxy_cap(_hwf, c, offset, offset));
174
175       offset = c >> 20;
176       if (!offset)
177         break;
178     }
179
180   assert (!_pcie_caps || _pcie_caps->offset() == 0x100);
181 #if 0
182   if (_pcie_caps && _pcie_caps->offset() != 0x100)
183     _pcie_caps->set_offset(0x100);
184 #endif
185 }
186
187 Pci_proxy_dev::Pci_proxy_dev(Hw::Pci::If *hwf)
188 : _hwf(hwf), _rom(0)
189 {
190   assert (hwf);
191   for (int i = 0; i < 6; ++i)
192     {
193       Resource *r = _hwf->bar(i);
194
195       if (!r)
196         {
197           _vbars[i] = 0;
198           continue;
199         }
200
201       if (_hwf->is_64bit_high_bar(i))
202         {
203           _vbars[i] = l4_uint64_t(r->start()) >> 32;
204         }
205       else
206         {
207           _vbars[i] = r->start();
208           if (r->type() == Resource::Io_res)
209             _vbars[i] |= 1;
210
211           if (r->is_64bit())
212             _vbars[i] |= 4;
213
214           if (r->prefetchable())
215             _vbars[i] |= 8;
216
217         }
218
219       //printf("  bar: %d = %08x\n", i, _vbars[i]);
220     }
221
222   if (_hwf->rom())
223     _rom = _hwf->rom()->start();
224
225   if (scan_pci_caps())
226     scan_pcie_caps();
227 }
228
229 int
230 Pci_proxy_dev::irq_enable(Irq_info *irq)
231 {
232   for (Resource_list::const_iterator i = host()->resources()->begin();
233        i != host()->resources()->end(); ++i)
234     {
235       Resource *res = *i;
236
237       if (!res->disabled() && res->type() == Resource::Irq_res)
238         {
239           irq->irq = res->start();
240           irq->trigger = !res->irq_is_level_triggered();
241           irq->polarity = res->irq_is_low_polarity();
242           d_printf(DBG_DEBUG, "Enable IRQ: irq=%d trg=%x pol=%x\n",
243                    irq->irq, irq->trigger, irq->polarity);
244           if (dlevel(DBG_DEBUG2))
245             dump();
246           return 0;
247         }
248     }
249   return -L4_EINVAL;
250 }
251
252
253
254 l4_uint32_t
255 Pci_proxy_dev::read_bar(int bar)
256 {
257   // d_printf(DBG_ALL, "   read bar[%x]: %08x\n", bar, _vbars[bar]);
258   return _vbars[bar];
259 }
260
261 void
262 Pci_proxy_dev::write_bar(int bar, l4_uint32_t v)
263 {
264   Hw::Pci::If *p = _hwf;
265
266   Resource *r = p->bar(bar);
267   if (!r)
268     return;
269
270   // printf("  write bar[%x]: %llx-%llx...\n", bar, r->abs_start(), r->abs_end());
271   l4_uint64_t size_mask = r->alignment();
272
273   if (r->type() == Resource::Io_res)
274     size_mask |= 0xffff0000;
275
276   if (p->is_64bit_high_bar(bar))
277     size_mask >>= 32;
278
279   _vbars[bar] = (_vbars[bar] & size_mask) | (v & ~size_mask);
280
281   // printf("    bar=%lx\n", _vbars[bar]);
282 }
283
284 void
285 Pci_proxy_dev::write_rom(l4_uint32_t v)
286 {
287   Hw::Pci::If *p = _hwf;
288
289   // printf("write rom bar %x %p\n", v, _dev->rom());
290   Resource *r = p->rom();
291   if (!r)
292     return;
293
294   l4_uint64_t size_mask = r->alignment();
295
296   _rom = (_rom & size_mask) | (v & (~size_mask | 1));
297
298   p->cfg_write(0x30, (r->start() & ~1U) | (v & 1), Hw::Pci::Cfg_long);
299 }
300
301 Pci_capability *
302 Pci_proxy_dev::find_pci_cap(unsigned offset) const
303 {
304   if (!_pci_caps || offset < 0x3c)
305     return 0;
306
307   for (Pci_capability *c = _pci_caps; c; c = c->next())
308     if (c->is_inside(offset))
309       return c;
310
311   return 0;
312 }
313
314 void
315 Pci_proxy_dev::add_pci_cap(Pci_capability *c)
316 {
317   Pci_capability **i;
318   for (i = &_pci_caps; *i; i = &(*i)->next())
319     if ((*i)->offset() > c->offset())
320       break;
321
322   c->next() = *i;
323   *i = c;
324 }
325
326 Pcie_capability *
327 Pci_proxy_dev::find_pcie_cap(unsigned offset) const
328 {
329   if (!_pcie_caps || offset < 0x100)
330     return 0;
331
332   for (Pcie_capability *c = _pcie_caps; c; c = c->next())
333     if (c->is_inside(offset))
334       return c;
335
336   return 0;
337 }
338
339 void
340 Pci_proxy_dev::add_pcie_cap(Pcie_capability *c)
341 {
342   Pcie_capability **i;
343   for (i = &_pcie_caps; *i; i = &(*i)->next())
344     if ((*i)->offset() > c->offset())
345       break;
346
347   c->next() = *i;
348   *i = c;
349 }
350
351 int
352 Pci_proxy_dev::cfg_read(int reg, l4_uint32_t *v, Cfg_width order)
353 {
354   Hw::Pci::If *p = _hwf;
355
356   l4_uint32_t buf;
357
358   l4_uint32_t const *r = &buf;
359   reg &= ~0U << order;
360   int dw_reg = reg & ~3;
361
362   if (Pci_capability *cap = find_pci_cap(dw_reg))
363     return cap->cfg_read(reg, v, order);
364
365   if (Pcie_capability *cap = find_pcie_cap(dw_reg))
366     return cap->cfg_read(reg, v, order);
367
368   switch (dw_reg)
369     {
370     case 0x00: buf = p->vendor_device_ids(); break;
371     case 0x08: buf = p->class_rev(); break;
372     case 0x04: buf = p->checked_cmd_read(); break;
373     /* simulate multi function on hdr type */
374     case 0x0c: p->cfg_read(dw_reg, &buf); buf |= 0x00800000; break;
375     case 0x10: /* bars 0 to 5 */
376     case 0x14:
377     case 0x18:
378     case 0x1c:
379     case 0x20:
380     case 0x24: buf = read_bar((dw_reg - 0x10) / 4); break;
381     case 0x2c: buf = p->subsys_vendor_ids(); break;
382     case 0x30: buf = read_rom(); break;
383     case 0x34: /* CAPS */
384                if (_pci_caps)
385                  buf = _pci_caps->offset();
386                else
387                  buf = 0;
388                break;
389     case 0x38: buf = 0; break; /* PCI 3.0 reserved */
390
391     case 0x100: /* empty PCIe cap */
392                buf = 0xffff; break;
393     default:   if (0) printf("pass unknown PCI cfg read for device %s: %x\n",
394                              host()->name(), reg);
395                /* fall through */
396     case 0x28:
397     case 0x3c:
398                /* pass trough the rest ... */
399                p->cfg_read(dw_reg, &buf);
400                break;
401     }
402
403   unsigned mask = ~0U >> (32 - (8U << order));
404   *v = (*r >> ((reg & 3) *8)) & mask;
405   return L4_EOK;
406 }
407
408
409 void
410 Pci_proxy_dev::_do_cmd_write(unsigned mask,unsigned value)
411 {
412   _hwf->checked_cmd_write(mask, value);
413 }
414
415 int
416 Pci_proxy_dev::_do_status_cmd_write(l4_uint32_t mask, l4_uint32_t value)
417 {
418   if (mask & 0xffff)
419     _do_cmd_write(mask & 0xffff, value & 0xffff);
420
421   // status register has 'write 1 to clear' semantics so we can always write
422   // 16bit with the bits masked that we do not want to affect.
423   if (mask & value & 0xffff0000)
424     _hwf->cfg_write(Pci::Config::Status, (value & mask) >> 16, Hw::Pci::Cfg_short);
425
426   return 0;
427 }
428
429 int
430 Pci_proxy_dev::_do_rom_bar_write(l4_uint32_t mask, l4_uint32_t value)
431 {
432   l4_uint32_t b = read_rom();
433
434   if ((value & mask & 1) && !(b & mask & 1) && !_hwf->enable_rom())
435     return 0;  // failed to enable
436
437   b &= ~mask;
438   b |= value & mask;
439   write_rom(b);
440   return 0;
441 }
442
443
444 int
445 Pci_proxy_dev::cfg_write(int reg, l4_uint32_t v, Cfg_width order)
446 {
447   Hw::Pci::If *p = _hwf;
448
449   reg &= ~0U << order;
450   l4_uint32_t const offset_32 = reg & 3;
451   l4_uint32_t const mask_32 = (~0U >> (32 - (8U << order))) << (offset_32 * 8);
452   l4_uint32_t const value_32 = v << (offset_32 * 8);
453
454   if (Pci_capability *cap = find_pci_cap(reg & ~3))
455     return cap->cfg_write(reg, v, order);
456
457   if (Pcie_capability *cap = find_pcie_cap(reg & ~3))
458     return cap->cfg_write(reg, v, order);
459
460   switch (reg & ~3)
461     {
462     case 0x00: return 0;
463     case 0x08: return 0;
464     case 0x04: return _do_status_cmd_write(mask_32, value_32);
465     /* simulate multi function on hdr type */
466     case 0x10: /* bars 0 to 5 */
467     case 0x14:
468     case 0x18:
469     case 0x1c:
470     case 0x20:
471     case 0x24:
472       {
473         l4_uint32_t b = read_bar(reg / 4 - 4);
474         b &= ~mask_32;
475         b |= value_32 & mask_32;
476         write_bar(reg / 4 - 4, b);
477         return 0;
478       }
479     case Hw::Pci::Config::Subsys_vendor:  return 0;
480     case Hw::Pci::Config::Rom_address:    return _do_rom_bar_write(mask_32, value_32);
481     case Hw::Pci::Config::Capability_ptr: return 0;
482     case 0x38: return 0;
483     /* pass trough the rest ... */
484     default:   if (0) printf("pass unknown PCI cfg write for device %s: %x\n",
485                              host()->name(), reg);
486                /* fall through */
487     case 0x0c:
488     case 0x28:
489     case 0x3c: return p->cfg_write(reg, v, order);
490     }
491 }
492
493 void
494 Pci_proxy_dev::dump() const
495 {
496   Hw::Pci::If *p = _hwf;
497
498   printf("       %04x:%02x:%02x.%x:\n",
499          0, p->bus_nr(), _hwf->device_nr(), _hwf->function_nr());
500 #if 0
501   char buf[130];
502   libpciids_name_device(buf, sizeof(buf), _dev->vendor(), _dev->device());
503   printf("              %s\n", buf);
504 #endif
505 }
506
507 Pci_dev::Msi_src *
508 Pci_proxy_dev::msi_src() const
509 {
510   assert (hwf());
511   return hwf()->get_msi_src();
512 }
513
514 // -----------------------------------------------------------------------
515 // Virtual PCI dummy device
516 // -----------------------------------------------------------------------
517
518 class Pci_dummy : public Pci_virtual_dev, public Device
519 {
520 private:
521   unsigned char _cfg_space[4*4];
522
523 public:
524   int irq_enable(Irq_info *irq)
525   {
526     irq->irq = -1;
527     return -1;
528   }
529
530   Pci_dummy()
531   {
532     add_feature(this);
533     _h = &_cfg_space[0];
534     _h_len = sizeof(_cfg_space);
535     cfg_hdr()->hdr_type = 0x80;
536     cfg_hdr()->vendor_device = 0x02000400;
537     cfg_hdr()->status = 0;
538     cfg_hdr()->class_rev = 0x36440000;
539     cfg_hdr()->cmd = 0x0;
540   }
541
542   bool match_hw_feature(const Hw::Dev_feature*) const { return false; }
543   void set_host(Device *d) { _host = d; }
544   Device *host() const { return _host; }
545
546 private:
547   Device *_host;
548 };
549
550
551 // ----------------------------------------------------------------------
552 // Basic virtual PCI bridge functionality
553 // ----------------------------------------------------------------------
554
555 Pci_bridge::Dev::Dev()
556 {
557   memset(_fns, 0, sizeof(_fns));
558 }
559
560 void
561 Pci_bridge::Dev::add_fn(Pci_dev *f)
562 {
563   for (unsigned i = 0; i < sizeof(_fns)/sizeof(_fns[0]); ++i)
564     {
565       if (!_fns[i])
566         {
567           _fns[i] = f;
568           return;
569         }
570     }
571 }
572
573 void
574 Pci_bridge::Dev::sort_fns()
575 {
576   // disabled sorting because the relation of two functions is questionable
577 #if 0
578   unsigned n;
579   for (n = 0; n < sizeof(_fns)/sizeof(_fns[0]) && _fns[n]; ++n)
580     ;
581
582   if (n < 2)
583     return;
584
585   bool exchg = false;
586
587   do
588     {
589       exchg = false;
590       for (unsigned i = 0; i < n - 1; ++i)
591         {
592           if (_fns[i]->dev()->function_nr() > _fns[i+1]->dev()->function_nr())
593             {
594               Pci_dev *t = _fns[i];
595               _fns[i] = _fns[i+1];
596               _fns[i+1] = t;
597               exchg = true;
598             }
599         }
600       n -= 1;
601     }
602   while (exchg && n >= 1);
603 #endif
604 }
605
606 void
607 Pci_bridge::Bus::add_fn(Pci_dev *pd, int slot)
608 {
609   if (slot >= 0)
610     {
611       _devs[slot].add_fn(pd);
612       _devs[slot].sort_fns();
613       return;
614     }
615
616   for (unsigned d = 0; d < Devs && !_devs[d].empty(); ++d)
617     if (_devs[d].cmp(pd))
618       {
619         _devs[d].add_fn(pd);
620         _devs[d].sort_fns();
621         return;
622       }
623
624   for (unsigned d = 0; d < Devs; ++d)
625     if (_devs[d].empty())
626       {
627         _devs[d].add_fn(pd);
628         return;
629       }
630 }
631
632 void
633 Pci_bridge::add_child(Device *d)
634 {
635   Pci_dev *vp = d->find_feature<Pci_dev>();
636
637   // hm, we do currently not add non PCI devices.
638   if (!vp)
639     return;
640
641   _bus.add_fn(vp);
642   Device::add_child(d);
643 }
644
645
646 void
647 Pci_bridge::add_child_fixed(Device *d, Pci_dev *vp, unsigned dn, unsigned fn)
648 {
649   _bus.dev(dn)->fn(fn, vp);
650   Device::add_child(d);
651 }
652
653
654 Pci_bridge *
655 Pci_bridge::find_bridge(unsigned bus)
656 {
657   // printf("PCI[%p]: look for bridge for bus %x %02x %02x\n", this, bus, _subordinate, _secondary);
658   if (bus == _secondary)
659     return this;
660
661   if (bus < _secondary || bus > _subordinate)
662     return 0;
663
664   for (unsigned d = 0; d < Bus::Devs; ++d)
665     for (unsigned f = 0; f < Dev::Fns; ++f)
666       {
667         Pci_dev *p = _bus.dev(d)->fn(f);
668         if (!p)
669           break;
670
671         Pci_bridge *b = dynamic_cast<Pci_bridge*>(p);
672         if (b && (b = b->find_bridge(bus)))
673           return b;
674       }
675
676   return 0;
677 }
678
679
680 Pci_dev *
681 Pci_bridge::child_dev(unsigned bus, unsigned char dev, unsigned char fn)
682 {
683   Pci_bridge *b = find_bridge(bus);
684   if (!b)
685     return 0;
686
687   if (dev >= Bus::Devs || fn >= Dev::Fns)
688     return 0;
689
690   return b->_bus.dev(dev)->fn(fn);
691 }
692
693 void
694 Pci_bridge::setup_bus()
695 {
696   for (unsigned d = 0; d < Bus::Devs; ++d)
697     for (unsigned f = 0; f < Dev::Fns; ++f)
698       {
699         Pci_dev *p = _bus.dev(d)->fn(f);
700         if (!p)
701           break;
702
703         Pci_bridge *b = dynamic_cast<Pci_bridge*>(p);
704         if (b)
705           {
706             b->_primary = _secondary;
707             if (b->_secondary <= _secondary)
708               {
709                 b->_secondary = ++_subordinate;
710                 b->_subordinate = b->_secondary;
711               }
712
713             b->setup_bus();
714
715             if (_subordinate < b->_subordinate)
716               _subordinate = b->_subordinate;
717           }
718       }
719 }
720
721 void
722 Pci_bridge::finalize_setup()
723 {
724   for (unsigned dn = 0; dn < Bus::Devs; ++dn)
725     {
726       if (!_bus.dev(dn)->empty())
727         continue;
728
729       for (unsigned fn = 0; fn < Dev::Fns; ++fn)
730         if (_bus.dev(dn)->fn(fn))
731           {
732             Pci_dummy *dummy = new Pci_dummy();
733             _bus.dev(dn)->fn(0, dummy);
734             Device::add_child(dummy);
735             break;
736           }
737     }
738 #if 0
739   for (VDevice *c = dynamic_cast<VDevice*>(children()); c; c = c->next())
740     c->finalize_setup();
741 #endif
742 }
743
744 namespace {
745   static Feature_factory_t<Pci_proxy_dev, Hw::Pci::Dev> __pci_f_factory1;
746   static Dev_factory_t<Pci_dummy> __pci_dummy_factory("PCI_dummy_device");
747 }
748
749 }
750