From cacb6136fa85d33bf4f47b5df89a1e9b12971cb0 Mon Sep 17 00:00:00 2001 From: Stefan Kalkowski Date: Tue, 1 Feb 2022 08:55:03 +0100 Subject: [PATCH] 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 --- repos/os/include/pci/config.h | 566 ++++++++++++++++++++++++++ repos/os/include/pci/types.h | 92 +++++ repos/os/src/app/pci_decode/bridge.h | 53 +++ repos/os/src/app/pci_decode/irq.h | 165 ++++++++ repos/os/src/app/pci_decode/main.cc | 262 ++++++++++++ repos/os/src/app/pci_decode/target.mk | 4 + 6 files changed, 1142 insertions(+) create mode 100644 repos/os/include/pci/config.h create mode 100644 repos/os/include/pci/types.h create mode 100644 repos/os/src/app/pci_decode/bridge.h create mode 100644 repos/os/src/app/pci_decode/irq.h create mode 100644 repos/os/src/app/pci_decode/main.cc create mode 100644 repos/os/src/app/pci_decode/target.mk 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)