2011-12-22 16:19:25 +01:00
|
|
|
/*
|
|
|
|
* \brief PCI-session component
|
|
|
|
* \author Norman Feske
|
|
|
|
* \date 2008-01-28
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
2013-01-10 21:44:47 +01:00
|
|
|
* Copyright (C) 2008-2013 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 General Public License version 2.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#ifndef _PCI_SESSION_COMPONENT_H_
|
|
|
|
#define _PCI_SESSION_COMPONENT_H_
|
|
|
|
|
2015-04-17 14:12:30 +02:00
|
|
|
/* base */
|
2015-04-16 15:54:07 +02:00
|
|
|
#include <base/allocator_guard.h>
|
2011-12-22 16:19:25 +01:00
|
|
|
#include <base/rpc_server.h>
|
2015-04-16 15:54:07 +02:00
|
|
|
#include <base/tslab.h>
|
|
|
|
|
2015-04-17 14:12:30 +02:00
|
|
|
#include <ram_session/connection.h>
|
2011-12-22 16:19:25 +01:00
|
|
|
#include <root/component.h>
|
2015-05-24 16:58:38 +02:00
|
|
|
#include <root/client.h>
|
2011-12-22 16:19:25 +01:00
|
|
|
|
2015-04-17 14:12:30 +02:00
|
|
|
/* os */
|
2013-02-18 10:06:34 +01:00
|
|
|
#include <io_mem_session/connection.h>
|
2015-04-22 17:06:10 +02:00
|
|
|
#include <os/session_policy.h>
|
2015-04-17 14:12:30 +02:00
|
|
|
#include <pci_session/pci_session.h>
|
2013-02-18 10:06:34 +01:00
|
|
|
|
2015-04-17 14:12:30 +02:00
|
|
|
/* local */
|
2011-12-22 16:19:25 +01:00
|
|
|
#include "pci_device_component.h"
|
|
|
|
#include "pci_config_access.h"
|
2013-02-18 10:08:14 +01:00
|
|
|
#include "pci_device_pd_ipc.h"
|
2011-12-22 16:19:25 +01:00
|
|
|
|
|
|
|
namespace Pci {
|
2015-04-17 14:12:30 +02:00
|
|
|
bool bus_valid(int bus = 0);
|
2015-05-26 20:12:17 +02:00
|
|
|
unsigned short bridge_bdf(unsigned char bus);
|
2015-04-17 14:12:30 +02:00
|
|
|
}
|
2011-12-22 16:19:25 +01:00
|
|
|
|
2015-04-17 14:12:30 +02:00
|
|
|
namespace Pci {
|
2011-12-22 16:19:25 +01:00
|
|
|
|
|
|
|
class Session_component : public Genode::Rpc_object<Session>
|
|
|
|
{
|
|
|
|
private:
|
|
|
|
|
2015-04-16 15:54:07 +02:00
|
|
|
Genode::Rpc_entrypoint *_ep;
|
|
|
|
Genode::Allocator_guard _md_alloc;
|
|
|
|
Genode::Tslab<Device_component, 4096 - 64> _device_slab;
|
|
|
|
Genode::List<Device_component> _device_list;
|
2015-05-24 16:58:38 +02:00
|
|
|
Device_pd_client _child;
|
2015-04-16 15:54:07 +02:00
|
|
|
Genode::Ram_connection *_ram;
|
|
|
|
Genode::Session_label _label;
|
|
|
|
Genode::Session_policy _policy;
|
2011-12-22 16:19:25 +01:00
|
|
|
|
2015-04-20 11:38:55 +02:00
|
|
|
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;
|
|
|
|
|
2015-05-26 20:12:17 +02:00
|
|
|
|
2011-12-22 16:19:25 +01:00
|
|
|
/**
|
2013-02-18 10:08:14 +01:00
|
|
|
* Scan PCI buses for a device
|
2011-12-22 16:19:25 +01:00
|
|
|
*
|
|
|
|
* \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;
|
|
|
|
}
|
|
|
|
|
2013-02-18 10:06:34 +01:00
|
|
|
/**
|
|
|
|
* 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::uint8_t bus, Genode::uint8_t dev,
|
|
|
|
Genode::uint8_t func)
|
|
|
|
{
|
|
|
|
using namespace Genode;
|
|
|
|
|
|
|
|
uint32_t bdf = (bus << 8) | ((dev & 0x1f) << 3) | (func & 0x7);
|
|
|
|
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);
|
2014-01-24 10:11:25 +01:00
|
|
|
|
2013-02-18 10:06:34 +01:00
|
|
|
return config_space;
|
|
|
|
}
|
2011-12-22 16:19:25 +01:00
|
|
|
|
2015-04-22 17:06:10 +02:00
|
|
|
/*
|
|
|
|
* List of aliases for PCI Class/Subclas/Prog I/F triple used
|
|
|
|
* by xml config for this platform driver
|
|
|
|
*/
|
|
|
|
unsigned class_subclass_prog(const char * 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},
|
2015-04-30 11:30:28 +02:00
|
|
|
{ "AUDIO" , 0x4, 0x00, 0x0},
|
2015-04-22 17:06:10 +02:00
|
|
|
{ "ETHERNET", 0x2, 0x00, 0x0},
|
|
|
|
{ "USB" , 0xc, 0x03, 0x0},
|
|
|
|
{ "VGA" , 0x3, 0x00, 0x0},
|
|
|
|
{ "WIFI" , 0x2, 0x80, 0x0}
|
|
|
|
};
|
|
|
|
|
|
|
|
for (unsigned i = 0; i < sizeof(aliases) / sizeof(aliases[0]); i++) {
|
|
|
|
if (strcmp(aliases[i].alias, name))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
return 0U | aliases[i].pci_class << 16 |
|
|
|
|
aliases[i].pci_subclass << 8 |
|
|
|
|
aliases[i].pci_progif;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ~0U;
|
|
|
|
}
|
|
|
|
|
2015-05-15 11:27:05 +02:00
|
|
|
/**
|
|
|
|
* Check whether msi usage was explicitly switched off
|
|
|
|
*/
|
|
|
|
bool msi_usage()
|
|
|
|
{
|
|
|
|
try {
|
|
|
|
char mode[8];
|
|
|
|
_policy.attribute("irq_mode").value(mode, sizeof(mode));
|
|
|
|
if (!Genode::strcmp("nomsi", mode))
|
|
|
|
return false;
|
|
|
|
} catch (Genode::Xml_node::Nonexistent_attribute) { }
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2015-04-22 17:06:10 +02:00
|
|
|
* 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) {
|
|
|
|
try {
|
|
|
|
/* enforce restriction based on name name */
|
|
|
|
char policy_name[8];
|
|
|
|
dev.attribute("name").value(policy_name,
|
|
|
|
sizeof(policy_name));
|
|
|
|
|
|
|
|
if (!strcmp(policy_name, name))
|
|
|
|
/* found identical match - permit access */
|
|
|
|
throw true;
|
|
|
|
|
|
|
|
} catch (Xml_attribute::Nonexistent_attribute) { }
|
|
|
|
});
|
|
|
|
} catch (bool result) { return result; }
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2015-05-15 11:27:05 +02:00
|
|
|
/**
|
2015-04-22 17:06:10 +02:00
|
|
|
* Check according session policy device usage
|
|
|
|
*/
|
|
|
|
bool permit_device(Genode::uint8_t b, Genode::uint8_t d,
|
|
|
|
Genode::uint8_t f, unsigned class_code)
|
|
|
|
{
|
|
|
|
using namespace Genode;
|
|
|
|
|
|
|
|
try {
|
|
|
|
_policy.for_each_sub_node("pci", [&] (Xml_node node) {
|
|
|
|
try {
|
|
|
|
unsigned bus, device, function;
|
|
|
|
|
|
|
|
node.attribute("bus").value(&bus);
|
|
|
|
node.attribute("device").value(&device);
|
|
|
|
node.attribute("function").value(&function);
|
|
|
|
|
|
|
|
if (b == bus && d == device && f == function)
|
|
|
|
throw true;
|
|
|
|
|
|
|
|
return;
|
|
|
|
} catch (Xml_attribute::Nonexistent_attribute) { }
|
|
|
|
|
|
|
|
/* enforce restriction based upon classes */
|
|
|
|
unsigned class_sub_prog = 0;
|
|
|
|
|
|
|
|
try {
|
|
|
|
char alias_class[32];
|
|
|
|
node.attribute("class").value(alias_class,
|
|
|
|
sizeof(alias_class));
|
|
|
|
|
|
|
|
class_sub_prog = class_subclass_prog(alias_class);
|
|
|
|
} catch (Xml_attribute::Nonexistent_attribute) {
|
|
|
|
return;
|
|
|
|
}
|
2015-05-26 10:40:22 +02:00
|
|
|
|
|
|
|
enum { DONT_CHECK_PROGIF = 8 };
|
|
|
|
/* if class/subclass don't match - deny */
|
2015-06-04 09:36:05 +02:00
|
|
|
if (class_sub_prog && (class_sub_prog ^ class_code) >> DONT_CHECK_PROGIF)
|
2015-04-22 17:06:10 +02:00
|
|
|
return;
|
|
|
|
|
|
|
|
/* if this bdf is used by some policy - deny */
|
|
|
|
if (find_dev_in_policy(b, d, f))
|
|
|
|
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_node().for_each_sub_node("policy", [&] (Xml_node policy) {
|
|
|
|
policy.for_each_sub_node("device", [&] (Xml_node device) {
|
|
|
|
try {
|
|
|
|
/* device attribute from policy node */
|
|
|
|
char policy_device[8];
|
|
|
|
device.attribute("name").value(policy_device, sizeof(policy_device));
|
|
|
|
|
|
|
|
if (!strcmp(policy_device, dev_name)) {
|
|
|
|
if (once)
|
|
|
|
throw true;
|
|
|
|
once = true;
|
|
|
|
}
|
|
|
|
} catch (Xml_node::Nonexistent_attribute) { }
|
|
|
|
});
|
|
|
|
});
|
|
|
|
} catch (bool result) { return result; }
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Lookup a given device name.
|
|
|
|
*/
|
|
|
|
bool find_dev_in_policy(Genode::uint8_t b, Genode::uint8_t d,
|
|
|
|
Genode::uint8_t f, bool once = true)
|
|
|
|
{
|
|
|
|
using namespace Genode;
|
|
|
|
|
|
|
|
try {
|
|
|
|
Xml_node xml(config()->xml_node());
|
|
|
|
xml.for_each_sub_node("policy", [&] (Xml_node policy) {
|
|
|
|
policy.for_each_sub_node("pci", [&] (Xml_node node) {
|
|
|
|
try {
|
|
|
|
unsigned bus, device, function;
|
|
|
|
|
|
|
|
node.attribute("bus").value(&bus);
|
|
|
|
node.attribute("device").value(&device);
|
|
|
|
node.attribute("function").value(&function);
|
|
|
|
|
|
|
|
if (b == bus && d == device && f == function) {
|
|
|
|
if (once)
|
|
|
|
throw true;
|
|
|
|
once = true;
|
|
|
|
}
|
|
|
|
} catch (Xml_node::Nonexistent_attribute) { }
|
|
|
|
});
|
|
|
|
});
|
|
|
|
} catch (bool result) { return result; }
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2011-12-22 16:19:25 +01:00
|
|
|
public:
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Constructor
|
|
|
|
*/
|
2015-05-24 16:58:38 +02:00
|
|
|
Session_component(Genode::Rpc_entrypoint *ep,
|
|
|
|
Genode::Allocator *md_alloc,
|
|
|
|
Genode::Root_capability &device_pd_root,
|
|
|
|
Genode::Ram_connection *ram,
|
|
|
|
const char *args)
|
2013-02-18 10:08:14 +01:00
|
|
|
:
|
2015-04-16 15:54:07 +02:00
|
|
|
_ep(ep),
|
|
|
|
_md_alloc(md_alloc, Genode::Arg_string::find_arg(args, "ram_quota").long_value(0)),
|
2015-05-24 16:58:38 +02:00
|
|
|
_device_slab(&_md_alloc), _child(Genode::reinterpret_cap_cast<Device_pd>(Genode::Native_capability())),
|
|
|
|
_ram(ram), _label(args), _policy(_label)
|
2015-04-22 17:06:10 +02:00
|
|
|
{
|
|
|
|
/* non-pci devices */
|
|
|
|
_policy.for_each_sub_node("device", [&] (Genode::Xml_node device_node) {
|
|
|
|
try {
|
|
|
|
char policy_device[8];
|
|
|
|
device_node.attribute("name").value(policy_device,
|
|
|
|
sizeof(policy_device));
|
|
|
|
|
|
|
|
enum { DOUBLET = false };
|
|
|
|
if (!find_dev_in_policy(policy_device, DOUBLET))
|
|
|
|
return;
|
|
|
|
|
|
|
|
PERR("'%s' - device '%s' is part of more than one policy",
|
|
|
|
_label.string(), policy_device);
|
|
|
|
} catch (Genode::Xml_node::Nonexistent_attribute) {
|
|
|
|
PERR("'%s' - device node misses a 'name' attribute",
|
|
|
|
_label.string());
|
|
|
|
}
|
|
|
|
throw Genode::Root::Unavailable();
|
|
|
|
});
|
|
|
|
|
2015-05-24 16:58:38 +02:00
|
|
|
if (device_pd_root.valid()) {
|
|
|
|
using Genode::Session_capability;
|
|
|
|
using Genode::Affinity;
|
|
|
|
|
|
|
|
Session_capability session = Genode::Root_client(device_pd_root).session("", Affinity());
|
|
|
|
Genode::Capability <Device_pd> cap = Genode::reinterpret_cap_cast<Device_pd>(session);
|
|
|
|
_child = Device_pd_client(cap);
|
|
|
|
}
|
|
|
|
|
2015-04-22 17:06:10 +02:00
|
|
|
/* pci devices */
|
|
|
|
_policy.for_each_sub_node("pci", [&] (Genode::Xml_node node) {
|
|
|
|
enum { INVALID_CLASS = 0x1000000U };
|
|
|
|
unsigned class_sub_prog = INVALID_CLASS;
|
|
|
|
|
|
|
|
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.
|
|
|
|
*/
|
|
|
|
try {
|
|
|
|
char alias_class[32];
|
|
|
|
node.attribute("class").value(alias_class,
|
|
|
|
sizeof(alias_class));
|
|
|
|
|
|
|
|
class_sub_prog = class_subclass_prog(alias_class);
|
|
|
|
if (class_sub_prog >= INVALID_CLASS) {
|
|
|
|
PERR("'%s' - invalid 'class' attribute '%s'",
|
|
|
|
_label.string(), alias_class);
|
|
|
|
throw Genode::Root::Unavailable();
|
|
|
|
}
|
|
|
|
} catch (Xml_attribute::Nonexistent_attribute) { }
|
|
|
|
|
|
|
|
/* if we read a class attribute all is fine */
|
|
|
|
if (class_sub_prog < INVALID_CLASS) {
|
|
|
|
/* sanity check that 'class' is the only attribute */
|
|
|
|
try {
|
|
|
|
node.attribute(1);
|
|
|
|
PERR("'%s' - attributes beside 'class' detected",
|
|
|
|
_label.string());
|
|
|
|
throw Genode::Root::Unavailable();
|
|
|
|
} 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);
|
|
|
|
PERR("'%s' - invalid number of pci node attributes",
|
|
|
|
_label.string());
|
|
|
|
throw Genode::Root::Unavailable();
|
|
|
|
} catch (Xml_attribute::Nonexistent_attribute) { }
|
|
|
|
|
|
|
|
try {
|
|
|
|
unsigned bus, device, function;
|
|
|
|
|
|
|
|
node.attribute("bus").value(&bus);
|
|
|
|
node.attribute("device").value(&device);
|
|
|
|
node.attribute("function").value(&function);
|
|
|
|
|
|
|
|
if ((bus >= Device_config::MAX_BUSES) ||
|
|
|
|
(device >= Device_config::MAX_DEVICES) ||
|
|
|
|
(function >= Device_config::MAX_FUNCTIONS))
|
|
|
|
throw Xml_attribute::Nonexistent_attribute();
|
|
|
|
|
|
|
|
enum { DOUBLET = false };
|
|
|
|
if (!find_dev_in_policy(bus, device, function, DOUBLET))
|
|
|
|
return;
|
|
|
|
|
|
|
|
PERR("'%s' - device '%2x:%2x.%x' is part of more than "
|
|
|
|
"one policy", _label.string(), bus, device,
|
|
|
|
function);
|
|
|
|
} catch (Xml_attribute::Nonexistent_attribute) {
|
|
|
|
PERR("'%s' - invalid pci node attributes for bdf",
|
|
|
|
_label.string());
|
|
|
|
}
|
|
|
|
throw Genode::Root::Unavailable();
|
|
|
|
});
|
|
|
|
}
|
2011-12-22 16:19:25 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Destructor
|
|
|
|
*/
|
|
|
|
~Session_component()
|
|
|
|
{
|
|
|
|
/* release all elements of the session's device list */
|
|
|
|
while (_device_list.first())
|
|
|
|
release_device(_device_list.first()->cap());
|
|
|
|
}
|
|
|
|
|
2015-04-16 15:54:07 +02:00
|
|
|
|
|
|
|
void upgrade_ram_quota(long quota) { _md_alloc.upgrade(quota); }
|
|
|
|
|
|
|
|
|
2013-02-18 10:06:34 +01:00
|
|
|
static void add_config_space(Genode::uint32_t bdf_start,
|
|
|
|
Genode::uint32_t func_count,
|
|
|
|
Genode::addr_t base)
|
|
|
|
{
|
|
|
|
using namespace Genode;
|
|
|
|
Config_space * space =
|
|
|
|
new (env()->heap()) Config_space(bdf_start, func_count,
|
|
|
|
base);
|
|
|
|
config_space_list().insert(space);
|
|
|
|
}
|
|
|
|
|
2011-12-22 16:19:25 +01:00
|
|
|
|
|
|
|
/***************************
|
|
|
|
** PCI session interface **
|
|
|
|
***************************/
|
|
|
|
|
2013-02-18 11:20:15 +01:00
|
|
|
Device_capability first_device(unsigned device_class,
|
|
|
|
unsigned class_mask) {
|
|
|
|
return next_device(Device_capability(), device_class, class_mask); }
|
2011-12-22 16:19:25 +01:00
|
|
|
|
2013-02-18 11:20:15 +01:00
|
|
|
Device_capability next_device(Device_capability prev_device,
|
|
|
|
unsigned device_class,
|
|
|
|
unsigned class_mask)
|
2011-12-22 16:19:25 +01:00
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Create the interface to the PCI config space.
|
|
|
|
* This involves the creation of I/O port sessions.
|
|
|
|
*/
|
|
|
|
Config_access config_access;
|
|
|
|
|
|
|
|
/* lookup device component for previous device */
|
2012-12-14 10:03:55 +01:00
|
|
|
Genode::Object_pool<Device_component>::Guard
|
|
|
|
prev(_ep->lookup_and_lock(prev_device));
|
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.
|
|
|
|
*/
|
2013-02-18 11:20:15 +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();
|
2013-02-18 11:20:15 +01:00
|
|
|
function = config.function_number();
|
2011-12-22 16:19:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2013-02-18 11:20:15 +01:00
|
|
|
* Scan buses for devices.
|
2011-12-22 16:19:25 +01:00
|
|
|
* If no device is found, return an invalid capability.
|
|
|
|
*/
|
|
|
|
Device_config config;
|
|
|
|
|
2015-04-22 17:06:10 +02:00
|
|
|
while (true) {
|
2013-02-18 11:20:15 +01:00
|
|
|
function += 1;
|
|
|
|
if (!_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();
|
2015-04-22 17:06:10 +02: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(bus, device, function,
|
|
|
|
config.class_code()))
|
|
|
|
break;
|
|
|
|
}
|
2013-02-18 10:06:34 +01:00
|
|
|
|
|
|
|
/* lookup if we have a extended pci config space */
|
|
|
|
Genode::addr_t config_space = lookup_config_space(bus, device,
|
|
|
|
function);
|
2013-02-18 10:08:14 +01:00
|
|
|
|
2011-12-22 16:19:25 +01:00
|
|
|
/*
|
|
|
|
* A device was found. Create a new device component for the
|
|
|
|
* device and return its capability.
|
|
|
|
*/
|
2015-04-16 15:54:07 +02:00
|
|
|
try {
|
2015-05-26 20:12:17 +02:00
|
|
|
Device_component * dev = new (_device_slab) Device_component(config, config_space, _ep, this, msi_usage());
|
2015-05-12 15:35:19 +02:00
|
|
|
|
2015-04-20 11:38:55 +02: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))
|
|
|
|
PERR("Device %2x:%2x.%u is used by more than one "
|
|
|
|
"driver - session '%s'.", bus, device, function,
|
|
|
|
_label.string());
|
|
|
|
else
|
|
|
|
bdf_in_use.set(Device_config::MAX_BUSES * bus +
|
|
|
|
Device_config::MAX_DEVICES * device +
|
|
|
|
function, 1);
|
|
|
|
|
2015-04-16 15:54:07 +02:00
|
|
|
_device_list.insert(dev);
|
|
|
|
return _ep->manage(dev);
|
|
|
|
} catch (Genode::Allocator::Out_of_memory) {
|
|
|
|
throw Device::Quota_exceeded();
|
|
|
|
}
|
2011-12-22 16:19:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void release_device(Device_capability device_cap)
|
|
|
|
{
|
|
|
|
/* lookup device component for previous device */
|
|
|
|
Device_component *device = dynamic_cast<Device_component *>
|
2012-12-14 10:03:55 +01:00
|
|
|
(_ep->lookup_and_lock(device_cap));
|
2011-12-22 16:19:25 +01:00
|
|
|
|
|
|
|
if (!device)
|
|
|
|
return;
|
|
|
|
|
2015-04-20 11:38:55 +02:00
|
|
|
unsigned const bus = device->config().bus_number();
|
|
|
|
unsigned const dev = device->config().device_number();
|
|
|
|
unsigned const func = device->config().function_number();
|
|
|
|
|
|
|
|
bdf_in_use.clear(Device_config::MAX_BUSES * bus +
|
|
|
|
Device_config::MAX_DEVICES * dev + func, 1);
|
|
|
|
|
2011-12-22 16:19:25 +01:00
|
|
|
_device_list.remove(device);
|
|
|
|
_ep->dissolve(device);
|
|
|
|
|
2015-04-16 15:54:07 +02:00
|
|
|
if (device->config().valid())
|
|
|
|
destroy(_device_slab, device);
|
|
|
|
else
|
|
|
|
destroy(_md_alloc, device);
|
2011-12-22 16:19:25 +01:00
|
|
|
}
|
2013-02-18 10:06:34 +01:00
|
|
|
|
2015-05-05 18:43:58 +02:00
|
|
|
Genode::Io_mem_dataspace_capability assign_device(Device_component * device)
|
2013-02-18 10:06:34 +01:00
|
|
|
{
|
|
|
|
using namespace Genode;
|
|
|
|
|
2015-05-05 18:43:58 +02:00
|
|
|
if (!device || !device->get_config_space().valid())
|
2013-02-18 10:06:34 +01:00
|
|
|
return Io_mem_dataspace_capability();
|
|
|
|
|
2015-05-05 18:43:58 +02:00
|
|
|
Io_mem_dataspace_capability io_mem = device->get_config_space();
|
2013-02-18 10:06:34 +01:00
|
|
|
|
2015-05-24 16:58:38 +02:00
|
|
|
if (_child.valid())
|
|
|
|
_child.assign_pci(io_mem);
|
2013-02-18 10:08:14 +01:00
|
|
|
|
2015-04-24 12:21:18 +02:00
|
|
|
/*
|
|
|
|
* By now forbid usage of extended pci config space dataspace,
|
|
|
|
* - until required.
|
|
|
|
*/
|
2015-05-05 18:43:58 +02:00
|
|
|
// return io_mem;
|
2015-04-24 12:21:18 +02:00
|
|
|
return Io_mem_dataspace_capability();
|
2013-02-18 10:06:34 +01:00
|
|
|
}
|
2013-02-18 10:30:29 +01:00
|
|
|
|
2015-05-05 18:43:58 +02:00
|
|
|
Genode::Io_mem_dataspace_capability config_extended(Device_capability device_cap)
|
|
|
|
{
|
|
|
|
using namespace Genode;
|
|
|
|
|
|
|
|
Object_pool<Device_component>::Guard
|
|
|
|
device(_ep->lookup_and_lock(device_cap));
|
|
|
|
|
|
|
|
return assign_device(device);
|
|
|
|
}
|
|
|
|
|
2015-02-08 13:02:11 +01:00
|
|
|
/**
|
|
|
|
* De-/Allocation of dma capable dataspaces
|
|
|
|
*/
|
|
|
|
typedef Genode::Ram_dataspace_capability Ram_capability;
|
|
|
|
|
2015-04-22 16:31:39 +02:00
|
|
|
Ram_capability alloc_dma_buffer(Genode::size_t size)
|
2013-02-18 10:08:14 +01:00
|
|
|
{
|
2015-04-16 15:54:07 +02:00
|
|
|
if (!_md_alloc.withdraw(size))
|
|
|
|
throw Device::Quota_exceeded();
|
|
|
|
|
|
|
|
Genode::Ram_session * const rs = Genode::env()->ram_session();
|
|
|
|
if (rs->transfer_quota(_ram->cap(), size)) {
|
|
|
|
_md_alloc.upgrade(size);
|
|
|
|
return Ram_capability();
|
|
|
|
}
|
|
|
|
|
|
|
|
Ram_capability ram_cap;
|
|
|
|
try {
|
|
|
|
|
|
|
|
ram_cap = _ram->alloc(size, Genode::UNCACHED);
|
|
|
|
} catch (Genode::Ram_session::Quota_exceeded) {
|
|
|
|
_md_alloc.upgrade(size);
|
2015-02-08 13:02:11 +01:00
|
|
|
return Ram_capability();
|
2015-04-16 15:54:07 +02:00
|
|
|
}
|
2013-02-18 10:08:14 +01:00
|
|
|
|
2015-05-24 16:58:38 +02:00
|
|
|
if (!ram_cap.valid() || !_child.valid())
|
2015-04-16 15:54:07 +02:00
|
|
|
return ram_cap;
|
2013-02-18 10:08:14 +01:00
|
|
|
|
2015-05-24 16:58:38 +02:00
|
|
|
_child.attach_dma_mem(ram_cap);
|
2013-02-18 10:08:14 +01:00
|
|
|
|
2015-04-16 15:54:07 +02:00
|
|
|
return ram_cap;
|
2013-02-18 10:08:14 +01:00
|
|
|
}
|
2014-01-24 10:11:25 +01:00
|
|
|
|
2015-04-22 16:31:39 +02:00
|
|
|
void free_dma_buffer(Ram_capability ram)
|
2014-01-24 10:11:25 +01:00
|
|
|
{
|
2015-02-08 13:02:11 +01:00
|
|
|
if (ram.valid())
|
|
|
|
_ram->free(ram);
|
2014-01-24 10:11:25 +01:00
|
|
|
}
|
2015-04-17 14:12:30 +02:00
|
|
|
|
|
|
|
Device_capability device(String const &name) override;
|
2011-12-22 16:19:25 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
class Root : public Genode::Root_component<Session_component>
|
|
|
|
{
|
|
|
|
private:
|
|
|
|
|
2015-05-24 16:58:38 +02:00
|
|
|
Genode::Root_capability _device_pd_root;
|
2015-02-08 13:02:11 +01:00
|
|
|
/* Ram_session for allocation of dma capable dataspaces */
|
|
|
|
Genode::Ram_connection _ram;
|
2013-02-18 10:06:34 +01:00
|
|
|
|
2015-05-26 20:12:17 +02:00
|
|
|
void _parse_report_rom(const char * acpi_rom)
|
2013-02-18 10:06:34 +01:00
|
|
|
{
|
|
|
|
using namespace Genode;
|
|
|
|
|
|
|
|
try {
|
2015-05-26 20:12:17 +02:00
|
|
|
Xml_node xml_acpi(acpi_rom);
|
|
|
|
if (!xml_acpi.has_type("acpi"))
|
|
|
|
throw 1;
|
|
|
|
|
2013-02-18 10:06:34 +01:00
|
|
|
unsigned i;
|
|
|
|
|
2015-05-26 20:12:17 +02:00
|
|
|
for (i = 0; i < xml_acpi.num_sub_nodes(); i++)
|
2013-02-18 10:06:34 +01:00
|
|
|
{
|
2015-05-26 20:12:17 +02:00
|
|
|
Xml_node node = xml_acpi.sub_node(i);
|
|
|
|
|
|
|
|
if (node.has_type("bdf")) {
|
|
|
|
|
|
|
|
uint32_t bdf_start = 0;
|
|
|
|
uint32_t func_count = 0;
|
|
|
|
addr_t base = 0;
|
2015-04-22 17:06:10 +02:00
|
|
|
|
2015-05-26 20:12:17 +02:00
|
|
|
node.attribute("start").value(&bdf_start);
|
|
|
|
node.attribute("count").value(&func_count);
|
|
|
|
node.attribute("base").value(&base);
|
2015-04-22 17:06:10 +02:00
|
|
|
|
2015-05-26 20:12:17 +02:00
|
|
|
Session_component::add_config_space(bdf_start,
|
|
|
|
func_count,
|
|
|
|
base);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (node.has_type("irq_override")) {
|
|
|
|
unsigned irq = 0xff;
|
|
|
|
unsigned gsi = 0xff;
|
|
|
|
unsigned flags = 0xff;
|
|
|
|
|
|
|
|
node.attribute("irq").value(&irq);
|
|
|
|
node.attribute("gsi").value(&gsi);
|
|
|
|
node.attribute("flags").value(&flags);
|
|
|
|
|
|
|
|
using Pci::Irq_override;
|
|
|
|
Irq_override::list()->insert(new (env()->heap()) Irq_override(irq, gsi, flags));
|
|
|
|
}
|
2013-02-18 10:06:34 +01:00
|
|
|
|
2015-05-26 20:12:17 +02:00
|
|
|
if (node.has_type("routing")) {
|
|
|
|
unsigned gsi;
|
|
|
|
unsigned bridge_bdf;
|
|
|
|
unsigned device;
|
|
|
|
unsigned device_pin;
|
2013-02-18 10:06:34 +01:00
|
|
|
|
2015-05-26 20:12:17 +02:00
|
|
|
node.attribute("gsi").value(&gsi);
|
|
|
|
node.attribute("bridge_bdf").value(&bridge_bdf);
|
|
|
|
node.attribute("device").value(&device);
|
|
|
|
node.attribute("device_pin").value(&device_pin);
|
|
|
|
|
|
|
|
Irq_routing::list()->insert(new (env()->heap()) Irq_routing(gsi, bridge_bdf, device, device_pin));
|
|
|
|
}
|
2013-02-18 10:06:34 +01:00
|
|
|
}
|
|
|
|
} catch (...) {
|
|
|
|
PERR("PCI config space data could not be parsed.");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-12-22 16:19:25 +01:00
|
|
|
protected:
|
|
|
|
|
|
|
|
Session_component *_create_session(const char *args)
|
|
|
|
{
|
2015-04-22 17:06:10 +02:00
|
|
|
try {
|
|
|
|
return new (md_alloc()) Session_component(ep(), md_alloc(),
|
2015-05-24 16:58:38 +02:00
|
|
|
_device_pd_root,
|
2015-04-22 17:06:10 +02:00
|
|
|
&_ram, args);
|
|
|
|
} catch (Genode::Session_policy::No_policy_defined) {
|
|
|
|
PERR("Invalid session request, no matching policy for '%s'",
|
|
|
|
Genode::Session_label(args).string());
|
|
|
|
throw Genode::Root::Unavailable();
|
|
|
|
}
|
2011-12-22 16:19:25 +01:00
|
|
|
}
|
|
|
|
|
2015-04-16 15:54:07 +02:00
|
|
|
|
|
|
|
void _upgrade_session(Session_component *s, const char *args) override
|
|
|
|
{
|
|
|
|
long ram_quota = Genode::Arg_string::find_arg(args, "ram_quota").long_value(0);
|
|
|
|
s->upgrade_ram_quota(ram_quota);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-12-22 16:19:25 +01:00
|
|
|
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
|
|
|
|
*/
|
2013-02-18 10:08:14 +01:00
|
|
|
Root(Genode::Rpc_entrypoint *ep, Genode::Allocator *md_alloc,
|
|
|
|
Genode::size_t pci_device_pd_ram_quota,
|
2015-05-26 20:12:17 +02:00
|
|
|
Genode::Root_capability &device_pd_root,
|
|
|
|
const char *acpi_rom)
|
2011-12-22 16:19:25 +01:00
|
|
|
:
|
2013-02-18 10:08:14 +01:00
|
|
|
Genode::Root_component<Session_component>(ep, md_alloc),
|
2015-05-24 16:58:38 +02:00
|
|
|
_device_pd_root(device_pd_root),
|
2015-02-08 13:02:11 +01:00
|
|
|
/* restrict physical address to 3G on 32bit with device_pd */
|
2015-05-24 16:58:38 +02:00
|
|
|
_ram("dma", 0, (device_pd_root.valid() && sizeof(void *) == 4) ?
|
2015-02-08 13:02:11 +01:00
|
|
|
0xc0000000UL : 0x100000000ULL)
|
2011-12-22 16:19:25 +01:00
|
|
|
{
|
2015-05-26 20:12:17 +02:00
|
|
|
/* enforce initial bus scan */
|
|
|
|
bus_valid();
|
|
|
|
|
|
|
|
if (acpi_rom)
|
|
|
|
_parse_report_rom(acpi_rom);
|
2013-02-18 10:06:34 +01:00
|
|
|
|
2015-02-08 13:02:11 +01:00
|
|
|
/* associate _ram session with ram_session of process */
|
|
|
|
_ram.ref_account(Genode::env()->ram_session_cap());
|
|
|
|
Genode::env()->ram_session()->transfer_quota(_ram.cap(), 0x1000);
|
2011-12-22 16:19:25 +01:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif /* _PCI_SESSION_COMPONENT_H_ */
|