/* * \brief Virtual PCI bus tree for DDE kit * \author Christian Helmuth * \date 2008-10-22 */ /* * Copyright (C) 2008-2013 Genode Labs GmbH * * This file is part of the Genode OS framework, which is distributed * under the terms of the GNU General Public License version 2. */ #ifndef _PCI_TREE_H_ #define _PCI_TREE_H_ #include #include #include #include #include #include #include namespace Dde_kit { using namespace Genode; class Pci_device : public Avl_node { public: static inline unsigned short knit_bdf(int bus, int dev, int fun) { return ((bus & 0xff) << 8) | ((dev & 0x1f) << 3) | (fun & 0x07); } private: Pci::Device_client _device; unsigned short _bdf; /* bus:device:function */ public: Pci_device(Pci::Device_capability device_cap) : _device(device_cap) { unsigned char bus = ~0, dev = ~0, fun = ~0; _device.bus_address(&bus, &dev, &fun); _bdf = knit_bdf(bus, dev, fun); } /*************** ** Accessors ** ***************/ unsigned short bdf() const { return _bdf; } unsigned char bus() const { return (_bdf >> 8) & 0xff; } unsigned char dev() const { return (_bdf >> 3) & 0x1f; } unsigned char fun() const { return _bdf & 0x07; } /******************************** ** Configuration space access ** ********************************/ uint32_t config_read(unsigned char address, Pci::Device::Access_size size); void config_write(unsigned char address, uint32_t val, Pci::Device::Access_size size); /** AVL node comparison */ bool higher(Pci_device *device) { return (_bdf < device->_bdf); } Pci_device *next(Pci_device *root, Side direction) { /* * The predecessor of a node is the right-most node of the left * subtree or the parent right after the first "left turn" up to * the root of the tree. Symmetrically, the successor of a node is * the left-most node of the right subtree or the parent right * after the first "right turn" up to the root of the tree. */ if (child(direction)) { Pci_device *n = child(direction); while (true) { if (!n->child(!direction)) return n; else n = n->child(!direction); } } else { Pci_device *n = this; for (Pci_device *parent = static_cast(n->_parent); n != root; n = parent, parent = static_cast(n->_parent)) { if (n == parent->child(!direction)) return parent; } return 0; } } void show() { if (child(LEFT)) child(LEFT)->show(); unsigned char ht = config_read(0x0e, Pci::Device::ACCESS_8BIT) & 0xff; PINF("%02x:%02x.%x %04x:%04x (%x) ht=%02x", bus(), dev(), fun(), _device.vendor_id(), _device.device_id(), _device.base_class(), ht); if (child(RIGHT)) child(RIGHT)->show(); } Ram_dataspace_capability alloc_dma_buffer(Pci::Connection &pci_drv, size_t size) { /* trigger that the device gets assigned to this driver */ for (unsigned i = 0; i < 2; i++) { try { pci_drv.config_extended(_device); break; } catch (Pci::Device::Quota_exceeded) { if (i == 1) return Ram_dataspace_capability(); Genode::env()->parent()->upgrade(pci_drv.cap(), "ram_quota=4096"); } } for (unsigned i = 0; i < 2; i++) { try { return pci_drv.alloc_dma_buffer(size); } catch (Pci::Device::Quota_exceeded) { if (i == 0) { char buf[32]; Genode::snprintf(buf, sizeof(buf), "ram_quota=%zd", size); Genode::env()->parent()->upgrade(pci_drv.cap(), buf); } } } return Ram_dataspace_capability(); } Genode::Io_port_session_capability io_port(unsigned short bar) { return _device.io_port(_device.phys_bar_to_virt(bar)); } }; class Pci_tree { public: class Not_found : public Exception { }; private: int _current_dev_num; Pci::Connection _pci_drv; Avl_tree _devices; Lock _lock; /* Lookup device for given BDF */ Pci_device *_lookup(unsigned short bdf) { if (!_devices.first()) throw Not_found(); Pci_device *d = _devices.first(); do { if (bdf == d->bdf()) return d; if (bdf < d->bdf()) d = d->child(Pci_device::LEFT); else d = d->child(Pci_device::RIGHT); } while (d); throw Not_found(); } /** Find lowest BDF */ void _first_bdf(int *bus, int *dev, int *fun) { if (!_devices.first()) throw Not_found(); Pci_device *first, *prev, *root; first = prev = root = _devices.first(); do { first = prev; prev = prev->next(root, Pci_device::LEFT); } while (prev); *bus = first->bus(); *dev = first->dev(); *fun = first->fun(); } /** Find next BDF */ void _next_bdf(Pci_device *prev, int *bus, int *dev, int *fun) { if (!_devices.first()) throw Not_found(); Pci_device *next = prev->next(_devices.first(), Pci_device::RIGHT); if (!next) throw Not_found(); *bus = next->bus(); *dev = next->dev(); *fun = next->fun(); } void _show_devices() { if (_devices.first()) _devices.first()->show(); } public: Pci_tree(unsigned device_class, unsigned class_mask); uint32_t config_read(int bus, int dev, int fun, unsigned char address, Pci::Device::Access_size size) { Lock::Guard lock_guard(_lock); unsigned short bdf = Pci_device::knit_bdf(bus, dev, fun); return _lookup(bdf)->config_read(address, size); } void config_write(int bus, int dev, int fun, unsigned char address, uint32_t val, Pci::Device::Access_size size) { Lock::Guard lock_guard(_lock); unsigned short bdf = Pci_device::knit_bdf(bus, dev, fun); _lookup(bdf)->config_write(address, val, size); } /** * Lookup first device */ void first_device(int *bus, int *dev, int *fun) { Lock::Guard lock_guard(_lock); _first_bdf(bus, dev, fun); } /** * Lookup next device */ void next_device(int *bus, int *dev, int *fun) { Lock::Guard lock_guard(_lock); Pci_device *d = _lookup(Pci_device::knit_bdf(*bus, *dev, *fun)); _next_bdf(d, bus, dev, fun); } Ram_dataspace_capability alloc_dma_buffer(int bus, int dev, int fun, size_t size) { Lock::Guard lock_guard(_lock); unsigned short bdf = Pci_device::knit_bdf(bus, dev, fun); return _lookup(bdf)->alloc_dma_buffer(_pci_drv, size); } Io_port_session_capability io_port(int bus, int dev, int fun, unsigned short bda) { Lock::Guard lock_guard(_lock); unsigned short bdf = Pci_device::knit_bdf(bus, dev, fun); return _lookup(bdf)->io_port(bda); } }; } #endif /* _PCI_TREE_H_ */