mirror of
https://github.com/genodelabs/genode.git
synced 2024-12-19 05:37:54 +00:00
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:
parent
87021d9fb1
commit
cacb6136fa
566
repos/os/include/pci/config.h
Normal file
566
repos/os/include/pci/config.h
Normal file
@ -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 <base/log.h>
|
||||
#include <pci/types.h>
|
||||
#include <util/reconstructible.h>
|
||||
#include <util/mmio.h>
|
||||
|
||||
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<Bar_32bit>();
|
||||
write<Bar_32bit>(0xffffffff);
|
||||
_conf = read<Bar_32bit>();
|
||||
write<Bar_32bit>(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<Upper_bits>()<<32) : 0UL)
|
||||
| Bar_32bit::Memory_base::masked(read<Bar_32bit>());
|
||||
}
|
||||
};
|
||||
|
||||
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<Control::Large_address_capable>()) {
|
||||
Genode::uint64_t addr = address;
|
||||
write<Address_64_upper>((Genode::uint32_t)(addr >> 32));
|
||||
write<Address_64_lower>((Genode::uint32_t)addr);
|
||||
write<Data_64>(data);
|
||||
} else {
|
||||
write<Address_32>((Genode::uint32_t)address);
|
||||
write<Data_32>(data);
|
||||
}
|
||||
write<Control::Enable>(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<Root_status::Pme>(1);
|
||||
write<Root_control::Pme_irq_enable>(1);
|
||||
};
|
||||
|
||||
void clear_dev_errors()
|
||||
{
|
||||
Device_status::access_t v = read<Device_status>();
|
||||
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<Device_status>(v);
|
||||
}
|
||||
|
||||
void link_bandwidth_management_enable()
|
||||
{
|
||||
write<Link_status::Lbm_status>(1);
|
||||
write<Link_control::Lbm_irq_enable>(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<Root_error_command>(v);
|
||||
};
|
||||
|
||||
void clear()
|
||||
{
|
||||
write<Root_error_status>(read<Root_error_status>());
|
||||
write<Correctable_error_status>(read<Correctable_error_status>());
|
||||
write<Uncorrectable_error_status>(read<Uncorrectable_error_status>());
|
||||
};
|
||||
};
|
||||
|
||||
Genode::Constructible<Power_management_capability> power_cap {};
|
||||
Genode::Constructible<Msi_capability> msi_cap {};
|
||||
Genode::Constructible<Msi_x_capability> msi_x_cap {};
|
||||
Genode::Constructible<Pci_express_capability> pci_e_cap {};
|
||||
Genode::Constructible<Advanced_error_reporting_capability> adv_err_cap {};
|
||||
|
||||
void clear_errors() {
|
||||
if (adv_err_cap.constructed()) adv_err_cap->clear(); }
|
||||
|
||||
void scan()
|
||||
{
|
||||
using namespace Genode;
|
||||
|
||||
if (!read<Status::Capabilities>())
|
||||
return;
|
||||
|
||||
uint16_t off = read<Capability_pointer>();
|
||||
while (off) {
|
||||
Pci_capability cap(base() + off);
|
||||
switch(cap.read<Pci_capability::Id>()) {
|
||||
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<Pci_capability::Id>(),
|
||||
" at offset ", Hex(base()+off));
|
||||
}
|
||||
off = cap.read<Pci_capability::Pointer>();
|
||||
}
|
||||
|
||||
if (!pci_e_cap.constructed())
|
||||
return;
|
||||
|
||||
off = PCI_E_EXTENDED_CAPS_OFFSET;
|
||||
while (off) {
|
||||
Pci_express_extended_capability cap(base() + off);
|
||||
switch (cap.read<Pci_express_extended_capability::Id>()) {
|
||||
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<Pci_express_extended_capability::Id>(),
|
||||
" at offset ", Hex(base()+off));
|
||||
}
|
||||
off = cap.read<Pci_express_extended_capability::Next_and_version::Offset>();
|
||||
}
|
||||
}
|
||||
|
||||
using Genode::Mmio::Mmio;
|
||||
|
||||
bool valid() {
|
||||
return read<Vendor>() != Vendor::INVALID; }
|
||||
|
||||
bool bridge()
|
||||
{
|
||||
return read<Header_type::Type>() == 1 ||
|
||||
read<Base_class_code>() == Base_class_code::BRIDGE;
|
||||
}
|
||||
|
||||
template <typename MEM_FN, typename IO_FN>
|
||||
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<Header_type::Type>()) ? 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<Sec_lat_timer_bus::Primary_bus>(); }
|
||||
|
||||
bus_t secondary_bus_number() {
|
||||
return (bus_t) read<Sec_lat_timer_bus::Secondary_bus>(); }
|
||||
|
||||
bus_t subordinate_bus_number() {
|
||||
return (bus_t) read<Sec_lat_timer_bus::Sub_bus>(); }
|
||||
};
|
||||
|
||||
#endif /* __INCLUDE__PCI__CONFIG_H__ */
|
92
repos/os/include/pci/types.h
Normal file
92
repos/os/include/pci/types.h
Normal file
@ -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 <base/stdint.h>
|
||||
#include <util/string.h>
|
||||
|
||||
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__ */
|
53
repos/os/src/app/pci_decode/bridge.h
Normal file
53
repos/os/src/app/pci_decode/bridge.h
Normal 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);
|
||||
}
|
||||
};
|
165
repos/os/src/app/pci_decode/irq.h
Normal file
165
repos/os/src/app/pci_decode/irq.h
Normal 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) {}
|
||||
};
|
262
repos/os/src/app/pci_decode/main.cc
Normal file
262
repos/os/src/app/pci_decode/main.cc
Normal 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); }
|
4
repos/os/src/app/pci_decode/target.mk
Normal file
4
repos/os/src/app/pci_decode/target.mk
Normal file
@ -0,0 +1,4 @@
|
||||
TARGET = pci_decode
|
||||
LIBS = base
|
||||
SRC_CC = main.cc
|
||||
INC_DIR = $(PRG_DIR)
|
Loading…
Reference in New Issue
Block a user