genode/repos/os/src/drivers/platform/spec/x86/pci_session_component.h

1110 lines
30 KiB
C
Raw Normal View History

2011-12-22 16:19:25 +01:00
/*
* \brief Platform session component
2011-12-22 16:19:25 +01:00
* \author Norman Feske
* \date 2008-01-28
*/
/*
* Copyright (C) 2008-2017 Genode Labs GmbH
2011-12-22 16:19:25 +01:00
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
2011-12-22 16:19:25 +01:00
*/
#pragma once
2011-12-22 16:19:25 +01:00
/* base */
#include <base/allocator_guard.h>
#include <base/attached_rom_dataspace.h>
#include <base/heap.h>
2011-12-22 16:19:25 +01:00
#include <base/rpc_server.h>
#include <base/tslab.h>
2011-12-22 16:19:25 +01:00
#include <root/component.h>
#include <util/mmio.h>
#include <util/retry.h>
#include <util/reconstructible.h>
/* os */
#include <io_mem_session/connection.h>
#include <os/reporter.h>
#include <os/session_policy.h>
#include <platform_session/platform_session.h>
#include <base/allocator_guard.h>
/* local */
#include "device_pd.h"
#include "pci_bridge.h"
#include "pci_config_access.h"
#include "pci_device_component.h"
2011-12-22 16:19:25 +01:00
typedef Genode::Ram_dataspace_capability Ram_capability;
namespace Platform {
unsigned short bridge_bdf(unsigned char bus);
class Pci_buses;
class Ram_dataspace;
class Rmrr;
class Root;
class Session_component;
}
2011-12-22 16:19:25 +01:00
class Platform::Ram_dataspace : public Genode::List<Ram_dataspace>::Element {
private:
Follow practices suggested by "Effective C++" The patch adjust the code of the base, base-<kernel>, and os repository. To adapt existing components to fix violations of the best practices suggested by "Effective C++" as reported by the -Weffc++ compiler argument. The changes follow the patterns outlined below: * A class with virtual functions can no longer publicly inherit base classed without a vtable. The inherited object may either be moved to a member variable, or inherited privately. The latter would be used for classes that inherit 'List::Element' or 'Avl_node'. In order to enable the 'List' and 'Avl_tree' to access the meta data, the 'List' must become a friend. * Instead of adding a virtual destructor to abstract base classes, we inherit the new 'Interface' class, which contains a virtual destructor. This way, single-line abstract base classes can stay as compact as they are now. The 'Interface' utility resides in base/include/util/interface.h. * With the new warnings enabled, all member variables must be explicitly initialized. Basic types may be initialized with '='. All other types are initialized with braces '{ ... }' or as class initializers. If basic types and non-basic types appear in a row, it is nice to only use the brace syntax (also for basic types) and align the braces. * If a class contains pointers as members, it must now also provide a copy constructor and assignment operator. In the most cases, one would make them private, effectively disallowing the objects to be copied. Unfortunately, this warning cannot be fixed be inheriting our existing 'Noncopyable' class (the compiler fails to detect that the inheriting class cannot be copied and still gives the error). For now, we have to manually add declarations for both the copy constructor and assignment operator as private class members. Those declarations should be prepended with a comment like this: /* * Noncopyable */ Thread(Thread const &); Thread &operator = (Thread const &); In the future, we should revisit these places and try to replace the pointers with references. In the presence of at least one reference member, the compiler would no longer implicitly generate a copy constructor. So we could remove the manual declaration. Issue #465
2017-12-21 15:42:15 +01:00
Ram_capability const _cap;
public:
Ram_dataspace(Ram_capability c) : _cap(c) { }
bool match(const Ram_capability &cap) const {
return cap.local_name() == _cap.local_name(); }
Ram_capability cap() const { return _cap; }
};
class Platform::Rmrr : public Genode::List<Platform::Rmrr>::Element
{
public:
class Bdf : public Genode::List<Bdf>::Element {
private:
Genode::uint8_t _bus, _dev, _func;
public:
Bdf(Genode::uint8_t bus, Genode::uint8_t dev,
Genode::uint8_t func)
: _bus(bus), _dev(dev), _func(func) { }
bool match(Genode::uint8_t bus, Genode::uint8_t dev,
Genode::uint8_t func) {
return bus == _bus && dev == _dev && func == _func; }
};
private:
Follow practices suggested by "Effective C++" The patch adjust the code of the base, base-<kernel>, and os repository. To adapt existing components to fix violations of the best practices suggested by "Effective C++" as reported by the -Weffc++ compiler argument. The changes follow the patterns outlined below: * A class with virtual functions can no longer publicly inherit base classed without a vtable. The inherited object may either be moved to a member variable, or inherited privately. The latter would be used for classes that inherit 'List::Element' or 'Avl_node'. In order to enable the 'List' and 'Avl_tree' to access the meta data, the 'List' must become a friend. * Instead of adding a virtual destructor to abstract base classes, we inherit the new 'Interface' class, which contains a virtual destructor. This way, single-line abstract base classes can stay as compact as they are now. The 'Interface' utility resides in base/include/util/interface.h. * With the new warnings enabled, all member variables must be explicitly initialized. Basic types may be initialized with '='. All other types are initialized with braces '{ ... }' or as class initializers. If basic types and non-basic types appear in a row, it is nice to only use the brace syntax (also for basic types) and align the braces. * If a class contains pointers as members, it must now also provide a copy constructor and assignment operator. In the most cases, one would make them private, effectively disallowing the objects to be copied. Unfortunately, this warning cannot be fixed be inheriting our existing 'Noncopyable' class (the compiler fails to detect that the inheriting class cannot be copied and still gives the error). For now, we have to manually add declarations for both the copy constructor and assignment operator as private class members. Those declarations should be prepended with a comment like this: /* * Noncopyable */ Thread(Thread const &); Thread &operator = (Thread const &); In the future, we should revisit these places and try to replace the pointers with references. In the presence of at least one reference member, the compiler would no longer implicitly generate a copy constructor. So we could remove the manual declaration. Issue #465
2017-12-21 15:42:15 +01:00
Genode::uint64_t const _start, _end;
Genode::Io_mem_dataspace_capability _cap { };
Genode::List<Bdf> _bdf_list { };
Follow practices suggested by "Effective C++" The patch adjust the code of the base, base-<kernel>, and os repository. To adapt existing components to fix violations of the best practices suggested by "Effective C++" as reported by the -Weffc++ compiler argument. The changes follow the patterns outlined below: * A class with virtual functions can no longer publicly inherit base classed without a vtable. The inherited object may either be moved to a member variable, or inherited privately. The latter would be used for classes that inherit 'List::Element' or 'Avl_node'. In order to enable the 'List' and 'Avl_tree' to access the meta data, the 'List' must become a friend. * Instead of adding a virtual destructor to abstract base classes, we inherit the new 'Interface' class, which contains a virtual destructor. This way, single-line abstract base classes can stay as compact as they are now. The 'Interface' utility resides in base/include/util/interface.h. * With the new warnings enabled, all member variables must be explicitly initialized. Basic types may be initialized with '='. All other types are initialized with braces '{ ... }' or as class initializers. If basic types and non-basic types appear in a row, it is nice to only use the brace syntax (also for basic types) and align the braces. * If a class contains pointers as members, it must now also provide a copy constructor and assignment operator. In the most cases, one would make them private, effectively disallowing the objects to be copied. Unfortunately, this warning cannot be fixed be inheriting our existing 'Noncopyable' class (the compiler fails to detect that the inheriting class cannot be copied and still gives the error). For now, we have to manually add declarations for both the copy constructor and assignment operator as private class members. Those declarations should be prepended with a comment like this: /* * Noncopyable */ Thread(Thread const &); Thread &operator = (Thread const &); In the future, we should revisit these places and try to replace the pointers with references. In the presence of at least one reference member, the compiler would no longer implicitly generate a copy constructor. So we could remove the manual declaration. Issue #465
2017-12-21 15:42:15 +01:00
Genode::Constructible<Genode::Io_mem_connection> _io_mem { };
public:
Rmrr(Genode::uint64_t start, Genode::uint64_t end)
: _start(start), _end(end)
{ }
Genode::Io_mem_dataspace_capability match(Genode::Env &env,
Device_config config)
{
Genode::uint8_t bus = config.bus_number();
Genode::uint8_t device = config.device_number();
Genode::uint8_t function = config.function_number();
for (Bdf *bdf = _bdf_list.first(); bdf; bdf = bdf->next()) {
if (!bdf->match(bus, device, function))
continue;
if (_cap.valid())
return _cap;
_io_mem.construct(env, _start, _end - _start + 1);
_cap = _io_mem->dataspace();
return _cap;
}
return Genode::Io_mem_dataspace_capability();
}
void add(Bdf * bdf) { _bdf_list.insert(bdf); }
static Genode::List<Rmrr> *list()
{
static Genode::List<Rmrr> _list;
return &_list;
}
};
2011-12-22 16:19:25 +01:00
class Platform::Pci_buses
{
private:
Follow practices suggested by "Effective C++" The patch adjust the code of the base, base-<kernel>, and os repository. To adapt existing components to fix violations of the best practices suggested by "Effective C++" as reported by the -Weffc++ compiler argument. The changes follow the patterns outlined below: * A class with virtual functions can no longer publicly inherit base classed without a vtable. The inherited object may either be moved to a member variable, or inherited privately. The latter would be used for classes that inherit 'List::Element' or 'Avl_node'. In order to enable the 'List' and 'Avl_tree' to access the meta data, the 'List' must become a friend. * Instead of adding a virtual destructor to abstract base classes, we inherit the new 'Interface' class, which contains a virtual destructor. This way, single-line abstract base classes can stay as compact as they are now. The 'Interface' utility resides in base/include/util/interface.h. * With the new warnings enabled, all member variables must be explicitly initialized. Basic types may be initialized with '='. All other types are initialized with braces '{ ... }' or as class initializers. If basic types and non-basic types appear in a row, it is nice to only use the brace syntax (also for basic types) and align the braces. * If a class contains pointers as members, it must now also provide a copy constructor and assignment operator. In the most cases, one would make them private, effectively disallowing the objects to be copied. Unfortunately, this warning cannot be fixed be inheriting our existing 'Noncopyable' class (the compiler fails to detect that the inheriting class cannot be copied and still gives the error). For now, we have to manually add declarations for both the copy constructor and assignment operator as private class members. Those declarations should be prepended with a comment like this: /* * Noncopyable */ Thread(Thread const &); Thread &operator = (Thread const &); In the future, we should revisit these places and try to replace the pointers with references. In the presence of at least one reference member, the compiler would no longer implicitly generate a copy constructor. So we could remove the manual declaration. Issue #465
2017-12-21 15:42:15 +01:00
Genode::Bit_array<Device_config::MAX_BUSES> _valid { };
void scan_bus(Config_access &config_access, Genode::Allocator &heap,
unsigned char bus = 0);
bool _bus_valid(int bus)
{
if (bus >= Device_config::MAX_BUSES)
return false;
return _valid.get(bus, 1);
}
public:
Pci_buses(Genode::Allocator &heap, Genode::Attached_io_mem_dataspace &pciconf)
{
Config_access c(pciconf);
scan_bus(c, heap);
}
/**
* Scan PCI buses for a device
*
* \param bus start scanning at bus number
* \param device start scanning at device number
* \param function start scanning at function number
* \param out_device_config device config information of the
* found device
* \param config_access interface for accessing the PCI
* configuration
* space
*
* \retval true device was found
* \retval false no device was found
*/
bool find_next(int bus, int device, int function,
Device_config *out_device_config,
Config_access *config_access)
{
for (; bus < Device_config::MAX_BUSES; bus++) {
if (!_bus_valid(bus))
continue;
for (; device < Device_config::MAX_DEVICES; device++) {
for (; function < Device_config::MAX_FUNCTIONS; function++) {
/* read config space */
Device_config config(bus, device, function, config_access);
if (config.valid()) {
*out_device_config = config;
return true;
}
}
function = 0; /* init value for next device */
}
device = 0; /* init value for next bus */
}
return false;
}
};
class Platform::Session_component : public Genode::Rpc_object<Session>
{
private:
Genode::Env &_env;
Genode::Attached_rom_dataspace &_config;
Genode::Attached_io_mem_dataspace &_pciconf;
Genode::Ram_quota_guard _ram_guard;
Genode::Cap_quota_guard _cap_guard;
Genode::Constrained_ram_allocator _env_ram {
_env.pd(), _ram_guard, _cap_guard };
Genode::Heap _md_alloc;
Genode::Session_label const _label;
Genode::Session_policy const _policy { _label, _config.xml() };
Follow practices suggested by "Effective C++" The patch adjust the code of the base, base-<kernel>, and os repository. To adapt existing components to fix violations of the best practices suggested by "Effective C++" as reported by the -Weffc++ compiler argument. The changes follow the patterns outlined below: * A class with virtual functions can no longer publicly inherit base classed without a vtable. The inherited object may either be moved to a member variable, or inherited privately. The latter would be used for classes that inherit 'List::Element' or 'Avl_node'. In order to enable the 'List' and 'Avl_tree' to access the meta data, the 'List' must become a friend. * Instead of adding a virtual destructor to abstract base classes, we inherit the new 'Interface' class, which contains a virtual destructor. This way, single-line abstract base classes can stay as compact as they are now. The 'Interface' utility resides in base/include/util/interface.h. * With the new warnings enabled, all member variables must be explicitly initialized. Basic types may be initialized with '='. All other types are initialized with braces '{ ... }' or as class initializers. If basic types and non-basic types appear in a row, it is nice to only use the brace syntax (also for basic types) and align the braces. * If a class contains pointers as members, it must now also provide a copy constructor and assignment operator. In the most cases, one would make them private, effectively disallowing the objects to be copied. Unfortunately, this warning cannot be fixed be inheriting our existing 'Noncopyable' class (the compiler fails to detect that the inheriting class cannot be copied and still gives the error). For now, we have to manually add declarations for both the copy constructor and assignment operator as private class members. Those declarations should be prepended with a comment like this: /* * Noncopyable */ Thread(Thread const &); Thread &operator = (Thread const &); In the future, we should revisit these places and try to replace the pointers with references. In the presence of at least one reference member, the compiler would no longer implicitly generate a copy constructor. So we could remove the manual declaration. Issue #465
2017-12-21 15:42:15 +01:00
Genode::List<Device_component> _device_list { };
Platform::Pci_buses &_pci_bus;
Genode::Heap &_global_heap;
bool _iommu;
/**
* Registry of RAM dataspaces allocated by the session
*/
Follow practices suggested by "Effective C++" The patch adjust the code of the base, base-<kernel>, and os repository. To adapt existing components to fix violations of the best practices suggested by "Effective C++" as reported by the -Weffc++ compiler argument. The changes follow the patterns outlined below: * A class with virtual functions can no longer publicly inherit base classed without a vtable. The inherited object may either be moved to a member variable, or inherited privately. The latter would be used for classes that inherit 'List::Element' or 'Avl_node'. In order to enable the 'List' and 'Avl_tree' to access the meta data, the 'List' must become a friend. * Instead of adding a virtual destructor to abstract base classes, we inherit the new 'Interface' class, which contains a virtual destructor. This way, single-line abstract base classes can stay as compact as they are now. The 'Interface' utility resides in base/include/util/interface.h. * With the new warnings enabled, all member variables must be explicitly initialized. Basic types may be initialized with '='. All other types are initialized with braces '{ ... }' or as class initializers. If basic types and non-basic types appear in a row, it is nice to only use the brace syntax (also for basic types) and align the braces. * If a class contains pointers as members, it must now also provide a copy constructor and assignment operator. In the most cases, one would make them private, effectively disallowing the objects to be copied. Unfortunately, this warning cannot be fixed be inheriting our existing 'Noncopyable' class (the compiler fails to detect that the inheriting class cannot be copied and still gives the error). For now, we have to manually add declarations for both the copy constructor and assignment operator as private class members. Those declarations should be prepended with a comment like this: /* * Noncopyable */ Thread(Thread const &); Thread &operator = (Thread const &); In the future, we should revisit these places and try to replace the pointers with references. In the presence of at least one reference member, the compiler would no longer implicitly generate a copy constructor. So we could remove the manual declaration. Issue #465
2017-12-21 15:42:15 +01:00
Genode::List<Platform::Ram_dataspace> _ram_caps { };
void _insert(Ram_capability cap) {
_ram_caps.insert(new (_md_alloc) Platform::Ram_dataspace(cap)); }
bool _remove(Ram_capability cap)
{
for (Platform::Ram_dataspace *ds = _ram_caps.first(); ds;
ds = ds->next()) {
if (!ds->match(cap))
continue;
_ram_caps.remove(ds);
destroy(_md_alloc, ds);
return true;
}
return false;
}
Platform::Device_pd _device_pd { _env, _label, _ram_guard, _cap_guard };
enum { MAX_PCI_DEVICES = Device_config::MAX_BUSES *
Device_config::MAX_DEVICES *
Device_config::MAX_FUNCTIONS };
static Genode::Bit_array<MAX_PCI_DEVICES> bdf_in_use;
/**
* List containing extended PCI config space information
*/
static Genode::List<Config_space> &config_space_list()
{
static Genode::List<Config_space> config_space;
return config_space;
}
/**
* Find for a given PCI device described by the bus:dev:func triple
* the corresponding extended 4K PCI config space address.
* A io mem dataspace is created and returned.
*/
Genode::addr_t lookup_config_space(Genode::uint16_t const bdf)
{
using namespace Genode;
addr_t config_space = ~0UL; /* invalid */
Config_space *e = config_space_list().first();
for (; e && (config_space == ~0UL); e = e->next())
config_space = e->lookup_config_space(bdf);
return config_space;
}
typedef Genode::String<32> Alias_name;
/*
* List of aliases for PCI Class/Subclas/Prog I/F triple used
* by xml config for this platform driver
*/
unsigned class_subclass_prog(Alias_name const &name)
{
using namespace Genode;
static struct {
const char * alias;
uint8_t pci_class, pci_subclass, pci_progif;
} const aliases [] = {
{ "AHCI" , 0x1, 0x06, 0x0},
{ "ALL" , 0x0, 0x00, 0x0},
{ "AUDIO" , 0x4, 0x01, 0x0},
{ "ETHERNET" , 0x2, 0x00, 0x0},
{ "HDAUDIO" , 0x4, 0x03, 0x0},
{ "NVME" , 0x1, 0x08, 0x2},
{ "USB" , 0xc, 0x03, 0x0},
{ "VGA" , 0x3, 0x00, 0x0},
{ "WIFI" , 0x2, 0x80, 0x0},
{ "ISABRIDGE", 0x6, 0x01, 0x0}
};
for (unsigned i = 0; i < sizeof(aliases) / sizeof(aliases[0]); i++) {
if (name != aliases[i].alias)
continue;
return 0U | aliases[i].pci_class << 16 |
aliases[i].pci_subclass << 8 |
aliases[i].pci_progif;
}
return ~0U;
}
/**
* Check device usage according to session policy
*/
bool permit_device(const char * name)
{
using namespace Genode;
try {
_policy.for_each_sub_node("device", [&] (Xml_node dev) {
/* enforce restriction based on name name */
if (dev.attribute_value("name", Genode::String<10>()) == name)
/* found identical match - permit access */
throw true;
});
} catch (bool result) { return result; }
return false;
}
static bool _bdf_exactly_specified(Xml_node node)
{
return node.has_attribute("bus")
&& node.has_attribute("device")
&& node.has_attribute("function");
}
struct Bdf
{
unsigned b, d, f;
bool equals(Bdf const &other) const
{
return other.b == b && other.d == d && other.f == f;
}
void print(Genode::Output &out) const
{
Genode::print(out, Genode::Hex(b), ":", Genode::Hex(d), ".", f);
}
};
static Bdf _bdf_from_xml(Xml_node node)
{
return Bdf { .b = node.attribute_value("bus", 0U),
.d = node.attribute_value("device", 0U),
.f = node.attribute_value("function", 0U) };
}
static bool _bdf_attributes_in_valid_range(Xml_node node)
{
return _bdf_exactly_specified(node)
&& (node.attribute_value("bus", 0U) < Device_config::MAX_BUSES)
&& (node.attribute_value("device", 0U) < Device_config::MAX_DEVICES)
&& (node.attribute_value("function", 0U) < Device_config::MAX_FUNCTIONS);
}
static bool _bdf_matches(Xml_node node, Bdf bdf)
{
return _bdf_from_xml(node).equals(bdf);
}
/**
* Check according session policy device usage
*/
bool permit_device(Bdf const bdf, unsigned class_code)
{
using namespace Genode;
2011-12-22 16:19:25 +01:00
try {
_policy.for_each_sub_node("pci", [&] (Xml_node node) {
if (_bdf_exactly_specified(node)) {
if (_bdf_matches(node, bdf))
throw true;
/* check also for class entry */
}
if (!node.has_attribute("class"))
return;
/* enforce restriction based upon classes */
auto alias = node.attribute_value("class", Alias_name());
unsigned const class_sub_prog = class_subclass_prog(alias);
enum { DONT_CHECK_PROGIF = 8 };
/* if class/subclass don't match - deny */
if (class_sub_prog && (class_sub_prog ^ class_code) >> DONT_CHECK_PROGIF)
return;
/* if this bdf is used by some policy - deny */
if (find_dev_in_policy(bdf))
return;
throw true;
});
} catch (bool result) { return result; }
return false;
}
/**
* Lookup a given device name.
*/
bool find_dev_in_policy(const char * dev_name, bool once = true)
{
using namespace Genode;
try {
_config.xml().for_each_sub_node("policy", [&] (Xml_node policy) {
policy.for_each_sub_node("device", [&] (Xml_node device) {
typedef Genode::String<10> Name;
if (device.attribute_value("name", Name()) == dev_name) {
if (once)
throw true;
once = true;
}
});
});
} catch (bool result) { return result; }
return false;
}
/**
* Lookup a given device name.
*/
bool find_dev_in_policy(Bdf const bdf, bool once = true)
{
using namespace Genode;
try {
Xml_node xml = _config.xml();
xml.for_each_sub_node("policy", [&] (Xml_node policy) {
policy.for_each_sub_node("pci", [&] (Xml_node node) {
if (_bdf_exactly_specified(node)) {
if (_bdf_matches(node, bdf)) {
if (once)
throw true;
once = true;
}
}
});
});
} catch (bool result) { return result; }
return false;
}
public:
/**
* Constructor
*/
Session_component(Genode::Env &env,
Genode::Attached_rom_dataspace &config,
Genode::Attached_io_mem_dataspace &pciconf,
Platform::Pci_buses &buses,
Genode::Heap &global_heap,
char const *args,
bool const iommu)
:
_env(env),
_config(config),
_pciconf(pciconf),
_ram_guard(Genode::ram_quota_from_args(args)),
_cap_guard(Genode::cap_quota_from_args(args)),
_md_alloc(_env_ram, env.rm()),
_label(Genode::label_from_args(args)),
_pci_bus(buses), _global_heap(global_heap), _iommu(iommu)
{
/* subtract the RPC session and session dataspace capabilities */
_cap_guard.withdraw(Genode::Cap_quota{2});
/* check policy for non-pci devices */
_policy.for_each_sub_node("device", [&] (Genode::Xml_node device_node) {
if (!device_node.has_attribute("name")) {
Genode::error("'", _label, "' - device node "
"misses 'name' attribute");
throw Genode::Service_denied();
}
typedef Genode::String<16> Name;
Name const name = device_node.attribute_value("name", Name());
enum { DOUBLET = false };
if (find_dev_in_policy(name.string(), DOUBLET)) {
Genode::error("'", _label, "' - device '", name, "' "
2017-04-20 20:06:08 +02:00
"is part of more than one policy");
throw Genode::Service_denied();
}
});
/* pci devices */
_policy.for_each_sub_node("pci", [&] (Genode::Xml_node node) {
enum { INVALID_CLASS = 0x1000000U };
using Genode::Xml_attribute;
/**
* Valid input is either a triple of 'bus', 'device',
* 'function' attributes or a single 'class' attribute.
* All other attribute names are traded as wrong.
*/
if (node.has_attribute("class")) {
Alias_name const alias = node.attribute_value("class", Alias_name());
if (class_subclass_prog(alias) >= INVALID_CLASS) {
Genode::error("'", _label, "' - invalid 'class' ",
"attribute '", alias, "'");
throw Genode::Service_denied();
}
/* sanity check that 'class' is the only attribute */
try {
node.attribute(1);
Genode::error("'", _label, "' - attributes beside 'class' detected");
throw Genode::Service_denied();
}
catch (Xml_attribute::Nonexistent_attribute) { }
/* we have a class and it is the only attribute */
return;
}
/* no 'class' attribute - now check for valid bdf triple */
try {
node.attribute(3);
Genode::error("'", _label, "' - "
"invalid number of pci node attributes");
throw Genode::Service_denied();
} catch (Xml_attribute::Nonexistent_attribute) { }
if (_bdf_exactly_specified(node)) {
if (!_bdf_attributes_in_valid_range(node)) {
Genode::error("'", _label, "' - "
"invalid pci node attributes for bdf");
throw Genode::Service_denied();
}
Bdf const bdf = _bdf_from_xml(node);
enum { DOUBLET = false };
if (find_dev_in_policy(bdf, DOUBLET)) {
Genode::error("'", _label, "' - device '", bdf, "' "
"is part of more than one policy");
throw Genode::Service_denied();
}
}
});
}
/**
* Destructor
*/
~Session_component()
{
/* release all elements of the session's device list */
while (_device_list.first())
release_device(_device_list.first()->cap());
while (Platform::Ram_dataspace *ds = _ram_caps.first()) {
_ram_caps.remove(ds);
_env_ram.free(ds->cap());
destroy(_md_alloc, ds);
}
}
void upgrade_resources(Genode::Session::Resources resources)
{
_ram_guard.upgrade(resources.ram_quota);
_cap_guard.upgrade(resources.cap_quota);
}
2011-12-22 16:19:25 +01:00
static void add_config_space(Genode::uint32_t bdf_start,
Genode::uint32_t func_count,
Genode::addr_t base,
Genode::Allocator &heap)
{
using namespace Genode;
Config_space * space =
new (heap) Config_space(bdf_start, func_count, base);
config_space_list().insert(space);
}
/**
* Check whether msi usage was explicitly switched off
*/
bool msi_usage()
{
typedef Genode::String<10> Mode;
return _policy.attribute_value("irq_mode", Mode()) != "nomsi";
}
/***************************
** PCI session interface **
***************************/
2011-12-22 16:19:25 +01:00
Device_capability first_device(unsigned device_class,
unsigned class_mask) override {
return next_device(Device_capability(), device_class, class_mask); }
Device_capability next_device(Device_capability prev_device,
unsigned device_class,
unsigned class_mask) override
{
/*
* Create the interface to the PCI config space.
*/
Config_access config_access(_pciconf);
2011-12-22 16:19:25 +01:00
/* lookup device component for previous device */
auto lambda = [&] (Device_component *prev)
2011-12-22 16:19:25 +01:00
{
2011-12-22 16:19:25 +01:00
/*
* Start bus scanning after the previous device's location.
* If no valid device was specified for 'prev_device',
* start at the beginning.
2011-12-22 16:19:25 +01:00
*/
int bus = 0, device = 0, function = -1;
2011-12-22 16:19:25 +01:00
if (prev) {
Device_config config = prev->config();
bus = config.bus_number();
device = config.device_number();
function = config.function_number();
}
2011-12-22 16:19:25 +01:00
/*
* Scan buses for devices.
* If no device is found, return an invalid capability.
*/
Device_config config;
while (true) {
function += 1;
if (!_pci_bus.find_next(bus, device, function, &config,
&config_access))
return Device_capability();
/* get new bdf values */
bus = config.bus_number();
device = config.device_number();
function = config.function_number();
2013-02-18 10:08:14 +01:00
/* if filter of driver don't match skip and continue */
if ((config.class_code() ^ device_class) & class_mask)
continue;
/* check that policy permit access to the matched device */
if (permit_device(Bdf { (unsigned)bus,
(unsigned)device,
(unsigned)function },
config.class_code()))
break;
}
2011-12-22 16:19:25 +01:00
/* lookup if we have a extended pci config space */
Genode::addr_t config_space =
lookup_config_space(config.bdf());
2011-12-22 16:19:25 +01:00
/*
* A device was found. Create a new device component for the
* device and return its capability.
*/
try {
Device_component * dev = new (_md_alloc)
Device_component(_env, config, config_space, config_access, *this,
_md_alloc, _global_heap);
2011-12-22 16:19:25 +01:00
/* if more than one driver uses the device - warn about */
if (bdf_in_use.get(Device_config::MAX_BUSES * bus +
Device_config::MAX_DEVICES * device +
function, 1))
Genode::error("Device ", config,
" is used by more than one driver - "
"session '", _label, "'.");
else
bdf_in_use.set(Device_config::MAX_BUSES * bus +
Device_config::MAX_DEVICES * device +
function, 1);
_device_list.insert(dev);
return _env.ep().rpc_ep().manage(dev);
}
catch (Genode::Out_of_ram) {
throw;
}
catch (Genode::Out_of_caps) {
Genode::warning("Out_of_caps during Device_component construction");
throw;
}
};
return _env.ep().rpc_ep().apply(prev_device, lambda);
}
void release_device(Device_capability device_cap) override
{
Device_component * device;
auto lambda = [&] (Device_component *d)
{
device = d;
if (!device)
return;
2011-12-22 16:19:25 +01:00
unsigned const bus = device->config().bus_number();
unsigned const dev = device->config().device_number();
unsigned const func = device->config().function_number();
if (bdf_in_use.get(Device_config::MAX_BUSES * bus +
Device_config::MAX_DEVICES * dev +
func, 1))
bdf_in_use.clear(Device_config::MAX_BUSES * bus +
Device_config::MAX_DEVICES * dev + func, 1);
_device_list.remove(device);
_env.ep().rpc_ep().dissolve(device);
};
/* lookup device component for previous device */
_env.ep().rpc_ep().apply(device_cap, lambda);
if (device && device->config().valid())
destroy(_md_alloc, device);
}
void assign_device(Device_component * device)
{
using namespace Genode;
if (!device || device->config_space() == ~0UL || !_iommu)
return;
2013-02-18 10:08:14 +01:00
try {
addr_t const function = device->config().bus_number() * 32 * 8 +
device->config().device_number() * 8 +
device->config().function_number();
addr_t const base_ecam = Dataspace_client(_pciconf.cap()).phys_addr();
addr_t const base_offset = 0x1000UL * function;
if (base_ecam + base_offset != device->config_space())
throw 1;
for (Rmrr *r = Rmrr::list()->first(); r; r = r->next()) {
Io_mem_dataspace_capability rmrr_cap = r->match(_env, device->config());
if (rmrr_cap.valid())
_device_pd.attach_dma_mem(rmrr_cap);
}
_device_pd.assign_pci(_pciconf.cap(), base_offset, device->config().bdf());
} catch (...) {
Genode::error("assignment to device pd or of RMRR region failed");
}
}
/**
* De-/Allocation of dma capable dataspaces
*/
Ram_capability alloc_dma_buffer(Genode::size_t const size) override
{
Ram_capability ram_cap = _env_ram.alloc(size, Genode::UNCACHED);
if (!ram_cap.valid())
return ram_cap;
try {
_device_pd.attach_dma_mem(ram_cap);
_insert(ram_cap);
} catch (Out_of_ram) {
_env_ram.free(ram_cap);
throw Genode::Out_of_ram();
} catch (Out_of_caps) {
_env_ram.free(ram_cap);
throw Genode::Out_of_caps();
}
return ram_cap;
}
2013-02-18 10:08:14 +01:00
void free_dma_buffer(Ram_capability ram_cap) override
{
if (!ram_cap.valid() || !_remove(ram_cap))
return;
_env_ram.free(ram_cap);
}
Device_capability device(String const &name) override;
};
2011-12-22 16:19:25 +01:00
class Platform::Root : public Genode::Root_component<Session_component>
{
private:
2011-12-22 16:19:25 +01:00
Genode::Env &_env;
Genode::Attached_rom_dataspace &_config;
Genode::Constructible<Genode::Attached_io_mem_dataspace> _pci_confspace { };
Genode::Constructible<Genode::Expanding_reporter> _pci_reporter { };
Genode::Heap _heap { _env.ram(), _env.rm() };
Genode::Constructible<Platform::Pci_buses> _buses { };
2011-12-22 16:19:25 +01:00
bool _iommu { false };
void _parse_report_rom(Genode::Env &env, const char * acpi_rom,
bool acpi_platform)
{
using namespace Genode;
Xml_node xml_acpi(acpi_rom);
if (!xml_acpi.has_type("acpi"))
throw 1;
xml_acpi.for_each_sub_node("bdf", [&] (Xml_node &node) {
uint32_t const bdf_start = node.attribute_value("start", 0U);
uint32_t const func_count = node.attribute_value("count", 0U);
addr_t const base = node.attribute_value("base", 0UL);
Session_component::add_config_space(bdf_start, func_count,
base, _heap);
Device_config const bdf_first(bdf_start);
Device_config const bdf_last(bdf_start + func_count - 1);
addr_t const memory_size = 0x1000UL * func_count;
/* Simplification: Only consider first config space and
* check if it is for domain 0 */
if (bdf_start || _pci_confspace.constructed()) {
warning("ECAM/MMCONF range ",
bdf_first, "-", bdf_last, " - addr ",
Hex_range<addr_t>(base, memory_size), " ignored");
return;
}
log("ECAM/MMCONF range ", bdf_first, "-", bdf_last, " - addr ",
Hex_range<addr_t>(base, memory_size));
_pci_confspace.construct(env, base, memory_size);
});
if (!_pci_confspace.constructed())
throw 2;
Config_access config_access(*_pci_confspace);
for (unsigned i = 0; i < xml_acpi.num_sub_nodes(); i++) {
Xml_node node = xml_acpi.sub_node(i);
if (node.has_type("bdf"))
continue;
if (node.has_type("irq_override")) {
unsigned const irq = node.attribute_value("irq", 0xffU);
unsigned const gsi = node.attribute_value("gsi", 0xffU);
unsigned const flags = node.attribute_value("flags", 0xffU);
if (!acpi_platform) {
warning("MADT IRQ ", irq, "-> GSI ", gsi, " flags ",
flags, " ignored");
continue;
}
using Platform::Irq_override;
Irq_override * o = new (_heap) Irq_override(irq, gsi,
flags);
Irq_override::list()->insert(o);
continue;
}
if (node.has_type("drhd")) {
_iommu = true;
continue;
}
if (node.has_type("rmrr")) {
uint64_t const
mem_start = node.attribute_value("start", (uint64_t)0),
mem_end = node.attribute_value("end", (uint64_t)0);
if (node.num_sub_nodes() == 0)
throw 3;
Rmrr * rmrr = new (_heap) Rmrr(mem_start, mem_end);
Rmrr::list()->insert(rmrr);
for (unsigned s = 0; s < node.num_sub_nodes(); s++) {
Xml_node scope = node.sub_node(s);
if (!scope.num_sub_nodes() || !scope.has_type("scope"))
throw 4;
unsigned bus = 0, dev = 0, func = 0;
scope.attribute("bus_start").value(bus);
for (unsigned p = 0; p < scope.num_sub_nodes(); p++) {
Xml_node path = scope.sub_node(p);
if (!path.has_type("path"))
throw 5;
path.attribute("dev") .value(dev);
path.attribute("func").value(func);
Device_config bridge(bus, dev, func,
&config_access);
if (bridge.pci_bridge())
/* PCI bridge spec 3.2.5.3, 3.2.5.4 */
bus = bridge.read(config_access, 0x19,
Device::ACCESS_8BIT);
}
rmrr->add(new (_heap) Rmrr::Bdf(bus, dev, func));
}
continue;
}
if (node.has_type("root_bridge")) {
node.attribute("bdf").value(Platform::Bridge::root_bridge_bdf);
continue;
}
if (!node.has_type("routing")) {
error ("unsupported node '", node.type(), "'");
throw __LINE__;
}
2011-12-22 16:19:25 +01:00
unsigned const gsi = node.attribute_value("gsi", 0U),
bridge_bdf = node.attribute_value("bridge_bdf", 0U),
device = node.attribute_value("device", 0U),
device_pin = node.attribute_value("device_pin", 0U);
/* drop routing information on non ACPI platform */
if (!acpi_platform)
continue;
Irq_routing * r = new (_heap) Irq_routing(gsi, bridge_bdf,
device, device_pin);
Irq_routing::list()->insert(r);
}
}
void _construct_buses()
{
Genode::Dataspace_client ds_pci_mmio(_pci_confspace->cap());
uint64_t const phys_addr = ds_pci_mmio.phys_addr();
uint64_t const phys_size = ds_pci_mmio.size();
uint64_t mmio_size = 0x10000000UL; /* max MMCONF memory */
/* try surviving wrong ACPI ECAM/MMCONF table information */
while (true) {
try {
_buses.construct(_heap, *_pci_confspace);
/* construction and scan succeeded */
break;
} catch (Platform::Config_access::Invalid_mmio_access) {
error("ECAM/MMCONF MMIO access out of bounds - "
"ACPI table information is wrong!");
_pci_confspace.destruct();
while (mmio_size > phys_size) {
try {
error(" adjust size from ", Hex(phys_size),
"->", Hex(mmio_size));
_pci_confspace.construct(_env, phys_addr, mmio_size);
/* got memory - try again */
break;
} catch (Genode::Service_denied) {
/* decrease by one bus memory size */
mmio_size -= 0x1000UL * 32 * 8;
}
}
if (mmio_size <= phys_size)
/* broken machine - you're lost */
throw;
}
}
}
protected:
Session_component *_create_session(const char *args)
{
try {
return new (md_alloc())
Session_component(_env, _config, *_pci_confspace, *_buses,
_heap, args, _iommu);
}
catch (Genode::Session_policy::No_policy_defined) {
Genode::error("Invalid session request, no matching policy for ",
"'", Genode::label_from_args(args).string(), "'");
throw Genode::Service_denied();
}
}
void _upgrade_session(Session_component *s, const char *args) override {
s->upgrade_resources(Genode::session_resources_from_args(args)); }
public:
/**
* Constructor
*
* \param ep entry point to be used for serving the PCI session
* and PCI device interface
* \param md_alloc meta-data allocator for allocating PCI-session
* components and PCI-device components
*/
Root(Genode::Env &env, Genode::Allocator &md_alloc,
Genode::Attached_rom_dataspace &config,
const char *acpi_rom,
bool acpi_platform)
:
Genode::Root_component<Session_component>(&env.ep().rpc_ep(),
&md_alloc),
_env(env), _config(config)
{
try {
_parse_report_rom(env, acpi_rom, acpi_platform);
} catch (...) {
Genode::error("ACPI report parsing error.");
throw;
2011-12-22 16:19:25 +01:00
}
if (Platform::Bridge::root_bridge_bdf < Platform::Bridge::INVALID_ROOT_BRIDGE) {
Device_config config(Platform::Bridge::root_bridge_bdf);
Genode::log("Root bridge: ", config);
} else
Genode::warning("Root bridge: unknown");
_construct_buses();
if (config.xml().has_sub_node("report")
&& config.xml().sub_node("report").attribute_value("pci", true)) {
_pci_reporter.construct(_env, "pci", "pci");
Config_access config_access(*_pci_confspace);
Device_config config;
int bus = 0, device = 0, function = -1;
_pci_reporter->generate([&] (Genode::Reporter::Xml_generator &xml) {
/* iterate over pci devices */
while (true) {
function += 1;
if (!(*_buses).find_next(bus, device, function, &config,
&config_access))
return;
bus = config.bus_number();
device = config.device_number();
function = config.function_number();
using Genode::String;
using Genode::Hex;
xml.node("device", [&] () {
xml.attribute("bdf" , String<8>(Hex(bus, Hex::Prefix::OMIT_PREFIX), ":", Hex(device, Hex::Prefix::OMIT_PREFIX), ".", function));
xml.attribute("vendor_id" , String<8>(Hex(config.vendor_id())));
xml.attribute("device_id" , String<8>(Hex(config.device_id())));
xml.attribute("class_code", String<12>(Hex(config.class_code())));
xml.attribute("bridge" , config.pci_bridge() ? "yes" : "no");
});
}
});
}
}
};