From 38c5abbaad0a78ff84790433c1ad16d488fa16ab Mon Sep 17 00:00:00 2001 From: Alexander Boettcher Date: Fri, 18 Mar 2016 16:31:00 +0100 Subject: [PATCH] libports: add acpica library Fixes #1962 --- repos/libports/lib/mk/acpica.mk | 27 ++ repos/libports/ports/acpica.hash | 1 + repos/libports/ports/acpica.port | 9 + repos/libports/run/acpica.run | 161 ++++++++++ repos/libports/src/app/acpica/README | 141 +++++++++ repos/libports/src/app/acpica/ac.h | 85 +++++ repos/libports/src/app/acpica/debug/printf.cc | 25 ++ repos/libports/src/app/acpica/debug/target.mk | 7 + repos/libports/src/app/acpica/ec.h | 291 ++++++++++++++++++ repos/libports/src/app/acpica/fixed.h | 85 +++++ repos/libports/src/app/acpica/lid.h | 83 +++++ repos/libports/src/app/acpica/os.cc | 277 +++++++++++++++++ repos/libports/src/app/acpica/printf.cc | 17 + repos/libports/src/app/acpica/reporter.h | 109 +++++++ repos/libports/src/app/acpica/sb.h | 227 ++++++++++++++ repos/libports/src/app/acpica/target.inc | 8 + repos/libports/src/app/acpica/target.mk | 4 + repos/libports/src/app/acpica/util.h | 31 ++ repos/libports/src/lib/acpica/acpica.patch | 76 +++++ repos/libports/src/lib/acpica/iomem.cc | 291 ++++++++++++++++++ repos/libports/src/lib/acpica/osl.cc | 289 +++++++++++++++++ repos/libports/src/lib/acpica/pci.cc | 137 +++++++++ repos/libports/src/lib/acpica/scan_root.cc | 100 ++++++ 23 files changed, 2481 insertions(+) create mode 100644 repos/libports/lib/mk/acpica.mk create mode 100644 repos/libports/ports/acpica.hash create mode 100644 repos/libports/ports/acpica.port create mode 100644 repos/libports/run/acpica.run create mode 100644 repos/libports/src/app/acpica/README create mode 100644 repos/libports/src/app/acpica/ac.h create mode 100644 repos/libports/src/app/acpica/debug/printf.cc create mode 100644 repos/libports/src/app/acpica/debug/target.mk create mode 100644 repos/libports/src/app/acpica/ec.h create mode 100644 repos/libports/src/app/acpica/fixed.h create mode 100644 repos/libports/src/app/acpica/lid.h create mode 100644 repos/libports/src/app/acpica/os.cc create mode 100644 repos/libports/src/app/acpica/printf.cc create mode 100644 repos/libports/src/app/acpica/reporter.h create mode 100644 repos/libports/src/app/acpica/sb.h create mode 100644 repos/libports/src/app/acpica/target.inc create mode 100644 repos/libports/src/app/acpica/target.mk create mode 100644 repos/libports/src/app/acpica/util.h create mode 100644 repos/libports/src/lib/acpica/acpica.patch create mode 100644 repos/libports/src/lib/acpica/iomem.cc create mode 100644 repos/libports/src/lib/acpica/osl.cc create mode 100644 repos/libports/src/lib/acpica/pci.cc create mode 100644 repos/libports/src/lib/acpica/scan_root.cc diff --git a/repos/libports/lib/mk/acpica.mk b/repos/libports/lib/mk/acpica.mk new file mode 100644 index 0000000000..aed6065cd3 --- /dev/null +++ b/repos/libports/lib/mk/acpica.mk @@ -0,0 +1,27 @@ +REQUIRES := x86 + +ACPICA_DIR := $(call select_from_ports,acpica)/src/lib/acpica +ACPICA_COMP := $(ACPICA_DIR)/source/components + +INC_DIR += $(ACPICA_DIR)/source/include + +SRC_C += debugger/dbdisply.c debugger/dbobject.c debugger/dbxface.c +SRC_C += $(addprefix disassembler/, $(notdir $(wildcard $(ACPICA_COMP)/disassembler/*.c))) +SRC_C += $(addprefix dispatcher/, $(notdir $(wildcard $(ACPICA_COMP)/dispatcher/*.c))) +SRC_C += $(addprefix events/, $(notdir $(wildcard $(ACPICA_COMP)/events/*.c))) +SRC_C += $(addprefix executer/, $(notdir $(wildcard $(ACPICA_COMP)/executer/*.c))) +SRC_C += $(addprefix hardware/, $(notdir $(wildcard $(ACPICA_COMP)/hardware/*.c))) +SRC_C += $(addprefix namespace/, $(notdir $(wildcard $(ACPICA_COMP)/namespace/*.c))) +SRC_C += $(addprefix parser/, $(notdir $(wildcard $(ACPICA_COMP)/parser/*.c))) +SRC_C += $(addprefix resources/, $(notdir $(wildcard $(ACPICA_COMP)/resources/*.c))) +SRC_C += $(addprefix tables/, $(notdir $(wildcard $(ACPICA_COMP)/tables/*.c))) +SRC_C += $(addprefix utilities/, $(notdir $(wildcard $(ACPICA_COMP)/utilities/*.c))) + +SRC_CC += osl.cc iomem.cc pci.cc +SRC_CC += scan_root.cc + +CC_OPT += -Wno-unused-function -Wno-unused-variable +CC_C_OPT += -DACPI_LIBRARY + +vpath %.c $(ACPICA_COMP) +vpath %.cc $(REP_DIR)/src/lib/acpica diff --git a/repos/libports/ports/acpica.hash b/repos/libports/ports/acpica.hash new file mode 100644 index 0000000000..3f2cc1f6e2 --- /dev/null +++ b/repos/libports/ports/acpica.hash @@ -0,0 +1 @@ +a3d820f28b860fdd9fd8c855f0fa2ec0b4beb859 diff --git a/repos/libports/ports/acpica.port b/repos/libports/ports/acpica.port new file mode 100644 index 0000000000..df5080f308 --- /dev/null +++ b/repos/libports/ports/acpica.port @@ -0,0 +1,9 @@ +LICENSE := BSD +VERSION := 20160212 +DOWNLOADS := acpica.archive + +URL(acpica) := http://acpica.org/sites/acpica/files/acpica-unix2-$(VERSION).tar.gz +SHA(acpica) := 9b4ceb8562952c16bfb70c567429509504f388b8 +DIR(acpica) := src/lib/acpica + +PATCHES := src/lib/acpica/acpica.patch diff --git a/repos/libports/run/acpica.run b/repos/libports/run/acpica.run new file mode 100644 index 0000000000..88b8b9e75f --- /dev/null +++ b/repos/libports/run/acpica.run @@ -0,0 +1,161 @@ +assert_spec acpi + +if { + ![have_spec hw] && + ![have_spec nova] +} { + puts "Platform is unsupported." + exit 0 +} + +set build_components { + core init + drivers/input + drivers/timer + server/dynamic_rom + server/report_rom + app/acpica + test/input +} + +source ${genode_dir}/repos/base/run/platform_drv.inc + +# override default platform driver policy +proc platform_drv_policy {} { + return { + + } +} + +append_platform_drv_build_components + +build $build_components + +create_boot_directory + +set config { + + + + + + + + + + + + + + + + + + + + + + + + + + + + + } + +append config { + + + + + + + + + + + + + + + + } + +append config { + + + + + + + + + + + + + + + + + } + +append config { + + + + + + + + + + + + + + + + + + + + + } + +append_platform_drv_config + +append config { + +} + +install_config $config + +set boot_modules { + core init + ld.lib.so libc.lib.so + timer + ps2_drv + report_rom + dynamic_rom + acpica + debug-acpica + test-input +} + +append_platform_drv_boot_modules + +build_boot_image $boot_modules + +append qemu_args "-nographic -m 128" + +run_genode_until {\[init -\> acpica\].*SCI IRQ:.*\n} 30 diff --git a/repos/libports/src/app/acpica/README b/repos/libports/src/app/acpica/README new file mode 100644 index 0000000000..e2bf6955cb --- /dev/null +++ b/repos/libports/src/app/acpica/README @@ -0,0 +1,141 @@ +This directory contains a application using the ported ACPI-CA +library (https://www.acpica.org) and reports ACPI state changes in form of +Genode reports. Additionally the application is capable to perform ACPI poweroff +and reset. + +Behavior +-------- + +General support for ACPI events compromises state changes from the following +sources: + +- ACPI Lid - open/closed +- ACPI Smart battery (SB) - charging/discharging and static information (capacity) +- ACPI fixed events - e.g. power button +- ACPI AC adapters - power cable plugged/un-plugged +- ACPI Embedded controller - some Fn-* keys and on some machines also Lid, AC, SB changes + +Whenever a state change is detected, a Genode report is generated, if a +config attribute "report" is set to "yes". The reports are named +'acpi_lid', 'acpi_battery', 'acpi_fixed', 'acpi_ac' and 'acpi_ec'. See below +for the xml syntax used so far. Please also look into the ACPI specification +for detailed description of some of the fields and their possible values. + +If the config attributes 'reset' or 'poweroff' are set to yes, the application +additionally looks for a ROM in XML format named 'system' and monitors +changes of the 'state' attribute: + +! + +If the ROM changes to 'state="reset"' the application tries to reset the +machine immediately. +If the ROM changes to 'state="poweroff"' the application tries to poweroff +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'. + +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. + +Excerpt of important parts of the acpica configuration +------------------------------------------------------ + +! +! +! ... +! +! +! +! +! ... +! +! +! +! +! ... +! +! +! ... +! +! + +Reports generated by the Genode acpica application +-------------------------------------------------- + +Report 'acpi_lid' - open/closed: + +! +! open +! + +! +! closed +! + + +Report 'acpi_ac' - power cable plugged-in /unplugged + +! +! online +! + +! +! offline +! + + +Report 'acpi_ec' - embedded controller events + +! +! +! triggered +! +! +! + + +Report 'acpi_battery' - smart battery status changes + +! +! +! BAT1 +! mA/mAh +! +! +! secondary +! +! +! +! +! +! BAT1 +! RT672 +! LiON +! ASP +! +! discharging +! +! +! +! +! + +! +! ... +! charging +! ... +! + +! +! ... +! battery not present +! ... +! diff --git a/repos/libports/src/app/acpica/ac.h b/repos/libports/src/app/acpica/ac.h new file mode 100644 index 0000000000..554218b4ad --- /dev/null +++ b/repos/libports/src/app/acpica/ac.h @@ -0,0 +1,85 @@ +/* + * \brief Handle ACPI AC adapter devices + * \author Alexander Boettcher + * + */ + +/* + * Copyright (C) 2016 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +class Ac : Acpica::Callback { + + private: + + Acpica::Reportstate * _report; + UINT64 _ac_state = 0; + UINT64 _ac_count = 0; + + public: + + Ac(void * report) + : _report(reinterpret_cast(report)) + { + if (_report) + _report->add_notify(this); + } + + void handle(ACPI_HANDLE ac, UINT32 value) + { + Acpica::Buffer onoff; + ACPI_STATUS res = AcpiEvaluateObjectTyped(ac, ACPI_STRING("_PSR"), + nullptr, &onoff, + ACPI_TYPE_INTEGER); + if (ACPI_FAILURE(res)) { + PDBG("failed - res=0x%x _PSR", res); + return; + } + + _ac_state = onoff.object.Integer.Value; + _ac_count++; + + PINF("%s - ac (%u)", + _ac_state == 0 ? "offline " : + _ac_state == 1 ? "online " : "unknown ", + value); + + if (_report) + _report->ac_event(); + } + + static ACPI_STATUS detect(ACPI_HANDLE ac, UINT32, void * report, void **) + { + Ac * obj = new (Genode::env()->heap()) Ac(report); + + ACPI_STATUS res = AcpiInstallNotifyHandler (ac, ACPI_DEVICE_NOTIFY, + handler, obj); + if (ACPI_FAILURE(res)) { + PERR("failed - '%s' res=0x%x", __func__, res); + delete obj; + return AE_OK; + } + + PINF("detected - ac"); + + handler(ac, 0, obj); + + return AE_OK; + } + + void generate(Genode::Xml_generator &xml) + { + xml.attribute("value", _ac_state); + xml.attribute("count", _ac_count); + + if (_ac_state == 0) + xml.append("offline"); + else if (_ac_state == 1) + xml.append("online"); + else + xml.append("unknown"); + } +}; diff --git a/repos/libports/src/app/acpica/debug/printf.cc b/repos/libports/src/app/acpica/debug/printf.cc new file mode 100644 index 0000000000..664fa3d58e --- /dev/null +++ b/repos/libports/src/app/acpica/debug/printf.cc @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2016 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include +#include + +extern "C" +void AcpiOsPrintf (const char *fmt, ...) +{ + va_list va; + va_start(va, fmt); + vprintf(fmt, va); + va_end(va); +} + +extern "C" +void AcpiOsVprintf (const char *fmt, va_list va) +{ + vprintf(fmt, va); +} + diff --git a/repos/libports/src/app/acpica/debug/target.mk b/repos/libports/src/app/acpica/debug/target.mk new file mode 100644 index 0000000000..d7e8ea769d --- /dev/null +++ b/repos/libports/src/app/acpica/debug/target.mk @@ -0,0 +1,7 @@ +TARGET = debug-acpica +LIBS = libc +SRC_CC = os.cc printf.cc + +include $(PRG_DIR)/../target.inc + +vpath os.cc $(PRG_DIR)/.. diff --git a/repos/libports/src/app/acpica/ec.h b/repos/libports/src/app/acpica/ec.h new file mode 100644 index 0000000000..239137aac4 --- /dev/null +++ b/repos/libports/src/app/acpica/ec.h @@ -0,0 +1,291 @@ +/* + * \brief Handle ACPI Embedded Controller devices + * \author Alexander Boettcher + * + */ + +/* + * Copyright (C) 2016 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + + +#include + +class Ec : Acpica::Callback { + + private: + unsigned short ec_port_cmdsta; + unsigned short ec_port_data; + + Genode::Io_port_connection * ec_cmdsta = nullptr; + Genode::Io_port_connection * ec_data = nullptr;; + + ACPI_HANDLE gpe_block; + + Acpica::Reportstate * _report; + + /* 12.2.1 Embedded Controller Status, EC_SC (R) */ + struct State : Genode::Register<8> { + struct Out_ful: Bitfield<0,1> { }; + struct In_ful : Bitfield<1,1> { }; + struct Sci_evt: Bitfield<5,1> { }; + }; + + /* 12.3. Embedded Controller Command Set */ + enum { RD_EC = 0x80, WR_EC = 0x81, QR_EC = 0x84 }; + + /* track data items reported by controller */ + struct Data : Genode::List::Element { + Genode::uint64_t count; + Genode::uint8_t data; + bool triggered; + + Data(Genode::uint8_t d) : count(0), data(d), triggered(false) { } + }; + Genode::List _list_data; + + public: + + Ec(void * report) + : + _report(reinterpret_cast(report)) + { } + + static UINT32 handler_gpe(ACPI_HANDLE dev, UINT32 gpe, void *context) + { + Ec * ec = reinterpret_cast(context); + + ACPI_GPE_EVENT_INFO * ev = AcpiEvGetGpeEventInfo(ec->gpe_block, gpe); + if (!ev || !ec->ec_cmdsta || !ec->ec_data) { + PERR("unknown GPE 0x%x", gpe); + return AE_OK; /* GPE is disabled and must be enabled explicitly */ + } + + if ((ACPI_GPE_DISPATCH_TYPE (ev->Flags) != ACPI_GPE_DISPATCH_HANDLER) || + !ev->Dispatch.Handler) { + PERR("unknown dispatch type, GPE 0x%x, flags=0x%x type=0x%x", + gpe, ev->Flags, ACPI_GPE_DISPATCH_TYPE (ev->Flags)); + return AE_OK; /* GPE is disabled and must be enabled explicitly */ + } + + State::access_t state = ec->ec_cmdsta->inb(ec->ec_port_cmdsta); + + if (!State::Sci_evt::get(state)) { + PERR("unknown status 0x%x", state); + return ACPI_REENABLE_GPE; /* gpe is acked and re-enabled */ + } + + ec->ec_cmdsta->outb(ec->ec_port_cmdsta, QR_EC); + do { + state = ec->ec_cmdsta->inb(ec->ec_port_cmdsta); + } while (!(State::Out_ful::get(state))); + + unsigned cnt = 0; + unsigned char data; + do { + data = ec->ec_data->inb(ec->ec_port_data); + state = ec->ec_cmdsta->inb(ec->ec_port_cmdsta); + + if (!ec->_report) + PINF("ec event - status 0x%x data 0x%x round=%u", state, + data, ++cnt); + } while (State::Out_ful::get(state)); + + if (ec->_report) { + Data * data_obj = ec->_list_data.first(); + for (; data_obj; data_obj = data_obj->next()) { + if (data_obj->data == data) + break; + } + + if (!data_obj) { + data_obj = new (Genode::env()->heap()) Data(data); + ec->_list_data.insert(data_obj); + } + data_obj->count ++; + data_obj->triggered = true; + + ec->_report->ec_event(); + } + + return ACPI_REENABLE_GPE; /* gpe is acked and re-enabled */ + } + + static ACPI_STATUS detect_io_ports(ACPI_RESOURCE *resource, + void *context) + { + Ec * ec = reinterpret_cast(context); + + if (resource->Type == ACPI_RESOURCE_TYPE_END_TAG) + return AE_OK; + + if (resource->Type != ACPI_RESOURCE_TYPE_IO) { + PWRN("unknown resource type %u", resource->Type); + return AE_OK; + } +/* + PDBG("TYPE IO: IoDecode 0x%x, alignment 0x%x, AddressLen 0x%x " + "min 0x%x max 0x%x", + resource->Data.Io.IoDecode, resource->Data.Io.Alignment, + resource->Data.Io.AddressLength, resource->Data.Io.Minimum, + resource->Data.Io.Maximum); +*/ + /* first port is data, second is status/cmd */ + if (resource->Data.Io.AddressLength != 1) + PERR("unsupported address length of %u", + resource->Data.Io.AddressLength); + + if (!ec->ec_data) { + ec->ec_port_data = resource->Data.Io.Minimum; + ec->ec_data = new (Genode::env()->heap()) Genode::Io_port_connection(ec->ec_port_data, 1); + } else + if (!ec->ec_cmdsta) { + ec->ec_port_cmdsta = resource->Data.Io.Minimum; + ec->ec_cmdsta = new (Genode::env()->heap()) Genode::Io_port_connection(ec->ec_port_cmdsta, 1); + } else + PERR("unknown io_port"); + + return AE_OK; + } + + static ACPI_STATUS handler_ec(UINT32 function, + ACPI_PHYSICAL_ADDRESS phys_addr, + UINT32 bitwidth, UINT64 *value, void *, + void *ec_void) + { + unsigned const bytes = bitwidth / 8; + /* bitwidth can be larger than 64bit - use char array */ + unsigned char *result = reinterpret_cast(value); + + if (bytes * 8 != bitwidth) { + PERR("unsupport bit width of %u", bitwidth); + return AE_BAD_PARAMETER; + } + + Ec * ec = reinterpret_cast(ec_void); + + switch (function & ACPI_IO_MASK) { + case ACPI_READ: + for (unsigned i = 0; i < bytes; i++) { + State::access_t state; + + /* write command */ + ec->ec_cmdsta->outb(ec->ec_port_cmdsta, RD_EC); + do { + state = ec->ec_cmdsta->inb(ec->ec_port_cmdsta); + } while (State::In_ful::get(state)); + + /* write address */ + ec->ec_data->outb(ec->ec_port_data, phys_addr + i); + do { + state = ec->ec_cmdsta->inb(ec->ec_port_cmdsta); + } while (!(State::Out_ful::get(state))); + + /* read value */ + result[i] = ec->ec_data->inb(ec->ec_port_data); + } + return AE_OK; + case ACPI_WRITE: + for (unsigned i = 0; i < bytes; i++) { + State::access_t state; + + /* write command */ + ec->ec_cmdsta->outb(ec->ec_port_cmdsta, WR_EC); + do { + state = ec->ec_cmdsta->inb(ec->ec_port_cmdsta); + } while (State::In_ful::get(state)); + + /* write address */ + ec->ec_data->outb(ec->ec_port_data, phys_addr + i); + do { + state = ec->ec_cmdsta->inb(ec->ec_port_cmdsta); + } while (State::In_ful::get(state)); + + /* write value */ + ec->ec_data->outb(ec->ec_port_data, result[i]); + do { + state = ec->ec_cmdsta->inb(ec->ec_port_cmdsta); + } while (State::In_ful::get(state)); + } + return AE_OK; + } + + return AE_BAD_PARAMETER; + } + + static ACPI_STATUS detect(ACPI_HANDLE ec, UINT32, void *report, void **) + { + Ec *ec_obj = new (Genode::env()->heap()) Ec(report); + + ACPI_STATUS res = AcpiWalkResources(ec, ACPI_STRING("_CRS"), + Ec::detect_io_ports, ec_obj); + if (ACPI_FAILURE(res)) { + PERR("failed - '%s' _CRS res=0x%x", __func__, res); + return AE_OK; + } + + res = AcpiInstallAddressSpaceHandler(ec, ACPI_ADR_SPACE_EC, + handler_ec, nullptr, + ec_obj); + if (ACPI_FAILURE(res)) { + PERR("failed - '%s' spacehandler res=0x%x", __func__, res); + return AE_OK; + } + + Acpica::Buffer sta; + res = AcpiEvaluateObjectTyped(ec, ACPI_STRING("_GPE"), nullptr, + &sta, ACPI_TYPE_INTEGER); + if (ACPI_FAILURE(res)) { + PERR("failed - '%s' _STA res=0x%x", __func__, res); + return AE_OK; + } + + UINT32 gpe_to_enable = sta.object.Integer.Value; + + /* if ec_obj->gpe_block stays null - it's GPE0/GPE1 */ + res = AcpiGetGpeDevice(gpe_to_enable, &ec_obj->gpe_block); + if (ACPI_FAILURE(res)) { + PERR("failed - '%s' get_device res=0x%x", __func__, res); + return AE_OK; + } + + res = AcpiInstallGpeHandler(ec_obj->gpe_block, gpe_to_enable, + ACPI_GPE_LEVEL_TRIGGERED, handler_gpe, + ec_obj); + if (ACPI_FAILURE(res)) { + PERR("failed - '%s' install_device res=0x%x", __func__, res); + return AE_OK; + } + + res = AcpiEnableGpe (ec_obj->gpe_block, gpe_to_enable); + if (ACPI_FAILURE(res)) { + PERR("failed - '%s' enable_gpe res=0x%x", __func__, res); + return AE_OK; + } + + PINF("detected - ec"); + + if (ec_obj->_report) + ec_obj->_report->add_notify(ec_obj); + + return AE_OK; + } + + void generate(Genode::Xml_generator &xml) + { + Data * data_obj = _list_data.first(); + for (; data_obj; data_obj = data_obj->next()) { + xml.node("data", [&] { + xml.attribute("value", data_obj->data); + xml.attribute("count", data_obj->count); + if (data_obj->triggered) { + xml.append("triggered"); + data_obj->triggered = false; + } + }); + } + } +}; diff --git a/repos/libports/src/app/acpica/fixed.h b/repos/libports/src/app/acpica/fixed.h new file mode 100644 index 0000000000..fe5339dc92 --- /dev/null +++ b/repos/libports/src/app/acpica/fixed.h @@ -0,0 +1,85 @@ +/* + * \brief Handle fixed ACPI events, e.g. power button and sleep button + * \author Alexander Boettcher + */ + +/* + * Copyright (C) 2016 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +class Fixed : Acpica::Callback { + + private: + + Acpica::Reportstate * _report; + + UINT64 _power_button_count = 0; + UINT64 _sleep_button_count = 0; + bool _power_button_pressed = false; + bool _sleep_button_pressed = false; + + public: + + Fixed(void * report) + : _report(reinterpret_cast(report)) + { + if (_report) + _report->add_notify(this); + } + + static + UINT32 handle_power_button(void *context) + { + Fixed * me = reinterpret_cast(context); + + me->_power_button_count++; + + if (me->_report) { + me->_power_button_pressed = true; + me->_report->fixed_event(); + } + + return AE_OK; + } + + static + UINT32 handle_sleep_button(void *context) + { + Fixed * me = reinterpret_cast(context); + + me->_sleep_button_count++; + + if (me->_report) { + me->_sleep_button_pressed = true; + me->_report->fixed_event(); + } + + return AE_OK; + } + + void generate(Genode::Xml_generator &xml) + { + if (_power_button_count) + xml.node("power_button", [&] { + xml.attribute("value", _power_button_pressed); + xml.attribute("count", _power_button_count); + if (_power_button_pressed) { + _power_button_pressed = false; + xml.append("pressed"); + } + }); + + if (_sleep_button_count) + xml.node("sleep_button", [&] { + xml.attribute("value", _sleep_button_pressed); + xml.attribute("count", _sleep_button_count); + if (_sleep_button_pressed) { + _sleep_button_pressed = false; + xml.append("pressed"); + } + }); + } +}; diff --git a/repos/libports/src/app/acpica/lid.h b/repos/libports/src/app/acpica/lid.h new file mode 100644 index 0000000000..1f9c8037a7 --- /dev/null +++ b/repos/libports/src/app/acpica/lid.h @@ -0,0 +1,83 @@ +/* + * \brief Handle ACPI LID device + * \author Alexander Boettcher + * + */ + +/* + * Copyright (C) 2016 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +class Lid : Acpica::Callback { + + private: + + Acpica::Reportstate * _report; + UINT64 _lid_state = 0; + UINT64 _lid_count = 0; + + public: + + Lid(void * report) + : _report(reinterpret_cast(report)) + { + if (_report) + _report->add_notify(this); + } + + void handle(ACPI_HANDLE lid, UINT32 value) + { + Acpica::Buffer onoff; + ACPI_STATUS res = AcpiEvaluateObjectTyped(lid, ACPI_STRING("_LID"), + nullptr, &onoff, + ACPI_TYPE_INTEGER); + if (ACPI_FAILURE(res)) { + PERR("failed - '%s' res=0x%x _PSR", __func__, res); + return; + } + + PINF("%s - lid (%u)", + onoff.object.Integer.Value ? "open " : "closed ", + value); + + _lid_state = onoff.object.Integer.Value; + _lid_count++; + + if (_report) + _report->lid_event(); + } + + static ACPI_STATUS detect(ACPI_HANDLE lid, UINT32, void * report, void **) + { + Lid * obj = new (Genode::env()->heap()) Lid(report); + + ACPI_STATUS res = AcpiInstallNotifyHandler (lid, ACPI_DEVICE_NOTIFY, + handler, obj); + if (ACPI_FAILURE(res)) { + PDBG("failed - %s res=0x%x LID adapter", __func__, res); + delete obj; + return AE_OK; + } + + PINF("detected - lid"); + + handler(lid, 0, obj); + + return AE_OK; + } + + void generate(Genode::Xml_generator &xml) + { + xml.node("lid", [&] { + xml.attribute("value", _lid_state); + xml.attribute("count", _lid_count); + if (_lid_state) + xml.append("open"); + else + xml.append("closed"); + }); + } +}; diff --git a/repos/libports/src/app/acpica/os.cc b/repos/libports/src/app/acpica/os.cc new file mode 100644 index 0000000000..2b39a5b198 --- /dev/null +++ b/repos/libports/src/app/acpica/os.cc @@ -0,0 +1,277 @@ +/* + * \brief Example app to utilize ACPICA library + * \author Alexander Boettcher + */ + +/* + * Copyright (C) 2016 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +extern "C" { +#include "acpi.h" +#include "accommon.h" +#include "acevents.h" +#include "acnamesp.h" +} + +namespace Acpica { + struct Main; + struct Statechange; + class Reportstate; +}; + +#include "util.h" +#include "reporter.h" +#include "fixed.h" +#include "ac.h" +#include "lid.h" +#include "sb.h" +#include "ec.h" + + +static void init_acpica(Acpica::Reportstate *report) { + + /* enable debugging: */ + /* AcpiDbgLevel |= ACPI_LV_IO | ACPI_LV_INTERRUPTS | ACPI_LV_INIT_NAMES; */ + + ACPI_STATUS status = AcpiInitializeSubsystem(); + if (status != AE_OK) { + PERR("%s:%u failed %u", __func__, __LINE__, status); + return; + } + + status = AcpiInitializeTables(nullptr, 0, true); + if (status != AE_OK) { + PERR("%s:%u failed %u", __func__, __LINE__, status); + return; + } + + status = AcpiLoadTables(); + if (status != AE_OK) { + PERR("%s:%u failed %u", __func__, __LINE__, status); + return; + } + + status = AcpiEnableSubsystem(ACPI_FULL_INITIALIZATION); + if (status != AE_OK) { + PERR("%s:%u failed %u", __func__, __LINE__, status); + return; + } + + status = AcpiInitializeObjects(ACPI_NO_DEVICE_INIT); + if (status != AE_OK) { + PERR("%s:%u failed %u", __func__, __LINE__, status); + return; + } + + /* Embedded controller */ + status = AcpiGetDevices(ACPI_STRING("PNP0C09"), Ec::detect, report, nullptr); + if (status != AE_OK) { + PERR("%s:%u failed %u", __func__, __LINE__, status); + return; + } + + status = AcpiInitializeObjects(ACPI_FULL_INITIALIZATION); + if (status != AE_OK) { + PERR("%s:%u failed %u", __func__, __LINE__, status); + return; + } + + status = AcpiUpdateAllGpes(); + if (status != AE_OK) { + PERR("%s:%u failed %u", __func__, __LINE__, status); + return; + } + + status = AcpiEnableAllRuntimeGpes(); + if (status != AE_OK) { + PERR("%s:%u failed %u", __func__, __LINE__, status); + return; + } + + /* note: ACPI_EVENT_PMTIMER claimed by nova kernel - not usable by us */ + Fixed * acpi_fixed = new (Genode::env()->heap()) Fixed(report); + + status = AcpiInstallFixedEventHandler(ACPI_EVENT_POWER_BUTTON, + Fixed::handle_power_button, + acpi_fixed); + if (status != AE_OK) + PINF("failed - power button registration - error=%u", status); + + status = AcpiInstallFixedEventHandler(ACPI_EVENT_SLEEP_BUTTON, + Fixed::handle_sleep_button, + acpi_fixed); + if (status != AE_OK) + PINF("failed - sleep button registration - error=%u", status); + + + /* AC Adapters and Power Source Objects */ + status = AcpiGetDevices(ACPI_STRING("ACPI0003"), Ac::detect, report, nullptr); + if (status != AE_OK) { + PERR("%s:%u failed %u", __func__, __LINE__, status); + return; + } + + /* Smart battery control devices */ + status = AcpiGetDevices(ACPI_STRING("PNP0C0A"), Battery::detect, report, nullptr); + if (status != AE_OK) { + PERR("%s:%u failed %u", __func__, __LINE__, status); + return; + } + + /* LID device */ + status = AcpiGetDevices(ACPI_STRING("PNP0C0D"), Lid::detect, report, nullptr); + if (status != AE_OK) { + PERR("%s:%u failed %u", __func__, __LINE__, status); + return; + } +} + +struct Acpica::Statechange +{ + Genode::Signal_handler _dispatcher; + Genode::Attached_rom_dataspace _system_state; + bool _enable_reset; + bool _enable_poweroff; + + Statechange(Genode::Entrypoint &ep, bool reset, bool poweroff) + : + _dispatcher(ep, *this, &Statechange::state_changed), + _system_state("system"), + _enable_reset(reset), _enable_poweroff(poweroff) + { + _system_state.sigh(_dispatcher); + + state_changed(); + } + + void state_changed() { + + _system_state.update(); + + if (!_system_state.is_valid()) return; + + Genode::Xml_node system(_system_state.local_addr(), + _system_state.size()); + + Genode::String<32> state; + system.attribute("state").value<32>(&state); + + if (_enable_poweroff && state == "poweroff") { + ACPI_STATUS res0 = AcpiEnterSleepStatePrep(5); + ACPI_STATUS res1 = AcpiEnterSleepState(5); + PERR("system poweroff failed - res=0x%x,0x%x", res0, res1); + return; + } + + if (_enable_reset && state == "reset") { + ACPI_STATUS res = AE_OK; + try { + res = AcpiReset(); + } catch (...) { } + + Genode::uint64_t const space_addr = AcpiGbl_FADT.ResetRegister.Address; + PERR("system reset failed - err=%x, reset=%u, spaceid=0x%x, " + "addr=0x%llx", res, + !!(AcpiGbl_FADT.Flags & ACPI_FADT_RESET_REGISTER), + AcpiGbl_FADT.ResetRegister.SpaceId, space_addr); + } + } +}; + +struct Acpica::Main { + + Genode::Signal_handler _sci_irq; + Genode::Lazy_volatile_object _sci_conn; + + Acpica::Reportstate * _report = nullptr; + + static struct Irq_handler { + UINT32 irq; + ACPI_OSD_HANDLER handler; + void *context; + } irq_handler; + + Main(Genode::Env &env) + : + _sci_irq(env.ep(), *this, &Main::acpi_irq) + { + bool enable_reset = Genode::config()->xml_node().attribute_value("reset", false); + bool enable_poweroff = Genode::config()->xml_node().attribute_value("poweroff", false); + bool enable_report = Genode::config()->xml_node().attribute_value("report", false); + + if (enable_report) + _report = new (Genode::env()->heap()) Acpica::Reportstate(); + + init_acpica(_report); + + if (enable_report) + _report->enable(); + + if (enable_reset || enable_poweroff) + new (Genode::env()->heap()) Acpica::Statechange(env.ep(), enable_reset, + enable_poweroff); + + /* setup IRQ */ + if (irq_handler.handler) { + _sci_conn.construct(irq_handler.irq); + + PINF("SCI IRQ: %u", irq_handler.irq); + + _sci_conn->sigh(_sci_irq); + _sci_conn->ack_irq(); + } else + PWRN("no IRQ handling available"); + + } + + void acpi_irq() + { + if (!irq_handler.handler) + return; + + UINT32 res = irq_handler.handler(irq_handler.context); + + _sci_conn->ack_irq(); + + AcpiOsWaitEventsComplete(); + + if (_report) + _report->generate_report(); + + if (res == ACPI_INTERRUPT_HANDLED) + return; + } +}; + +struct Acpica::Main::Irq_handler Acpica::Main::irq_handler; + + +ACPI_STATUS AcpiOsInstallInterruptHandler(UINT32 irq, ACPI_OSD_HANDLER handler, + void *context) +{ + Acpica::Main::irq_handler.irq = irq; + Acpica::Main::irq_handler.handler = handler; + Acpica::Main::irq_handler.context = context; + return AE_OK; +} + + +Genode::size_t Component::stack_size() { return 4*1024*sizeof(Genode::addr_t); } +void Component::construct(Genode::Env &env) { static Acpica::Main main(env); } diff --git a/repos/libports/src/app/acpica/printf.cc b/repos/libports/src/app/acpica/printf.cc new file mode 100644 index 0000000000..dc7ab66099 --- /dev/null +++ b/repos/libports/src/app/acpica/printf.cc @@ -0,0 +1,17 @@ +/* + * Copyright (C) 2016 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include + +extern "C" +void AcpiOsPrintf (const char *fmt, ...) +{ } + +extern "C" +void AcpiOsVprintf (const char *fmt, va_list va) +{ } + diff --git a/repos/libports/src/app/acpica/reporter.h b/repos/libports/src/app/acpica/reporter.h new file mode 100644 index 0000000000..8a4e6ceddd --- /dev/null +++ b/repos/libports/src/app/acpica/reporter.h @@ -0,0 +1,109 @@ +/* + * \brief Generate xml reports of various ACPI devices, e.g. + * Lid, Embedded Controller (EC), AC Adapter, + * Smart Battery (SB) and ACPI fixed events (power, sleep button) + * \author Alexander Boettcher + */ + +/* + * Copyright (C) 2016 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +class Ac; +class Battery; +class Ec; +class Fixed; +class Lid; + +class Acpica::Reportstate { + + private: + + Genode::Reporter _reporter_lid { "acpi_lid" }; + Genode::Reporter _reporter_ac { "acpi_ac" }; + Genode::Reporter _reporter_sb { "acpi_battery" }; + Genode::Reporter _reporter_ec { "acpi_ec" }; + Genode::Reporter _reporter_fix { "acpi_fixed" }; + + bool _changed_lid = false; + bool _changed_ac = false; + bool _changed_sb = false; + bool _changed_ec = false; + bool _changed_fixed = false; + + Genode::List > _list_sb; + Genode::List > _list_ec; + Genode::List > _list_ac; + Callback * _fixed; + Callback * _lid; + + public: + + Reportstate() { } + + void add_notify(Acpica::Callback * s) { _list_sb.insert(s); } + void add_notify(Acpica::Callback * f) { _fixed = f; } + void add_notify(Acpica::Callback * l) { _lid = l; } + void add_notify(Acpica::Callback * e) { _list_ec.insert(e); } + void add_notify(Acpica::Callback * a) { _list_ac.insert(a); } + + void enable() { + _reporter_ac.enabled(true); + _reporter_ec.enabled(true); + _reporter_sb.enabled(true); + _reporter_lid.enabled(true); + _reporter_fix.enabled(true); + } + + void battery_event() { _changed_sb = true; } + void ec_event() { _changed_ec = true; } + void fixed_event() { _changed_fixed = true; } + void lid_event() { _changed_lid = true; } + void ac_event() { _changed_ac = true; battery_event(); } + + void generate_report() + { + if (_changed_lid) { + _changed_lid = false; + if (_lid) + Genode::Reporter::Xml_generator xml(_reporter_lid, [&] () { + _lid->generate(xml); + }); + } + + if (_changed_ac) { + _changed_ac = false; + Genode::Reporter::Xml_generator xml(_reporter_ac, [&] () { + for (Callback * ac = _list_ac.first(); ac; ac = ac->next()) + xml.node("ac", [&] { ac->generate(xml); }); + }); + } + + if (_changed_ec) { + _changed_ec = false; + Genode::Reporter::Xml_generator xml(_reporter_ec, [&] () { + for (Callback * ec = _list_ec.first(); ec; ec = ec->next()) + xml.node("ec", [&] { ec->generate(xml); }); + }); + } + + if (_changed_sb) { + _changed_sb = false; + Genode::Reporter::Xml_generator xml(_reporter_sb, [&] () { + for (Callback * sb = _list_sb.first(); sb; sb = sb->next()) + xml.node("sb", [&] { sb->generate(xml); }); + }); + } + + if (_changed_fixed) { + _changed_fixed = false; + if (_fixed) + Genode::Reporter::Xml_generator xml(_reporter_fix, [&] () { + _fixed->generate(xml); + }); + } + } +}; diff --git a/repos/libports/src/app/acpica/sb.h b/repos/libports/src/app/acpica/sb.h new file mode 100644 index 0000000000..27fd9d3fbe --- /dev/null +++ b/repos/libports/src/app/acpica/sb.h @@ -0,0 +1,227 @@ +/* + * \brief Handle ACPI Smart Battery Subsystem devices + * \author Alexander Boettcher + * + */ + +/* + * Copyright (C) 2016 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +class Battery : Acpica::Callback { + + private: + + Acpica::Reportstate * _report; + ACPI_HANDLE _sb; + + public: + + Battery(void * report, ACPI_HANDLE sb) + : _report(reinterpret_cast(report)), _sb(sb) + { + if (_report) + _report->add_notify(this); + } + + void handle(ACPI_HANDLE sb, UINT32 value) + { + if (_report) + _report->battery_event(); + } + + static ACPI_STATUS detect(ACPI_HANDLE sb, UINT32, void *report, void **) + { + Battery * dev_obj = new (Genode::env()->heap()) Battery(report, sb); + + ACPI_STATUS res = AcpiInstallNotifyHandler (sb, ACPI_DEVICE_NOTIFY, + handler, dev_obj); + if (ACPI_FAILURE(res)) { + PERR("failed - '%s' res=0x%x", __func__, res); + delete dev_obj; + return AE_OK; + } + + Acpica::Buffer battery_name; + AcpiGetName (sb, ACPI_SINGLE_NAME, &battery_name); + if (ACPI_FAILURE(res)) { + PERR("failed - '%s' battery name res=0x%x", __func__, res); + return AE_OK; + } + + Acpica::Buffer sta; + res = AcpiEvaluateObjectTyped(sb, ACPI_STRING("_STA"), nullptr, &sta, + ACPI_TYPE_INTEGER); + if (ACPI_FAILURE(res)) { + PERR("failed - '%s' _STA res=0x%x", __func__, res); + return AE_OK; + } + + /* ACPI spec - 10.2.2.1 _BIF (Battery Information) */ + Acpica::Buffer battery; + res = AcpiEvaluateObjectTyped(sb, ACPI_STRING("_BIF"), + nullptr, &battery, + ACPI_TYPE_PACKAGE); + ACPI_OBJECT * obj = reinterpret_cast(battery.object); + if (ACPI_FAILURE(res) || !obj) { + PERR("failed - '%s' _BIF res=0x%x", __func__, res); + return AE_OK; + } + + unsigned long long const val = sta.object.Integer.Value; + + if (obj->Package.Count < 13 || + obj->Package.Elements[0].Type != ACPI_TYPE_INTEGER || + obj->Package.Elements[12].Type != ACPI_TYPE_STRING || + obj->Package.Elements[11].Type != ACPI_TYPE_STRING || + obj->Package.Elements[10].Type != ACPI_TYPE_STRING || + obj->Package.Elements[9].Type != ACPI_TYPE_STRING) + { + PINF("detected - battery '%s' - unknown state (0x%llx%s)", + battery_name.object, val, + val & ACPI_STA_BATTERY_PRESENT ? "" : "(not present)"); + return AE_OK; + } + + PINF("detected - battery '%s' type='%s' OEM='%s' state=0x%llx%s " + "model='%s' serial='%s'", + battery_name.object, + obj->Package.Elements[11].String.Pointer, + obj->Package.Elements[12].String.Pointer, + val, val & ACPI_STA_BATTERY_PRESENT ? "" : "(not present)", + obj->Package.Elements[10].String.Pointer, + obj->Package.Elements[9].String.Pointer); + + return AE_OK; + } + + void generate(Genode::Xml_generator &xml) + { + info(xml); + status(xml); + } + + void info(Genode::Xml_generator &xml) + { + /* ACPI spec - 10.2.2.1 _BIF (Battery Information) */ + Acpica::Buffer battery; + ACPI_STATUS res = AcpiEvaluateObjectTyped(_sb, ACPI_STRING("_BIF"), + nullptr, &battery, + ACPI_TYPE_PACKAGE); + ACPI_OBJECT * obj = reinterpret_cast(battery.object); + if (ACPI_FAILURE(res) || !obj || obj->Package.Count != 13) { + PERR("failed - '%s' _BIF res=0x%x", __func__, res); + return; + } + + Acpica::Buffer battery_name; + AcpiGetName (_sb, ACPI_SINGLE_NAME, &battery_name); + if (ACPI_FAILURE(res)) + xml.node("name", [&] { xml.append("unknown"); }); + else + xml.node("name", [&] { xml.append(battery_name.object); }); + + const char * node_name[] = { + "powerunit", "design_capacity", "last_full_capacity", + "technology", "voltage", "warning_capacity", "low_capacity", + "granularity1", "granularity2", "serial", "model", "type", + "oem" + }; + + if (sizeof(node_name) / sizeof(node_name[0]) != obj->Package.Count) + return; + + for (unsigned i = 0; i < 9; i++) { + ACPI_OBJECT * v = &obj->Package.Elements[i]; + + xml.node(node_name[i], [&] { + if (v->Type != ACPI_TYPE_INTEGER) { + xml.append("unknown"); + return; + } + + xml.attribute("value", v->Integer.Value); + + if (i == 0) + xml.append(v->Integer.Value == 0 ? "mW/mWh" : + v->Integer.Value == 1 ? "mA/mAh" : + "unknown"); + if (i == 3) + xml.append(v->Integer.Value == 0 ? "primary" : + v->Integer.Value == 1 ? "secondary" : + "unknown"); + }); + } + + for (unsigned i = 9; i < obj->Package.Count; i++) { + ACPI_OBJECT * v = &obj->Package.Elements[i]; + + xml.node(node_name[i], [&] { + + if (v->Type != ACPI_TYPE_STRING) + return; + + xml.append(v->String.Pointer); + }); + } + } + + void status(Genode::Xml_generator &xml) + { + /* 10.2.2.6 _BST (Battery Status) */ + Acpica::Buffer dynamic; + ACPI_STATUS res = AcpiEvaluateObjectTyped(_sb, ACPI_STRING("_BST"), + nullptr, &dynamic, + ACPI_TYPE_PACKAGE); + ACPI_OBJECT * obj = reinterpret_cast(dynamic.object); + if (ACPI_FAILURE(res) || !obj || + obj->Package.Count != 4) { + PERR("failed - '%s' _BST res=0x%x", __func__, res); + return; + } + + Acpica::Buffer sta; + res = AcpiEvaluateObjectTyped(_sb, ACPI_STRING("_STA"), nullptr, + &sta, ACPI_TYPE_INTEGER); + if (ACPI_FAILURE(res)) { + xml.node("status", [&] { xml.append("unknown"); }); + } else + xml.node("status", [&] { + xml.attribute("value", sta.object.Integer.Value); + /* see "6.3.7 _STA" for more human readable decoding */ + if (!(sta.object.Integer.Value & ACPI_STA_BATTERY_PRESENT)) + xml.append("battery not present"); + }); + + const char * node_name[] = { + "state", "present_rate", "remaining_capacity", + "present_voltage" + }; + + if (sizeof(node_name) / sizeof(node_name[0]) != obj->Package.Count) + return; + + for (unsigned i = 0; i < obj->Package.Count; i++) { + ACPI_OBJECT * v = &obj->Package.Elements[i]; + + xml.node(node_name[i], [&] { + if (v->Type != ACPI_TYPE_INTEGER) { + xml.append("unknown"); + return; + } + + xml.attribute("value", v->Integer.Value); + + if (i != 0) + return; + + if (v->Integer.Value & 0x1) xml.append("discharging"); + if (v->Integer.Value & 0x2) xml.append("charging"); + if (v->Integer.Value & 0x4) xml.append("critical low"); + }); + } + } +}; diff --git a/repos/libports/src/app/acpica/target.inc b/repos/libports/src/app/acpica/target.inc new file mode 100644 index 0000000000..0abca8b5b1 --- /dev/null +++ b/repos/libports/src/app/acpica/target.inc @@ -0,0 +1,8 @@ +REQUIRES := x86 + +LIBS += base acpica config + +CC_OPT += -Wno-unused-function -Wno-unused-variable +CC_C_OPT += -DACPI_LIBRARY + +INC_DIR += $(call select_from_ports,acpica)/src/lib/acpica/source/include diff --git a/repos/libports/src/app/acpica/target.mk b/repos/libports/src/app/acpica/target.mk new file mode 100644 index 0000000000..7a5e7880e0 --- /dev/null +++ b/repos/libports/src/app/acpica/target.mk @@ -0,0 +1,4 @@ +TARGET = acpica +SRC_CC = os.cc printf.cc + +include $(PRG_DIR)/target.inc diff --git a/repos/libports/src/app/acpica/util.h b/repos/libports/src/app/acpica/util.h new file mode 100644 index 0000000000..dfeff7cbc1 --- /dev/null +++ b/repos/libports/src/app/acpica/util.h @@ -0,0 +1,31 @@ +namespace Acpica { + template class Buffer; + template class Callback; +} + +template +class Acpica::Buffer : public ACPI_BUFFER +{ + public: + T object; + Buffer() { + Pointer = &object; + Length = sizeof(object); + } + +} __attribute__((packed)); + +template +class Acpica::Callback : public Genode::List >::Element +{ + public: + + static void handler(ACPI_HANDLE h, UINT32 value, void * context) + { + reinterpret_cast(context)->handle(h, value); + } + + void generate(Genode::Xml_generator &xml) { + reinterpret_cast(this)->generate(xml); + } +}; diff --git a/repos/libports/src/lib/acpica/acpica.patch b/repos/libports/src/lib/acpica/acpica.patch new file mode 100644 index 0000000000..e0fb454a99 --- /dev/null +++ b/repos/libports/src/lib/acpica/acpica.patch @@ -0,0 +1,76 @@ ++++ src/lib/acpica/source/include/platform/acenv.h +@@ -222,7 +222,15 @@ + + /* Unknown environment */ + +-#error Unknown target environment ++#ifdef __x86_64__ ++#define ACPI_MACHINE_WIDTH 64 ++#else ++#define ACPI_MACHINE_WIDTH 32 ++#define ACPI_USE_NATIVE_DIVIDE ++#endif ++ ++/* required for va_arg - otherwise below some alternative is defined */ ++#include + #endif + + /*! [End] no source code translation !*/ ++++ src/lib/acpica/source/components/events/evevent.c +@@ -188,6 +188,9 @@ + UINT32 i; + ACPI_STATUS Status; + ++ /* read enabled events by kernel and don't disable them */ ++ UINT32 FixedEnable; ++ (void) AcpiHwRegisterRead (ACPI_REGISTER_PM1_ENABLE, &FixedEnable); + + /* + * Initialize the structure that keeps track of fixed event handlers and +@@ -198,6 +201,12 @@ + AcpiGbl_FixedEventHandlers[i].Handler = NULL; + AcpiGbl_FixedEventHandlers[i].Context = NULL; + ++ if (FixedEnable & AcpiGbl_FixedEventInfo[i].EnableBitMask) ++ { ++ AcpiOsPrintf (" Genode: SKIP disabling event '%u'(%x) - enabled by kernel!\n", i, AcpiGbl_FixedEventInfo[i].EnableBitMask); ++ continue; ++ } ++ + /* Disable the fixed event */ + + if (AcpiGbl_FixedEventInfo[i].EnableRegisterId != 0xFF) +@@ -257,10 +266,13 @@ + */ + for (i = 0; i < ACPI_NUM_FIXED_EVENTS; i++) + { +- /* Both the status and enable bits must be on for this event */ +- +- if ((FixedStatus & AcpiGbl_FixedEventInfo[i].StatusBitMask) && +- (FixedEnable & AcpiGbl_FixedEventInfo[i].EnableBitMask)) ++ /* kernel 'signals' the fixed event by disabling it in the enable ++ * register. Check for events, that have registered handlers and that ++ * are disabled in the enable register. If found, re-enable event. ++ */ ++ if (AcpiGbl_FixedEventInfo[i].EnableBitMask && ++ AcpiGbl_FixedEventHandlers[i].Handler && ++ !(FixedEnable & AcpiGbl_FixedEventInfo[i].EnableBitMask)) + { + /* + * Found an active (signalled) event. Invoke global event +@@ -303,12 +315,10 @@ + + ACPI_FUNCTION_ENTRY (); + +- +- /* Clear the status bit */ +- ++ /* Re-enable event - kernel disabled it */ + (void) AcpiWriteBitRegister ( +- AcpiGbl_FixedEventInfo[Event].StatusRegisterId, +- ACPI_CLEAR_STATUS); ++ AcpiGbl_FixedEventInfo[Event].EnableRegisterId, ++ ACPI_ENABLE_EVENT); + + /* + * Make sure that a handler exists. If not, report an error diff --git a/repos/libports/src/lib/acpica/iomem.cc b/repos/libports/src/lib/acpica/iomem.cc new file mode 100644 index 0000000000..e1f569e966 --- /dev/null +++ b/repos/libports/src/lib/acpica/iomem.cc @@ -0,0 +1,291 @@ +/* + * \brief I/O memory backend for ACPICA library + * \author Alexander Boettcher + * + */ + +/* + * Copyright (C) 2016 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include +#include +#include + +#include +#include + +extern "C" { +#include "acpi.h" +#include "acpiosxf.h" +} + +#define FAIL(retval) \ + { \ + PERR("%s:%u called - dead", __func__, __LINE__); \ + Genode::Lock lock; \ + while (1) lock.lock(); \ + return retval; \ + } + +namespace Acpica { +class Io_mem; +}; + +class Acpica::Io_mem +{ + private: + + ACPI_PHYSICAL_ADDRESS _phys; + ACPI_SIZE _size; + Genode::uint8_t * _virt; + Genode::Io_mem_session_capability _io_mem; + unsigned _ref; + + static Acpica::Io_mem _ios[16]; + + public: + + bool valid() const { return _io_mem.valid(); } + bool refs() const { return _ref != ~0U; } + bool contains_virt (const Genode::uint8_t * v, const ACPI_SIZE s) const + { + return _virt <= v && v + s <= _virt + _size; + } + bool contains_phys (const ACPI_PHYSICAL_ADDRESS p, const ACPI_SIZE s) const + { + return _phys <= p && p + s <= _phys + _size; + } + + Genode::addr_t to_virt(ACPI_PHYSICAL_ADDRESS p) { + _ref ++; + return reinterpret_cast(_virt + (p - _phys)); + } + + bool ref_dec() { return --_ref; } + + template + static void apply_to_all(FUNC const &func = [] () { } ) + { + for (unsigned i = 0; i < sizeof(_ios) / sizeof(_ios[0]); i++) + func(_ios[i]); + } + + void invalidate(ACPI_SIZE s) + { + if (_io_mem.valid() && refs()) + Genode::env()->parent()->close(_io_mem); + + ACPI_PHYSICAL_ADDRESS const p = _phys; + + _phys = _size = 0; + _virt = nullptr; + _io_mem = Genode::Io_mem_session_capability(); + + if (refs()) + return; + + _ref = 0; + + /** + * Continue in order to look for the larger entry that replaced + * this one. Required to decrement ref count. + */ + apply_to_all([&] (Acpica::Io_mem &io_mem) { + if (!io_mem.contains_phys(p, s) || !io_mem.refs()) + return; + + if (io_mem.ref_dec()) + return; + + io_mem.invalidate(s); + }); + } + + template + static Genode::addr_t apply_u(FUNC const &func = [] () { } ) + { + for (unsigned i = 0; i < sizeof(_ios) / sizeof(_ios[0]); i++) + { + Genode::addr_t r = func(_ios[i]); + if (r) return r; + } + return 0UL; + } + + template + static Acpica::Io_mem * apply_p(FUNC const &func = [] () { } ) + { + for (unsigned i = 0; i < sizeof(_ios) / sizeof(_ios[0]); i++) + { + Acpica::Io_mem * r = func(_ios[i]); + if (r) return r; + } + return nullptr; + } + + static Acpica::Io_mem * allocate(ACPI_PHYSICAL_ADDRESS p, ACPI_SIZE s, + unsigned r) + { + return Acpica::Io_mem::apply_p([&] (Acpica::Io_mem &io_mem) { + if (io_mem.valid()) + return reinterpret_cast(0); + + io_mem._phys = p & ~0xFFFUL; + io_mem._size = Genode::align_addr(p + s - io_mem._phys, 12); + io_mem._ref = r; + io_mem._virt = 0; + + Genode::Io_mem_connection io(io_mem._phys, io_mem._size); + io.on_destruction(Genode::Io_mem_connection::KEEP_OPEN); + io_mem._io_mem = io; + + return &io_mem; + }); + } + + static Genode::addr_t insert(ACPI_PHYSICAL_ADDRESS p, ACPI_SIZE s) + { + Acpica::Io_mem * io_mem = allocate(p, s, 1); + if (!io_mem) + return 0UL; + + io_mem->_virt = Genode::env()->rm_session()->attach(Genode::Io_mem_session_client(io_mem->_io_mem).dataspace(), io_mem->_size); + + return reinterpret_cast(io_mem->_virt); + } + + Genode::addr_t pre_expand(ACPI_PHYSICAL_ADDRESS p, ACPI_SIZE s) + { + Genode::env()->parent()->close(_io_mem); + + Genode::addr_t xsize = _phys - p + _size; + if (!allocate(p, xsize, _ref)) + FAIL(0) + + return _expand(p, s); + } + + Genode::addr_t post_expand(ACPI_PHYSICAL_ADDRESS p, ACPI_SIZE s) + { + Genode::env()->parent()->close(_io_mem); + + ACPI_SIZE xsize = p + s - _phys; + if (!allocate(_phys, xsize, _ref)) + FAIL(0) + + return _expand(p, s); + } + + Genode::addr_t _expand(ACPI_PHYSICAL_ADDRESS const p, ACPI_SIZE const s) + { + /* mark this element as a stale reference */ + _ref = ~0U; + + /* find new created entry */ + Genode::addr_t res = Acpica::Io_mem::apply_u([&] (Acpica::Io_mem &io_mem) { + if (!io_mem.valid() || !io_mem.refs() || + !io_mem.contains_phys(p, s)) + return 0UL; + + Genode::Io_mem_dataspace_capability const io_ds = Genode::Io_mem_session_client(io_mem._io_mem).dataspace(); + + /* re-attach mem of stale entries partially using this iomem */ + unsigned stale_count = 0; + Acpica::Io_mem::apply_to_all([&] (Acpica::Io_mem &io2) { + if (!io2.valid() || !io_mem.contains_phys(io2._phys, 0)) + return; + + if (io2.refs()) + return; + + Genode::addr_t off_phys = io2._phys - io_mem._phys; + stale_count ++; + + io2._io_mem = io_mem._io_mem; + Genode::addr_t virt = reinterpret_cast(io2._virt); + + Genode::env()->rm_session()->attach_at(io_ds, virt, + io2._size, off_phys); + }); + + /** + * In case the old *this* entry was solely a placeholder for + * other stale entries, the new one *io_mem* becomes just + * a placeholder too -> meaning ref must be increased by 1. + */ + if (io_mem._ref == stale_count) + io_mem._ref ++; + + if (io_mem._virt) + FAIL(0UL); + + /* attach whole memory */ + io_mem._virt = Genode::env()->rm_session()->attach(io_ds); + + return io_mem.to_virt(p); + }); + + /* should never happen */ + if (!res) + FAIL(0) + + return res; + } +}; + +Acpica::Io_mem Acpica::Io_mem::_ios[16]; + + +void * AcpiOsMapMemory (ACPI_PHYSICAL_ADDRESS phys, ACPI_SIZE size) +{ + Genode::addr_t virt = Acpica::Io_mem::apply_u([&] (Acpica::Io_mem &io_mem) { + if (!io_mem.valid() || !io_mem.refs()) + return 0UL; + + if (io_mem.contains_phys(phys, size)) + /* we have already a mapping in which the request fits */ + return io_mem.to_virt(phys); + + if (io_mem.contains_phys(phys + 1, 0)) + /* phys is within region but end is outside region */ + return io_mem.post_expand(phys, size); + + if (io_mem.contains_phys(phys + size - 1, 0)) + /* phys starts before region and end is within region */ + return io_mem.pre_expand(phys, size); + + return 0UL; + }); + + if (virt) + return reinterpret_cast(virt); + + virt= Acpica::Io_mem::insert(phys, size); + if (virt) + return reinterpret_cast(virt + (phys & 0xfffU)); + + FAIL(nullptr) +} + +void AcpiOsUnmapMemory (void * ptr, ACPI_SIZE size) +{ + Genode::uint8_t const * virt = reinterpret_cast(ptr); + + if (Acpica::Io_mem::apply_u([&] (Acpica::Io_mem &io_mem) { + if (!io_mem.valid() || !io_mem.contains_virt(virt, size)) + return 0; + + if (io_mem.refs() && io_mem.ref_dec()) + return 1; + + io_mem.invalidate(size); + return 1; + })) + return; + + FAIL() +} diff --git a/repos/libports/src/lib/acpica/osl.cc b/repos/libports/src/lib/acpica/osl.cc new file mode 100644 index 0000000000..15f5b6c6d0 --- /dev/null +++ b/repos/libports/src/lib/acpica/osl.cc @@ -0,0 +1,289 @@ +/* + * \brief OS specific backend for ACPICA library + * \author Alexander Boettcher + * + */ + +/* + * Copyright (C) 2016 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include +#include +#include + +#include +#include + +extern "C" { +#include "acpi.h" +#include "acpiosxf.h" +} + +#define FAIL(retval) \ + { \ + PERR("%s:%u called - dead", __func__, __LINE__); \ + Genode::Lock lock; \ + while (1) lock.lock(); \ + return retval; \ + } + +ACPI_STATUS AcpiOsPredefinedOverride (const ACPI_PREDEFINED_NAMES *pre, + ACPI_STRING *newobj) +{ + *newobj = nullptr; + return AE_OK; +} + + +void * AcpiOsAllocate (ACPI_SIZE size) { + return Genode::env()->heap()->alloc(size); } + + +void AcpiOsFree (void *ptr) +{ + if (Genode::env()->heap()->need_size_for_free()) + PWRN("%s called - warning - ptr=%p", __func__, ptr); + + Genode::env()->heap()->free(ptr, 0); +} + +ACPI_STATUS AcpiOsCreateLock (ACPI_SPINLOCK *spin_lock) +{ + *spin_lock = new (Genode::env()->heap()) Genode::Lock(); + return AE_OK; +} + +ACPI_CPU_FLAGS AcpiOsAcquireLock (ACPI_SPINLOCK h) +{ + Genode::Lock *lock = reinterpret_cast(h); + lock->lock(); + + return AE_OK; +} + +void AcpiOsReleaseLock (ACPI_SPINLOCK h, ACPI_CPU_FLAGS flags) +{ + Genode::Lock *lock = reinterpret_cast(h); + + if (flags != AE_OK) + PWRN("warning - unknown flags in %s", __func__); + + lock->unlock(); + + return; +} + +ACPI_STATUS AcpiOsCreateSemaphore (UINT32 max, UINT32 initial, + ACPI_SEMAPHORE *sem) +{ + *sem = new (Genode::env()->heap()) Genode::Semaphore(initial); + return AE_OK; +} + +ACPI_STATUS AcpiOsWaitSemaphore(ACPI_SEMAPHORE h, UINT32 units, + UINT16 timeout_ms) +{ + Genode::Semaphore *sem = reinterpret_cast(h); + + if (!units) + FAIL(AE_BAD_PARAMETER) + + /** + * Timeouts not supported yet ... + * == 0 means - try and don't block - we're single threaded - ignore + * == 0xfff means - wait endless - fine + */ + if (0 < timeout_ms && timeout_ms < 0xffff) + FAIL(AE_BAD_PARAMETER) + + /* timeout == forever case */ + while (units) { + sem->down(); + units--; + } + + return AE_OK; +} + +ACPI_STATUS AcpiOsSignalSemaphore (ACPI_SEMAPHORE h, UINT32 units) +{ + Genode::Semaphore *sem = reinterpret_cast(h); + + while (units) { + sem->up(); + units--; + } + + return AE_OK; +} + +ACPI_STATUS AcpiOsDeleteSemaphore (ACPI_SEMAPHORE) + FAIL(AE_BAD_PARAMETER) + +ACPI_THREAD_ID AcpiOsGetThreadId (void) { + return reinterpret_cast(Genode::Thread::myself()); } + +ACPI_STATUS AcpiOsTableOverride (ACPI_TABLE_HEADER *x, ACPI_TABLE_HEADER **y) +{ + *y = nullptr; + return AE_OK; +} + +ACPI_STATUS AcpiOsPhysicalTableOverride (ACPI_TABLE_HEADER *x, + ACPI_PHYSICAL_ADDRESS *y, UINT32 *z) +{ + *y = 0; + return AE_OK; +} + +ACPI_STATUS AcpiOsReadPort (ACPI_IO_ADDRESS port, UINT32 *value, UINT32 width) +{ + if (width % 8 != 0) + FAIL(AE_BAD_PARAMETER) + + /* the I/O port may be owned by drivers, which will cause exceptions */ + try { + unsigned const bytes = width / 8; + Genode::Io_port_connection io_port(port, bytes); + + switch (bytes) { + case 1 : + *value = io_port.inb(port); + break; + case 2 : + *value = io_port.inw(port); + break; + case 4 : + *value = io_port.inl(port); + break; + default: + FAIL(AE_BAD_PARAMETER) + } + } catch (Genode::Parent::Service_denied) { + return AE_BAD_PARAMETER; + } + + return AE_OK; +} + +ACPI_STATUS AcpiOsWritePort (ACPI_IO_ADDRESS port, UINT32 value, UINT32 width) +{ + if (width % 8 != 0) + FAIL(AE_BAD_PARAMETER) + + /* the I/O port may be owned by drivers, which will cause exceptions */ + try { + unsigned const bytes = width / 8; + Genode::Io_port_connection io_port(port, bytes); + + switch (bytes) { + case 1 : + io_port.outb(port, value); + break; + case 2 : + io_port.outw(port, value); + break; + case 4 : + io_port.outl(port, value); + break; + default: + FAIL(AE_BAD_PARAMETER) + } + } catch (Genode::Parent::Service_denied) { + return AE_BAD_PARAMETER; + } + + return AE_OK; +} + +static struct { + ACPI_EXECUTE_TYPE type; + ACPI_OSD_EXEC_CALLBACK func; + void *context; +} deferred[8]; + +ACPI_STATUS AcpiOsExecute(ACPI_EXECUTE_TYPE type, ACPI_OSD_EXEC_CALLBACK func, + void *context) +{ + if (type == OSL_GPE_HANDLER) { + func(context); + return AE_OK; + } + + if (type != OSL_NOTIFY_HANDLER) + FAIL(AE_BAD_PARAMETER) + + for (unsigned i = 0; i < sizeof(deferred) / sizeof (deferred[0]); i++) { + if (deferred[i].func) + continue; + + deferred[i].type = type; + deferred[i].func = func; + deferred[i].context = context; + return AE_OK; + } + PERR("Queue full for deferred handlers"); + return AE_BAD_PARAMETER; +} + +void AcpiOsWaitEventsComplete() +{ + for (unsigned i = 0; i < sizeof(deferred) / sizeof (deferred[0]); i++) { + if (!deferred[i].func) + continue; + + ACPI_OSD_EXEC_CALLBACK func = deferred[i].func; + deferred[i].func = nullptr; + + func(deferred[i].context); + } +} + +void AcpiOsSleep (UINT64 sleep_ms) +{ + PDBG("%s %llu ms", __func__, sleep_ms); + + static Timer::Connection conn; + conn.msleep(sleep_ms); + return; +} + + +/******************************** + * unsupported/unused functions * + ********************************/ + +ACPI_STATUS AcpiOsSignal (UINT32, void *) + FAIL(AE_BAD_PARAMETER) + +UINT64 AcpiOsGetTimer (void) + FAIL(0) + +void AcpiOsStall (UINT32) + FAIL() + +ACPI_STATUS AcpiOsReadMemory (ACPI_PHYSICAL_ADDRESS, UINT64 *, UINT32) + FAIL(AE_BAD_PARAMETER) + +ACPI_STATUS AcpiOsWriteMemory (ACPI_PHYSICAL_ADDRESS, UINT64, UINT32) + FAIL(AE_BAD_PARAMETER) + +ACPI_STATUS AcpiOsRemoveInterruptHandler (UINT32, ACPI_OSD_HANDLER) + FAIL(AE_BAD_PARAMETER) + +ACPI_STATUS AcpiOsGetLine (char *, UINT32, UINT32 *) + FAIL(AE_BAD_PARAMETER) + +extern "C" +{ +void AcpiAhMatchUuid() FAIL() +void AcpiAhMatchHardwareId() FAIL() +void AcpiDbCommandDispatch() FAIL() +void AcpiDbSetOutputDestination() FAIL() +void MpSaveSerialInfo() FAIL() +void MpSaveGpioInfo() FAIL() +} diff --git a/repos/libports/src/lib/acpica/pci.cc b/repos/libports/src/lib/acpica/pci.cc new file mode 100644 index 0000000000..27453d2bb6 --- /dev/null +++ b/repos/libports/src/lib/acpica/pci.cc @@ -0,0 +1,137 @@ +/* + * \brief PCI specific backend for ACPICA library + * \author Alexander Boettcher + * + */ + +/* + * Copyright (C) 2016 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include +#include + +extern "C" { +#include "acpi.h" +#include "acpiosxf.h" +} + +ACPI_STATUS AcpiOsInitialize (void) +{ + /* XXX - acpi_drv uses IOMEM concurrently to us - wait until it is done */ + PINF("wait for platform drv"); + try { + Platform::Connection conn; + } catch (...) { PERR("did not get Platform connection"); } + PINF("wait for platform drv - done"); + return AE_OK; +} + +ACPI_STATUS AcpiOsReadPciConfiguration (ACPI_PCI_ID *pcidev, UINT32 reg, + UINT64 *value, UINT32 width) +{ + Platform::Connection conn; + Platform::Device_capability cap = conn.first_device(); + + while (cap.valid()) { + Platform::Device_client client(cap); + + unsigned char bus, dev, fn; + client.bus_address(&bus, &dev, &fn); + + if (pcidev->Bus == bus && pcidev->Device == dev && + pcidev->Function == fn) { + + Platform::Device_client::Access_size access_size; + switch (width) { + case 8: + access_size = Platform::Device_client::Access_size::ACCESS_8BIT; + break; + case 16: + access_size = Platform::Device_client::Access_size::ACCESS_16BIT; + break; + case 32: + access_size = Platform::Device_client::Access_size::ACCESS_32BIT; + break; + default: + PERR("%s : unsupported access size %u", __func__, width); + conn.release_device(client); + return AE_ERROR; + }; + + *value = client.config_read(reg, access_size); + + PINF("%s: %x:%x.%x reg=0x%x width=%u -> value=0x%llx", + __func__, bus, dev, fn, reg, width, *value); + + conn.release_device(client); + return AE_OK; + } + + cap = conn.next_device(cap); + + conn.release_device(client); + } + + PERR("%s unknown device - segment=%u bdf=%x:%x.%x reg=0x%x width=0x%x", + __func__, pcidev->Segment, pcidev->Bus, pcidev->Device, + pcidev->Function, reg, width); + + return AE_ERROR; +} + +ACPI_STATUS AcpiOsWritePciConfiguration (ACPI_PCI_ID *pcidev, UINT32 reg, + UINT64 value, UINT32 width) +{ + Platform::Connection conn; + Platform::Device_capability cap = conn.first_device(); + + while (cap.valid()) { + Platform::Device_client client(cap); + + unsigned char bus, dev, fn; + client.bus_address(&bus, &dev, &fn); + + if (pcidev->Bus == bus && pcidev->Device == dev && + pcidev->Function == fn) { + + Platform::Device_client::Access_size access_size; + switch (width) { + case 8: + access_size = Platform::Device_client::Access_size::ACCESS_8BIT; + break; + case 16: + access_size = Platform::Device_client::Access_size::ACCESS_16BIT; + break; + case 32: + access_size = Platform::Device_client::Access_size::ACCESS_32BIT; + break; + default: + PERR("%s : unsupported access size %u", __func__, width); + conn.release_device(client); + return AE_ERROR; + }; + + client.config_write(reg, value, access_size); + + PWRN("%s: %x:%x.%x reg=0x%x width=%u value=0x%llx", + __func__, bus, dev, fn, reg, width, value); + + conn.release_device(client); + return AE_OK; + } + + cap = conn.next_device(cap); + + conn.release_device(client); + } + + PERR("%s unknown device - segment=%u bdf=%x:%x.%x reg=0x%x width=0x%x", + __func__, pcidev->Segment, pcidev->Bus, pcidev->Device, + pcidev->Function, reg, width); + + return AE_ERROR; +} diff --git a/repos/libports/src/lib/acpica/scan_root.cc b/repos/libports/src/lib/acpica/scan_root.cc new file mode 100644 index 0000000000..e13d93f86e --- /dev/null +++ b/repos/libports/src/lib/acpica/scan_root.cc @@ -0,0 +1,100 @@ +/* + * \brief Lookup code for initial ACPI RSDP pointer + * \author Alexander Boettcher + */ + +/* + * Copyright (C) 2016 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include +#include + +extern "C" { +#include "acpi.h" +} + +namespace Genode { + class Acpi_table; +} + +class Genode::Acpi_table +{ + private: + + /* BIOS range to scan for RSDP */ + enum { BIOS_BASE = 0xe0000, BIOS_SIZE = 0x20000 }; + + /** + * Search for RSDP pointer signature in area + */ + uint8_t *_search_rsdp(uint8_t *area) + { + for (addr_t addr = 0; area && addr < BIOS_SIZE; addr += 16) + /* XXX checksum table */ + if (!memcmp(area + addr, "RSD PTR ", 8)) + return area + addr; + + return nullptr; + } + + /** + * Return 'Root System Descriptor Pointer' (ACPI spec 5.2.5.1) + */ + uint64_t _rsdp() + { + uint8_t * local = 0; + + /* try BIOS area */ + { + Genode::Attached_io_mem_dataspace io_mem(BIOS_BASE, BIOS_SIZE); + local = _search_rsdp(io_mem.local_addr()); + if (local) + return BIOS_BASE + (local - io_mem.local_addr()); + } + + /* search EBDA (BIOS addr + 0x40e) */ + try { + unsigned short base = 0; + { + Genode::Attached_io_mem_dataspace io_mem(0, 0x1000); + local = io_mem.local_addr(); + if (local) + base = (*reinterpret_cast(local + 0x40e)) << 4; + } + + if (!base) + return 0; + + Genode::Attached_io_mem_dataspace io_mem(base, 1024); + local = _search_rsdp(io_mem.local_addr()); + + if (local) + return base + (local - io_mem.local_addr()); + } catch (...) { + PWRN("failed to scan EBDA for RSDP root"); + } + + return 0; + } + + public: + + Acpi_table() { } + + Genode::addr_t phys_rsdp() + { + Genode::uint64_t phys_rsdp = _rsdp(); + return phys_rsdp; + } +}; + +ACPI_PHYSICAL_ADDRESS AcpiOsGetRootPointer (void) +{ + Genode::Acpi_table acpi_table; + ACPI_PHYSICAL_ADDRESS rsdp = acpi_table.phys_rsdp(); + return rsdp; +}