pci: enforce policy on device discovery

Fixes #1486
devel
Alexander Boettcher 8 years ago committed by Christian Helmuth
parent 8743575dcf
commit 7770a0fbbe

@ -178,7 +178,7 @@ class Pci_policy : public Genode::Slave_policy, public Pci::Provider
void _acpi_session()
{
Pci::Session_capability session;
const char *args = "ram_quota=4K";
const char *args = "label=\"acpi_drv\", ram_quota=4K";
try {
using namespace Genode;

@ -31,7 +31,7 @@ class Nonpci::Ps2 : public Pci::Device_component
Ps2(Genode::Rpc_entrypoint * ep, Pci::Session_component * session)
:
Pci::Device_component(ep, IRQ_KEYBOARD),
Pci::Device_component(ep, session, IRQ_KEYBOARD),
_irq_mouse(IRQ_MOUSE)
{ }
@ -72,6 +72,12 @@ Pci::Device_capability Pci::Session_component::device(String const &name) {
return Device_capability();
}
if (!permit_device(devices[devices_i])) {
PERR("Denied access to device '%s' for session '%s'", device_name,
_label.string());
return Device_capability();
}
try {
Device_component * dev = nullptr;

@ -19,13 +19,13 @@
#include <base/rpc_server.h>
#include <base/printf.h>
#include <io_mem_session/io_mem_session.h>
#include <io_mem_session/connection.h>
#include "pci_device_config.h"
#include "irq.h"
namespace Pci { class Device_component; }
namespace Pci { class Device_component; class Session_component; }
class Pci::Device_component : public Genode::Rpc_object<Pci::Device>,
public Genode::List<Device_component>::Element
@ -37,6 +37,7 @@ class Pci::Device_component : public Genode::Rpc_object<Pci::Device>,
Genode::Io_mem_connection *_io_mem;
Config_access _config_access;
Genode::Rpc_entrypoint *_ep;
Pci::Session_component *_session;
Irq_session_component _irq_session;
enum { PCI_IRQ = 0x3c };
@ -47,10 +48,11 @@ class Pci::Device_component : public Genode::Rpc_object<Pci::Device>,
* Constructor
*/
Device_component(Device_config device_config, Genode::addr_t addr,
Genode::Rpc_entrypoint *ep)
Genode::Rpc_entrypoint *ep,
Pci::Session_component * session)
:
_device_config(device_config), _config_space(addr),
_io_mem(0), _ep(ep),
_io_mem(0), _ep(ep), _session(session),
_irq_session(_device_config.read(&_config_access, PCI_IRQ,
Pci::Device::ACCESS_8BIT))
{
@ -60,9 +62,11 @@ class Pci::Device_component : public Genode::Rpc_object<Pci::Device>,
/**
* Constructor for non PCI devices
*/
Device_component(Genode::Rpc_entrypoint *ep, unsigned irq)
Device_component(Genode::Rpc_entrypoint * ep,
Pci::Session_component * session, unsigned irq)
:
_config_space(~0UL), _io_mem(0), _ep(ep), _irq_session(irq)
_config_space(~0UL), _io_mem(0), _ep(ep), _session(session),
_irq_session(irq)
{
_ep->manage(&_irq_session);
}

@ -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:

Loading…
Cancel
Save