app/pci_decode: prepare pci device information

To discharge the generic platform driver from certain PCI bus scanning,
and ACPI + kernel specifics, this commit introduces a new component,
which consumes the acpi drivers report and the platform_info from core
to prepare a devices ROM for the platform driver that contains all
PCI devices and its resources.

Fix genodelabs/genode#4495
This commit is contained in:
Stefan Kalkowski
2022-02-01 08:55:03 +01:00
committed by Christian Helmuth
parent 87021d9fb1
commit cacb6136fa
6 changed files with 1142 additions and 0 deletions

View File

@ -0,0 +1,53 @@
/*
* \brief Bridge related PCI information
* \author Stefan Kalkowski
* \date 2022-05-04
*/
/*
* 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/registry.h>
#include <pci/types.h>
using namespace Genode;
struct Bridge : Registry<Bridge>::Element
{
Pci::Bdf bdf;
Pci::bus_t from;
Pci::bus_t to;
Registry<Bridge> sub_bridges {};
Bridge(Registry<Bridge> & registry,
Pci::Bdf bdf,
Pci::bus_t from,
Pci::bus_t to)
:
Registry<Bridge>::Element(registry, *this),
bdf(bdf), from(from), to(to) { }
bool behind(Pci::bus_t bus) {
return from <= bus && bus <= to; }
template <typename FN>
void find_bridge(Pci::bus_t bus, FN const & fn) {
if (!behind(bus))
return;
bool found = false;
sub_bridges.for_each([&] (Bridge & b) {
if (!b.behind(bus))
return;
b.find_bridge(bus, fn);
found = true;
});
if (!found) fn(*this);
}
};

View File

@ -0,0 +1,165 @@
/*
* \brief Interrupt related ACPI information in list models
* \author Stefan Kalkowski
* \date 2021-12-12
*/
/*
* Copyright (C) 2021 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/heap.h>
#include <pci/types.h>
#include <util/list_model.h>
#include <util/register.h>
#include <bridge.h>
using namespace Genode;
using namespace Pci;
struct Irq_override : List_model<Irq_override>::Element
{
struct Flags : Register<8>
{
struct Polarity : Bitfield<0, 2>
{
enum { HIGH = 1, LOW = 3 };
};
struct Mode : Bitfield<2, 2>
{
enum { EDGE = 1, LEVEL = 3 };
};
};
irq_line_t from;
irq_line_t to;
Flags::access_t flags;
Irq_override(irq_line_t from,
irq_line_t to,
Flags::access_t flags)
: from(from), to(to), flags(flags) {}
void generate(Xml_generator & generator, irq_line_t & irq)
{
if (irq != from)
return;
irq = to;
using Polarity = Flags::Polarity;
Flags::access_t polarity = Polarity::get(flags);
if (polarity == Polarity::HIGH)
generator.attribute("polarity", "high");
if (polarity == Polarity::LOW)
generator.attribute("polarity", "low");
using Mode = Irq_override::Flags::Mode;
Flags::access_t mode = Mode::get(flags);
if (mode == Mode::EDGE)
generator.attribute("mode", "edge");
if (mode == Mode::LEVEL)
generator.attribute("mode", "level");
}
};
struct Irq_override_policy : List_model<Irq_override>::Update_policy
{
Heap & heap;
void destroy_element(Irq_override & irq) {
destroy(heap, &irq); }
Irq_override & create_element(Xml_node node)
{
return *(new (heap)
Irq_override(node.attribute_value<uint8_t>("irq", 0xff),
node.attribute_value<uint8_t>("gsi", 0xff),
node.attribute_value<uint8_t>("flags", 0)));
}
void update_element(Irq_override &, Xml_node) {}
static bool element_matches_xml_node(Irq_override const & irq,
Genode::Xml_node node) {
return irq.from == node.attribute_value("irq", ~0U); }
static bool node_is_element(Genode::Xml_node node) {
return node.has_type("irq_override"); }
Irq_override_policy(Heap & heap) : heap(heap) {}
};
struct Irq_routing : List_model<Irq_routing>::Element
{
Bdf bridge_bdf;
dev_t dev;
irq_pin_t pin;
irq_line_t to;
Irq_routing(Bdf bridge_bdf,
dev_t dev,
irq_pin_t pin,
irq_line_t to)
:
bridge_bdf(bridge_bdf),
dev(dev), pin(pin), to(to) {}
void route(Bridge & bridge,
dev_t device,
irq_pin_t p,
irq_line_t & irq)
{
if (!(bridge_bdf == bridge.bdf && dev == device && pin == p))
return;
irq = to;
}
};
struct Irq_routing_policy : List_model<Irq_routing>::Update_policy
{
Heap & heap;
void destroy_element(Irq_routing & irq) {
destroy(heap, &irq); }
Irq_routing & create_element(Xml_node node)
{
rid_t bridge_bdf = node.attribute_value<rid_t>("bridge_bdf", 0xff);
return *(new (heap)
Irq_routing(Bdf::bdf(bridge_bdf),
node.attribute_value<uint8_t>("device", 0xff),
node.attribute_value<uint8_t>("device_pin", 0xff),
node.attribute_value<uint8_t>("gsi", 0xff)));
}
void update_element(Irq_routing &, Xml_node) {}
static bool element_matches_xml_node(Irq_routing const & ir,
Genode::Xml_node node)
{
rid_t bridge_bdf = node.attribute_value<rid_t>("bridge_bdf", 0xff);
return ir.bridge_bdf == Bdf::bdf(bridge_bdf) &&
ir.dev == node.attribute_value<uint8_t>("device", 0xff) &&
ir.pin == node.attribute_value<uint8_t>("device_pin", 0xff);
}
static bool node_is_element(Genode::Xml_node node) {
return node.has_type("routing"); }
Irq_routing_policy(Heap & heap) : heap(heap) {}
};

View File

@ -0,0 +1,262 @@
/*
* \brief PCI configuration space decoder
* \author Stefan Kalkowski
* \date 2021-12-12
*/
/*
* Copyright (C) 2021 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 <base/attached_rom_dataspace.h>
#include <base/component.h>
#include <base/env.h>
#include <base/heap.h>
#include <os/reporter.h>
#include <irq.h>
#include <pci/config.h>
using namespace Genode;
using namespace Pci;
struct Main
{
Env & env;
Heap heap { env.ram(), env.rm() };
Attached_rom_dataspace platform_info { env, "platform_info" };
Attached_rom_dataspace sys_rom { env, "system" };
Signal_handler<Main> sys_rom_handler { env.ep(), *this,
&Main::sys_rom_update };
Expanding_reporter pci_reporter { env, "devices", "devices" };
Registry<Bridge> bridge_registry {}; /* contains host bridges */
unsigned msi_number { 0U };
bool apic_capable { false };
bool msi_capable { false };
List_model<Irq_routing> irq_routing_list {};
List_model<Irq_override> irq_override_list {};
Constructible<Attached_io_mem_dataspace> pci_config_ds {};
void parse_pci_function(Bdf bdf, Config & cfg,
addr_t cfg_phys_base,
Xml_generator & generator);
void parse_pci_bus(bus_t bus, bus_t offset, addr_t base,
addr_t phys_base, Xml_generator & generator);
void parse_irq_override_rules(Xml_node & xml);
void parse_pci_config_spaces(Xml_node & xml);
void sys_rom_update();
template <typename FN>
void for_bridge(Pci::bus_t bus, FN const & fn)
{
bridge_registry.for_each([&] (Bridge & b) {
if (b.behind(bus)) b.find_bridge(bus, fn); });
}
Main(Env & env);
};
void Main::parse_pci_function(Bdf bdf,
Config & cfg,
addr_t cfg_phys_base,
Xml_generator & generator)
{
cfg.scan();
Config::Vendor::access_t vendor = cfg.read<Config::Vendor>();
Config::Device::access_t device = cfg.read<Config::Device>();
Config::Header_type::Type::access_t type =
cfg.read<Config::Header_type::Type>();
Config::Class_code_rev_id::Class_code::access_t dclass =
cfg.read<Config::Class_code_rev_id::Class_code>();
if (type) {
for_bridge(bdf.bus, [&] (Bridge & parent) {
Config_type1 bcfg(cfg.base());
new (heap) Bridge(parent.sub_bridges, bdf,
bcfg.secondary_bus_number(),
bcfg.subordinate_bus_number());
});
}
bool msi = cfg.msi_cap.constructed();
bool msi_x = cfg.msi_x_cap.constructed();
irq_pin_t irq_pin = cfg.read<Config::Irq_pin>();
generator.node("device", [&]
{
generator.attribute("name", Bdf::string(bdf));
generator.attribute("type", "pci");
generator.node("pci-config", [&]
{
generator.attribute("address", String<16>(Hex(cfg_phys_base)));
generator.attribute("bus", String<16>(Hex(bdf.bus)));
generator.attribute("device", String<16>(Hex(bdf.dev)));
generator.attribute("function", String<16>(Hex(bdf.fn)));
generator.attribute("vendor_id", String<16>(Hex(vendor)));
generator.attribute("device_id", String<16>(Hex(device)));
generator.attribute("class", String<16>(Hex(dclass)));
generator.attribute("bridge", cfg.bridge() ? "yes" : "no");
});
cfg.for_each_bar([&] (uint64_t addr, size_t size) {
generator.node("io_mem", [&]
{
generator.attribute("address", String<16>(Hex(addr)));
generator.attribute("size", String<16>(Hex(size)));
});
}, [&] (uint64_t addr, size_t size) {
generator.node("io_port_range", [&]
{
generator.attribute("address", String<16>(Hex(addr)));
generator.attribute("size", String<16>(Hex(size)));
});
});
/* IRQ pins count from 1-4 (INTA-D), zero means no IRQ defined */
if (!irq_pin)
return;
generator.node("irq", [&]
{
if (msi_capable && msi_x) {
generator.attribute("type", "msi-x");
generator.attribute("number", msi_number++);
return;
}
if (msi_capable && msi) {
generator.attribute("type", "msi");
generator.attribute("number", msi_number++);
return;
}
irq_line_t irq = cfg.read<Config::Irq_line>();
for_bridge(bdf.bus, [&] (Bridge & b) {
irq_routing_list.for_each([&] (Irq_routing & ir) {
ir.route(b, bdf.dev, irq_pin-1, irq); });
});
irq_override_list.for_each([&] (Irq_override & io) {
io.generate(generator, irq); });
generator.attribute("number", irq);
});
});
}
void Main::parse_pci_bus(bus_t bus,
bus_t offset,
addr_t base,
addr_t phys_base,
Xml_generator & generator)
{
auto per_function = [&] (addr_t config_base, addr_t config_phys_base,
dev_t dev, func_t fn) {
Config cfg(config_base);
if (!cfg.valid())
return true;
parse_pci_function({(bus_t)(bus+offset), dev, fn}, cfg,
config_phys_base, generator);
return !(fn == 0 && !cfg.read<Config::Header_type::Multi_function>());
};
for (dev_t dev = 0; dev < DEVICES_PER_BUS_MAX; dev++) {
for (func_t fn = 0; fn < FUNCTION_PER_DEVICE_MAX; fn++) {
unsigned factor = (bus * DEVICES_PER_BUS_MAX + dev) *
FUNCTION_PER_DEVICE_MAX + fn;
addr_t config_base = base + factor * FUNCTION_CONFIG_SPACE_SIZE;
addr_t config_phys_base =
phys_base + factor * FUNCTION_CONFIG_SPACE_SIZE;
if (!per_function(config_base, config_phys_base, dev, fn))
break;
}
}
}
void Main::parse_pci_config_spaces(Xml_node & xml)
{
pci_reporter.generate([&] (Xml_generator & generator)
{
unsigned host_bridge_num = 0;
xml.for_each_sub_node("bdf", [&] (Xml_node & xml)
{
addr_t const start = xml.attribute_value("start", 0UL);
addr_t const base = xml.attribute_value("base", 0UL);
size_t const count = xml.attribute_value("count", 0UL);
bus_t const bus_off = (bus_t) (start / FUNCTION_PER_BUS_MAX);
bus_t const bus_count = (bus_t) (count / FUNCTION_PER_BUS_MAX);
if (host_bridge_num++) {
error("We do not support multiple host bridges by now!");
return;
}
new (heap) Bridge(bridge_registry, { bus_off, 0, 0 },
bus_off, bus_count);
pci_config_ds.construct(env, base, count * FUNCTION_CONFIG_SPACE_SIZE);
for (bus_t bus = 0; bus < bus_count; bus++)
parse_pci_bus((bus_t)bus, bus_off,
(addr_t)pci_config_ds->local_addr<void>(),
base, generator);
pci_config_ds.destruct();
});
});
}
void Main::sys_rom_update()
{
sys_rom.update();
Xml_node xml = sys_rom.xml();
if (apic_capable) {
Irq_override_policy policy(heap);
irq_override_list.update_from_xml(policy, xml);
}
if (apic_capable) {
Irq_routing_policy policy(heap);
irq_routing_list.update_from_xml(policy, xml);
}
parse_pci_config_spaces(xml);
}
Main::Main(Env & env) : env(env)
{
sys_rom.sigh(sys_rom_handler);
platform_info.xml().with_sub_node("kernel", [&] (Xml_node xml)
{
apic_capable = xml.attribute_value("acpi", false);
msi_capable = xml.attribute_value("msi", false);
});
}
void Component::construct(Genode::Env &env) { static Main main(env); }

View File

@ -0,0 +1,4 @@
TARGET = pci_decode
LIBS = base
SRC_CC = main.cc
INC_DIR = $(PRG_DIR)