diff --git a/repos/os/include/pci/config.h b/repos/os/include/pci/config.h
new file mode 100644
index 0000000000..32acf218b4
--- /dev/null
+++ b/repos/os/include/pci/config.h
@@ -0,0 +1,566 @@
+/*
+ * \brief PCI, PCI-x, PCI-Express configuration declarations
+ * \author Stefan Kalkowski
+ * \date 2021-12-01
+ */
+
+/*
+ * 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.
+ */
+
+#ifndef __INCLUDE__PCI__CONFIG_H__
+#define __INCLUDE__PCI__CONFIG_H__
+
+#include
+#include
+#include
+#include
+
+namespace Pci {
+ struct Config;
+ struct Config_type0;
+ struct Config_type1;
+
+ enum {
+ DEVICES_PER_BUS_MAX = 32,
+ FUNCTION_PER_DEVICE_MAX = 8,
+ FUNCTION_PER_BUS_MAX = DEVICES_PER_BUS_MAX *
+ FUNCTION_PER_DEVICE_MAX,
+ FUNCTION_CONFIG_SPACE_SIZE = 4096,
+ };
+};
+
+struct Pci::Config : Genode::Mmio
+{
+ struct Vendor : Register<0x0, 16>
+ {
+ enum { INVALID = 0xffff };
+ };
+
+ struct Device : Register<0x2, 16> {};
+
+ struct Command : Register<0x4, 16>
+ {
+ struct Io_space_enable : Bitfield<0, 1> {};
+ struct Memory_space_enable : Bitfield<1, 1> {};
+ struct Bus_master_enable : Bitfield<2, 1> {};
+ struct Special_cycle_enable : Bitfield<3, 1> {};
+ struct Memory_write_invalidate : Bitfield<4, 1> {};
+ struct Vga_palette_snoop : Bitfield<5, 1> {};
+ struct Parity_error_response : Bitfield<6, 1> {};
+ struct Idsel : Bitfield<7, 1> {};
+ struct Serror_enable : Bitfield<8, 1> {};
+ struct Interrupt_enable : Bitfield<10, 1> {};
+ };
+
+ struct Status : Register<0x6, 16>
+ {
+ struct Interrupt : Bitfield<3,1> {};
+ struct Capabilities : Bitfield<4,1> {};
+ };
+
+ struct Class_code_rev_id : Register<0x8, 32>
+ {
+ struct Class_code : Bitfield<8, 24> {};
+ };
+
+ struct Iface_class_code : Register<0x9, 8> {};
+ struct Sub_class_code : Register<0xa, 8> {};
+ struct Base_class_code : Register<0xb, 8>
+ {
+ enum { BRIDGE = 6 };
+ };
+
+ struct Header_type : Register<0xe, 8>
+ {
+ struct Type : Bitfield<0,7> {};
+ struct Multi_function : Bitfield<7,1> {};
+ };
+
+ struct Base_address : Mmio
+ {
+ struct Bar_32bit : Register<0, 32>
+ {
+ struct Memory_space_indicator : Bitfield<0,1>
+ {
+ enum { MEMORY = 0, IO = 1 };
+ };
+
+ struct Memory_type : Bitfield<1,2>
+ {
+ enum { SIZE_32BIT = 0, SIZE_64BIT = 2 };
+ };
+
+ struct Io_base : Bitfield<2, 30> {};
+ struct Memory_base : Bitfield<7, 25> {};
+ };
+
+ struct Upper_bits : Register<0x4, 32> { };
+
+ Bar_32bit::access_t _conf { 0 };
+
+ Base_address(Genode::addr_t base) : Mmio(base)
+ {
+ Bar_32bit::access_t v = read();
+ write(0xffffffff);
+ _conf = read();
+ write(v);
+ }
+
+ bool valid() { return _conf != 0; }
+
+ bool memory() {
+ return !Bar_32bit::Memory_space_indicator::get(_conf); }
+
+ bool bit64()
+ {
+ return Bar_32bit::Memory_type::get(_conf) ==
+ Bar_32bit::Memory_type::SIZE_64BIT;
+ }
+
+ Genode::size_t size()
+ {
+ return 1 + (memory() ? ~Bar_32bit::Memory_base::masked(_conf)
+ : ~Bar_32bit::Io_base::masked(_conf));
+ }
+
+ Genode::uint64_t addr()
+ {
+ return (bit64() ? ((Genode::uint64_t)read()<<32) : 0UL)
+ | Bar_32bit::Memory_base::masked(read());
+ }
+ };
+
+ enum Base_addresses {
+ BASE_ADDRESS_0 = 0x10,
+ BASE_ADDRESS_COUNT_TYPE_0 = 6,
+ BASE_ADDRESS_COUNT_TYPE_1 = 2,
+ };
+
+ struct Capability_pointer : Register<0x34, 8> {};
+
+ struct Irq_line : Register<0x3c, 8> {};
+ struct Irq_pin : Register<0x3d, 8> {};
+
+
+ /**********************
+ ** PCI Capabilities **
+ **********************/
+
+ struct Pci_capability : Genode::Mmio
+ {
+ struct Id : Register<0,8>
+ {
+ enum {
+ POWER_MANAGEMENT = 0x1,
+ AGP = 0x2,
+ VITAL_PRODUCT = 0x3,
+ MSI = 0x5,
+ VENDOR = 0x9,
+ DEBUG = 0xa,
+ BRIDGE_SUB = 0xd,
+ PCI_E = 0x10,
+ MSI_X = 0x11,
+ SATA = 0x12,
+ ADVANCED = 0x13,
+ };
+ };
+
+ struct Pointer : Register<1,8> {};
+
+ using Genode::Mmio::Mmio;
+ };
+
+
+ struct Power_management_capability : Pci_capability
+ {
+ struct Capabilities : Register<0x2, 16> {};
+ struct Control_status : Register<0x4, 16>
+ {
+ struct Pme_status : Bitfield<15,1> {};
+ };
+ struct Data : Register<0x7, 8> {};
+
+ using Pci_capability::Pci_capability;
+ };
+
+
+ struct Msi_capability : Pci_capability
+ {
+ struct Control : Register<0x2, 16>
+ {
+ struct Enable : Bitfield<0,1> {};
+ struct Multi_message_capable : Bitfield<1,3> {};
+ struct Multi_message_enable : Bitfield<4,3> {};
+ struct Large_address_capable : Bitfield<7,1> {};
+ };
+
+ struct Address_32 : Register<0x4, 32> {};
+ struct Data_32 : Register<0x8, 16> {};
+
+ struct Address_64_lower : Register<0x4, 32> {};
+ struct Address_64_upper : Register<0x8, 32> {};
+ struct Data_64 : Register<0xc, 16> {};
+
+ using Pci_capability::Pci_capability;
+
+ void enable(Genode::addr_t address,
+ Genode::uint16_t data)
+ {
+ if (read()) {
+ Genode::uint64_t addr = address;
+ write((Genode::uint32_t)(addr >> 32));
+ write((Genode::uint32_t)addr);
+ write(data);
+ } else {
+ write((Genode::uint32_t)address);
+ write(data);
+ }
+ write(1);
+ };
+ };
+
+
+ struct Msi_x_capability : Pci_capability
+ {
+ struct Control : Register<0x2, 16>
+ {
+ struct Size : Bitfield<0, 11> {};
+ struct Function_mask : Bitfield<14, 1> {};
+ struct Enable : Bitfield<15, 1> {};
+ };
+
+ struct Table : Register<0x4, 32>
+ {
+ struct Bar_index : Bitfield<0, 3> {};
+ struct Offset : Bitfield<3, 29> {};
+ };
+
+ struct Pending_bit_array : Register<0x8, 32>
+ {
+ struct Bar_index : Bitfield<0, 3> {};
+ struct Offset : Bitfield<3, 29> {};
+ };
+
+ struct Table_entry : Genode::Mmio
+ {
+ struct Address_64_lower : Register<0x0, 32> { };
+ struct Address_64_upper : Register<0x4, 32> { };
+ struct Data : Register<0x8, 32> { };
+ struct Vector_control : Register<0xc, 32>
+ {
+ struct Mask : Bitfield <0, 1> { };
+ };
+
+ using Genode::Mmio::Mmio;
+ };
+
+ using Pci_capability::Pci_capability;
+ };
+
+
+ struct Pci_express_capability : Pci_capability
+ {
+ struct Capabilities : Register<0x2, 16> {};
+ struct Device_capabilities : Register<0x4, 32> {};
+ struct Device_control : Register<0x8, 16> {};
+
+ struct Device_status : Register<0xa, 16>
+ {
+ struct Correctable_error : Bitfield<0, 1> {};
+ struct Non_fatal_error : Bitfield<1, 1> {};
+ struct Fatal_error : Bitfield<2, 1> {};
+ struct Unsupported_request : Bitfield<3, 1> {};
+ struct Aux_power : Bitfield<4, 1> {};
+ struct Transactions_pending : Bitfield<5, 1> {};
+ };
+
+ struct Link_capabilities : Register<0xc, 32>
+ {
+ struct Max_link_speed : Bitfield<0, 4> {};
+ };
+
+ struct Link_control : Register<0x10, 16>
+ {
+ struct Lbm_irq_enable : Bitfield<10,1> {};
+ };
+
+ struct Link_status : Register<0x12, 16>
+ {
+ struct Lbm_status : Bitfield<10,1> {};
+ };
+
+ struct Slot_capabilities : Register<0x14, 32> {};
+ struct Slot_control : Register<0x18, 16> {};
+ struct Slot_status : Register<0x1a, 16> {};
+
+ struct Root_control : Register<0x1c, 16>
+ {
+ struct Pme_irq_enable : Bitfield<3,1> {};
+ };
+
+ struct Root_status : Register<0x20, 32>
+ {
+ struct Pme : Bitfield<16,1> {};
+ };
+
+ struct Device_capabilities_2 : Register<0x24, 32> {};
+ struct Device_control_2 : Register<0x28, 16> {};
+ struct Device_status_2 : Register<0x2a, 16> {};
+ struct Link_capabilities_2 : Register<0x2c, 32> {};
+
+ struct Link_control_2 : Register<0x30, 16>
+ {
+ struct Link_speed : Bitfield<0, 4> {};
+ };
+
+ struct Link_status_2 : Register<0x32, 16> {};
+ struct Slot_capabilities_2 : Register<0x34, 32> {};
+ struct Slot_control_2 : Register<0x38, 16> {};
+ struct Slot_status_2 : Register<0x3a, 16> {};
+
+ using Pci_capability::Pci_capability;
+
+ void power_management_event_enable()
+ {
+ write(1);
+ write(1);
+ };
+
+ void clear_dev_errors()
+ {
+ Device_status::access_t v = read();
+ Device_status::Correctable_error::set(v,1);
+ Device_status::Non_fatal_error::set(v,1);
+ Device_status::Fatal_error::set(v,1);
+ Device_status::Unsupported_request::set(v,1);
+ Device_status::Aux_power::set(v,1);
+ write(v);
+ }
+
+ void link_bandwidth_management_enable()
+ {
+ write(1);
+ write(1);
+ }
+ };
+
+
+ /*********************************
+ ** PCI-E extended capabilities **
+ *********************************/
+
+ enum { PCI_E_EXTENDED_CAPS_OFFSET = 0x100U };
+
+ struct Pci_express_extended_capability : Genode::Mmio
+ {
+ struct Id : Register<0,16>
+ {
+ enum {
+ INVALID = 0x0,
+ ADVANCED_ERROR_REPORTING = 0x1,
+ VIRTUAL_CHANNEL = 0x2,
+ DEVICE_SERIAL_NUMBER = 0x3,
+ POWER_BUDGETING = 0x4,
+ VENDOR = 0xb,
+ MULTI_ROOT_IO_VIRT = 0x11,
+ };
+ };
+
+ struct Next_and_version : Register<16, 8>
+ {
+ struct Offset : Bitfield<4, 12> {};
+ };
+
+ using Genode::Mmio::Mmio;
+ };
+
+
+ struct Advanced_error_reporting_capability : Pci_express_extended_capability
+ {
+ struct Uncorrectable_error_status : Register<0x4, 32> {};
+ struct Correctable_error_status : Register<0x10, 32> {};
+
+ struct Root_error_command : Register<0x2c, 32>
+ {
+ struct Correctable_error_enable : Bitfield<0,1> {};
+ struct Non_fatal_error_enable : Bitfield<1,1> {};
+ struct Fatal_error_enable : Bitfield<2,1> {};
+ };
+
+ struct Root_error_status : Register<0x30, 32> {};
+
+ using Pci_express_extended_capability::Pci_express_extended_capability;
+
+ void enable()
+ {
+ Root_error_command::access_t v = 0;
+ Root_error_command::Correctable_error_enable::set(v,1);
+ Root_error_command::Non_fatal_error_enable::set(v,1);
+ Root_error_command::Fatal_error_enable::set(v,1);
+ write(v);
+ };
+
+ void clear()
+ {
+ write(read());
+ write(read());
+ write(read());
+ };
+ };
+
+ Genode::Constructible power_cap {};
+ Genode::Constructible msi_cap {};
+ Genode::Constructible msi_x_cap {};
+ Genode::Constructible pci_e_cap {};
+ Genode::Constructible adv_err_cap {};
+
+ void clear_errors() {
+ if (adv_err_cap.constructed()) adv_err_cap->clear(); }
+
+ void scan()
+ {
+ using namespace Genode;
+
+ if (!read())
+ return;
+
+ uint16_t off = read();
+ while (off) {
+ Pci_capability cap(base() + off);
+ switch(cap.read()) {
+ case Pci_capability::Id::POWER_MANAGEMENT:
+ power_cap.construct(base()+off); break;
+ case Pci_capability::Id::MSI:
+ msi_cap.construct(base()+off); break;
+ case Pci_capability::Id::MSI_X:
+ msi_x_cap.construct(base()+off); break;
+ case Pci_capability::Id::PCI_E:
+ pci_e_cap.construct(base()+off); break;
+
+ case Pci_capability::Id::AGP:
+ case Pci_capability::Id::VITAL_PRODUCT:
+ case Pci_capability::Id::SATA:
+ case Pci_capability::Id::VENDOR:
+ case Pci_capability::Id::ADVANCED:
+ case Pci_capability::Id::BRIDGE_SUB:
+ case Pci_capability::Id::DEBUG:
+ break;
+
+ default:
+ warning("Found unhandled capability ",
+ cap.read(),
+ " at offset ", Hex(base()+off));
+ }
+ off = cap.read();
+ }
+
+ if (!pci_e_cap.constructed())
+ return;
+
+ off = PCI_E_EXTENDED_CAPS_OFFSET;
+ while (off) {
+ Pci_express_extended_capability cap(base() + off);
+ switch (cap.read()) {
+ case Pci_express_extended_capability::Id::INVALID:
+ return;
+ case Pci_express_extended_capability::Id::ADVANCED_ERROR_REPORTING:
+ adv_err_cap.construct(base() + off); break;
+
+ case Pci_express_extended_capability::Id::VENDOR:
+ case Pci_express_extended_capability::Id::VIRTUAL_CHANNEL:
+ case Pci_express_extended_capability::Id::MULTI_ROOT_IO_VIRT:
+ break;
+
+ default:
+ warning("Found unhandled extended capability ",
+ cap.read(),
+ " at offset ", Hex(base()+off));
+ }
+ off = cap.read();
+ }
+ }
+
+ using Genode::Mmio::Mmio;
+
+ bool valid() {
+ return read() != Vendor::INVALID; }
+
+ bool bridge()
+ {
+ return read() == 1 ||
+ read() == Base_class_code::BRIDGE;
+ }
+
+ template
+ void for_each_bar(MEM_FN const & memory, IO_FN const & io)
+ {
+ Genode::addr_t const reg_addr = base() + BASE_ADDRESS_0;
+ Genode::size_t const reg_cnt =
+ (read()) ? BASE_ADDRESS_COUNT_TYPE_1
+ : BASE_ADDRESS_COUNT_TYPE_0;
+
+ for (unsigned i = 0; i < reg_cnt; i++) {
+ Base_address reg0(reg_addr + i*0x4);
+ if (!reg0.valid())
+ continue;
+ if (reg0.memory()) {
+ if (reg0.bit64()) i++;
+ memory(reg0.addr(), reg0.size());
+ } else
+ io(reg0.addr(), reg0.size());
+ }
+ };
+};
+
+
+struct Pci::Config_type0 : Pci::Config
+{
+ struct Expansion_rom_base_addr : Register<0x30, 32> {};
+
+ using Pci::Config::Config;
+};
+
+
+struct Pci::Config_type1 : Pci::Config
+{
+ struct Sec_lat_timer_bus : Register<0x18, 32>
+ {
+ struct Primary_bus : Bitfield<0, 8> {};
+ struct Secondary_bus : Bitfield<8, 8> {};
+ struct Sub_bus : Bitfield<16, 8> {};
+ };
+
+ struct Io_base_limit : Register<0x1c, 16> {};
+
+ struct Memory_base_limit : Register<0x20, 32> {};
+
+ struct Prefetchable_memory_base : Register<0x24, 32> {};
+ struct Prefetchable_memory_base_upper : Register<0x28, 32> {};
+ struct Prefetchable_memory_limit_upper : Register<0x2c, 32> {};
+
+ struct Io_base_limit_upper : Register<0x30, 32> {};
+
+ struct Expansion_rom_base_addr : Register<0x38, 32> {};
+
+ struct Bridge_control : Register<0x3e, 16>
+ {
+ struct Serror : Bitfield<1, 1> {};
+ };
+
+ using Pci::Config::Config;
+
+ bus_t primary_bus_number() {
+ return (bus_t) read(); }
+
+ bus_t secondary_bus_number() {
+ return (bus_t) read(); }
+
+ bus_t subordinate_bus_number() {
+ return (bus_t) read(); }
+};
+
+#endif /* __INCLUDE__PCI__CONFIG_H__ */
diff --git a/repos/os/include/pci/types.h b/repos/os/include/pci/types.h
new file mode 100644
index 0000000000..d18c219b9e
--- /dev/null
+++ b/repos/os/include/pci/types.h
@@ -0,0 +1,92 @@
+/*
+ * \brief PCI basic types
+ * \author Stefan Kalkowski
+ * \date 2021-12-01
+ */
+
+/*
+ * 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.
+ */
+
+#ifndef __INCLUDE__PCI__TYPES_H__
+#define __INCLUDE__PCI__TYPES_H__
+
+#include
+#include
+
+namespace Pci {
+ /**
+ * Topology POD-types: bus, device, function
+ */
+ using bus_t = Genode::uint8_t;
+ using dev_t = Genode::uint8_t;
+ using func_t = Genode::uint8_t;
+
+ /* Bus, device, function encoded as routing ID */
+ using rid_t = Genode::uint16_t;
+
+ /**
+ * Bus, device, function as C++ object representation
+ */
+ struct Bdf;
+
+ /**
+ * Further POD-types found in PCI configuration space
+ */
+ using irq_line_t = Genode::uint8_t;
+ using irq_pin_t = Genode::uint8_t;
+ using vendor_t = Genode::uint16_t;
+ using device_t = Genode::uint16_t;
+ using class_t = Genode::uint32_t;
+}
+
+
+struct Pci::Bdf
+{
+ bus_t bus;
+ dev_t dev;
+ func_t fn;
+
+ struct Routing_id : Genode::Register<16>
+ {
+ struct Function : Bitfield<0,3> {};
+ struct Device : Bitfield<3,5> {};
+ struct Bus : Bitfield<8,8> {};
+ };
+
+ static Bdf bdf(rid_t rid)
+ {
+ return { (Pci::bus_t) Routing_id::Bus::get(rid),
+ (Pci::dev_t) Routing_id::Device::get(rid),
+ (Pci::func_t) Routing_id::Function::get(rid) };
+ }
+
+ static rid_t rid(Bdf bdf)
+ {
+ Routing_id::access_t rid = 0;
+ Routing_id::Bus::set(rid, bdf.bus);
+ Routing_id::Device::set(rid, bdf.dev);
+ Routing_id::Function::set(rid, bdf.fn);
+ return rid;
+ }
+
+ bool operator == (Bdf const &id) const {
+ return id.bus == bus && id.dev == dev && id.fn == fn; }
+
+
+ static Genode::String<16> string(Bdf bdf)
+ {
+ using namespace Genode;
+ return String<16>(Hex(bdf.bus, Hex::OMIT_PREFIX, Hex::PAD), ":",
+ Hex(bdf.dev, Hex::OMIT_PREFIX, Hex::PAD), ".",
+ bdf.fn);
+ }
+
+ void print(Genode::Output &out) const {
+ string(*this).print(out); }
+};
+
+#endif /* __INCLUDE__PCI__TYPES_H__ */
diff --git a/repos/os/src/app/pci_decode/bridge.h b/repos/os/src/app/pci_decode/bridge.h
new file mode 100644
index 0000000000..6e4dc1327c
--- /dev/null
+++ b/repos/os/src/app/pci_decode/bridge.h
@@ -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
+#include
+
+using namespace Genode;
+
+struct Bridge : Registry::Element
+{
+ Pci::Bdf bdf;
+ Pci::bus_t from;
+ Pci::bus_t to;
+
+ Registry sub_bridges {};
+
+ Bridge(Registry & registry,
+ Pci::Bdf bdf,
+ Pci::bus_t from,
+ Pci::bus_t to)
+ :
+ Registry::Element(registry, *this),
+ bdf(bdf), from(from), to(to) { }
+
+ bool behind(Pci::bus_t bus) {
+ return from <= bus && bus <= to; }
+
+ template
+ 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);
+ }
+};
diff --git a/repos/os/src/app/pci_decode/irq.h b/repos/os/src/app/pci_decode/irq.h
new file mode 100644
index 0000000000..8b17fae5b4
--- /dev/null
+++ b/repos/os/src/app/pci_decode/irq.h
@@ -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
+#include
+#include
+#include
+
+#include
+
+using namespace Genode;
+using namespace Pci;
+
+
+struct Irq_override : List_model::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::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("irq", 0xff),
+ node.attribute_value("gsi", 0xff),
+ node.attribute_value("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::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::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("bridge_bdf", 0xff);
+ return *(new (heap)
+ Irq_routing(Bdf::bdf(bridge_bdf),
+ node.attribute_value("device", 0xff),
+ node.attribute_value("device_pin", 0xff),
+ node.attribute_value("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("bridge_bdf", 0xff);
+ return ir.bridge_bdf == Bdf::bdf(bridge_bdf) &&
+ ir.dev == node.attribute_value("device", 0xff) &&
+ ir.pin == node.attribute_value("device_pin", 0xff);
+ }
+
+ static bool node_is_element(Genode::Xml_node node) {
+ return node.has_type("routing"); }
+
+ Irq_routing_policy(Heap & heap) : heap(heap) {}
+};
diff --git a/repos/os/src/app/pci_decode/main.cc b/repos/os/src/app/pci_decode/main.cc
new file mode 100644
index 0000000000..c988c27a78
--- /dev/null
+++ b/repos/os/src/app/pci_decode/main.cc
@@ -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
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+
+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 sys_rom_handler { env.ep(), *this,
+ &Main::sys_rom_update };
+ Expanding_reporter pci_reporter { env, "devices", "devices" };
+ Registry bridge_registry {}; /* contains host bridges */
+
+ unsigned msi_number { 0U };
+
+ bool apic_capable { false };
+ bool msi_capable { false };
+
+ List_model irq_routing_list {};
+ List_model irq_override_list {};
+
+ Constructible 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
+ 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::Device::access_t device = cfg.read();
+ Config::Header_type::Type::access_t type =
+ cfg.read();
+ Config::Class_code_rev_id::Class_code::access_t dclass =
+ cfg.read();
+
+ 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();
+
+ 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();
+
+ 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());
+ };
+
+ 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(),
+ 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); }
diff --git a/repos/os/src/app/pci_decode/target.mk b/repos/os/src/app/pci_decode/target.mk
new file mode 100644
index 0000000000..00aab2c32b
--- /dev/null
+++ b/repos/os/src/app/pci_decode/target.mk
@@ -0,0 +1,4 @@
+TARGET = pci_decode
+LIBS = base
+SRC_CC = main.cc
+INC_DIR = $(PRG_DIR)