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 <bdf> node. An exception is thrown if the
first <bdf> node is not for PCI domain zero or if multiple <bdf> 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
This commit is contained in:
Reto Buerki 2017-10-19 10:13:35 +02:00 committed by Christian Helmuth
parent 661be648d5
commit 47724c68c2
13 changed files with 319 additions and 323 deletions

View File

@ -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 {
<service name="ROM" label="system"> <child name="acpi_report_rom"/> </service>}
}
@ -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 {
<start name="acpi_drv" } [platform_drv_priority] { caps="400" >
@ -175,7 +182,7 @@ proc platform_drv_config {} {
<provides>
<service name="Platform"/>}
append_if [have_spec acpi] drv_config {
append_if [have_spec x86] drv_config {
<service name="Acpi"/>}
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 {
<service name="ROM" label="acpi"> <child name="acpi_report_rom"/> </service>}
append_if [have_spec rpi] drv_config {

View File

@ -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:
! </route>
! ...
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:
!<start name="platform_drv">
! ...
! <config acpi="no" system="no">
! ...
! </config>
! ...
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'.
!<start name="platform_drv">
! <config acpi="yes" acpi_ready="yes">
! <config acpi_ready="yes">
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'.
!<start name="platform_drv">
! <config acpi="yes" system="yes">
If the attribute 'state' in the system XML ROM turns to 'reset'
!<system state="reset"/>
the platform driver will try to reset the machine, if the required I/O ports
are owned by it.
Supported PCI class aliases
---------------------------

View File

@ -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");

View File

@ -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);
};

View File

@ -57,8 +57,9 @@ struct Platform::Main
return;
const char * report_addr = acpi_rom->local_addr<const char>();
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<char>(),
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<char>(),
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();
}
};

View File

@ -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();

View File

@ -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 <util/bit_array.h>
#include <io_port_session/connection.h>
#include <base/attached_io_mem_dataspace.h>
#include <base/attached_rom_dataspace.h>
#include <platform_device/platform_device.h>
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 <unsigned port>
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<REG_ADDR>()->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<char>() + 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<REG_DATA>()->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<REG_DATA>()->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<REG_DATA>()->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<REG_ADDR>()->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<char>() + 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<REG_DATA>()->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<REG_DATA>()->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<REG_DATA>()->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<REG_ADDR>()->outb(reg, value);
break;
case Device::ACCESS_16BIT:
_io_port<REG_ADDR>()->outw(reg, value);
break;
case Device::ACCESS_32BIT:
_io_port<REG_ADDR>()->outl(reg, value);
break;
default:
return false;
}
return true;
}
};
}

View File

@ -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;

View File

@ -15,6 +15,7 @@
/* base */
#include <base/rpc_server.h>
#include <io_port_session/connection.h>
#include <io_mem_session/connection.h>
#include <util/list.h>
#include <util/mmio.h>
@ -45,13 +46,11 @@ class Platform::Device_component : public Genode::Rpc_object<Platform::Device>,
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<Genode::Io_mem_connection> _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<Platform::Device>,
{
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<Platform::Device>,
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<Platform::Device>,
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<Platform::Device>,
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<Platform::Device>,
*/
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<Platform::Device>,
* 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<Platform::Device>,
_io_port_conn[i] = nullptr;
}
/**
* De-constructor
*/
@ -267,26 +271,8 @@ class Platform::Device_component : public Genode::Rpc_object<Platform::Device>,
** 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<Platform::Device>,
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);
}

View File

@ -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<Config_space>::Element

View File

@ -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<Session>
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<Session>
/**
* 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<Session>
{
/*
* 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<Session>
*/
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<Session>
/* 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<Session>
{
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<Session_component>
Genode::Env &_env;
Genode::Attached_rom_dataspace &_config;
Genode::Constructible<Genode::Attached_io_mem_dataspace> _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<Platform::Pci_buses> _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<addr_t>(base, memory_size), " ignored");
return;
}
log("ECAM/MMCONF range ", bdf_first, "-", bdf_last, " - addr ",
Hex_range<addr_t>(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<Session_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<Session_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<Session_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<Session_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<Session_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<Session_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<Session_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<Session_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<Session_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<Session_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<Session_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<Session_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");
}
};

View File

@ -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"
: "");

View File

@ -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