mirror of
https://github.com/genodelabs/genode.git
synced 2024-12-23 23:42:32 +00:00
e7ba0b7371
On-demand initialization prevents read-write operations on BARs of invalid devices at construction time, which may result in surprising behavior later on, for example, when resetting X260 notebooks via ACPI information.
607 lines
15 KiB
C++
607 lines
15 KiB
C++
/*
|
|
* \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 Fast_back_to_back_enable : Bitfield<9, 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 Revision : Bitfield<0, 8> {};
|
|
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 Memory_prefetchable : Bitfield<3,1> {};
|
|
|
|
struct Io_base : Bitfield<2, 30> {};
|
|
struct Memory_base : Bitfield<7, 25> {};
|
|
};
|
|
|
|
struct Upper_bits : Register<0x4, 32> { };
|
|
|
|
Bar_32bit::access_t _conf_value { 0 };
|
|
|
|
Bar_32bit::access_t _conf()
|
|
{
|
|
/*
|
|
* Initialize _conf_value on demand only to prevent read-write
|
|
* operations on BARs of invalid devices at construction time.
|
|
*/
|
|
if (!_conf_value) {
|
|
Bar_32bit::access_t v = read<Bar_32bit>();
|
|
write<Bar_32bit>(0xffffffff);
|
|
_conf_value = read<Bar_32bit>();
|
|
write<Bar_32bit>(v);
|
|
}
|
|
return _conf_value;
|
|
}
|
|
|
|
Base_address(Genode::addr_t base) : Mmio(base) { }
|
|
|
|
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;
|
|
}
|
|
|
|
bool prefetchable() {
|
|
return Bar_32bit::Memory_prefetchable::get(_conf()); }
|
|
|
|
Genode::size_t size()
|
|
{
|
|
return 1 + (memory() ? ~Bar_32bit::Memory_base::masked(_conf())
|
|
: ~Bar_32bit::Io_base::masked(_conf()));
|
|
}
|
|
|
|
Genode::uint64_t addr()
|
|
{
|
|
if (memory())
|
|
return (bit64()
|
|
? ((Genode::uint64_t)read<Upper_bits>()<<32) : 0UL)
|
|
| Bar_32bit::Memory_base::masked(read<Bar_32bit>());
|
|
else
|
|
return Bar_32bit::Io_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>
|
|
{
|
|
enum { UNKNOWN = 0xff };
|
|
};
|
|
|
|
struct Irq_pin : Register<0x3d, 8>
|
|
{
|
|
enum { NO_INT = 0, INTA, INTB, INTC, INTD };
|
|
};
|
|
|
|
|
|
/**********************
|
|
** 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 Slots : Bitfield<0, 10> {};
|
|
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
|
|
{
|
|
enum { SIZE = 16 };
|
|
|
|
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;
|
|
|
|
Genode::uint8_t bar() {
|
|
return (Genode::uint8_t) read<Table::Bar_index>(); }
|
|
|
|
Genode::size_t table_offset() {
|
|
return read<Table::Offset>() << 3; }
|
|
|
|
unsigned slots() { return read<Control::Slots>(); }
|
|
|
|
void enable()
|
|
{
|
|
Control::access_t ctrl = read<Control>();
|
|
Control::Function_mask::set(ctrl, 0);
|
|
Control::Enable::set(ctrl, 1);
|
|
write<Control>(ctrl);
|
|
}
|
|
};
|
|
|
|
|
|
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<0x0, 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<0x2, 16>
|
|
{
|
|
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 {};
|
|
|
|
Base_address bar0 { base() + BASE_ADDRESS_0 };
|
|
Base_address bar1 { base() + BASE_ADDRESS_0 + 0x4 };
|
|
|
|
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;
|
|
|
|
default:
|
|
/* ignore unhandled capability */ ;
|
|
}
|
|
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;
|
|
|
|
default:
|
|
/* ignore unhandled extended capability */ ;
|
|
}
|
|
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()) {
|
|
memory(reg0.addr(), reg0.size(), i, reg0.prefetchable());
|
|
if (reg0.bit64()) i++;
|
|
} else
|
|
io(reg0.addr(), reg0.size(), i);
|
|
}
|
|
};
|
|
};
|
|
|
|
|
|
struct Pci::Config_type0 : Pci::Config
|
|
{
|
|
struct Expansion_rom_base_addr : Register<0x30, 32> {};
|
|
|
|
using Pci::Config::Config;
|
|
|
|
Base_address bar2 { base() + BASE_ADDRESS_0 + 0x8 };
|
|
Base_address bar3 { base() + BASE_ADDRESS_0 + 0xc };
|
|
Base_address bar4 { base() + BASE_ADDRESS_0 + 0x10 };
|
|
Base_address bar5 { base() + BASE_ADDRESS_0 + 0x14 };
|
|
|
|
struct Subsystem_vendor : Register<0x2c, 16> { };
|
|
struct Subsystem_device : Register<0x2e, 16> { };
|
|
};
|
|
|
|
|
|
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 : Register<0x20, 16> {};
|
|
struct Memory_limit : Register<0x22, 16> {};
|
|
|
|
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__ */
|