From 47724c68c2c27a430e9a60c55ebb42f8d4ebf941 Mon Sep 17 00:00:00 2001 From: Reto Buerki Date: Thu, 19 Oct 2017 10:13:35 +0200 Subject: [PATCH] platform_drv/x86: Switch to ECAM/MMCONF Switch port I/O based PCI config space access to memory-mapped IO. The base address of the PCI configuration space is acquired by mapping the ACPI ROM and reading the first node. An exception is thrown if the first node is not for PCI domain zero or if multiple nodes exist. This is to reduce complexity and also because multiple PCI domains are rare. The PCI configuration space is accessed via I/O mem dataspace which is created in the platform_drv root and then passed on to the PCI session, device components and finally to the actual PCI config access instances. The memory access code is implemented in a way to make it work with Muen subject monitor (SM) device emulation and also general x86 targets. On Muen, the simplified device emulation code (which works also for Linux) always returns 0xffff in EAX to indicate a non-existing device. Therefore, EAX is enforced in the assembly templates. Fixes #2547 --- repos/base/run/platform_drv.inc | 25 +- repos/os/src/drivers/platform/spec/x86/README | 36 +-- .../drivers/platform/spec/x86/device_pd.cc | 11 +- .../src/drivers/platform/spec/x86/device_pd.h | 3 +- .../os/src/drivers/platform/spec/x86/main.cc | 45 +--- .../platform/spec/x86/nonpci_devices.cc | 16 +- .../platform/spec/x86/pci_config_access.h | 138 ++++------ .../drivers/platform/spec/x86/pci_device.cc | 18 +- .../platform/spec/x86/pci_device_component.h | 52 ++-- .../platform/spec/x86/pci_device_config.h | 31 ++- .../platform/spec/x86/pci_session_component.h | 253 +++++++++++------- .../src/drivers/platform/spec/x86/session.cc | 12 +- tool/run/power_on/qemu | 2 + 13 files changed, 319 insertions(+), 323 deletions(-) diff --git a/repos/base/run/platform_drv.inc b/repos/base/run/platform_drv.inc index e5ad966074..edde5c9eb7 100644 --- a/repos/base/run/platform_drv.inc +++ b/repos/base/run/platform_drv.inc @@ -54,8 +54,8 @@ proc audio_drv_binary { } { proc platform_drv_build_components {} { set drv_build_components "" lappend_if [have_platform_drv] drv_build_components drivers/platform - lappend_if [have_spec acpi] drv_build_components drivers/acpi - lappend_if [have_spec acpi] drv_build_components server/report_rom + lappend_if [have_spec x86] drv_build_components drivers/acpi + lappend_if [have_spec x86] drv_build_components server/report_rom return $drv_build_components } @@ -68,9 +68,16 @@ proc append_platform_drv_build_components {} { proc platform_drv_boot_modules {} { set drv_boot_modules "" lappend_if [have_platform_drv] drv_boot_modules platform_drv - lappend_if [have_spec acpi] drv_boot_modules acpi_drv - lappend_if [have_spec acpi] drv_boot_modules report_rom - lappend_if [have_spec muen] drv_boot_modules acpi + lappend_if [have_spec x86] drv_boot_modules report_rom + + if {[have_spec x86]} { + if {[have_spec muen]} { + lappend drv_boot_modules acpi + } else { + lappend drv_boot_modules acpi_drv + } + } + return $drv_boot_modules } @@ -104,7 +111,7 @@ proc platform_drv_policy {} { proc platform_drv_priority {} { return "" } proc platform_drv_add_routing {} { - if {[have_spec acpi]} { + if {[have_spec x86]} { return { } } @@ -127,7 +134,7 @@ proc platform_drv_config_config {} { proc platform_drv_config {} { set drv_config "" - if {[have_spec acpi]} { + if {[have_spec x86] && ![have_spec muen] && ![have_spec linux]} { append drv_config { @@ -175,7 +182,7 @@ proc platform_drv_config {} { } - append_if [have_spec acpi] drv_config { + append_if [have_spec x86] drv_config { } append_if [have_spec arm] drv_config { @@ -187,7 +194,7 @@ proc platform_drv_config {} { append drv_config "[platform_drv_add_routing]" - append_if [have_spec acpi] drv_config { + append_if [expr [have_spec x86] && ![have_spec muen]] drv_config { } append_if [have_spec rpi] drv_config { diff --git a/repos/os/src/drivers/platform/spec/x86/README b/repos/os/src/drivers/platform/spec/x86/README index e9a7edfe7a..950990bc5e 100644 --- a/repos/os/src/drivers/platform/spec/x86/README +++ b/repos/os/src/drivers/platform/spec/x86/README @@ -87,10 +87,10 @@ physical memory address below 4G. Another example is that on 32bit hosts physical to virtual identical mappings of DMA memory for the device_pd (required when IOMMU is used) must be below the kernel memory boundary (3G). -By default the platform driver waits on startup on a report of the acpi driver, -which conatins further information about the platform the platform driver can -not discover (e.g. IRQ re-routing information, pci config extended space -information). +The platform driver waits on startup on the first valid ACPI report, typically +provided dynamically by the acpi driver. +The report contains further information about the hardware the platform driver can +not discover (e.g. IRQ re-routing information, PCI ECAM/MMCONF information). A specific route to a report_rom service named 'acpi_report_rom' looks as in the following: @@ -105,17 +105,6 @@ in the following: ! ! ... -For platforms which don't support or require the ACPI information - -e.g. base-okl4, base-pistachio, base-fiasco - the platform driver can be -configured to not wait for the acpi report: - -! -! ... -! -! ... -! -! ... - Synchronize ACPI startup and platform driver -------------------------------------------- @@ -123,7 +112,7 @@ If the config attribute 'acpi_ready' is set to 'yes', the platform driver monitors a ROM in XML format named 'acpi_ready'. ! -! +! The platform driver will announce its service not as 'Platform', but instead as 'Acpi' first. @@ -137,21 +126,6 @@ system state changes to "acpi_ready in the XML ROM 'acpi_ready': the platform driver will announce the platform session as 'Platform', so that drivers may start to operate with the platform driver. -Hardware reset --------------- -If the config attribute 'system' is set to 'yes', the platform driver monitors -a ROM in XML format named 'system'. - -! -! - -If the attribute 'state' in the system XML ROM turns to 'reset' - -! - -the platform driver will try to reset the machine, if the required I/O ports -are owned by it. - Supported PCI class aliases --------------------------- diff --git a/repos/os/src/drivers/platform/spec/x86/device_pd.cc b/repos/os/src/drivers/platform/spec/x86/device_pd.cc index a1a97091f6..924f59d14f 100644 --- a/repos/os/src/drivers/platform/spec/x86/device_pd.cc +++ b/repos/os/src/drivers/platform/spec/x86/device_pd.cc @@ -58,21 +58,22 @@ void Platform::Device_pd::attach_dma_mem(Genode::Dataspace_capability ds_cap) } } -void Platform::Device_pd::assign_pci(Genode::Io_mem_dataspace_capability io_mem_cap, - Genode::uint16_t rid) +void Platform::Device_pd::assign_pci(Genode::Io_mem_dataspace_capability const io_mem_cap, + Genode::addr_t const offset, + Genode::uint16_t const rid) { using namespace Genode; Dataspace_client ds_client(io_mem_cap); - addr_t page = _address_space.attach(io_mem_cap); + addr_t page = _address_space.attach(io_mem_cap, 0x1000, offset); /* sanity check */ if (!page) throw Region_map::Region_conflict(); /* trigger eager mapping of memory */ - _pd.map(page, ds_client.size()); + _pd.map(page, 0x1000); /* utility to print rid value */ struct Rid @@ -92,7 +93,7 @@ void Platform::Device_pd::assign_pci(Genode::Io_mem_dataspace_capability io_mem_ /* try to assign pci device to this protection domain */ if (!_pd.assign_pci(page, rid)) Genode::error(_label, ": assignment of PCI device ", Rid(rid), " failed ", - "phys=", Genode::Hex(ds_client.phys_addr()), " " + "phys=", Genode::Hex(ds_client.phys_addr() + offset), " " "virt=", Genode::Hex(page)); else Genode::log(_label,": assignment of PCI device ", Rid(rid), " succeeded"); diff --git a/repos/os/src/drivers/platform/spec/x86/device_pd.h b/repos/os/src/drivers/platform/spec/x86/device_pd.h index 9c53d7bc74..a52cecf484 100644 --- a/repos/os/src/drivers/platform/spec/x86/device_pd.h +++ b/repos/os/src/drivers/platform/spec/x86/device_pd.h @@ -109,5 +109,6 @@ class Platform::Device_pd } void attach_dma_mem(Genode::Dataspace_capability); - void assign_pci(Genode::Io_mem_dataspace_capability, Genode::uint16_t); + void assign_pci(Genode::Io_mem_dataspace_capability const, + Genode::addr_t const, Genode::uint16_t const); }; diff --git a/repos/os/src/drivers/platform/spec/x86/main.cc b/repos/os/src/drivers/platform/spec/x86/main.cc index 1d41939c43..d4d4802b29 100644 --- a/repos/os/src/drivers/platform/spec/x86/main.cc +++ b/repos/os/src/drivers/platform/spec/x86/main.cc @@ -57,8 +57,9 @@ struct Platform::Main return; const char * report_addr = acpi_rom->local_addr(); + bool const acpi_platform = _config.xml().attribute_value("acpi", true); - root.construct(_env, sliced_heap, _config, report_addr); + root.construct(_env, sliced_heap, _config, report_addr, acpi_platform); root_cap = _env.ep().manage(*root); @@ -71,26 +72,12 @@ struct Platform::Main void system_update() { - if (system_state.is_constructed()) - system_state->update(); - if (acpi_ready.is_constructed()) acpi_ready->update(); if (!root.is_constructed()) return; - if (system_state.is_constructed() && system_state->is_valid()) { - Genode::Xml_node system(system_state->local_addr(), - system_state->size()); - - typedef Genode::String<16> Value; - const Value state = system.attribute_value("state", Value("unknown")); - - if (state == "reset") - root->system_reset(); - } - if (acpi_ready.is_constructed() && acpi_ready->is_valid()) { Genode::Xml_node system(acpi_ready->local_addr(), acpi_ready->size()); @@ -112,34 +99,20 @@ struct Platform::Main _system_report(_env.ep(), *this, &Main::system_update) { const Genode::Xml_node config = _config.xml(); - bool const system_rom = config.attribute_value("system", false); - bool const wait_for_acpi = config.attribute_value("acpi", true); - _acpi_ready = config.attribute_value("acpi_ready", false); - if (system_rom) { - /* wait for system state changes, e.g. reset and acpi_ready */ - system_state.construct(env, "system"); - system_state->sigh(_system_report); - } + _acpi_ready = config.attribute_value("acpi_ready", false); if (_acpi_ready) { acpi_ready.construct(env, "acpi_ready"); acpi_ready->sigh(_system_report); } - if (wait_for_acpi) { - /* for ACPI support, wait for the first valid acpi report */ - acpi_rom.construct(env, "acpi"); - acpi_rom->sigh(_acpi_report); - /* check if already valid */ - acpi_update(); - system_update(); - return; - } - - /* non ACPI platform case */ - root.construct(_env, sliced_heap, _config, nullptr); - _env.parent().announce(_env.ep().manage(*root)); + /* wait for the first valid acpi report */ + acpi_rom.construct(env, "acpi"); + acpi_rom->sigh(_acpi_report); + /* check if already valid */ + acpi_update(); + system_update(); } }; diff --git a/repos/os/src/drivers/platform/spec/x86/nonpci_devices.cc b/repos/os/src/drivers/platform/spec/x86/nonpci_devices.cc index 432b84f939..7179a20f47 100644 --- a/repos/os/src/drivers/platform/spec/x86/nonpci_devices.cc +++ b/repos/os/src/drivers/platform/spec/x86/nonpci_devices.cc @@ -36,10 +36,12 @@ class Nonpci::Ps2 : public Platform::Device_component public: - Ps2(Genode::Env &env, Platform::Session_component &session, + Ps2(Genode::Env &env, + Genode::Attached_io_mem_dataspace &pciconf, + Platform::Session_component &session, Genode::Allocator &heap_for_irq) : - Platform::Device_component(env, session, IRQ_KEYBOARD, heap_for_irq), + Platform::Device_component(env, pciconf, session, IRQ_KEYBOARD, heap_for_irq), _ep(env.ep().rpc_ep()), _irq_mouse(IRQ_MOUSE, ~0UL, env, heap_for_irq), _data(env, REG_DATA, ACCESS_WIDTH), @@ -99,10 +101,12 @@ class Nonpci::Pit : public Platform::Device_component public: - Pit(Genode::Env &env, Platform::Session_component &session, + Pit(Genode::Env &env, + Genode::Attached_io_mem_dataspace &pciconf, + Platform::Session_component &session, Genode::Allocator &heap_for_irq) : - Platform::Device_component(env, session, IRQ_PIT, heap_for_irq), + Platform::Device_component(env, pciconf, session, IRQ_PIT, heap_for_irq), _ports(env, PIT_PORT, PORTS_WIDTH) { } @@ -150,10 +154,10 @@ Platform::Device_capability Platform::Session_component::device(String const &na switch(devices_i) { case 0: - dev = new (_md_alloc) Nonpci::Ps2(_env, *this, _global_heap); + dev = new (_md_alloc) Nonpci::Ps2(_env, _pciconf, *this, _global_heap); break; case 1: - dev = new (_md_alloc) Nonpci::Pit(_env, *this, _global_heap); + dev = new (_md_alloc) Nonpci::Pit(_env, _pciconf, *this, _global_heap); break; default: return Device_capability(); diff --git a/repos/os/src/drivers/platform/spec/x86/pci_config_access.h b/repos/os/src/drivers/platform/spec/x86/pci_config_access.h index 94ba9dc727..ac282ee6b1 100644 --- a/repos/os/src/drivers/platform/spec/x86/pci_config_access.h +++ b/repos/os/src/drivers/platform/spec/x86/pci_config_access.h @@ -1,6 +1,7 @@ /* * \brief Interface for accessing PCI configuration registers * \author Norman Feske + * \author Reto Buerki * \date 2008-01-29 */ @@ -15,60 +16,36 @@ #define _X86_PCI_CONFIG_ACCESS_H_ #include -#include +#include +#include #include +using namespace Genode; + namespace Platform { class Config_access { private: - Genode::Env &_env; - - enum { REG_ADDR = 0xcf8, REG_DATA = 0xcfc, REG_SIZE = 4 }; + Attached_io_mem_dataspace &_pciconf; + Genode::size_t const _pciconf_size; /** - * Request interface to access an I/O port - */ - template - Genode::Io_port_session *_io_port() - { - /* - * Open I/O-port session when first called. - * The number of instances of the io_port_session_client - * variable depends on the number of instances of the - * template function. - * - * Thanks to this mechanism, the sessions to the PCI - * ports are created lazily when PCI service is first - * used. If the PCI bus driver is just started but not - * used yet, other processes are able to access the PCI - * config space. - * - * Once created, each I/O-port session persists until - * the PCI driver gets killed by its parent. - */ - static Genode::Io_port_connection io_port(_env, port, REG_SIZE); - return &io_port; - } - - /** - * Generate configuration address + * Calculate device offset from BDF * * \param bus target PCI bus ID (0..255) * \param device target device ID (0..31) * \param function target function ID (0..7) - * \param addr target byte within targeted PCI config space (0..255) * - * \return configuration address (written to REG_ADDR register) + * \return device base address */ - unsigned _cfg_addr(int bus, int device, int function, int addr) { - return ( (1 << 31) | - (bus << 16) | - (device << 11) | - (function << 8) | - (addr & ~3) ); } + unsigned _dev_base(int bus, int device, int function) + { + return ((bus << 20) | + (device << 15) | + (function << 12)); + } Genode::Bit_array<256> _used { }; @@ -81,7 +58,16 @@ namespace Platform { public: - Config_access(Genode::Env &env) : _env(env) { } + class Invalid_mmio_access : Genode::Exception { }; + + Config_access(Attached_io_mem_dataspace &pciconf) + : + _pciconf(pciconf), + _pciconf_size(Dataspace_client(_pciconf.cap()).size()) + { } + + Config_access(Config_access &c) + : _pciconf(c._pciconf), _pciconf_size(c._pciconf_size) { } /** * Read value from config space of specified device/function @@ -100,26 +86,41 @@ namespace Platform { unsigned char addr, Device::Access_size size, bool track = true) { - /* write target address */ - _io_port()->outl(REG_ADDR, _cfg_addr(bus, device, function, addr)); + unsigned ret; + unsigned const offset = _dev_base(bus, device, function) + addr; + char const * const field = _pciconf.local_addr() + offset; - /* return read value */ + if (offset >= _pciconf_size) + throw Invalid_mmio_access(); + + /* + * Memory access code is implemented in a way to make it work + * with Muen subject monitor (SM) device emulation and also + * general x86 targets. On Muen, the simplified device + * emulation code (which also works for Linux) always returns + * 0xffff in EAX to indicate a non-existing device. Therefore, + * we enforce the usage of EAX in the following assembly + * templates. Also clear excess bits before return to guarantee + * the requested size. + */ switch (size) { case Device::ACCESS_8BIT: if (track) _use_register(addr, 1); - - return _io_port()->inb(REG_DATA + (addr & 3)); + asm volatile("movb %1,%%al" :"=a" (ret) :"m" (*((volatile unsigned char *)field)) :"memory"); + return ret & 0xff; case Device::ACCESS_16BIT: if (track) _use_register(addr, 2); - return _io_port()->inw(REG_DATA + (addr & 2)); + asm volatile("movw %1,%%ax" :"=a" (ret) :"m" (*(volatile unsigned short *)field) :"memory"); + return ret & 0xffff; case Device::ACCESS_32BIT: if (track) _use_register(addr, 4); - return _io_port()->inl(REG_DATA); + asm volatile("movl %1,%%eax" :"=a" (ret) :"m" (*(volatile unsigned int *)field) :"memory"); + return ret; default: return ~0U; } @@ -141,29 +142,34 @@ namespace Platform { unsigned value, Device::Access_size size, bool track = true) { - /* write target address */ - _io_port()->outl(REG_ADDR, _cfg_addr(bus, device, - function, addr)); + unsigned const offset = _dev_base(bus, device, function) + addr; + char const * const field = _pciconf.local_addr() + offset; - /* write value to targeted address */ + if (offset >= _pciconf_size) + throw Invalid_mmio_access(); + + /* + * Write value to targeted address, see read() comment above + * for an explanation of the assembly templates + */ switch (size) { case Device::ACCESS_8BIT: if (track) _use_register(addr, 1); - _io_port()->outb(REG_DATA + (addr & 3), value); + asm volatile("movb %%al,%1" : :"a" (value), "m" (*(volatile unsigned char *)field) :"memory"); break; case Device::ACCESS_16BIT: if (track) _use_register(addr, 2); - _io_port()->outw(REG_DATA + (addr & 2), value); + asm volatile("movw %%ax,%1" : :"a" (value), "m" (*(volatile unsigned char *)field) :"memory"); break; case Device::ACCESS_32BIT: if (track) _use_register(addr, 4); - _io_port()->outl(REG_DATA, value); + asm volatile("movl %%eax,%1" : :"a" (value), "m" (*(volatile unsigned char *)field) :"memory"); break; } } @@ -181,32 +187,6 @@ namespace Platform { return true; } } - - bool reset_support(unsigned reg, unsigned reg_size) const - { - return (REG_ADDR <= reg) && - reg + reg_size <= REG_ADDR + REG_SIZE; - } - - bool system_reset(unsigned reg, unsigned long long value, - const Device::Access_size &access_size) - { - switch (access_size) { - case Device::ACCESS_8BIT: - _io_port()->outb(reg, value); - break; - case Device::ACCESS_16BIT: - _io_port()->outw(reg, value); - break; - case Device::ACCESS_32BIT: - _io_port()->outl(reg, value); - break; - default: - return false; - } - - return true; - } }; } diff --git a/repos/os/src/drivers/platform/spec/x86/pci_device.cc b/repos/os/src/drivers/platform/spec/x86/pci_device.cc index 2fed491fa0..11a5e440d1 100644 --- a/repos/os/src/drivers/platform/spec/x86/pci_device.cc +++ b/repos/os/src/drivers/platform/spec/x86/pci_device.cc @@ -99,7 +99,7 @@ void Platform::Device_component::config_write(unsigned char address, switch (address) { case 0x40 ... 0xff: /* allow access to device-specific registers if not used by us */ - if (!_device_config.reg_in_use(&_config_access, address, size)) + if (!_device_config.reg_in_use(_config_access, address, size)) break; Genode::error(_device_config, " write access to " @@ -134,7 +134,7 @@ void Platform::Device_component::config_write(unsigned char address, } } - _device_config.write(&_config_access, address, value, size, + _device_config.write(_config_access, address, value, size, _device_config.DONT_TRACK_ACCESS); } @@ -172,11 +172,11 @@ Genode::Irq_session_capability Platform::Device_component::irq(Genode::uint8_t i Genode::addr_t msi_address = _irq_session->msi_address(); Genode::uint32_t msi_value = _irq_session->msi_data(); - Genode::uint16_t msi = _device_config.read(&_config_access, + Genode::uint16_t msi = _device_config.read(_config_access, msi_cap + 2, Platform::Device::ACCESS_16BIT); - _device_config.write(&_config_access, msi_cap + 0x4, msi_address, + _device_config.write(_config_access, msi_cap + 0x4, msi_address, Platform::Device::ACCESS_32BIT); if (msi & CAP_MSI_64) { @@ -184,19 +184,19 @@ Genode::Irq_session_capability Platform::Device_component::irq(Genode::uint8_t i ? (Genode::uint64_t)msi_address >> 32 : 0UL; - _device_config.write(&_config_access, msi_cap + 0x8, + _device_config.write(_config_access, msi_cap + 0x8, upper_address, Platform::Device::ACCESS_32BIT); - _device_config.write(&_config_access, msi_cap + 0xc, + _device_config.write(_config_access, msi_cap + 0xc, msi_value, Platform::Device::ACCESS_16BIT); } else - _device_config.write(&_config_access, msi_cap + 0x8, msi_value, + _device_config.write(_config_access, msi_cap + 0x8, msi_value, Platform::Device::ACCESS_16BIT); /* enable MSI */ - _device_config.write(&_config_access, msi_cap + 2, + _device_config.write(_config_access, msi_cap + 2, msi ^ MSI_ENABLED, Platform::Device::ACCESS_8BIT); } @@ -204,7 +204,7 @@ Genode::Irq_session_capability Platform::Device_component::irq(Genode::uint8_t i bool msi_64 = false; bool msi_mask = false; if (msi_cap) { - Genode::uint16_t msi = _device_config.read(&_config_access, + Genode::uint16_t msi = _device_config.read(_config_access, msi_cap + 2, Platform::Device::ACCESS_16BIT); msi_64 = msi & CAP_MSI_64; diff --git a/repos/os/src/drivers/platform/spec/x86/pci_device_component.h b/repos/os/src/drivers/platform/spec/x86/pci_device_component.h index 7123be741e..8d9089e8fd 100644 --- a/repos/os/src/drivers/platform/spec/x86/pci_device_component.h +++ b/repos/os/src/drivers/platform/spec/x86/pci_device_component.h @@ -15,6 +15,7 @@ /* base */ #include +#include #include #include #include @@ -45,13 +46,11 @@ class Platform::Device_component : public Genode::Rpc_object, Genode::Env &_env; Device_config _device_config { }; Genode::addr_t _config_space; - Config_access _config_access { _env }; + Config_access _config_access; Platform::Session_component &_session; unsigned short _irq_line; Irq_session_component *_irq_session = nullptr; - Genode::Constructible _io_mem_config_extended { }; - Genode::Allocator &_global_heap; class Io_mem : public Genode::Io_mem_connection, @@ -108,18 +107,18 @@ class Platform::Device_component : public Genode::Rpc_object, { enum { PCI_STATUS = 0x6, PCI_CAP_OFFSET = 0x34, CAP_MSI = 0x5 }; - Status::access_t status = Status::read(_device_config.read(&_config_access, + Status::access_t status = Status::read(_device_config.read(_config_access, PCI_STATUS, Platform::Device::ACCESS_16BIT)); if (!Status::Capabilities::get(status)) return 0; - Genode::uint8_t cap = _device_config.read(&_config_access, + Genode::uint8_t cap = _device_config.read(_config_access, PCI_CAP_OFFSET, Platform::Device::ACCESS_8BIT); for (Genode::uint16_t val = 0; cap; cap = val >> 8) { - val = _device_config.read(&_config_access, cap, + val = _device_config.read(_config_access, cap, Platform::Device::ACCESS_16BIT); if ((val & 0xff) != CAP_MSI) continue; @@ -139,7 +138,7 @@ class Platform::Device_component : public Genode::Rpc_object, using Genode::uint16_t; using Genode::uint8_t; - uint8_t pin = _device_config.read(&_config_access, PCI_IRQ_PIN, + uint8_t pin = _device_config.read(_config_access, PCI_IRQ_PIN, Platform::Device::ACCESS_8BIT); if (!pin) return Irq_session_component::INVALID_IRQ; @@ -160,12 +159,12 @@ class Platform::Device_component : public Genode::Rpc_object, if (!cap) return irq; - uint16_t msi = _device_config.read(&_config_access, cap + 2, + uint16_t msi = _device_config.read(_config_access, cap + 2, Platform::Device::ACCESS_16BIT); if (msi & MSI_ENABLED) /* disable MSI */ - _device_config.write(&_config_access, cap + 2, + _device_config.write(_config_access, cap + 2, msi ^ MSI_ENABLED, Platform::Device::ACCESS_8BIT); @@ -185,10 +184,10 @@ class Platform::Device_component : public Genode::Rpc_object, if (_device_config.pci_bridge()) return; - unsigned cmd = _device_config.read(&_config_access, PCI_CMD_REG, + unsigned cmd = _device_config.read(_config_access, PCI_CMD_REG, Platform::Device::ACCESS_16BIT); if (cmd & PCI_CMD_DMA) - _device_config.write(&_config_access, PCI_CMD_REG, + _device_config.write(_config_access, PCI_CMD_REG, cmd ^ PCI_CMD_DMA, Platform::Device::ACCESS_16BIT); } @@ -200,14 +199,16 @@ class Platform::Device_component : public Genode::Rpc_object, */ Device_component(Genode::Env &env, Device_config device_config, Genode::addr_t addr, + Config_access &config_access, Platform::Session_component &session, Genode::Allocator &md_alloc, Genode::Allocator &global_heap) : _env(env), _device_config(device_config), _config_space(addr), + _config_access(config_access), _session(session), - _irq_line(_device_config.read(&_config_access, PCI_IRQ_LINE, + _irq_line(_device_config.read(_config_access, PCI_IRQ_LINE, Platform::Device::ACCESS_8BIT)), _global_heap(global_heap), _slab_ioport(&md_alloc, &_slab_ioport_block_data), @@ -224,11 +225,13 @@ class Platform::Device_component : public Genode::Rpc_object, * Constructor for non PCI devices */ Device_component(Genode::Env &env, + Genode::Attached_io_mem_dataspace &pciconf, Platform::Session_component &session, unsigned irq, Genode::Allocator &global_heap) : _env(env), _config_space(~0UL), + _config_access(pciconf), _session(session), _irq_line(irq), _global_heap(global_heap), @@ -239,6 +242,7 @@ class Platform::Device_component : public Genode::Rpc_object, _io_port_conn[i] = nullptr; } + /** * De-constructor */ @@ -267,26 +271,8 @@ class Platform::Device_component : public Genode::Rpc_object, ** Methods used solely by pci session ** ****************************************/ - Device_config config() { return _device_config; } - - Genode::Io_mem_dataspace_capability get_config_space() - { - if (_config_space == ~0UL) - return Genode::Io_mem_dataspace_capability(); - - if (!_io_mem_config_extended.constructed()) { - try { - _io_mem_config_extended.construct(_env, _config_space, 0x1000); - } catch (...) { - _config_space = ~0UL; - } - } - - if (!_io_mem_config_extended.constructed()) - return Genode::Io_mem_dataspace_capability(); - - return _io_mem_config_extended->dataspace(); - } + Device_config config() const { return _device_config; } + Genode::addr_t config_space() const { return _config_space; } /************************** ** PCI-device interface ** @@ -317,7 +303,7 @@ class Platform::Device_component : public Genode::Rpc_object, unsigned config_read(unsigned char address, Access_size size) override { - return _device_config.read(&_config_access, address, size, + return _device_config.read(_config_access, address, size, _device_config.DONT_TRACK_ACCESS); } diff --git a/repos/os/src/drivers/platform/spec/x86/pci_device_config.h b/repos/os/src/drivers/platform/spec/x86/pci_device_config.h index 26206a4055..71348cb3cb 100644 --- a/repos/os/src/drivers/platform/spec/x86/pci_device_config.h +++ b/repos/os/src/drivers/platform/spec/x86/pci_device_config.h @@ -24,7 +24,9 @@ namespace Platform { { private: - int _bus = 0, _device = 0, _function = 0; /* location at PCI bus */ + uint8_t _bus = 0; + uint8_t _device = 0; + uint8_t _function = 0; /* location at PCI bus */ /* * Information provided by the PCI config space @@ -68,6 +70,13 @@ namespace Platform { */ Device_config() : _vendor_id(INVALID_VENDOR) { } + Device_config(unsigned bdf) + : + _bus((bdf >> 8) & 0xff), + _device((bdf >> 3) & 0x1f), + _function(bdf & 0x7) + { } + Device_config(int bus, int device, int function, Config_access *pci_config): _bus(bus), _device(device), _function(function) @@ -149,8 +158,8 @@ namespace Platform { { using Genode::print; using Genode::Hex; - print(out, Hex(_bus, Hex::Prefix::OMIT_PREFIX), - ":", Hex(_device, Hex::Prefix::OMIT_PREFIX), + print(out, Hex(_bus, Hex::Prefix::OMIT_PREFIX, Hex::Pad::PAD), + ":", Hex(_device, Hex::Prefix::OMIT_PREFIX, Hex::Pad::PAD), ".", Hex(_function, Hex::Prefix::OMIT_PREFIX)); } @@ -190,27 +199,27 @@ namespace Platform { * Read configuration space */ enum { DONT_TRACK_ACCESS = false }; - unsigned read(Config_access *pci_config, unsigned char address, + unsigned read(Config_access &pci_config, unsigned char address, Device::Access_size size, bool track = true) { - return pci_config->read(_bus, _device, _function, address, - size, track); + return pci_config.read(_bus, _device, _function, address, + size, track); } /** * Write configuration space */ - void write(Config_access *pci_config, unsigned char address, + void write(Config_access &pci_config, unsigned char address, unsigned long value, Device::Access_size size, bool track = true) { - pci_config->write(_bus, _device, _function, address, value, - size, track); + pci_config.write(_bus, _device, _function, address, value, + size, track); } - bool reg_in_use(Config_access *pci_config, unsigned char address, + bool reg_in_use(Config_access &pci_config, unsigned char address, Device::Access_size size) { - return pci_config->reg_in_use(address, size); } + return pci_config.reg_in_use(address, size); } }; class Config_space : private Genode::List::Element diff --git a/repos/os/src/drivers/platform/spec/x86/pci_session_component.h b/repos/os/src/drivers/platform/spec/x86/pci_session_component.h index 581e458e50..e858c55dff 100644 --- a/repos/os/src/drivers/platform/spec/x86/pci_session_component.h +++ b/repos/os/src/drivers/platform/spec/x86/pci_session_component.h @@ -1,5 +1,5 @@ /* - * \brief platform session component + * \brief Platform session component * \author Norman Feske * \date 2008-01-28 */ @@ -152,9 +152,9 @@ class Platform::Pci_buses public: - Pci_buses(Genode::Env &env, Genode::Allocator &heap) + Pci_buses(Genode::Allocator &heap, Genode::Attached_io_mem_dataspace &pciconf) { - Config_access c(env); + Config_access c(pciconf); scan_bus(c, heap); } @@ -207,6 +207,7 @@ class Platform::Session_component : public Genode::Rpc_object Genode::Env &_env; Genode::Attached_rom_dataspace &_config; + Genode::Attached_io_mem_dataspace &_pciconf; Genode::Ram_quota_guard _ram_guard; Genode::Cap_quota_guard _cap_guard; Genode::Constrained_ram_allocator _env_ram { @@ -455,14 +456,16 @@ class Platform::Session_component : public Genode::Rpc_object /** * Constructor */ - Session_component(Genode::Env &env, - Genode::Attached_rom_dataspace &config, - Platform::Pci_buses &buses, - Genode::Heap &global_heap, - char const *args) + Session_component(Genode::Env &env, + Genode::Attached_rom_dataspace &config, + Genode::Attached_io_mem_dataspace &pciconf, + Platform::Pci_buses &buses, + Genode::Heap &global_heap, + char const *args) : _env(env), _config(config), + _pciconf(pciconf), _ram_guard(Genode::ram_quota_from_args(args)), _cap_guard(Genode::cap_quota_from_args(args)), _md_alloc(_env_ram, env.rm()), @@ -637,9 +640,8 @@ class Platform::Session_component : public Genode::Rpc_object { /* * Create the interface to the PCI config space. - * This involves the creation of I/O port sessions. */ - Config_access config_access(_env); + Config_access config_access(_pciconf); /* lookup device component for previous device */ auto lambda = [&] (Device_component *prev) @@ -696,7 +698,7 @@ class Platform::Session_component : public Genode::Rpc_object */ try { Device_component * dev = new (_md_alloc) - Device_component(_env, config, config_space, *this, + Device_component(_env, config, config_space, config_access, *this, _md_alloc, _global_heap); /* if more than one driver uses the device - warn about */ @@ -751,11 +753,7 @@ class Platform::Session_component : public Genode::Rpc_object /* lookup device component for previous device */ _env.ep().rpc_ep().apply(device_cap, lambda); - if (!device) return; - - if (device->config().valid()) - destroy(_md_alloc, device); - else + if (device && device->config().valid()) destroy(_md_alloc, device); } @@ -763,13 +761,20 @@ class Platform::Session_component : public Genode::Rpc_object { using namespace Genode; - if (!device || !device->get_config_space().valid()) + if (!device || device->config_space() == ~0UL) return; - Io_mem_dataspace_capability io_mem = device->get_config_space(); - try { - _device_pd.assign_pci(io_mem, device->config().bdf()); + addr_t const function = device->config().bus_number() * 32 * 8 + + device->config().device_number() * 8 + + device->config().function_number(); + addr_t const base_ecam = Dataspace_client(_pciconf.cap()).phys_addr(); + addr_t const base_offset = 0x1000UL * function; + + if (base_ecam + base_offset != device->config_space()) + throw 1; + + _device_pd.assign_pci(_pciconf.cap(), base_offset, device->config().bdf()); for (Rmrr *r = Rmrr::list()->first(); r; r = r->next()) { Io_mem_dataspace_capability rmrr_cap = r->match(_env, device->config()); @@ -846,37 +851,65 @@ class Platform::Root : public Genode::Root_component Genode::Env &_env; Genode::Attached_rom_dataspace &_config; + Genode::Constructible _pci_confspace { }; + Genode::Reporter _pci_reporter { _env, "pci" }; - Genode::Heap _heap { _env.ram(), _env.rm() }; - Platform::Pci_buses _buses { _env, _heap }; + Genode::Heap _heap { _env.ram(), _env.rm() }; - void _parse_report_rom(Genode::Env &env, const char * acpi_rom) + Genode::Constructible _buses { }; + + void _parse_report_rom(Genode::Env &env, const char * acpi_rom, + bool acpi_platform) { using namespace Genode; - Config_access config_access(env); - Xml_node xml_acpi(acpi_rom); if (!xml_acpi.has_type("acpi")) throw 1; + xml_acpi.for_each_sub_node("bdf", [&] (Xml_node &node) { + + uint32_t bdf_start = 0; + uint32_t func_count = 0; + addr_t base = 0; + + node.attribute("start").value(&bdf_start); + node.attribute("count").value(&func_count); + node.attribute("base").value(&base); + + Session_component::add_config_space(bdf_start, func_count, + base, _heap); + + Device_config const bdf_first(bdf_start); + Device_config const bdf_last(bdf_start + func_count - 1); + addr_t const memory_size = 0x1000UL * func_count; + + /* Simplification: Only consider first config space and + * check if it is for domain 0 */ + if (bdf_start || _pci_confspace.constructed()) { + warning("ECAM/MMCONF range ", + bdf_first, "-", bdf_last, " - addr ", + Hex_range(base, memory_size), " ignored"); + return; + } + + log("ECAM/MMCONF range ", bdf_first, "-", bdf_last, " - addr ", + Hex_range(base, memory_size)); + + _pci_confspace.construct(env, base, memory_size); + }); + + if (!_pci_confspace.constructed()) + throw 2; + + Config_access config_access(*_pci_confspace); + for (unsigned i = 0; i < xml_acpi.num_sub_nodes(); i++) { Xml_node node = xml_acpi.sub_node(i); - if (node.has_type("bdf")) { - - uint32_t bdf_start = 0; - uint32_t func_count = 0; - addr_t base = 0; - - node.attribute("start").value(&bdf_start); - node.attribute("count").value(&func_count); - node.attribute("base").value(&base); - - Session_component::add_config_space(bdf_start, func_count, - base, _heap); - } + if (node.has_type("bdf")) + continue; if (node.has_type("irq_override")) { unsigned irq = 0xff; @@ -887,10 +920,17 @@ class Platform::Root : public Genode::Root_component node.attribute("gsi").value(&gsi); node.attribute("flags").value(&flags); + if (!acpi_platform) { + warning("MADT IRQ ", irq, "-> GSI ", gsi, " flags ", + flags, " ignored"); + continue; + } + using Platform::Irq_override; Irq_override * o = new (_heap) Irq_override(irq, gsi, flags); Irq_override::list()->insert(o); + continue; } if (node.has_type("rmrr")) { @@ -899,7 +939,7 @@ class Platform::Root : public Genode::Root_component node.attribute("end").value(&mem_end); if (node.num_sub_nodes() == 0) - throw 2; + throw 3; Rmrr * rmrr = new (_heap) Rmrr(mem_start, mem_end); Rmrr::list()->insert(rmrr); @@ -907,7 +947,7 @@ class Platform::Root : public Genode::Root_component for (unsigned s = 0; s < node.num_sub_nodes(); s++) { Xml_node scope = node.sub_node(s); if (!scope.num_sub_nodes() || !scope.has_type("scope")) - throw 3; + throw 4; unsigned bus = 0, dev = 0, func = 0; scope.attribute("bus_start").value(&bus); @@ -915,7 +955,7 @@ class Platform::Root : public Genode::Root_component for (unsigned p = 0; p < scope.num_sub_nodes(); p++) { Xml_node path = scope.sub_node(p); if (!path.has_type("path")) - throw 4; + throw 5; path.attribute("dev").value(&dev); path.attribute("func").value(&func); @@ -924,12 +964,13 @@ class Platform::Root : public Genode::Root_component &config_access); if (bridge.pci_bridge()) /* PCI bridge spec 3.2.5.3, 3.2.5.4 */ - bus = bridge.read(&config_access, 0x19, + bus = bridge.read(config_access, 0x19, Device::ACCESS_8BIT); } rmrr->add(new (_heap) Rmrr::Bdf(bus, dev, func)); } + continue; } if (node.has_type("fadt")) { @@ -937,10 +978,13 @@ class Platform::Root : public Genode::Root_component node.attribute("reset_type").value(&fadt.reset_type); node.attribute("reset_addr").value(&fadt.reset_addr); node.attribute("reset_value").value(&fadt.reset_value); + continue; } - if (!node.has_type("routing")) - continue; + if (!node.has_type("routing")) { + error ("unsupported node '", node.type(), "'"); + throw __LINE__; + } unsigned gsi; unsigned bridge_bdf; @@ -960,6 +1004,20 @@ class Platform::Root : public Genode::Root_component if (!config.valid()) continue; + /* drop routing information on non ACPI platform */ + if (!acpi_platform) { + if (config.pci_bridge()) + continue; + + Device_config dev(device << 3); + + enum { PCI_IRQ_LINE = 0x3c }; + uint8_t pin = dev.read(config_access, PCI_IRQ_LINE, + Platform::Device::ACCESS_8BIT); + warning(dev, " ignore ACPI IRQ routing: ", pin, "->", gsi); + continue; + } + if (!config.pci_bridge() && bridge_bdf != 0) /** * If the bridge bdf has not a type header of a bridge in @@ -979,13 +1037,52 @@ class Platform::Root : public Genode::Root_component } } + void _construct_buses() + { + Genode::Dataspace_client ds_pci_mmio(_pci_confspace->cap()); + uint64_t const phys_addr = ds_pci_mmio.phys_addr(); + uint64_t const phys_size = ds_pci_mmio.size(); + uint64_t mmio_size = 0x10000000UL; /* max MMCONF memory */ + + /* try surviving wrong ACPI ECAM/MMCONF table information */ + while (true) { + try { + _buses.construct(_heap, *_pci_confspace); + /* construction and scan succeeded */ + break; + } catch (Platform::Config_access::Invalid_mmio_access) { + + error("ECAM/MMCONF MMIO access out of bounds - " + "ACPI table information is wrong!"); + + _pci_confspace.destruct(); + + while (mmio_size > phys_size) { + try { + error(" adjust size from ", Hex(phys_size), + "->", Hex(mmio_size)); + _pci_confspace.construct(_env, phys_addr, mmio_size); + /* got memory - try again */ + break; + } catch (Genode::Service_denied) { + /* decrease by one bus memory size */ + mmio_size -= 0x1000UL * 32 * 8; + } + } + if (mmio_size <= phys_size) + /* broken machine - you're lost */ + throw; + } + } + } + protected: Session_component *_create_session(const char *args) { try { return new (md_alloc()) - Session_component(_env, _config, _buses, _heap, args); + Session_component(_env, _config, *_pci_confspace, *_buses, _heap, args); } catch (Genode::Session_policy::No_policy_defined) { Genode::error("Invalid session request, no matching policy for ", @@ -1010,27 +1107,30 @@ class Platform::Root : public Genode::Root_component */ Root(Genode::Env &env, Genode::Allocator &md_alloc, Genode::Attached_rom_dataspace &config, - const char *acpi_rom) + const char *acpi_rom, + bool acpi_platform) : Genode::Root_component(&env.ep().rpc_ep(), &md_alloc), _env(env), _config(config) { - if (acpi_rom) { - try { - _parse_report_rom(env, acpi_rom); - } catch (...) { - Genode::error("PCI config space data could not be parsed."); - } + + try { + _parse_report_rom(env, acpi_rom, acpi_platform); + } catch (...) { + Genode::error("ACPI report parsing error."); + throw; } + _construct_buses(); + _pci_reporter.enabled(config.xml().has_sub_node("report") && config.xml().sub_node("report") .attribute_value("pci", true)); if (_pci_reporter.enabled()) { - Config_access config_access(env); + Config_access config_access(*_pci_confspace); Device_config config; int bus = 0, device = 0, function = -1; @@ -1038,8 +1138,8 @@ class Platform::Root : public Genode::Root_component /* iterate over pci devices */ while (true) { function += 1; - if (!_buses.find_next(bus, device, function, &config, - &config_access)) + if (!(*_buses).find_next(bus, device, function, &config, + &config_access)) return; bus = config.bus_number(); @@ -1060,47 +1160,4 @@ class Platform::Root : public Genode::Root_component }); } } - - void system_reset() - { - const bool io_port_space = (Fadt::Gas::Address_space::get(fadt.reset_type) == Fadt::Gas::Address_space::SYSTEM_IO); - - if (!io_port_space) - return; - - Config_access config_access(_env); - const unsigned raw_access_size = Fadt::Gas::Access_size::get(fadt.reset_type); - const bool reset_support = config_access.reset_support(fadt.reset_addr, raw_access_size); - if (!reset_support) - return; - - const bool feature_reset = Fadt::Features::Reset::get(fadt.features); - - if (!feature_reset) { - Genode::warning("system reset failed - feature not supported"); - return; - } - - Device::Access_size access_size = Device::ACCESS_8BIT; - - unsigned raw_size = Fadt::Gas::Access_size::get(fadt.reset_type); - switch (raw_size) { - case Fadt::Gas::Access_size::WORD: - access_size = Device::ACCESS_16BIT; - break; - case Fadt::Gas::Access_size::DWORD: - access_size = Device::ACCESS_32BIT; - break; - case Fadt::Gas::Access_size::QWORD: - Genode::error("system reset failed - unsupported access size"); - return; - default: - break; - } - - config_access.system_reset(fadt.reset_addr, fadt.reset_value, - access_size); - /* if we are getting here - the reset failed */ - Genode::warning("system reset failed"); - } }; diff --git a/repos/os/src/drivers/platform/spec/x86/session.cc b/repos/os/src/drivers/platform/spec/x86/session.cc index 57b008c809..301760cd62 100644 --- a/repos/os/src/drivers/platform/spec/x86/session.cc +++ b/repos/os/src/drivers/platform/spec/x86/session.cc @@ -55,9 +55,9 @@ void Platform::Pci_buses::scan_bus(Config_access &config_access, /* scan behind bridge */ if (config.pci_bridge()) { /* PCI bridge spec 3.2.5.3, 3.2.5.4 */ - unsigned char sec_bus = config.read(&config_access, 0x19, + unsigned char sec_bus = config.read(config_access, 0x19, Device::ACCESS_8BIT); - unsigned char sub_bus = config.read(&config_access, 0x20, + unsigned char sub_bus = config.read(config_access, 0x20, Device::ACCESS_8BIT); bridges()->insert(new (heap) Bridge(bus, dev, fun, sec_bus, @@ -68,16 +68,18 @@ void Platform::Pci_buses::scan_bus(Config_access &config_access, PCI_CMD_MASK = 0x7 /* IOPORT, MEM, DMA */ }; - unsigned short cmd = config.read(&config_access, PCI_CMD_REG, + unsigned short cmd = config.read(config_access, PCI_CMD_REG, Platform::Device::ACCESS_16BIT); if ((cmd & PCI_CMD_MASK) != PCI_CMD_MASK) { - config.write(&config_access, PCI_CMD_REG, + config.write(config_access, PCI_CMD_REG, cmd | PCI_CMD_MASK, Platform::Device::ACCESS_16BIT); } - Genode::log(config, " - bridge ", sec_bus, ":0.0", + Genode::log(config, " - bridge ", + Hex(sec_bus, Hex::Prefix::OMIT_PREFIX, Hex::Pad::PAD), + ":00.0", ((cmd & PCI_CMD_MASK) != PCI_CMD_MASK) ? " enabled" : ""); diff --git a/tool/run/power_on/qemu b/tool/run/power_on/qemu index e1748f6f52..e35a200f3c 100644 --- a/tool/run/power_on/qemu +++ b/tool/run/power_on/qemu @@ -124,6 +124,8 @@ proc run_power_on { } { puts "Aborting, cannot execute Qemu without a ISO or disk image" exit -4 } } } } + + append qemu_args " -machine q35 " } # on ARM, we supply the boot image as kernel