]> rtime.felk.cvut.cz Git - l4.git/blob - l4/pkg/io/server/src/virt/pci/vpci.cc
update
[l4.git] / l4 / pkg / 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
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 "virt/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       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() == 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::const_iterator i = host()->resources()->begin();
134        i != host()->resources()->end(); ++i)
135     {
136       Resource *res = *i;
137
138       if (!res->disabled() && res->type() == Resource::Irq_res)
139         {
140           irq->irq = res->start();
141           irq->trigger = !res->irq_is_level_triggered();
142           irq->polarity = res->irq_is_low_polarity();
143           d_printf(DBG_DEBUG, "Enable IRQ: %d %x %x\n", irq->irq, irq->trigger, irq->polarity);
144           if (dlevel(DBG_DEBUG2))
145             dump();
146           return 0;
147         }
148     }
149   return -L4_EINVAL;
150 }
151
152
153
154 l4_uint32_t
155 Pci_proxy_dev::read_bar(int bar)
156 {
157   // d_printf(DBG_ALL, "   read bar[%x]: %08x\n", bar, _vbars[bar]);
158   return _vbars[bar];
159 }
160
161 void
162 Pci_proxy_dev::write_bar(int bar, l4_uint32_t v)
163 {
164   Hw::Pci::If *p = _hwf;
165
166   Resource *r = p->bar(bar);
167   if (!r)
168     return;
169
170   // printf("  write bar[%x]: %llx-%llx...\n", bar, r->abs_start(), r->abs_end());
171   l4_uint64_t size_mask = r->alignment();
172
173   if (r->type() == Resource::Io_res)
174     size_mask |= 0xffff0000;
175
176   if (p->is_64bit_high_bar(bar))
177     size_mask >>= 32;
178
179   _vbars[bar] = (_vbars[bar] & size_mask) | (v & ~size_mask);
180
181   // printf("    bar=%lx\n", _vbars[bar]);
182 }
183
184 void
185 Pci_proxy_dev::write_rom(l4_uint32_t v)
186 {
187   Hw::Pci::If *p = _hwf;
188
189   // printf("write rom bar %x %p\n", v, _dev->rom());
190   Resource *r = p->rom();
191   if (!r)
192     return;
193
194   l4_uint64_t size_mask = r->alignment();
195
196   _rom = (_rom & size_mask) | (v & (~size_mask | 1));
197
198   p->cfg_write(0x30, (r->start() & ~1U) | (v & 1), Hw::Pci::Cfg_long);
199 }
200
201 int
202 Pci_proxy_dev::cfg_read(int reg, l4_uint32_t *v, Cfg_width order)
203 {
204   Hw::Pci::If *p = _hwf;
205
206   l4_uint32_t buf;
207
208   l4_uint32_t const *r = &buf;
209   reg &= ~0U << order;
210   int dw_reg = reg & ~3;
211   switch (dw_reg)
212     {
213     case 0x00: buf = p->vendor_device_ids(); break;
214     case 0x08: buf = p->class_rev(); break;
215     case 0x04: p->cfg_read(dw_reg, &buf); break;
216     /* simulate multi function on hdr type */
217     case 0x0c: p->cfg_read(dw_reg, &buf); buf |= 0x00800000; break;
218     case 0x10: /* bars 0 to 5 */
219     case 0x14:
220     case 0x18:
221     case 0x1c:
222     case 0x20:
223     case 0x24: buf = read_bar((dw_reg - 0x10) / 4); break;
224     case 0x2c: buf = p->subsys_vendor_ids(); break;
225     case 0x30: buf = read_rom(); break;
226     case 0x34: /* CAPS */
227     case 0x38: buf = 0; break; /* PCI 3.0 reserved */
228     /* latency and IRQs */
229     case 0x3c: p->cfg_read(dw_reg, &buf); break;
230     /* pass trough the rest ... */
231     default:   p->cfg_read(dw_reg, &buf); break;
232     }
233
234   unsigned mask = ~0U >> (32 - (8U << order));
235   *v = (*r >> ((reg & 3) *8)) & mask;
236   return L4_EOK;
237 }
238
239
240 void
241 Pci_proxy_dev::_do_cmd_write(unsigned mask,unsigned value)
242 {
243   l4_uint16_t cmd = _hwf->cfg_read<l4_uint16_t>(Pci::Config::Command);
244   l4_uint16_t ncmd = cmd & ~mask; // get unmodified bits
245   ncmd |= value & mask; // set new bits
246
247   if (ncmd == cmd) // nothing modified so ignore the write
248     return;
249
250   unsigned enable_decoders = _hwf->recheck_bars(ncmd & ~cmd & 3);
251   if ((ncmd & ~cmd & 3) && !enable_decoders)
252     {
253       d_printf(DBG_WARN, "warning: could not set bars, disable decoders\n");
254       ncmd &= (~3U | enable_decoders);
255     }
256
257   _hwf->cfg_write(Pci::Config::Command, ncmd);
258 }
259
260 int
261 Pci_proxy_dev::_do_status_cmd_write(l4_uint32_t mask, l4_uint32_t value)
262 {
263   if (mask & 0xffff)
264     _do_cmd_write(mask & 0xffff, value & 0xffff);
265
266   // status register has 'write 1 to clear' semantics so we can always write
267   // 16bit with the bits masked that we do not want to affect.
268   if (mask & value & 0xffff0000)
269     _hwf->cfg_write(Pci::Config::Status, (value & mask) >> 16, Hw::Pci::Cfg_short);
270
271   return 0;
272 }
273
274 int
275 Pci_proxy_dev::_do_rom_bar_write(l4_uint32_t mask, l4_uint32_t value)
276 {
277   l4_uint32_t b = read_rom();
278
279   if ((value & mask & 1) && !(b & mask & 1) && !_hwf->enable_rom())
280     return 0;  // failed to enable
281
282   b &= ~mask;
283   b |= value & mask;
284   write_rom(b);
285   return 0;
286 }
287
288
289 int
290 Pci_proxy_dev::cfg_write(int reg, l4_uint32_t v, Cfg_width order)
291 {
292   Hw::Pci::If *p = _hwf;
293
294   reg &= ~0U << order;
295   l4_uint32_t const offset_32 = reg & 3;
296   l4_uint32_t const mask_32 = (~0U >> (32 - (8U << order))) << (offset_32 * 8);
297   l4_uint32_t const value_32 = v << (offset_32 * 8);
298
299
300   switch (reg & ~3)
301     {
302     case 0x00: return 0;
303     case 0x08: return 0;
304     case 0x04: return _do_status_cmd_write(mask_32, value_32);
305     /* simulate multi function on hdr type */
306     case 0x10: /* bars 0 to 5 */
307     case 0x14:
308     case 0x18:
309     case 0x1c:
310     case 0x20:
311     case 0x24:
312       {
313         l4_uint32_t b = read_bar(reg / 4 - 4);
314         b &= ~mask_32;
315         b |= value_32 & mask_32;
316         write_bar(reg / 4 - 4, b);
317         return 0;
318       }
319     case 0x2c: return 0;
320     case 0x30: return _do_rom_bar_write(mask_32, value_32);
321     case 0x34: return 0;
322     case 0x38: return 0;
323     /* pass trough the rest ... */
324     default:   return p->cfg_write(reg, v, order);
325     }
326 }
327
328 void
329 Pci_proxy_dev::dump() const
330 {
331   Hw::Pci::If *p = _hwf;
332
333   printf("       %04x:%02x:%02x.%x:\n",
334          0, p->bus_nr(), _hwf->device_nr(), _hwf->function_nr());
335 #if 0
336   char buf[130];
337   libpciids_name_device(buf, sizeof(buf), _dev->vendor(), _dev->device());
338   printf("              %s\n", buf);
339 #endif
340 }
341
342
343 // -----------------------------------------------------------------------
344 // Virtual PCI dummy device
345 // -----------------------------------------------------------------------
346
347 class Pci_dummy : public Pci_virtual_dev, public Device
348 {
349 private:
350   unsigned char _cfg_space[4*4];
351
352 public:
353   int irq_enable(Irq_info *irq)
354   {
355     irq->irq = -1;
356     return -1;
357   }
358
359   Pci_dummy()
360   {
361     add_feature(this);
362     _h = &_cfg_space[0];
363     _h_len = sizeof(_cfg_space);
364     cfg_hdr()->hdr_type = 0x80;
365     cfg_hdr()->vendor_device = 0x02000400;
366     cfg_hdr()->status = 0;
367     cfg_hdr()->class_rev = 0x36440000;
368     cfg_hdr()->cmd = 0x0;
369   }
370
371   bool match_hw_feature(const Hw::Dev_feature*) const { return false; }
372   int dispatch(l4_umword_t, l4_uint32_t, L4::Ipc::Iostream&)
373   { return -L4_ENOSYS; }
374   void set_host(Device *d) { _host = d; }
375   Device *host() const { return _host; }
376
377 private:
378   Device *_host;
379 };
380
381
382 // ----------------------------------------------------------------------
383 // Basic virtual PCI bridge functionality
384 // ----------------------------------------------------------------------
385
386 Pci_bridge::Dev::Dev()
387 {
388   memset(_fns, 0, sizeof(_fns));
389 }
390
391 void
392 Pci_bridge::Dev::add_fn(Pci_dev *f)
393 {
394   for (unsigned i = 0; i < sizeof(_fns)/sizeof(_fns[0]); ++i)
395     {
396       if (!_fns[i])
397         {
398           _fns[i] = f;
399           return;
400         }
401     }
402 }
403
404 void
405 Pci_bridge::Dev::sort_fns()
406 {
407   // disabled sorting because the relation of two functions is questionable
408 #if 0
409   unsigned n;
410   for (n = 0; n < sizeof(_fns)/sizeof(_fns[0]) && _fns[n]; ++n)
411     ;
412
413   if (n < 2)
414     return;
415
416   bool exchg = false;
417
418   do
419     {
420       exchg = false;
421       for (unsigned i = 0; i < n - 1; ++i)
422         {
423           if (_fns[i]->dev()->function_nr() > _fns[i+1]->dev()->function_nr())
424             {
425               Pci_dev *t = _fns[i];
426               _fns[i] = _fns[i+1];
427               _fns[i+1] = t;
428               exchg = true;
429             }
430         }
431       n -= 1;
432     }
433   while (exchg && n >= 1);
434 #endif
435 }
436
437 void
438 Pci_bridge::Bus::add_fn(Pci_dev *pd, int slot)
439 {
440   if (slot >= 0)
441     {
442       _devs[slot].add_fn(pd);
443       _devs[slot].sort_fns();
444       return;
445     }
446
447   for (unsigned d = 0; d < Devs && !_devs[d].empty(); ++d)
448     if (_devs[d].cmp(pd))
449       {
450         _devs[d].add_fn(pd);
451         _devs[d].sort_fns();
452         return;
453       }
454
455   for (unsigned d = 0; d < Devs; ++d)
456     if (_devs[d].empty())
457       {
458         _devs[d].add_fn(pd);
459         return;
460       }
461 }
462
463 void
464 Pci_bridge::add_child(Device *d)
465 {
466   Pci_dev *vp = d->find_feature<Pci_dev>();
467
468   // hm, we do currently not add non PCI devices.
469   if (!vp)
470     return;
471
472   _bus.add_fn(vp);
473   Device::add_child(d);
474 }
475
476
477 void
478 Pci_bridge::add_child_fixed(Device *d, Pci_dev *vp, unsigned dn, unsigned fn)
479 {
480   _bus.dev(dn)->fn(fn, vp);
481   Device::add_child(d);
482 }
483
484
485 Pci_bridge *
486 Pci_bridge::find_bridge(unsigned bus)
487 {
488   // printf("PCI[%p]: look for bridge for bus %x %02x %02x\n", this, bus, _subordinate, _secondary);
489   if (bus == _secondary)
490     return this;
491
492   if (bus < _secondary || bus > _subordinate)
493     return 0;
494
495   for (unsigned d = 0; d < Bus::Devs; ++d)
496     for (unsigned f = 0; f < Dev::Fns; ++f)
497       {
498         Pci_dev *p = _bus.dev(d)->fn(f);
499         if (!p)
500           break;
501
502         Pci_bridge *b = dynamic_cast<Pci_bridge*>(p);
503         if (b && (b = b->find_bridge(bus)))
504           return b;
505       }
506
507   return 0;
508 }
509
510
511 bool
512 Pci_bridge::child_dev(unsigned bus, unsigned char dev, unsigned char fn,
513                        Pci_dev **rd)
514 {
515   Pci_bridge *b = find_bridge(bus);
516   if (!b)
517     {
518       *rd = 0;
519       return true;
520     }
521
522   if (dev >= Bus::Devs || fn >= Dev::Fns)
523     {
524       *rd = 0;
525       return true;
526     }
527
528   *rd = b->_bus.dev(dev)->fn(fn);
529   return true;
530 }
531
532 void
533 Pci_bridge::setup_bus()
534 {
535   for (unsigned d = 0; d < Bus::Devs; ++d)
536     for (unsigned f = 0; f < Dev::Fns; ++f)
537       {
538         Pci_dev *p = _bus.dev(d)->fn(f);
539         if (!p)
540           break;
541
542         Pci_bridge *b = dynamic_cast<Pci_bridge*>(p);
543         if (b)
544           {
545             b->_primary = _secondary;
546             if (b->_secondary <= _secondary)
547               {
548                 b->_secondary = ++_subordinate;
549                 b->_subordinate = b->_secondary;
550               }
551
552             b->setup_bus();
553
554             if (_subordinate < b->_subordinate)
555               _subordinate = b->_subordinate;
556           }
557       }
558 }
559
560 void
561 Pci_bridge::finalize_setup()
562 {
563   for (unsigned dn = 0; dn < Bus::Devs; ++dn)
564     {
565       if (!_bus.dev(dn)->empty())
566         continue;
567
568       for (unsigned fn = 0; fn < Dev::Fns; ++fn)
569         if (_bus.dev(dn)->fn(fn))
570           {
571             Pci_dummy *dummy = new Pci_dummy();
572             _bus.dev(dn)->fn(0, dummy);
573             Device::add_child(dummy);
574             break;
575           }
576     }
577 #if 0
578   for (VDevice *c = dynamic_cast<VDevice*>(children()); c; c = c->next())
579     c->finalize_setup();
580 #endif
581 }
582
583 namespace {
584   static Feature_factory_t<Pci_proxy_dev, Hw::Pci::Dev> __pci_f_factory1;
585   static Dev_factory_t<Pci_dummy> __pci_dummy_factory("PCI_dummy_device");
586 }
587
588 }
589