|
|
|
@ -22,6 +22,7 @@
|
|
|
|
|
/* os */
|
|
|
|
|
#include <io_mem_session/connection.h>
|
|
|
|
|
#include <os/config.h>
|
|
|
|
|
#include <os/session_policy.h>
|
|
|
|
|
#include <pci_session/pci_session.h>
|
|
|
|
|
|
|
|
|
|
/* local */
|
|
|
|
@ -44,6 +45,8 @@ namespace Pci {
|
|
|
|
|
Genode::List<Device_component> _device_list;
|
|
|
|
|
Device_pd_client *_child;
|
|
|
|
|
Genode::Ram_connection *_ram;
|
|
|
|
|
Genode::Session_label _label;
|
|
|
|
|
Genode::Session_policy _policy;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Scan PCI buses for a device
|
|
|
|
@ -115,6 +118,173 @@ namespace Pci {
|
|
|
|
|
return config_space;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* 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},
|
|
|
|
|
{ "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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* 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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* 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;
|
|
|
|
|
}
|
|
|
|
|
/* if this does not identical matches - deny access */
|
|
|
|
|
if ((class_sub_prog & class_code) != class_sub_prog)
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
@ -123,9 +293,105 @@ namespace Pci {
|
|
|
|
|
Session_component(Genode::Rpc_entrypoint *ep,
|
|
|
|
|
Genode::Allocator *md_alloc,
|
|
|
|
|
Device_pd_client *child,
|
|
|
|
|
Genode::Ram_connection *ram)
|
|
|
|
|
Genode::Ram_connection *ram,
|
|
|
|
|
const char *args)
|
|
|
|
|
:
|
|
|
|
|
_ep(ep), _md_alloc(md_alloc), _child(child), _ram(ram) { }
|
|
|
|
|
_ep(ep), _md_alloc(md_alloc),
|
|
|
|
|
_child(child), _ram(ram), _label(args), _policy(_label)
|
|
|
|
|
{
|
|
|
|
|
/* 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();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
/* 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();
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Destructor
|
|
|
|
@ -191,8 +457,7 @@ namespace Pci {
|
|
|
|
|
*/
|
|
|
|
|
Device_config config;
|
|
|
|
|
|
|
|
|
|
do
|
|
|
|
|
{
|
|
|
|
|
while (true) {
|
|
|
|
|
function += 1;
|
|
|
|
|
if (!_find_next(bus, device, function, &config, &config_access))
|
|
|
|
|
return Device_capability();
|
|
|
|
@ -201,7 +466,16 @@ namespace Pci {
|
|
|
|
|
bus = config.bus_number();
|
|
|
|
|
device = config.device_number();
|
|
|
|
|
function = config.function_number();
|
|
|
|
|
} while ((config.class_code() ^ device_class) & class_mask);
|
|
|
|
|
|
|
|
|
|
/* 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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* lookup if we have a extended pci config space */
|
|
|
|
|
Genode::addr_t config_space = lookup_config_space(bus, device,
|
|
|
|
@ -214,7 +488,7 @@ namespace Pci {
|
|
|
|
|
* FIXME: check and adjust session quota
|
|
|
|
|
*/
|
|
|
|
|
Device_component *device_component =
|
|
|
|
|
new (_md_alloc) Device_component(config, config_space, _ep);
|
|
|
|
|
new (_md_alloc) Device_component(config, config_space, _ep, this);
|
|
|
|
|
|
|
|
|
|
if (!device_component)
|
|
|
|
|
return Device_capability();
|
|
|
|
@ -323,6 +597,10 @@ namespace Pci {
|
|
|
|
|
for (i = 0; i < config()->xml_node().num_sub_nodes(); i++)
|
|
|
|
|
{
|
|
|
|
|
Xml_node node = config()->xml_node().sub_node(i);
|
|
|
|
|
|
|
|
|
|
if (!node.has_type("bdf"))
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
uint32_t bdf_start = 0;
|
|
|
|
|
uint32_t func_count = 0;
|
|
|
|
|
addr_t base = 0;
|
|
|
|
@ -348,9 +626,15 @@ namespace Pci {
|
|
|
|
|
/* FIXME: extract quota from args */
|
|
|
|
|
/* FIXME: pass quota to session-component constructor */
|
|
|
|
|
|
|
|
|
|
return new (md_alloc()) Session_component(ep(), md_alloc(),
|
|
|
|
|
_pd_device_client,
|
|
|
|
|
&_ram);
|
|
|
|
|
try {
|
|
|
|
|
return new (md_alloc()) Session_component(ep(), md_alloc(),
|
|
|
|
|
_pd_device_client,
|
|
|
|
|
&_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();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|