acpica: generate report usable by platform_drv

Fixes #2816
This commit is contained in:
Alexander Boettcher 2018-04-29 16:16:57 +02:00 committed by Christian Helmuth
parent 874815ebf6
commit 0efa67893e
11 changed files with 634 additions and 41 deletions

View File

@ -22,8 +22,11 @@ namespace Genode {
namespace Acpica {
struct Wait_acpi_ready { bool enabled; };
struct Act_as_acpi_drv { bool enabled; };
void init(Genode::Env &, Genode::Allocator &, Wait_acpi_ready);
void init(Genode::Env &, Genode::Allocator &, Wait_acpi_ready,
Act_as_acpi_drv);
void use_platform_drv();
}
#endif /* _INCLUDE__ACPICA__ACPICA_H_ */

View File

@ -43,24 +43,32 @@ the machine immediately.
The attempt to reset or to poweroff may fail. One reason, we have seen so far,
is that the required resources are already owned by other components in the
system. E.g. for 'reset' on some machines the platform driver posses the
required I/O ports and the acpica application don't get access to. On such
systems the platform driver can be configured to react on the 'state="reset"'
system state change. The platform_drv can be configured to monitor
the 'system' ROM by adding a config attribute named 'system' and set to 'yes'.
system.
Furthermore the ACPICA library triggers depended on the ACPI table content
I/O operations on various PCI devices and partly re-configure it. Because of
this a policy rule at the platform driver is required, that permits access
to the required devices.
Acpica as acpi_drv replacement
------------------------------
The application acpica may also be run as replacement of the original acpi_drv
when the 'act_as_acpi_drv' attribute is set to yes:
! <config ... acpi_ready="yes" act_as_acpi_drv="yes"/>
The acpica driver will parse in this mode the ACPI tables and will generate
the same content in the ACPI report, which is transformed by a report_rom
service into a ACPI ROM expected initially by the platform driver.
Excerpt of important parts of the acpica configuration
------------------------------------------------------
!<start name="acpica">
! <!-- <binary name="debug-acpica"/> -->
! ...
! <config reset="no" poweroff="no" report="yes" acpi_ready="yes"/>
! <config reset="no" poweroff="no" report="yes" acpi_ready="yes" act_as_acpi_drv="no"/>
! <route>
! <service name="ROM" label="system"> <child name="..."/> </service>
! <service name="Report"> <child name="..."/> </service>

View File

@ -0,0 +1,139 @@
/*
* \brief Handle PCI Root bridge
* \author Alexander Boettcher
*
*/
/*
* Copyright (C) 2018 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.
*/
class Bridge {
private:
ACPI_HANDLE _bridge;
unsigned bdf_bridge(ACPI_HANDLE bridge)
{
/* address (high word = device, low word = function) (6.1.1) */
unsigned bridge_adr = 0;
/* Base bus number (6.5.5) */
unsigned bridge_bbn = 0;
/* Segment object located under host bridge (6.5.6) */
unsigned bridge_seg = 0;
Acpica::Buffer<ACPI_OBJECT> adr;
ACPI_STATUS res = AcpiEvaluateObjectTyped(bridge,
ACPI_STRING("_ADR"),
nullptr, &adr,
ACPI_TYPE_INTEGER);
if (res != AE_OK) {
Genode::error("could not detect address of bridge - ", res);
return 0;
} else
bridge_adr = adr.object.Integer.Value;
Acpica::Buffer<ACPI_OBJECT> bbn;
res = AcpiEvaluateObjectTyped(bridge, ACPI_STRING("_BBN"),
nullptr, &bbn, ACPI_TYPE_INTEGER);
if (res != AE_OK) {
Genode::warning("_BBN missing for bridge");
} else
bridge_bbn = bbn.object.Integer.Value;
Acpica::Buffer<ACPI_OBJECT> seg;
res = AcpiEvaluateObjectTyped(bridge, ACPI_STRING("_SEG"),
nullptr, &seg, ACPI_TYPE_INTEGER);
/* according to ACPI spec assume segment 0 if method unavailable */
if (res == AE_OK)
bridge_seg = seg.object.Integer.Value;
unsigned const bridge_bdf = ((0xffffU & bridge_seg) << 16) |
((0x00ffU & bridge_bbn) << 8) |
(0xffU & ((bridge_adr >> 16) << 3)) |
(bridge_adr & 0x7);
return bridge_bdf;
}
void _gen_bridge(ACPI_HANDLE bridge, Genode::Xml_generator &xml,
unsigned const bridge_bdf)
{
Acpica::Buffer<char [2 * 4096]> irqs;
ACPI_STATUS res = AcpiGetIrqRoutingTable (bridge, &irqs);
if (res != AE_OK) {
Genode::error("buffer for PCI IRQ routing information to "
"small - ", irqs.Length, " required");
return;
}
ACPI_PCI_ROUTING_TABLE *s = reinterpret_cast<ACPI_PCI_ROUTING_TABLE *>(irqs.Pointer);
ACPI_PCI_ROUTING_TABLE *e = reinterpret_cast<ACPI_PCI_ROUTING_TABLE *>(reinterpret_cast<unsigned long>(&irqs) + irqs.Length);
for (ACPI_PCI_ROUTING_TABLE *c = s; c < e && c->Length; ) {
using Genode::Hex;
using Genode::String;
xml.node("routing", [&] () {
xml.attribute("gsi", String<16>(Hex(c->SourceIndex)));
xml.attribute("bridge_bdf", String<16>(Hex(bridge_bdf)));
xml.attribute("device", String<16>(Hex((c->Address >> 16) & 0x1f)));
xml.attribute("device_pin", String<16>(Hex(c->Pin)));
});
c = reinterpret_cast<ACPI_PCI_ROUTING_TABLE *>(reinterpret_cast<unsigned long>(c) + c->Length);
}
}
void _sub_bridges(ACPI_HANDLE handle, Genode::Xml_generator &xml)
{
ACPI_STATUS res = AcpiEvaluateObject(handle, ACPI_STRING("_PRT"),
nullptr, nullptr);
if (res != AE_OK)
return;
/* got another bridge, generate irq routing information to xml */
Bridge::_gen_bridge(handle, xml, bdf_bridge(handle));
ACPI_HANDLE child = nullptr;
/* lookup next bridge behind the bridge */
while (AE_OK == (res = AcpiGetNextObject(ACPI_TYPE_DEVICE, handle,
child, &child)))
{
_sub_bridges(child, xml);
}
}
public:
Bridge(void *, ACPI_HANDLE bridge)
:
_bridge(bridge)
{ }
static ACPI_STATUS detect(ACPI_HANDLE bridge, UINT32, void * m,
void **return_bridge);
void generate(Genode::Xml_generator &xml)
{
unsigned const root_bridge_bdf = bdf_bridge(_bridge);
xml.node("root_bridge", [&] () {
xml.attribute("bdf", Genode::String<8>(Genode::Hex(root_bridge_bdf)));
});
/* irq routing information of this (pci root) bridge */
_gen_bridge(_bridge, xml, root_bridge_bdf);
/* lookup all pci-to-pci bridges and add irq routing information */
_sub_bridges(_bridge, xml);
}
};

View File

@ -117,7 +117,7 @@ struct Acpica::Main
void *context;
} irq_handler;
void init_acpica(Acpica::Wait_acpi_ready);
void init_acpica(Acpica::Wait_acpi_ready, Acpica::Act_as_acpi_drv);
Main(Genode::Env &env)
:
@ -128,11 +128,13 @@ struct Acpica::Main
bool const enable_poweroff = config.xml().attribute_value("poweroff", false);
bool const enable_report = config.xml().attribute_value("report", false);
bool const enable_ready = config.xml().attribute_value("acpi_ready", false);
bool const act_as_acpi_drv = config.xml().attribute_value("act_as_acpi_drv", false);
if (enable_report)
report = new (heap) Acpica::Reportstate(env);
init_acpica(Wait_acpi_ready{enable_ready});
init_acpica(Wait_acpi_ready{enable_ready},
Act_as_acpi_drv{act_as_acpi_drv});
if (enable_report)
report->enable();
@ -187,10 +189,45 @@ struct Acpica::Main
#include "lid.h"
#include "sb.h"
#include "ec.h"
#include "bridge.h"
void Acpica::Main::init_acpica(Wait_acpi_ready wait_acpi_ready)
ACPI_STATUS init_pic_mode()
{
Acpica::init(env, heap, wait_acpi_ready);
ACPI_OBJECT_LIST arguments;
ACPI_OBJECT argument;
arguments.Count = 1;
arguments.Pointer = &argument;
enum { PIC = 0, APIC = 1, SAPIC = 2};
argument.Type = ACPI_TYPE_INTEGER;
argument.Integer.Value = APIC;
return AcpiEvaluateObject(ACPI_ROOT_OBJECT, ACPI_STRING("_PIC"),
&arguments, nullptr);
}
ACPI_STATUS Bridge::detect(ACPI_HANDLE bridge, UINT32, void * m,
void **return_bridge)
{
Acpica::Main * main = reinterpret_cast<Acpica::Main *>(m);
Bridge * dev_obj = new (main->heap) Bridge(main->report, bridge);
if (*return_bridge == (void *)PCI_ROOT_HID_STRING)
Genode::log("detected - bridge - PCI root bridge");
if (*return_bridge == (void *)PCI_EXPRESS_ROOT_HID_STRING)
Genode::log("detected - bridge - PCIE root bridge");
*return_bridge = dev_obj;
return AE_OK;
}
void Acpica::Main::init_acpica(Wait_acpi_ready wait_acpi_ready,
Act_as_acpi_drv act_as_acpi_drv)
{
Acpica::init(env, heap, wait_acpi_ready, act_as_acpi_drv);
/* enable debugging: */
/* AcpiDbgLevel |= ACPI_LV_IO | ACPI_LV_INTERRUPTS | ACPI_LV_INIT_NAMES; */
@ -225,6 +262,13 @@ void Acpica::Main::init_acpica(Wait_acpi_ready wait_acpi_ready)
return;
}
/* set APIC mode */
status = init_pic_mode();
if (status != AE_OK) {
Genode::error("Setting PIC mode failed, status=", status);
return;
}
/* Embedded controller */
status = AcpiGetDevices(ACPI_STRING("PNP0C09"), Ec::detect, this, nullptr);
if (status != AE_OK) {
@ -286,6 +330,35 @@ void Acpica::Main::init_acpica(Wait_acpi_ready wait_acpi_ready)
Genode::error("AcpiGetDevices (PNP0C0D) failed, status=", status);
return;
}
if (act_as_acpi_drv.enabled) {
/* lookup PCI root bridge */
void * pci_bridge = (void *)PCI_ROOT_HID_STRING;
status = AcpiGetDevices(ACPI_STRING(PCI_ROOT_HID_STRING), Bridge::detect,
this, &pci_bridge);
if (status != AE_OK || pci_bridge == (void *)PCI_ROOT_HID_STRING)
pci_bridge = nullptr;
/* lookup PCI Express root bridge */
void * pcie_bridge = (void *)PCI_EXPRESS_ROOT_HID_STRING;
status = AcpiGetDevices(ACPI_STRING(PCI_EXPRESS_ROOT_HID_STRING),
Bridge::detect, this, &pcie_bridge);
if (status != AE_OK || pcie_bridge == (void *)PCI_EXPRESS_ROOT_HID_STRING)
pcie_bridge = nullptr;
if (pcie_bridge && pci_bridge)
Genode::log("PCI and PCIE root bridge found - using PCIE for IRQ "
"routing information");
Bridge *bridge = pcie_bridge ? reinterpret_cast<Bridge *>(pcie_bridge)
: reinterpret_cast<Bridge *>(pci_bridge);
/* Generate report for platform driver */
Acpica::generate_report(env, bridge);
}
/* Tell PCI backend to use platform_drv for PCI device access from now on */
Acpica::use_platform_drv();
}

View File

@ -0,0 +1,167 @@
/*
* \brief Generate XML report
* \author Alexander Boettcher
*/
/*
* Copyright (C) 2018 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#include <base/env.h>
#include <os/reporter.h>
#include "util.h"
#include "bridge.h"
using Genode::Reporter;
extern void AcpiGenodeFreeIOMem(ACPI_PHYSICAL_ADDRESS const phys, ACPI_SIZE const size);
template <typename H, typename S, typename F, typename FSIZE>
void for_each_element(H const head, S *, F const &fn, FSIZE const &fn_size)
{
for(S const * e = reinterpret_cast<S const * const>(head + 1);
e < reinterpret_cast<S const *>(reinterpret_cast<char const *>(head) + head->Header.Length);
e = reinterpret_cast<S const *>(reinterpret_cast<char const *>(e) + fn_size(e)))
{
fn(e);
}
}
static void add_madt(ACPI_TABLE_MADT const * const madt,
Reporter::Xml_generator &xml)
{
typedef ACPI_SUBTABLE_HEADER Madt_sub;
using Genode::String;
for_each_element(madt, (Madt_sub *) nullptr, [&](Madt_sub const * const s) {
if (s->Type != ACPI_MADT_TYPE_INTERRUPT_OVERRIDE)
return;
typedef ACPI_MADT_INTERRUPT_OVERRIDE Irq;
Irq const * const irq = reinterpret_cast<Irq const * const>(s);
xml.node("irq_override", [&] () {
xml.attribute("irq", irq->SourceIrq);
xml.attribute("gsi", irq->GlobalIrq);
xml.attribute("flags", String<16>(Genode::Hex(irq->IntiFlags)));
xml.attribute("bus", irq->Bus);
});
}, [](Madt_sub const * const s) { return s->Length; });
}
static void add_mcfg(ACPI_TABLE_MCFG const * const mcfg,
Reporter::Xml_generator &xml)
{
using namespace Genode;
typedef ACPI_MCFG_ALLOCATION Mcfg_sub;
for_each_element(mcfg, (Mcfg_sub *) nullptr, [&](Mcfg_sub const * const e) {
/* bus_count * up to 32 devices * 8 function per device * 4k */
uint32_t const bus_count = e->EndBusNumber - e->StartBusNumber + 1;
uint32_t const func_count = bus_count * 32 * 8;
uint32_t const bus_start = e->StartBusNumber * 32 * 8;
xml.node("bdf", [&] () {
xml.attribute("start", bus_start);
xml.attribute("count", func_count);
xml.attribute("base", String<24>(Hex(e->Address)));
});
/* force freeing I/O mem so that platform driver can use it XXX */
AcpiGenodeFreeIOMem(e->Address, 0x1000UL * func_count);
}, [](Mcfg_sub const * const e) { return sizeof(*e); });
}
static void add_dmar(ACPI_TABLE_DMAR const * const dmar_table,
Reporter::Xml_generator &xml)
{
using Genode::String;
using Genode::Hex;
auto scope_length = [](ACPI_DMAR_DEVICE_SCOPE const * const e) {
return e->Length; };
auto scope_lambda = [&](ACPI_DMAR_DEVICE_SCOPE const * const e) {
xml.node("scope", [&] () {
xml.attribute("bus_start", e->Bus);
xml.attribute("type", e->EntryType);
unsigned const count = (e->Length < 6) ? 0 : ((e->Length - 6) / 2);
ACPI_DMAR_PCI_PATH * path = ACPI_CAST_PTR(ACPI_DMAR_PCI_PATH, e + 1);
for (unsigned i = 0; i < count; i++) {
xml.node("path", [&] () {
xml.attribute("dev", String<8>(Hex(path->Device)));
xml.attribute("func", String<8>(Hex(path->Function)));
});
}
});
};
for_each_element(dmar_table, (ACPI_DMAR_HEADER *) nullptr, [&](ACPI_DMAR_HEADER const * const e) {
if (e->Type == ACPI_DMAR_TYPE_RESERVED_MEMORY) {
ACPI_DMAR_RESERVED_MEMORY const * const dmar = ACPI_CAST_PTR (ACPI_DMAR_RESERVED_MEMORY, e);
xml.node("rmrr", [&] () {
xml.attribute("start", String<24>(Hex(dmar->BaseAddress)));
xml.attribute("end" , String<24>(Hex(dmar->EndAddress)));
for_each_element(dmar, (ACPI_DMAR_DEVICE_SCOPE *) nullptr,
scope_lambda, scope_length);
});
} else
if (e->Type == ACPI_DMAR_TYPE_HARDWARE_UNIT) {
ACPI_DMAR_HARDWARE_UNIT const * const drhd = ACPI_CAST_PTR (ACPI_DMAR_HARDWARE_UNIT, e);
xml.node("drhd", [&] () {
xml.attribute("phys", String<24>(Hex(drhd->Address)));
xml.attribute("flags", String<4>(Hex(drhd->Flags)));
xml.attribute("segment", String<8>(Hex(drhd->Segment)));
for_each_element(drhd, (ACPI_DMAR_DEVICE_SCOPE *) nullptr,
scope_lambda, scope_length);
});
}
}, [](ACPI_DMAR_HEADER const * const e) { return e->Length; });
}
void Acpica::generate_report(Genode::Env &env, Bridge *pci_root_bridge)
{
enum { REPORT_SIZE = 5 * 4096 };
static Reporter acpi(env, "acpi", "acpi", REPORT_SIZE);
acpi.enabled(true);
Reporter::Xml_generator xml(acpi, [&] () {
ACPI_TABLE_HEADER *header = nullptr;
ACPI_STATUS status = AcpiGetTable((char *)ACPI_SIG_MADT, 0, &header);
if (status == AE_OK)
add_madt(reinterpret_cast<ACPI_TABLE_MADT *>(header), xml);
status = AcpiGetTable((char *)ACPI_SIG_MCFG, 0, &header);
if (status == AE_OK)
add_mcfg(reinterpret_cast<ACPI_TABLE_MCFG *>(header), xml);
for (unsigned instance = 1; ; instance ++) {
status = AcpiGetTable(ACPI_STRING(ACPI_SIG_DMAR), instance,
&header);
if (status != AE_OK)
break;
add_dmar(reinterpret_cast<ACPI_TABLE_DMAR *>(header), xml);
}
if (pci_root_bridge)
pci_root_bridge->generate(xml);
});
}

View File

@ -1,5 +1,5 @@
TARGET := acpica
SRC_CC := os.cc printf.cc
SRC_CC := os.cc printf.cc report.cc
REQUIRES := x86
LIBS += base acpica

View File

@ -1,6 +1,25 @@
/*
* \brief Some utils
* \author Alexander Boettcher
*/
/*
* Copyright (C) 2016-2018 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.
*/
extern "C" {
#include "acpi.h"
}
class Bridge;
namespace Acpica {
template<typename> class Buffer;
template<typename> class Callback;
void generate_report(Genode::Env &, Bridge *);
}
template <typename T>

View File

@ -28,6 +28,7 @@ struct Acpica::Env
Genode::Allocator &heap;
Wait_acpi_ready const wait_acpi_ready;
bool use_platform_drv;
Genode::Parent::Service_name announce_for_acpica {
wait_acpi_ready.enabled ? "Acpi" : Platform::Session::service_name() };
@ -37,16 +38,15 @@ struct Acpica::Env
Genode::Id_space<Genode::Parent::Client>::Element id_space_element {
parent_client, env.id_space() };
Genode::Capability<Platform::Session> cap {
Genode::reinterpret_cap_cast<Platform::Session>(
env.session(announce_for_acpica,
id_space_element.id(),
"ram_quota=36K", Genode::Affinity())) };
Genode::Constructible<Genode::Capability<Platform::Session>> cap;
Genode::Constructible<Platform::Client> platform;
Platform::Client platform { cap };
Env(Genode::Env &env, Genode::Allocator &heap, Wait_acpi_ready wait_acpi_ready)
: env(env), heap(heap), wait_acpi_ready(wait_acpi_ready) { }
Env(Genode::Env &env, Genode::Allocator &heap,
Wait_acpi_ready wait_acpi_ready)
:
env(env), heap(heap), wait_acpi_ready(wait_acpi_ready),
use_platform_drv(!wait_acpi_ready.enabled)
{ }
};
static Genode::Constructible<Acpica::Env> instance;
@ -54,11 +54,29 @@ static Genode::Constructible<Acpica::Env> instance;
Genode::Allocator & Acpica::heap() { return instance->heap; }
Genode::Env & Acpica::env() { return instance->env; }
Platform::Client & Acpica::platform() { return instance->platform; }
Platform::Client & Acpica::platform()
{
if (!instance->cap.constructed()) {
instance->cap.construct(Genode::reinterpret_cap_cast<Platform::Session>(
instance->env.session(instance->announce_for_acpica,
instance->id_space_element.id(),
"ram_quota=36K", Genode::Affinity())));
instance->platform.construct(*instance->cap);
}
return *instance->platform;
}
bool Acpica::platform_drv() { return instance->use_platform_drv; }
void Acpica::use_platform_drv() { instance->use_platform_drv = true; }
void Acpica::init(Genode::Env &env, Genode::Allocator &heap,
Wait_acpi_ready wait_acpi_ready)
Wait_acpi_ready const wait_acpi_ready,
Act_as_acpi_drv const act_as_acpi_drv)
{
instance.construct(env, heap, wait_acpi_ready);
/* if not running as acpi_drv, block until original acpi_drv is done */
if (!act_as_acpi_drv.enabled)
platform();
}

View File

@ -22,6 +22,7 @@ namespace Acpica {
Genode::Env & env();
Genode::Allocator & heap();
Platform::Client & platform();
bool platform_drv();
}
#endif /* _ACPICA__ENV_H_ */

View File

@ -18,6 +18,7 @@
#include <util/misc_math.h>
#include <io_mem_session/connection.h>
#include <region_map/client.h>
#include <rm_session/connection.h>
#include "env.h"
@ -124,6 +125,7 @@ class Acpica::Io_mem
Genode::Io_mem_connection *_io_mem = nullptr;
unsigned _ref = 0;
static Genode::Rm_connection *rm_conn;
static Acpica::Io_mem _ios[32];
public:
@ -144,6 +146,55 @@ class Acpica::Io_mem
return reinterpret_cast<Genode::addr_t>(_virt + (p - _phys));
}
static void force_free_overlap(ACPI_PHYSICAL_ADDRESS const phys,
ACPI_SIZE const size)
{
Acpica::Io_mem::apply_u([&] (Acpica::Io_mem &io_mem) {
if (io_mem.unused() && !io_mem.stale())
return 0;
/* skip non overlapping ranges */
if ((phys + size <= io_mem._phys) ||
(io_mem._phys + io_mem._size <= phys))
return 0;
while (io_mem._ref > 1) {
io_mem.ref_dec();
}
Genode::warning(" force freeing I/O memory",
" unused=", io_mem.unused(),
" stale=" , io_mem.stale(),
" phys=" , Genode::Hex(io_mem._phys),
" size=" , Genode::Hex(io_mem._size),
" virt=" , io_mem._virt,
" io_ptr=", io_mem._io_mem,
" refcnt=", io_mem._ref);
/* allocate region on heap and don't free, otherwise in destructor the connection will be closed */
if (!rm_conn)
rm_conn = new (Acpica::heap()) Genode::Rm_connection(Acpica::env());
/* create managed dataspace to let virt region reserved */
Genode::Region_map_client managed_region(rm_conn->create(io_mem._size));
/* remember virt, since it get invalid during invalidate() */
Genode::addr_t const re_attach_virt = reinterpret_cast<Genode::addr_t>(io_mem._virt);
/* drop I/O mem and virt region get's freed */
io_mem.invalidate();
/* re-attach dummy managed dataspace to virt region */
Genode::addr_t const re_attached_virt = Acpica::env().rm().attach_at(managed_region.dataspace(), re_attach_virt);
if (re_attach_virt != re_attached_virt)
FAIL(0);
if (!io_mem.unused() || io_mem.stale())
FAIL(0);
return 0;
});
}
bool ref_dec() { return --_ref; }
template <typename FUNC>
@ -310,6 +361,7 @@ class Acpica::Io_mem
};
Acpica::Io_mem Acpica::Io_mem::_ios[32];
Genode::Rm_connection * Acpica::Io_mem::rm_conn { nullptr };
static ACPI_TABLE_RSDP faked_rsdp;
enum { FAKED_PHYS_RSDP_ADDR = 1 };
@ -390,3 +442,8 @@ void AcpiOsUnmapMemory (void * ptr, ACPI_SIZE size)
FAIL()
}
void AcpiGenodeFreeIOMem(ACPI_PHYSICAL_ADDRESS const phys, ACPI_SIZE const size)
{
Acpica::Io_mem::force_free_overlap(phys, size);
}

