diff --git a/repos/os/src/drivers/acpi/main.cc b/repos/os/src/drivers/acpi/main.cc
index 9a4ae7c7a..ea7435556 100644
--- a/repos/os/src/drivers/acpi/main.cc
+++ b/repos/os/src/drivers/acpi/main.cc
@@ -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;
diff --git a/repos/os/src/drivers/pci/nonpci_devices.cc b/repos/os/src/drivers/pci/nonpci_devices.cc
index 938caa285..afd27685a 100644
--- a/repos/os/src/drivers/pci/nonpci_devices.cc
+++ b/repos/os/src/drivers/pci/nonpci_devices.cc
@@ -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;
diff --git a/repos/os/src/drivers/pci/pci_device_component.h b/repos/os/src/drivers/pci/pci_device_component.h
index a7b6a62cb..749b64e7d 100644
--- a/repos/os/src/drivers/pci/pci_device_component.h
+++ b/repos/os/src/drivers/pci/pci_device_component.h
@@ -19,13 +19,13 @@
#include
#include
-#include
+#include
#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,
public Genode::List::Element
@@ -37,6 +37,7 @@ class Pci::Device_component : public Genode::Rpc_object,
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,
* 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,
/**
* 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);
}
diff --git a/repos/os/src/drivers/pci/pci_session_component.h b/repos/os/src/drivers/pci/pci_session_component.h
index 75a788f88..08225f276 100644
--- a/repos/os/src/drivers/pci/pci_session_component.h
+++ b/repos/os/src/drivers/pci/pci_session_component.h
@@ -22,6 +22,7 @@
/* os */
#include
#include
+#include
#include
/* local */
@@ -44,6 +45,8 @@ namespace Pci {
Genode::List _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: