mirror of
https://github.com/genodelabs/genode.git
synced 2025-02-21 02:01:38 +00:00
platform_drv: add PCI device support
* Parse PCI specific information from devices ROM * Enable DMA, I/O memory and I/O port access dependent on BARs in config space * Introduce device PD for Nova + IOMMU support * Enable MSIs if available * Add PCI specific policy rules Fixes genodelabs/genode#4502
This commit is contained in:
parent
6b92006565
commit
9370e5e4d0
@ -47,6 +47,21 @@ ROM will contain detailed information about physical resources of the devices
|
||||
of its virtual bus. This is only useful when using ported legacy drivers, which
|
||||
operate with global names of physical resources.
|
||||
|
||||
Policy for PCI devices
|
||||
----------------------
|
||||
|
||||
Policies for PCI devices do not necessarily need to state the name of
|
||||
the device, but can either state a class of devices, or a vendor/device
|
||||
pair of identifiers inside a pci sub-node:
|
||||
|
||||
! <config>
|
||||
! <policy label="usb_drv -> ">
|
||||
! <pci class="USB"/>
|
||||
! </policy>
|
||||
! <policy label="nvme_drv -> ">
|
||||
! <pci vendor_id="0x1987" device_id="0x5007"/>
|
||||
! </policy>
|
||||
|
||||
Report facilities
|
||||
-----------------
|
||||
|
||||
|
@ -12,6 +12,7 @@
|
||||
*/
|
||||
|
||||
#include <device.h>
|
||||
#include <pci.h>
|
||||
#include <device_component.h>
|
||||
#include <session_component.h>
|
||||
|
||||
@ -78,6 +79,7 @@ void Driver::Device::acquire(Session_component & sc)
|
||||
}
|
||||
});
|
||||
|
||||
pci_enable(sc.env(), sc.device_pd(), *this);
|
||||
sc.update_devices_rom();
|
||||
sc.devices().update_report();
|
||||
}
|
||||
@ -88,6 +90,8 @@ void Driver::Device::release(Session_component & sc)
|
||||
if (!(_owner == sc))
|
||||
return;
|
||||
|
||||
pci_disable(sc.env(), *this);
|
||||
|
||||
_reset_domain_list.for_each([&] (Reset_domain & r)
|
||||
{
|
||||
sc.devices().resets().apply(r.name, [&] (Driver::Reset &reset) {
|
||||
@ -156,6 +160,13 @@ void Driver::Device::report(Xml_generator & xml, Device_model & devices,
|
||||
});
|
||||
});
|
||||
});
|
||||
_pci_config_list.for_each([&] (Pci_config &pci) {
|
||||
xml.node("pci-config", [&] () {
|
||||
xml.attribute("vendor_id", String<16>(Hex(pci.vendor_id)));
|
||||
xml.attribute("device_id", String<16>(Hex(pci.device_id)));
|
||||
xml.attribute("class", String<16>(Hex(pci.class_code)));
|
||||
});
|
||||
});
|
||||
|
||||
_report_platform_specifics(xml, devices);
|
||||
});
|
||||
|
@ -11,12 +11,13 @@
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#ifndef _SRC__DRIVERS__PLATFORM__SPEC__ARM__DEVICE_H_
|
||||
#define _SRC__DRIVERS__PLATFORM__SPEC__ARM__DEVICE_H_
|
||||
#ifndef _SRC__DRIVERS__PLATFORM__DEVICE_H_
|
||||
#define _SRC__DRIVERS__PLATFORM__DEVICE_H_
|
||||
|
||||
#include <base/allocator.h>
|
||||
#include <base/heap.h>
|
||||
#include <os/reporter.h>
|
||||
#include <pci/types.h>
|
||||
#include <platform_session/device.h>
|
||||
#include <util/list.h>
|
||||
#include <util/list_model.h>
|
||||
@ -41,6 +42,7 @@ namespace Driver {
|
||||
struct Clock_update_policy;
|
||||
struct Reset_domain_update_policy;
|
||||
struct Power_domain_update_policy;
|
||||
struct Pci_config_update_policy;
|
||||
}
|
||||
|
||||
|
||||
@ -141,6 +143,36 @@ class Driver::Device : private List_model<Device>::Element
|
||||
Reset_domain(Name name) : name(name) {}
|
||||
};
|
||||
|
||||
struct Pci_config : List_model<Pci_config>::Element
|
||||
{
|
||||
addr_t addr;
|
||||
Pci::bus_t bus_num;
|
||||
Pci::dev_t dev_num;
|
||||
Pci::func_t func_num;
|
||||
Pci::vendor_t vendor_id;
|
||||
Pci::device_t device_id;
|
||||
Pci::class_t class_code;
|
||||
bool bridge;
|
||||
|
||||
Pci_config(addr_t addr,
|
||||
Pci::bus_t bus_num,
|
||||
Pci::dev_t dev_num,
|
||||
Pci::func_t func_num,
|
||||
Pci::vendor_t vendor_id,
|
||||
Pci::device_t device_id,
|
||||
Pci::class_t class_code,
|
||||
bool bridge)
|
||||
:
|
||||
addr(addr),
|
||||
bus_num(bus_num),
|
||||
dev_num(dev_num),
|
||||
func_num(func_num),
|
||||
vendor_id(vendor_id),
|
||||
device_id(device_id),
|
||||
class_code(class_code),
|
||||
bridge(bridge) {}
|
||||
};
|
||||
|
||||
Device(Name name, Type type);
|
||||
virtual ~Device();
|
||||
|
||||
@ -155,7 +187,7 @@ class Driver::Device : private List_model<Device>::Element
|
||||
{
|
||||
unsigned idx = 0;
|
||||
_irq_list.for_each([&] (Irq const & irq) {
|
||||
fn(idx++, irq.number, irq.type, irq.polarity, irq.mode, 0); });
|
||||
fn(idx++, irq.number, irq.type, irq.polarity, irq.mode); });
|
||||
}
|
||||
|
||||
template <typename FN> void for_each_io_mem(FN const & fn)
|
||||
@ -172,6 +204,23 @@ class Driver::Device : private List_model<Device>::Element
|
||||
fn(idx++, ipr.addr, ipr.size); });
|
||||
}
|
||||
|
||||
template <typename FN> void for_pci_config(FN const & fn)
|
||||
{
|
||||
/*
|
||||
* we allow only one PCI config per device,
|
||||
* even if more were declared
|
||||
*/
|
||||
bool found = false;
|
||||
_pci_config_list.for_each([&] (Pci_config const & cfg) {
|
||||
if (found) {
|
||||
warning("Only one pci-config is supported per device!");
|
||||
return;
|
||||
}
|
||||
found = true;
|
||||
fn(cfg);
|
||||
});
|
||||
}
|
||||
|
||||
void report(Xml_generator &, Device_model &, bool);
|
||||
|
||||
protected:
|
||||
@ -193,6 +242,7 @@ class Driver::Device : private List_model<Device>::Element
|
||||
List_model<Clock> _clock_list {};
|
||||
List_model<Power_domain> _power_domain_list {};
|
||||
List_model<Reset_domain> _reset_domain_list {};
|
||||
List_model<Pci_config> _pci_config_list {};
|
||||
|
||||
/*
|
||||
* Noncopyable
|
||||
@ -487,4 +537,48 @@ struct Driver::Reset_domain_update_policy
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* _SRC__DRIVERS__PLATFORM__SPEC__ARM__DEVICE_H_ */
|
||||
|
||||
struct Driver::Pci_config_update_policy
|
||||
: Genode::List_model<Device::Pci_config>::Update_policy
|
||||
{
|
||||
Genode::Allocator & alloc;
|
||||
|
||||
Pci_config_update_policy(Genode::Allocator & alloc) : alloc(alloc) {}
|
||||
|
||||
void destroy_element(Element & pd) {
|
||||
Genode::destroy(alloc, &pd); }
|
||||
|
||||
Element & create_element(Genode::Xml_node node)
|
||||
{
|
||||
using namespace Pci;
|
||||
|
||||
addr_t addr = node.attribute_value("address", ~0UL);
|
||||
bus_t bus_num = node.attribute_value<bus_t>("bus", 0);
|
||||
dev_t dev_num = node.attribute_value<dev_t>("device", 0);
|
||||
func_t func_num = node.attribute_value<func_t>("function", 0);
|
||||
vendor_t vendor_id = node.attribute_value<vendor_t>("vendor_id",
|
||||
0xffff);
|
||||
device_t device_id = node.attribute_value<device_t>("device_id",
|
||||
0xffff);
|
||||
class_t class_code = node.attribute_value<class_t>("class", 0xff);
|
||||
bool bridge = node.attribute_value("bridge", false);
|
||||
|
||||
return *(new (alloc) Element(addr, bus_num, dev_num, func_num,
|
||||
vendor_id, device_id, class_code, bridge));
|
||||
}
|
||||
|
||||
void update_element(Element &, Genode::Xml_node) {}
|
||||
|
||||
static bool element_matches_xml_node(Element const & e, Genode::Xml_node node)
|
||||
{
|
||||
addr_t addr = node.attribute_value("address", ~0UL);
|
||||
return addr == e.addr;
|
||||
}
|
||||
|
||||
static bool node_is_element(Genode::Xml_node node)
|
||||
{
|
||||
return node.has_type("pci-config");
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* _SRC__DRIVERS__PLATFORM__DEVICE_H_ */
|
||||
|
@ -13,6 +13,7 @@
|
||||
|
||||
#include <device.h>
|
||||
#include <device_component.h>
|
||||
#include <pci.h>
|
||||
#include <session_component.h>
|
||||
|
||||
using Driver::Device_component;
|
||||
@ -72,19 +73,17 @@ Genode::Irq_session_capability Device_component::irq(unsigned idx)
|
||||
return;
|
||||
|
||||
if (!irq.irq.constructed()) {
|
||||
|
||||
/*
|
||||
* Unfortunately, we have to deliver the PCI config space address
|
||||
* to the IRQ session for working MSI(-x) on NOVA. It is used
|
||||
* for IOMMU configuration as some kind of access control
|
||||
*
|
||||
* Once, the IOMMU support is solved kernel-independent, this
|
||||
* attribute has to be removed from the IRQs
|
||||
*/
|
||||
addr_t pci_cfg_addr = (irq.type != Device::Irq::LEGACY)
|
||||
? irq.pci_config_addr : 0;
|
||||
addr_t pci_cfg_addr = 0;
|
||||
if (irq.type != Device::Irq::LEGACY) {
|
||||
if (_pci_config.constructed()) pci_cfg_addr = _pci_config->addr;
|
||||
else
|
||||
error("MSI(-x) detected for device without pci-config!");
|
||||
}
|
||||
irq.irq.construct(_session.env(), irq.number, irq.mode, irq.polarity,
|
||||
pci_cfg_addr);
|
||||
Irq_session::Info info = irq.irq->info();
|
||||
if (info.type == Irq_session::Info::MSI)
|
||||
pci_msi_enable(_session.env(), pci_cfg_addr, info);
|
||||
}
|
||||
|
||||
cap = irq.irq->cap();
|
||||
@ -137,15 +136,13 @@ Device_component::Device_component(Registry<Device_component> & registry,
|
||||
unsigned nr,
|
||||
Device::Irq::Type type,
|
||||
Irq_session::Polarity polarity,
|
||||
Irq_session::Trigger mode,
|
||||
addr_t pci_cfg_addr)
|
||||
Irq_session::Trigger mode)
|
||||
{
|
||||
session.ram_quota_guard().withdraw(Ram_quota{Irq_session::RAM_QUOTA});
|
||||
_ram_quota += Irq_session::RAM_QUOTA;
|
||||
session.cap_quota_guard().withdraw(Cap_quota{Irq_session::CAP_QUOTA});
|
||||
_cap_quota += Irq_session::CAP_QUOTA;
|
||||
new (session.heap()) Irq(_irq_registry, idx, nr, type,
|
||||
polarity, mode, pci_cfg_addr);
|
||||
new (session.heap()) Irq(_irq_registry, idx, nr, type, polarity, mode);
|
||||
});
|
||||
|
||||
device.for_each_io_mem([&] (unsigned idx, Range range)
|
||||
@ -167,6 +164,15 @@ Device_component::Device_component(Registry<Device_component> & registry,
|
||||
new (session.heap()) Io_port_range(_io_port_range_registry,
|
||||
idx, addr, size);
|
||||
});
|
||||
|
||||
device.for_pci_config([&] (Device::Pci_config const & cfg)
|
||||
{
|
||||
session.ram_quota_guard().withdraw(Ram_quota{Io_mem_session::RAM_QUOTA});
|
||||
_ram_quota += Io_mem_session::RAM_QUOTA;
|
||||
session.cap_quota_guard().withdraw(Cap_quota{Io_mem_session::CAP_QUOTA});
|
||||
_cap_quota += Io_mem_session::CAP_QUOTA;
|
||||
_pci_config.construct(cfg.addr);
|
||||
});
|
||||
} catch(...) {
|
||||
_release_resources();
|
||||
throw;
|
||||
|
@ -43,7 +43,6 @@ class Driver::Device_component : public Rpc_object<Platform::Device_interface,
|
||||
Device::Irq::Type type;
|
||||
Irq_session::Polarity polarity;
|
||||
Irq_session::Trigger mode;
|
||||
addr_t pci_config_addr;
|
||||
Constructible<Irq_connection> irq {};
|
||||
|
||||
Irq(Registry<Irq> & registry,
|
||||
@ -51,13 +50,11 @@ class Driver::Device_component : public Rpc_object<Platform::Device_interface,
|
||||
unsigned number,
|
||||
Device::Irq::Type type,
|
||||
Irq_session::Polarity polarity,
|
||||
Irq_session::Trigger mode,
|
||||
addr_t pci_config_addr)
|
||||
Irq_session::Trigger mode)
|
||||
:
|
||||
Registry<Irq>::Element(registry, *this),
|
||||
idx(idx), number(number), type(type),
|
||||
polarity(polarity), mode(mode),
|
||||
pci_config_addr(pci_config_addr) {}
|
||||
polarity(polarity), mode(mode) {}
|
||||
};
|
||||
|
||||
struct Io_mem : Registry<Io_mem>::Element
|
||||
@ -90,6 +87,13 @@ class Driver::Device_component : public Rpc_object<Platform::Device_interface,
|
||||
idx(idx), addr(addr), size(size) {}
|
||||
};
|
||||
|
||||
struct Pci_config
|
||||
{
|
||||
addr_t addr;
|
||||
|
||||
Pci_config(addr_t addr) : addr(addr) {}
|
||||
};
|
||||
|
||||
Device_component(Registry<Device_component> & registry,
|
||||
Session_component & session,
|
||||
Driver::Device & device);
|
||||
@ -117,6 +121,7 @@ class Driver::Device_component : public Rpc_object<Platform::Device_interface,
|
||||
Registry<Irq> _irq_registry {};
|
||||
Registry<Io_mem> _io_mem_registry {};
|
||||
Registry<Io_port_range> _io_port_range_registry {};
|
||||
Constructible<Pci_config> _pci_config {};
|
||||
|
||||
void _release_resources();
|
||||
|
||||
|
@ -53,6 +53,11 @@ void Device_model::destroy_element(Device & device)
|
||||
device._reset_domain_list.destroy_all_elements(policy);
|
||||
}
|
||||
|
||||
{
|
||||
Pci_config_update_policy policy(_heap);
|
||||
device._pci_config_list.destroy_all_elements(policy);
|
||||
}
|
||||
|
||||
Genode::destroy(_heap, &device);
|
||||
}
|
||||
|
||||
@ -102,4 +107,9 @@ void Device_model::update_element(Device & device,
|
||||
Reset_domain_update_policy policy(_heap);
|
||||
device._reset_domain_list.update_from_xml(policy, node);
|
||||
}
|
||||
|
||||
{
|
||||
Pci_config_update_policy policy(_heap);
|
||||
device._pci_config_list.update_from_xml(policy, node);
|
||||
}
|
||||
}
|
||||
|
135
repos/os/src/drivers/platform/device_pd.cc
Normal file
135
repos/os/src/drivers/platform/device_pd.cc
Normal file
@ -0,0 +1,135 @@
|
||||
/*
|
||||
* \brief Pci device protection for platform driver
|
||||
* \author Alexander Boettcher
|
||||
* \author Stefan Kalkowski
|
||||
* \date 2013-02-10
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2013-2022 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#include <base/log.h>
|
||||
#include <dataspace/client.h>
|
||||
#include <region_map/client.h>
|
||||
#include <pd_session/client.h>
|
||||
|
||||
#include <util/retry.h>
|
||||
|
||||
#include <device_pd.h>
|
||||
|
||||
using namespace Driver;
|
||||
|
||||
Device_pd::Region_map_client::Local_addr
|
||||
Device_pd::Region_map_client::attach(Dataspace_capability ds,
|
||||
size_t size,
|
||||
off_t offset,
|
||||
bool use_local_addr,
|
||||
Local_addr local_addr,
|
||||
bool executable,
|
||||
bool writeable)
|
||||
{
|
||||
return retry<Out_of_ram>(
|
||||
[&] () {
|
||||
return retry<Out_of_caps>(
|
||||
[&] () {
|
||||
return Genode::Region_map_client::attach(ds, size, offset,
|
||||
use_local_addr,
|
||||
local_addr,
|
||||
executable,
|
||||
writeable); },
|
||||
[&] () {
|
||||
upgrade_caps();
|
||||
}
|
||||
);
|
||||
},
|
||||
[&] () { upgrade_ram(); }
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
void Device_pd::Region_map_client::upgrade_ram()
|
||||
{
|
||||
Ram_quota const ram { 4096 };
|
||||
_ram_guard.withdraw(ram);
|
||||
_env.pd().transfer_quota(_pd.rpc_cap(), ram);
|
||||
}
|
||||
|
||||
|
||||
void Device_pd::Region_map_client::upgrade_caps()
|
||||
{
|
||||
Cap_quota const caps { 2 };
|
||||
_cap_guard.withdraw(caps);
|
||||
_env.pd().transfer_quota(_pd.rpc_cap(), caps);
|
||||
}
|
||||
|
||||
|
||||
void Device_pd::attach_dma_mem(Dataspace_capability ds_cap,
|
||||
addr_t const dma_addr)
|
||||
{
|
||||
using namespace Genode;
|
||||
|
||||
bool retry = false;
|
||||
Dataspace_client ds_client(ds_cap);
|
||||
|
||||
do {
|
||||
_pd.attach_dma(ds_cap, dma_addr).with_result(
|
||||
[&] (Pd_session::Attach_dma_ok) {
|
||||
/* trigger eager mapping of memory */
|
||||
_pd.map(dma_addr, ds_client.size());
|
||||
retry = false;
|
||||
},
|
||||
[&] (Pd_session::Attach_dma_error e) {
|
||||
switch (e) {
|
||||
case Pd_session::Attach_dma_error::OUT_OF_RAM:
|
||||
_address_space.upgrade_ram();
|
||||
retry = true;
|
||||
break;
|
||||
case Pd_session::Attach_dma_error::OUT_OF_CAPS:
|
||||
_address_space.upgrade_caps();
|
||||
retry = true;
|
||||
break;
|
||||
case Pd_session::Attach_dma_error::DENIED:
|
||||
_address_space.detach(dma_addr);
|
||||
error("Device PD: attach_dma denied!");
|
||||
break;
|
||||
}
|
||||
}
|
||||
);
|
||||
} while (retry);
|
||||
}
|
||||
|
||||
|
||||
void Device_pd::assign_pci(Io_mem_dataspace_capability const io_mem_cap,
|
||||
Pci::Bdf const bdf)
|
||||
{
|
||||
addr_t addr = _address_space.attach(io_mem_cap, 0x1000);
|
||||
|
||||
/* sanity check */
|
||||
if (!addr)
|
||||
throw Region_map::Region_conflict();
|
||||
|
||||
/* trigger eager mapping of memory */
|
||||
_pd.map(addr, 0x1000);
|
||||
|
||||
/* try to assign pci device to this protection domain */
|
||||
if (!_pd.assign_pci(addr, Pci::Bdf::rid(bdf)))
|
||||
error("Assignment of PCI device ", bdf, " to device PD failed!");
|
||||
|
||||
/* we don't need the mapping anymore */
|
||||
_address_space.detach(addr);
|
||||
}
|
||||
|
||||
|
||||
Device_pd::Device_pd(Env & env,
|
||||
Ram_quota_guard & ram_guard,
|
||||
Cap_quota_guard & cap_guard)
|
||||
:
|
||||
_pd(env, Pd_connection::Device_pd()),
|
||||
_address_space(env, _pd, ram_guard, cap_guard)
|
||||
{
|
||||
_pd.ref_account(env.pd_session_cap());
|
||||
}
|
83
repos/os/src/drivers/platform/device_pd.h
Normal file
83
repos/os/src/drivers/platform/device_pd.h
Normal file
@ -0,0 +1,83 @@
|
||||
/*
|
||||
* \brief Device PD handling for the platform driver
|
||||
* \author Alexander Boettcher
|
||||
* \date 2015-11-05
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2015-2022 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#ifndef _SRC__DRIVERS__PLATFORM__DEVICE_PD_H_
|
||||
#define _SRC__DRIVERS__PLATFORM__DEVICE_PD_H_
|
||||
|
||||
/* base */
|
||||
#include <base/env.h>
|
||||
#include <base/quota_guard.h>
|
||||
#include <region_map/client.h>
|
||||
#include <pci/types.h>
|
||||
#include <pd_session/connection.h>
|
||||
#include <io_mem_session/capability.h>
|
||||
|
||||
namespace Driver {
|
||||
using namespace Genode;
|
||||
class Device_pd;
|
||||
}
|
||||
|
||||
class Driver::Device_pd
|
||||
{
|
||||
private:
|
||||
|
||||
Pd_connection _pd;
|
||||
|
||||
/**
|
||||
* Custom handling of PD-session depletion during attach operations
|
||||
*
|
||||
* The default implementation of 'env.rm()' automatically issues a resource
|
||||
* request if the PD session quota gets exhausted. For the device PD, we don't
|
||||
* want to issue resource requests but let the platform driver reflect this
|
||||
* condition to its client.
|
||||
*/
|
||||
struct Region_map_client : Genode::Region_map_client
|
||||
{
|
||||
Env & _env;
|
||||
Pd_connection & _pd;
|
||||
Ram_quota_guard & _ram_guard;
|
||||
Cap_quota_guard & _cap_guard;
|
||||
|
||||
Region_map_client(Env & env,
|
||||
Pd_connection & pd,
|
||||
Ram_quota_guard & ram_guard,
|
||||
Cap_quota_guard & cap_guard)
|
||||
:
|
||||
Genode::Region_map_client(pd.address_space()),
|
||||
_env(env), _pd(pd),
|
||||
_ram_guard(ram_guard), _cap_guard(cap_guard)
|
||||
{ }
|
||||
|
||||
Local_addr attach(Dataspace_capability ds,
|
||||
size_t size = 0,
|
||||
off_t offset = 0,
|
||||
bool use_local_addr = false,
|
||||
Local_addr local_addr = (void *)0,
|
||||
bool executable = false,
|
||||
bool writeable = true) override;
|
||||
|
||||
void upgrade_ram();
|
||||
void upgrade_caps();
|
||||
} _address_space;
|
||||
|
||||
public:
|
||||
|
||||
Device_pd(Env &env,
|
||||
Ram_quota_guard &ram_guard,
|
||||
Cap_quota_guard &cap_guard);
|
||||
|
||||
void attach_dma_mem(Dataspace_capability, addr_t dma_addr);
|
||||
void assign_pci(Io_mem_dataspace_capability const, Pci::Bdf const);
|
||||
};
|
||||
|
||||
#endif /* _SRC__DRIVERS__PLATFORM__DEVICE_PD_H_ */
|
165
repos/os/src/drivers/platform/pci.cc
Normal file
165
repos/os/src/drivers/platform/pci.cc
Normal file
@ -0,0 +1,165 @@
|
||||
/*
|
||||
* \brief Platform driver - PCI helper utilities
|
||||
* \author Stefan Kalkowski
|
||||
* \date 2022-05-02
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2022 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#include <base/attached_io_mem_dataspace.h>
|
||||
#include <pci/config.h>
|
||||
#include <util/reconstructible.h>
|
||||
|
||||
#include <device.h>
|
||||
#include <device_pd.h>
|
||||
#include <pci.h>
|
||||
|
||||
using namespace Genode;
|
||||
using namespace Pci;
|
||||
|
||||
struct Config_helper
|
||||
{
|
||||
Driver::Device & _dev;
|
||||
Driver::Device::Pci_config const & _cfg;
|
||||
|
||||
Attached_io_mem_dataspace _io_mem;
|
||||
Config _config { (addr_t)_io_mem.local_addr<void>() };
|
||||
|
||||
Config_helper(Env & env,
|
||||
Driver::Device & dev,
|
||||
Driver::Device::Pci_config const & cfg)
|
||||
: _dev(dev), _cfg(cfg), _io_mem(env, cfg.addr, 0x1000) { }
|
||||
|
||||
void enable(Driver::Device_pd & pd)
|
||||
{
|
||||
pd.assign_pci(_io_mem.cap(),
|
||||
{ _cfg.bus_num, _cfg.dev_num, _cfg.func_num });
|
||||
|
||||
Config::Command::access_t cmd =
|
||||
_config.read<Config::Command>();
|
||||
|
||||
/* always allow DMA operations */
|
||||
Config::Command::Bus_master_enable::set(cmd, 1);
|
||||
|
||||
/* enable memory space when I/O mem is defined */
|
||||
_dev.for_each_io_mem([&] (unsigned, Driver::Device::Range) {
|
||||
Config::Command::Memory_space_enable::set(cmd, 1); });
|
||||
|
||||
/* enable i/o space when I/O ports are defined */
|
||||
_dev.for_each_io_port_range([&] (unsigned, uint16_t, uint16_t) {
|
||||
Config::Command::Io_space_enable::set(cmd, 1); });
|
||||
|
||||
_config.write<Config::Command>(cmd);
|
||||
}
|
||||
|
||||
void disable()
|
||||
{
|
||||
Config::Command::access_t cmd =
|
||||
_config.read<Config::Command>();
|
||||
Config::Command::Io_space_enable::set(cmd, 0);
|
||||
Config::Command::Memory_space_enable::set(cmd, 0);
|
||||
Config::Command::Bus_master_enable::set(cmd, 0);
|
||||
Config::Command::Interrupt_enable::set(cmd, 0);
|
||||
_config.write<Config::Command>(cmd);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
void Driver::pci_enable(Env & env, Device_pd & pd, Device & dev)
|
||||
{
|
||||
dev.for_pci_config([&] (Device::Pci_config const & pc) {
|
||||
Config_helper(env, dev, pc).enable(pd); });
|
||||
}
|
||||
|
||||
|
||||
void Driver::pci_disable(Env & env, Device & dev)
|
||||
{
|
||||
dev.for_pci_config([&] (Device::Pci_config const & pc) {
|
||||
Config_helper(env, dev, pc).disable(); });
|
||||
}
|
||||
|
||||
|
||||
void Driver::pci_msi_enable(Env & env, addr_t cfg_space, Irq_session::Info info)
|
||||
{
|
||||
Attached_io_mem_dataspace io_mem { env, cfg_space, 0x1000 };
|
||||
Config config { (addr_t)io_mem.local_addr<void>() };
|
||||
config.scan();
|
||||
if (config.msi_cap.constructed())
|
||||
config.msi_cap->enable(info.address, (uint16_t)info.value);
|
||||
else error("Device does not support MSI(-x)!");
|
||||
}
|
||||
|
||||
|
||||
static inline String<16>
|
||||
pci_class_code_alias(uint32_t class_code)
|
||||
{
|
||||
enum { WILDCARD = 0xff };
|
||||
|
||||
uint8_t const b = (class_code >> 16) & 0xff;
|
||||
uint8_t const s = (class_code >> 8) & 0xff;
|
||||
uint8_t const i = class_code & 0xff;
|
||||
|
||||
static struct Alias
|
||||
{
|
||||
String<16> name;
|
||||
uint8_t base;
|
||||
uint8_t sub;
|
||||
uint8_t iface;
|
||||
|
||||
bool matches(uint8_t b, uint8_t s, uint8_t i) const
|
||||
{
|
||||
return (base == WILDCARD || base == b) &&
|
||||
(sub == WILDCARD || sub == s) &&
|
||||
(iface == WILDCARD || iface == i);
|
||||
}
|
||||
} const aliases [] = {
|
||||
{ "NVME" , 0x01, 0x08, 0x02 },
|
||||
{ "USB" , 0x0c, 0x03, 0x00 }, /* UHCI */
|
||||
{ "USB" , 0x0c, 0x03, 0x10 }, /* OHCI */
|
||||
{ "USB" , 0x0c, 0x03, 0x20 }, /* EHCI */
|
||||
{ "USB" , 0x0c, 0x03, 0x30 }, /* XHCI */
|
||||
{ "VGA" , 0x03, 0x00, 0x00 },
|
||||
{ "AHCI" , 0x01, 0x06, WILDCARD },
|
||||
{ "AUDIO" , 0x04, 0x01, WILDCARD },
|
||||
{ "ETHERNET" , 0x02, 0x00, WILDCARD },
|
||||
{ "HDAUDIO" , 0x04, 0x03, WILDCARD },
|
||||
{ "ISABRIDGE", 0x06, 0x01, WILDCARD },
|
||||
{ "WIFI" , 0x02, 0x80, WILDCARD },
|
||||
};
|
||||
|
||||
for (Alias const & alias : aliases)
|
||||
if (alias.matches(b, s, i))
|
||||
return alias.name;
|
||||
|
||||
return "ALL";
|
||||
}
|
||||
|
||||
|
||||
bool Driver::pci_device_matches(Session_policy const & policy, Device & dev)
|
||||
{
|
||||
bool ret = false;
|
||||
|
||||
policy.for_each_sub_node("pci", [&] (Xml_node node)
|
||||
{
|
||||
if (dev.type() != "pci")
|
||||
return;
|
||||
|
||||
String<16> class_code = node.attribute_value("class", String<16>());
|
||||
vendor_t vendor_id = node.attribute_value<vendor_t>("vendor_id", 0);
|
||||
device_t device_id = node.attribute_value<device_t>("device_id", 0);
|
||||
|
||||
dev.for_pci_config([&] (Device::Pci_config cfg)
|
||||
{
|
||||
if ((pci_class_code_alias(cfg.class_code) == class_code) ||
|
||||
(vendor_id == cfg.vendor_id && device_id == cfg.device_id))
|
||||
ret = true;
|
||||
});
|
||||
});
|
||||
|
||||
return ret;
|
||||
}
|
33
repos/os/src/drivers/platform/pci.h
Normal file
33
repos/os/src/drivers/platform/pci.h
Normal file
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* \brief Platform driver - PCI helper utilities
|
||||
* \author Stefan Kalkowski
|
||||
* \date 2022-05-02
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2022 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#ifndef _SRC__DRIVERS__PLATFORM__PCI_H_
|
||||
#define _SRC__DRIVERS__PLATFORM__PCI_H_
|
||||
|
||||
#include <base/env.h>
|
||||
#include <irq_session/irq_session.h>
|
||||
#include <os/session_policy.h>
|
||||
|
||||
namespace Driver {
|
||||
class Device;
|
||||
class Device_pd;
|
||||
|
||||
void pci_enable(Genode::Env & env, Device_pd & pd, Device & dev);
|
||||
void pci_disable(Genode::Env & env, Device & dev);
|
||||
void pci_msi_enable(Genode::Env & env, addr_t cfg_space,
|
||||
Genode::Irq_session::Info info);
|
||||
bool pci_device_matches(Genode::Session_policy const & policy,
|
||||
Device & dev);
|
||||
}
|
||||
|
||||
#endif /* _SRC__DRIVERS__PLATFORM__PCI_H_ */
|
@ -11,8 +11,8 @@
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#ifndef _SRC__DRIVERS__PLATFORM__SPEC__ARM__ROOT_H_
|
||||
#define _SRC__DRIVERS__PLATFORM__SPEC__ARM__ROOT_H_
|
||||
#ifndef _SRC__DRIVERS__PLATFORM__ROOT_H_
|
||||
#define _SRC__DRIVERS__PLATFORM__ROOT_H_
|
||||
|
||||
#include <base/attached_rom_dataspace.h>
|
||||
#include <base/registry.h>
|
||||
@ -46,4 +46,4 @@ class Driver::Root : public Root_component<Session_component>
|
||||
Registry<Session_component> _sessions {};
|
||||
};
|
||||
|
||||
#endif /* _SRC__DRIVERS__PLATFORM__SPEC__ARM__ROOT_H_ */
|
||||
#endif /* _SRC__DRIVERS__PLATFORM__ROOT_H_ */
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include <dataspace/client.h>
|
||||
|
||||
#include <device.h>
|
||||
#include <pci.h>
|
||||
#include <session_component.h>
|
||||
|
||||
using Driver::Session_component;
|
||||
@ -54,6 +55,12 @@ bool Session_component::matches(Device & dev) const
|
||||
|
||||
try {
|
||||
Session_policy const policy { label(), _config.xml() };
|
||||
|
||||
/* check PCI devices */
|
||||
if (pci_device_matches(policy, dev))
|
||||
return true;
|
||||
|
||||
/* check for dedicated device name */
|
||||
policy.for_each_sub_node("device", [&] (Xml_node node) {
|
||||
if (dev.name() == node.attribute_value("name", Device::Name()))
|
||||
ret = true;
|
||||
@ -114,6 +121,9 @@ Driver::Device_model & Session_component::devices() { return _devices; }
|
||||
Genode::Heap & Session_component::heap() { return _md_alloc; }
|
||||
|
||||
|
||||
Driver::Device_pd & Session_component::device_pd() { return _device_pd; }
|
||||
|
||||
|
||||
void Session_component::update_devices_rom()
|
||||
{
|
||||
_rom_session.trigger_update();
|
||||
@ -175,7 +185,9 @@ Session_component::alloc_dma_buffer(size_t const size, Cache cache)
|
||||
if (!ram_cap.valid()) return ram_cap;
|
||||
|
||||
try {
|
||||
new (heap()) Dma_buffer(_buffer_registry, ram_cap);
|
||||
Dma_buffer & buf =
|
||||
*(new (heap()) Dma_buffer(_buffer_registry, ram_cap));
|
||||
_device_pd.attach_dma_mem(ram_cap, _env.pd().dma_addr(buf.cap));
|
||||
} catch (Out_of_ram) {
|
||||
_env_ram.free(ram_cap);
|
||||
throw;
|
||||
|
@ -11,8 +11,8 @@
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#ifndef _SRC__DRIVERS__PLATFORM__SPEC__ARM__SESSION_COMPONENT_H_
|
||||
#define _SRC__DRIVERS__PLATFORM__SPEC__ARM__SESSION_COMPONENT_H_
|
||||
#ifndef _SRC__DRIVERS__PLATFORM__SESSION_COMPONENT_H_
|
||||
#define _SRC__DRIVERS__PLATFORM__SESSION_COMPONENT_H_
|
||||
|
||||
#include <base/attached_rom_dataspace.h>
|
||||
#include <base/env.h>
|
||||
@ -25,6 +25,7 @@
|
||||
#include <platform_session/platform_session.h>
|
||||
|
||||
#include <device_component.h>
|
||||
#include <device_pd.h>
|
||||
|
||||
namespace Driver {
|
||||
class Session_component;
|
||||
@ -58,6 +59,7 @@ class Driver::Session_component
|
||||
Env & env();
|
||||
Heap & heap();
|
||||
Device_model & devices();
|
||||
Device_pd & device_pd();
|
||||
|
||||
bool matches(Device &) const;
|
||||
void update_devices_rom();
|
||||
@ -110,6 +112,9 @@ class Driver::Session_component
|
||||
_env.rm(), *this };
|
||||
bool _info;
|
||||
Policy_version _version;
|
||||
Device_pd _device_pd { _env,
|
||||
_ram_quota_guard(),
|
||||
_cap_quota_guard() };
|
||||
|
||||
Device_capability _acquire(Device & device);
|
||||
void _release_device(Device_component & dc);
|
||||
@ -129,4 +134,4 @@ class Driver::Session_component
|
||||
void produce_xml(Xml_generator &xml) override;
|
||||
};
|
||||
|
||||
#endif /* _SRC__DRIVERS__PLATFORM__SPEC__ARM__SESSION_COMPONENT_H_ */
|
||||
#endif /* _SRC__DRIVERS__PLATFORM__SESSION_COMPONENT_H_ */
|
||||
|
@ -1,4 +1,11 @@
|
||||
TARGET = platform_drv
|
||||
SRC_CC = device.cc device_component.cc device_model_policy.cc main.cc session_component.cc root.cc
|
||||
SRC_CC += device.cc
|
||||
SRC_CC += device_component.cc
|
||||
SRC_CC += device_model_policy.cc
|
||||
SRC_CC += device_pd.cc
|
||||
SRC_CC += main.cc
|
||||
SRC_CC += pci.cc
|
||||
SRC_CC += root.cc
|
||||
SRC_CC += session_component.cc
|
||||
INC_DIR = $(PRG_DIR)
|
||||
LIBS = base
|
||||
|
Loading…
x
Reference in New Issue
Block a user