View File

@ -12,6 +12,7 @@
*/
#include <base/log.h>
#include <io_port_session/connection.h>
#include "env.h"
@ -34,18 +35,125 @@ struct Bdf
void print(Genode::Output &out) const
{
using Genode::Hex;
Genode::print(out, Hex(bus, Hex::OMIT_PREFIX), ":",
Hex(dev, Hex::OMIT_PREFIX), ".",
Genode::print(out, Hex(bus, Hex::OMIT_PREFIX, Hex::PAD), ":",
Hex(dev, Hex::OMIT_PREFIX, Hex::PAD), ".",
Hex(fn, Hex::OMIT_PREFIX), " ");
}
};
static void dump_read(char const * const func, ACPI_PCI_ID *pcidev,
UINT32 reg, UINT64 value, UINT32 width)
{
using namespace Genode;
log(func, ": ", Bdf(pcidev->Bus, pcidev->Device, pcidev->Function), " "
"reg=", Hex(reg, Hex::PREFIX, Hex::PAD), " "
"width=", width, width < 10 ? " " : "", " -> "
"value=", Genode::Hex(value));
}
static void dump_write(char const * const func, ACPI_PCI_ID *pcidev,
UINT32 reg, UINT64 value, UINT32 width)
{
using namespace Genode;
warning(func, ": ", Bdf(pcidev->Bus, pcidev->Device, pcidev->Function), " "
"reg=", Hex(reg, Hex::PREFIX, Hex::PAD), " "
"width=", width, width < 10 ? " " : "", " -> "
"value=", Genode::Hex(value));
}
static void dump_error(char const * const func, ACPI_PCI_ID *pcidev,
UINT32 reg, UINT32 width)
{
error(func, " unknown device - segment=", pcidev->Segment, " ",
"bdf=", Bdf(pcidev->Bus, pcidev->Device, pcidev->Function), " ",
"reg=", Genode::Hex(reg), " "
"width=", Genode::Hex(width));
}
/*******************************
* Accessing PCI via I/O ports *
*******************************/
enum { REG_ADDR = 0xcf8, REG_DATA = 0xcfc, REG_SIZE = 4 };
static Genode::Io_port_connection &pci_io_port() {
static Genode::Io_port_connection conn(Acpica::env(), REG_ADDR, REG_SIZE);
return conn;
}
static unsigned pci_io_cfg_addr(unsigned const bus, unsigned const device,
unsigned const function, unsigned const addr)
{
return (1U << 31) |
(bus << 16) |
((device & 0x1fU) << 11) |
((function & 0x07U) << 8) |
(addr & ~3U);
}
static unsigned pci_io_read(unsigned const bus, unsigned const device,
unsigned const function, unsigned const addr,
unsigned const width)
{
/* write target address */
pci_io_port().outl(REG_ADDR, pci_io_cfg_addr(bus, device, function, addr));
switch (width) {
case 8:
return pci_io_port().inb(REG_DATA + (addr & 3));
case 16:
return pci_io_port().inw(REG_DATA + (addr & 2));
case 32:
return pci_io_port().inl(REG_DATA);
default:
return ~0U;
}
}
static void pci_io_write(unsigned const bus, unsigned const device,
unsigned const function, unsigned const addr,
unsigned const width, unsigned value)
{
/* write target address */
pci_io_port().outl(REG_ADDR, pci_io_cfg_addr(bus, device, function, addr));
switch (width) {
case 8:
pci_io_port().outb(REG_DATA + (addr & 3), value);
return;
case 16:
pci_io_port().outw(REG_DATA + (addr & 2), value);
return;
case 32:
pci_io_port().outl(REG_DATA, value);
return;
}
}
/*************************
* Acpica PCI OS backend *
*************************/
ACPI_STATUS AcpiOsInitialize (void) { return AE_OK; }
ACPI_STATUS AcpiOsReadPciConfiguration (ACPI_PCI_ID *pcidev, UINT32 reg,
UINT64 *value, UINT32 width)
{
if (!Acpica::platform_drv()) {
try {
*value = pci_io_read(pcidev->Bus, pcidev->Device, pcidev->Function,
reg, width);
dump_read(__func__, pcidev, reg, *value, width);
} catch (...) {
dump_error(__func__, pcidev, reg, width);
return AE_ERROR;
}
return AE_OK;
}
Platform::Device_capability cap = Acpica::platform().first_device();
while (cap.valid()) {
@ -76,10 +184,7 @@ ACPI_STATUS AcpiOsReadPciConfiguration (ACPI_PCI_ID *pcidev, UINT32 reg,
*value = client.config_read(reg, access_size);
Genode::log(__func__, ": ", Bdf(bus, dev, fn),
"reg=", Genode::Hex(reg), " "
"width=", width, " -> "
"value=", Genode::Hex(*value));
dump_read(__func__, pcidev, reg, *value, width);
Acpica::platform().release_device(client);
return AE_OK;
@ -90,10 +195,7 @@ ACPI_STATUS AcpiOsReadPciConfiguration (ACPI_PCI_ID *pcidev, UINT32 reg,
Acpica::platform().release_device(client);
}
Genode::error(__func__, " unknown device - segment=", pcidev->Segment, " "
"bdf=", Bdf(pcidev->Bus, pcidev->Device, pcidev->Function), " "
"reg=", Genode::Hex(reg), " "
"width=", Genode::Hex(width));
dump_error(__func__, pcidev, reg, width);
return AE_ERROR;
}
@ -101,6 +203,18 @@ ACPI_STATUS AcpiOsReadPciConfiguration (ACPI_PCI_ID *pcidev, UINT32 reg,
ACPI_STATUS AcpiOsWritePciConfiguration (ACPI_PCI_ID *pcidev, UINT32 reg,
UINT64 value, UINT32 width)
{
if (!Acpica::platform_drv()) {
try {
dump_write(__func__, pcidev, reg, value, width);
pci_io_write(pcidev->Bus, pcidev->Device, pcidev->Function, reg,
width, value);
return AE_OK;
} catch (...) {
dump_error(__func__, pcidev, reg, width);
return AE_ERROR;
}
}
Platform::Device_capability cap = Acpica::platform().first_device();
while (cap.valid()) {
@ -131,10 +245,7 @@ ACPI_STATUS AcpiOsWritePciConfiguration (ACPI_PCI_ID *pcidev, UINT32 reg,
client.config_write(reg, value, access_size);
Genode::warning(__func__, ": ", Bdf(bus, dev, fn), " "
"reg=", Genode::Hex(reg), " "
"width=", width, " "
"value=", Genode::Hex(value));
dump_write(__func__, pcidev, reg, value, width);
Acpica::platform().release_device(client);
return AE_OK;
@ -145,10 +256,7 @@ ACPI_STATUS AcpiOsWritePciConfiguration (ACPI_PCI_ID *pcidev, UINT32 reg,
Acpica::platform().release_device(client);
}
Genode::error(__func__, " unknown device - segment=", pcidev->Segment, " ",
"bdf=", Bdf(pcidev->Bus, pcidev->Device, pcidev->Function), " ",
"reg=", Genode::Hex(reg), " "
"width=", Genode::Hex(width));
dump_error(__func__, pcidev, reg, width);
return AE_ERROR;
}