genode/repos/dde_linux/src/include/lx_kit/pci_dev_registry.h
Christian Prochaska 369d60bc21 dde_linux: iterate over PCI devices in ascending BDF order
On a Lenovo ThinkCentre M57p, the system locks up when the UHCI controller
BIOS handoff (disabling bit 4 in the LEGSUP register) for the controller
with PCI BDF 00:1d:2 is attempted before the handoff for the controller
with BDF 00:1a:0.

Fixes #3138
2019-02-12 10:33:15 +01:00

158 lines
3.7 KiB
C++

/*
* \brief Registry of PCI devices
* \author Norman Feske
* \date 2015-09-09
*/
/*
* Copyright (C) 2015-2017 Genode Labs GmbH
*
* This file is distributed under the terms of the GNU General Public License
* version 2.
*/
#ifndef _LX_KIT__PCI_DEV_REGISTRY_H_
#define _LX_KIT__PCI_DEV_REGISTRY_H_
/* Linux emulation environment includes */
#include <lx_kit/pci.h>
#include <lx_kit/internal/pci_dev.h>
#include <io_port_session/connection.h>
namespace Lx {
class Pci_dev_registry;
/**
* Return singleton 'Pci_dev_registry' object
*
* Implementation must be provided by the driver.
*/
Pci_dev_registry *pci_dev_registry(Genode::Env *env = nullptr);
}
class Lx::Pci_dev_registry
{
private:
Lx_kit::List<Pci_dev> _devs;
Genode::Env &_env;
public:
Pci_dev_registry(Genode::Env &env) : _env(env) { }
void insert(Pci_dev *pci_dev)
{
/*
* Keep PCI devices in natural bus order. Otherwise on a Lenovo
* ThinkCentre M57p, the system locks up when the UHCI controller
* BIOS handoff (disabling bit 4 in the LEGSUP register) for the
* controller with PCI BDF 00:1d:2 is attempted before the handoff
* for the controller with BDF 00:1a:0.
*/
_devs.append(pci_dev);
}
void remove(Pci_dev *pci_dev)
{
_devs.remove(pci_dev);
}
Pci_dev* first() { return _devs.first(); }
Genode::Io_mem_dataspace_capability io_mem(Genode::addr_t phys,
Genode::Cache_attribute cache_attribute,
Genode::size_t size,
Genode::addr_t &offset)
{
enum { PCI_ROM_RESOURCE = 6 };
for (Pci_dev *d = _devs.first(); d; d = d->next()) {
unsigned bar = 0;
for (; bar < PCI_ROM_RESOURCE; bar++) {
if ((pci_resource_flags(d, bar) & IORESOURCE_MEM) &&
(pci_resource_start(d, bar) <= phys) &&
(pci_resource_end(d, bar) >= (phys+size-1)))
break;
}
if (bar >= PCI_ROM_RESOURCE)
continue;
/* offset from the beginning of the PCI resource */
offset = phys - pci_resource_start(d, bar);
Genode::Io_mem_session_capability io_mem_cap =
d->io_mem(bar, cache_attribute);
return Genode::Io_mem_session_client(io_mem_cap).dataspace();
}
Genode::error("device using I/O memory of address ",
Genode::Hex(phys), " is unknown");
return Genode::Io_mem_dataspace_capability();
}
template <typename T>
T io_read(unsigned port)
{
/* try I/O access on all PCI devices */
for (Pci_dev *d = _devs.first(); d; d = d->next()) {
T value = 0;
if (d->io_port().in<T>(port, &value))
return value;
}
try {
Genode::Io_port_connection iox(_env, port, sizeof(T));
switch (sizeof(T)) {
case 1:
return iox.inb(port);
case 2:
return iox.inw(port);
case 4:
return iox.inl(port);
}
} catch (...) {
Genode::error("unknown exception io_read");
}
Genode::warning("I/O port(", port, ") read failed");
return (T)~0U;
}
template <typename T>
void io_write(unsigned port, T value)
{
/* try I/O access on all PCI devices, return on first success */
for (Pci_dev *d = _devs.first(); d; d = d->next()) {
if (d->io_port().out<T>(port, value))
return;
}
try {
Genode::Io_port_connection iox(_env, port, sizeof(T));
switch (sizeof(T)) {
case 1:
iox.outb(port, value);
return;
case 2:
iox.outw(port, value);
return;
case 4:
iox.outl(port, value);
return;
}
} catch (...) {
Genode::error("unknown exception io_write");
}
Genode::warning("I/O port(", port, ") write failed");
}
};
#endif /* _LX_KIT__PCI_DEV_REGISTRY_H_ */