diff --git a/repos/libports/include/acpica/acpica.h b/repos/libports/include/acpica/acpica.h index 989b9697b3..7033906291 100644 --- a/repos/libports/include/acpica/acpica.h +++ b/repos/libports/include/acpica/acpica.h @@ -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_ */ diff --git a/repos/libports/src/app/acpica/README b/repos/libports/src/app/acpica/README index 7415ac5079..ec0129edd7 100644 --- a/repos/libports/src/app/acpica/README +++ b/repos/libports/src/app/acpica/README @@ -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: + +! + +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 ------------------------------------------------------ ! ! ! ... -! +! ! ! ! diff --git a/repos/libports/src/app/acpica/bridge.h b/repos/libports/src/app/acpica/bridge.h new file mode 100644 index 0000000000..7bea7fbe9f --- /dev/null +++ b/repos/libports/src/app/acpica/bridge.h @@ -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 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 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 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 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(irqs.Pointer); + ACPI_PCI_ROUTING_TABLE *e = reinterpret_cast(reinterpret_cast(&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(reinterpret_cast(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); + } +}; diff --git a/repos/libports/src/app/acpica/os.cc b/repos/libports/src/app/acpica/os.cc index 926c420b14..0bdf31ab23 100644 --- a/repos/libports/src/app/acpica/os.cc +++ b/repos/libports/src/app/acpica/os.cc @@ -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(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(pcie_bridge) + : reinterpret_cast(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(); } diff --git a/repos/libports/src/app/acpica/report.cc b/repos/libports/src/app/acpica/report.cc new file mode 100644 index 0000000000..da12f58570 --- /dev/null +++ b/repos/libports/src/app/acpica/report.cc @@ -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 +#include + +#include "util.h" +#include "bridge.h" + +using Genode::Reporter; + +extern void AcpiGenodeFreeIOMem(ACPI_PHYSICAL_ADDRESS const phys, ACPI_SIZE const size); + +template +void for_each_element(H const head, S *, F const &fn, FSIZE const &fn_size) +{ + for(S const * e = reinterpret_cast(head + 1); + e < reinterpret_cast(reinterpret_cast(head) + head->Header.Length); + e = reinterpret_cast(reinterpret_cast(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(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(header), xml); + + status = AcpiGetTable((char *)ACPI_SIG_MCFG, 0, &header); + if (status == AE_OK) + add_mcfg(reinterpret_cast(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(header), xml); + } + + if (pci_root_bridge) + pci_root_bridge->generate(xml); + }); +} diff --git a/repos/libports/src/app/acpica/target.mk b/repos/libports/src/app/acpica/target.mk index 1a09936831..0db7d0f48c 100644 --- a/repos/libports/src/app/acpica/target.mk +++ b/repos/libports/src/app/acpica/target.mk @@ -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 diff --git a/repos/libports/src/app/acpica/util.h b/repos/libports/src/app/acpica/util.h index dfeff7cbc1..578f82e1a6 100644 --- a/repos/libports/src/app/acpica/util.h +++ b/repos/libports/src/app/acpica/util.h @@ -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 class Buffer; template class Callback; + void generate_report(Genode::Env &, Bridge *); } template diff --git a/repos/libports/src/lib/acpica/env.cc b/repos/libports/src/lib/acpica/env.cc index 9cb43efd56..5d3cb4abb4 100644 --- a/repos/libports/src/lib/acpica/env.cc +++ b/repos/libports/src/lib/acpica/env.cc @@ -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::Element id_space_element { parent_client, env.id_space() }; - Genode::Capability cap { - Genode::reinterpret_cap_cast( - env.session(announce_for_acpica, - id_space_element.id(), - "ram_quota=36K", Genode::Affinity())) }; + Genode::Constructible> cap; + Genode::Constructible 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 instance; @@ -54,11 +54,29 @@ static Genode::Constructible 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( + 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(); } diff --git a/repos/libports/src/lib/acpica/env.h b/repos/libports/src/lib/acpica/env.h index 82c7f8ead9..ec92270992 100644 --- a/repos/libports/src/lib/acpica/env.h +++ b/repos/libports/src/lib/acpica/env.h @@ -22,6 +22,7 @@ namespace Acpica { Genode::Env & env(); Genode::Allocator & heap(); Platform::Client & platform(); + bool platform_drv(); } #endif /* _ACPICA__ENV_H_ */ diff --git a/repos/libports/src/lib/acpica/iomem.cc b/repos/libports/src/lib/acpica/iomem.cc index 45de9fe9b4..34334a7ab4 100644 --- a/repos/libports/src/lib/acpica/iomem.cc +++ b/repos/libports/src/lib/acpica/iomem.cc @@ -18,6 +18,7 @@ #include #include +#include #include #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(_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(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 @@ -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); +} diff --git a/repos/libports/src/lib/acpica/pci.cc b/repos/libports/src/lib/acpica/pci.cc index 7d51ef8a66..40a557023b 100644 --- a/repos/libports/src/lib/acpica/pci.cc +++ b/repos/libports/src/lib/acpica/pci.cc @@ -12,6 +12,7 @@ */ #include +#include #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; }