libports: add acpica library

Fixes #1962
This commit is contained in:
Alexander Boettcher 2016-03-18 16:31:00 +01:00 committed by Christian Helmuth
parent ee7f965061
commit 38c5abbaad
23 changed files with 2481 additions and 0 deletions

View File

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

View File

@ -0,0 +1 @@
a3d820f28b860fdd9fd8c855f0fa2ec0b4beb859

View File

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

View File

@ -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 {
<policy label="ps2_drv"> <device name="PS2"/> </policy>
<policy label="acpica"> <pci class="ALL"/> </policy>}
}
append_platform_drv_build_components
build $build_components
create_boot_directory
set config {
<config>
<parent-provides>
<service name="IRQ"/>
<service name="RAM"/>
<service name="ROM"/>
<service name="LOG"/>
<service name="RM"/>
<service name="IO_MEM" />
<service name="IO_PORT" />
</parent-provides>
<default-route>
<any-service> <parent/> <any-child/> </any-service>
</default-route>
<start name="timer">
<resource name="RAM" quantum="2M"/>
<provides><service name="Timer"/></provides>
</start>
<start name="acpica">
<!-- <binary name="debug-acpica"/> -->
<resource name="RAM" quantum="4M"/>
<config ld_verbose="yes" reset="no" poweroff="no" report="yes">
<!-- required for "debug-acpica":
<libc stdout="/dev/log">
<vfs> <dir name="dev"> <log/> </dir> </vfs>
</libc>
-->
</config>
<route>
<service name="ROM" label="system"> <child name="dynamic_rom"/> </service>
<service name="Report"> <child name="acpi_state"/> </service>
<any-service> <parent/> <any-child/> </any-service>
</route>
</start>}
append config {
<start name="acpi_state">
<binary name="report_rom"/>
<resource name="RAM" quantum="2M"/>
<provides>
<service name="ROM" />
<service name="Report" />
</provides>
<config verbose="yes">
</config>
<route>
<service name="LOG"> <parent/> </service>
<service name="SIGNAL"> <parent/> </service>
<service name="CAP"> <parent/> </service>
<service name="RM"> <parent/> </service>
</route>
</start>}
append config {
<start name="dynamic_rom">
<resource name="RAM" quantum="4M"/>
<provides><service name="ROM"/></provides>
<config verbose="yes">
<rom name="system">
<inline description="set system state to 'normal'">
<system state="normal"/>
</inline>
<sleep milliseconds="5000" />
<inline description="set system state to 'reset'">
<system state="reset"/>
</inline>
<!--
<inline description="set system state to 'poweroff'">
<system state="poweroff"/>
</inline>
-->
<sleep milliseconds="500" />
</rom>
</config>
</start>}
append config {
<start name="ps2_drv">
<resource name="RAM" quantum="2M"/>
<provides><service name="Input"/></provides>
<config verbose_keyboard="no" verbose_mouse="no" verbose_scancodes="no"/>
<route>
<service name="LOG"> <parent/> </service>
<service name="RM"> <parent/> </service>
<service name="Platform"> <child name="platform_drv"/> </service>
</route>
</start>
<start name="test-input">
<resource name="RAM" quantum="1M"/>
<route>
<service name="LOG"> <parent/> </service>
<service name="RM"> <parent/> </service>
<service name="Input"> <child name="ps2_drv"/> </service>
<service name="Timer"> <child name="timer"/> </service>
</route>
</start>}
append_platform_drv_config
append config {
</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

View File

@ -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:
!<system state="something"/>
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
------------------------------------------------------
!<start name="acpica">
! <!-- <binary name="debug-acpica"/> -->
! ...
! <config reset="no" poweroff="no" report="yes"/>
! <route>
! <service name="ROM" label="system"> <child name="..."/> </service>
! <service name="Report"> <child name="..."/> </service>
! ...
! </route>
!</start>
!
!<start name="platform_drv" >
! ...
! <config acpi="yes" system="yes">
! <policy label="acpica"> <pci class="ALL"/> </policy>
! ...
! </config>
!</start>
Reports generated by the Genode acpica application
--------------------------------------------------
Report 'acpi_lid' - open/closed:
!<acpi_lid>
! <lid value="1" count="1">open</lid>
!</acpi_lid>
!<acpi_lid>
! <lid value="0" count="3">closed</lid>
!</acpi_lid>
Report 'acpi_ac' - power cable plugged-in /unplugged
!<acpi_ac>
! <ac value="1" count="1">online</ac>
!</acpi_ac>
!<acpi_ac>
! <ac value="0" count="2">offline</ac>
!</acpi_ac>
Report 'acpi_ec' - embedded controller events
!<acpi_ec>
! <ec>
! <data value="42" count="1">triggered</data>
! <data value="43" count="1"/>
! </ec>
!</acpi_ec>
Report 'acpi_battery' - smart battery status changes
!<acpi_battery>
! <sb>
! <name>BAT1</name>
! <powerunit value="1">mA/mAh</powerunit>
! <design_capacity value="4800"/>
! <last_full_capacity value="5417"/>
! <technology value="1">secondary</technology>
! <voltage value="12608"/>
! <warning_capacity value="325"/>
! <low_capacity value="162"/>
! <granularity1 value="0"/>
! <granularity2 value="0"/>
! <serial>BAT1</serial>
! <model>RT672</model>
! <type>LiON</type>
! <oem>ASP</oem>
! <status value="31"/>
! <state value="1">discharging</state>
! <present_rate value="0"/>
! <remaining_capacity value="5663"/>
! <present_voltage value="12524"/>
! </sb>
!</acpi_battery>
!<acpi_battery>
! ...
! <state value="2">charging</state>
! ...
!</acpi_battery>
!<acpi_battery>
! ...
! <status value="15">battery not present</status>
! ...
!</acpi_battery>

View File

@ -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<Ac> {
private:
Acpica::Reportstate * _report;
UINT64 _ac_state = 0;
UINT64 _ac_count = 0;
public:
Ac(void * report)
: _report(reinterpret_cast<Acpica::Reportstate *>(report))
{
if (_report)
_report->add_notify(this);
}
void handle(ACPI_HANDLE ac, UINT32 value)
{
Acpica::Buffer<ACPI_OBJECT> 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");
}
};

View File

@ -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 <stdarg.h>
#include <stdio.h>
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);
}

View File

@ -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)/..

View File

@ -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 <util/mmio.h>
class Ec : Acpica::Callback<Ec> {
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<Data>::Element {
Genode::uint64_t count;
Genode::uint8_t data;
bool triggered;
Data(Genode::uint8_t d) : count(0), data(d), triggered(false) { }
};
Genode::List<Data> _list_data;
public:
Ec(void * report)
:
_report(reinterpret_cast<Acpica::Reportstate *>(report))
{ }
static UINT32 handler_gpe(ACPI_HANDLE dev, UINT32 gpe, void *context)
{
Ec * ec = reinterpret_cast<Ec *>(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<Ec *>(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<unsigned char *>(value);
if (bytes * 8 != bitwidth) {
PERR("unsupport bit width of %u", bitwidth);
return AE_BAD_PARAMETER;
}
Ec * ec = reinterpret_cast<Ec *>(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<ACPI_OBJECT> 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;
}
});
}
}
};

View File

@ -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<Fixed> {
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<Acpica::Reportstate *>(report))
{
if (_report)
_report->add_notify(this);
}
static
UINT32 handle_power_button(void *context)
{
Fixed * me = reinterpret_cast<Fixed *>(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<Fixed *>(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");
}
});
}
};

View File

@ -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<Lid> {
private:
Acpica::Reportstate * _report;
UINT64 _lid_state = 0;
UINT64 _lid_count = 0;
public:
Lid(void * report)
: _report(reinterpret_cast<Acpica::Reportstate *>(report))
{
if (_report)
_report->add_notify(this);
}
void handle(ACPI_HANDLE lid, UINT32 value)
{
Acpica::Buffer<ACPI_OBJECT> 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");
});
}
};

View File

@ -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 <base/component.h>
#include <base/printf.h>
#include <base/signal.h>
#include <irq_session/connection.h>
#include <io_port_session/connection.h>
#include <os/attached_rom_dataspace.h>
#include <os/config.h>
#include <os/reporter.h>
#include <util/volatile_object.h>
#include <util/xml_node.h>
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<Acpica::Statechange> _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<char>(),
_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<Acpica::Main> _sci_irq;
Genode::Lazy_volatile_object<Genode::Irq_connection> _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); }

View File

@ -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 <base/printf.h>
extern "C"
void AcpiOsPrintf (const char *fmt, ...)
{ }
extern "C"
void AcpiOsVprintf (const char *fmt, va_list va)
{ }

View File

@ -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<Callback<Battery> > _list_sb;
Genode::List<Callback<Ec> > _list_ec;
Genode::List<Callback<Ac> > _list_ac;
Callback<Fixed> * _fixed;
Callback<Lid> * _lid;
public:
Reportstate() { }
void add_notify(Acpica::Callback<Battery> * s) { _list_sb.insert(s); }
void add_notify(Acpica::Callback<Fixed> * f) { _fixed = f; }
void add_notify(Acpica::Callback<Lid> * l) { _lid = l; }
void add_notify(Acpica::Callback<Ec> * e) { _list_ec.insert(e); }
void add_notify(Acpica::Callback<Ac> * 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> * 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> * 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<Battery> * 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);
});
}
}
};

View File

@ -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<Battery> {
private:
Acpica::Reportstate * _report;
ACPI_HANDLE _sb;
public:
Battery(void * report, ACPI_HANDLE sb)
: _report(reinterpret_cast<Acpica::Reportstate *>(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<char [8]> 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<ACPI_OBJECT> 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<char [512]> battery;
res = AcpiEvaluateObjectTyped(sb, ACPI_STRING("_BIF"),
nullptr, &battery,
ACPI_TYPE_PACKAGE);
ACPI_OBJECT * obj = reinterpret_cast<ACPI_OBJECT *>(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<char [512]> battery;
ACPI_STATUS res = AcpiEvaluateObjectTyped(_sb, ACPI_STRING("_BIF"),
nullptr, &battery,
ACPI_TYPE_PACKAGE);
ACPI_OBJECT * obj = reinterpret_cast<ACPI_OBJECT *>(battery.object);
if (ACPI_FAILURE(res) || !obj || obj->Package.Count != 13) {
PERR("failed - '%s' _BIF res=0x%x", __func__, res);
return;
}
Acpica::Buffer<char [8]> 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<char [256]> dynamic;
ACPI_STATUS res = AcpiEvaluateObjectTyped(_sb, ACPI_STRING("_BST"),
nullptr, &dynamic,
ACPI_TYPE_PACKAGE);
ACPI_OBJECT * obj = reinterpret_cast<ACPI_OBJECT *>(dynamic.object);
if (ACPI_FAILURE(res) || !obj ||
obj->Package.Count != 4) {
PERR("failed - '%s' _BST res=0x%x", __func__, res);
return;
}
Acpica::Buffer<ACPI_OBJECT> 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");
});
}
}
};

View File

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

View File

@ -0,0 +1,4 @@
TARGET = acpica
SRC_CC = os.cc printf.cc
include $(PRG_DIR)/target.inc

View File

@ -0,0 +1,31 @@
namespace Acpica {
template<typename> class Buffer;
template<typename> class Callback;
}
template <typename T>
class Acpica::Buffer : public ACPI_BUFFER
{
public:
T object;
Buffer() {
Pointer = &object;
Length = sizeof(object);
}
} __attribute__((packed));
template <typename T>
class Acpica::Callback : public Genode::List<Acpica::Callback<T> >::Element
{
public:
static void handler(ACPI_HANDLE h, UINT32 value, void * context)
{
reinterpret_cast<T *>(context)->handle(h, value);
}
void generate(Genode::Xml_generator &xml) {
reinterpret_cast<T *>(this)->generate(xml);
}
};

View File

@ -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 <stdarg.h>
#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

View File

@ -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 <base/printf.h>
#include <base/env.h>
#include <util/misc_math.h>
#include <io_mem_session/connection.h>
#include <rm_session/connection.h>
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<Genode::addr_t>(_virt + (p - _phys));
}
bool ref_dec() { return --_ref; }
template <typename FUNC>
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 <typename FUNC>
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 <typename FUNC>
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<Acpica::Io_mem *>(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<Genode::addr_t>(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<Genode::addr_t>(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<void *>(virt);
virt= Acpica::Io_mem::insert(phys, size);
if (virt)
return reinterpret_cast<void *>(virt + (phys & 0xfffU));
FAIL(nullptr)
}
void AcpiOsUnmapMemory (void * ptr, ACPI_SIZE size)
{
Genode::uint8_t const * virt = reinterpret_cast<Genode::uint8_t *>(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()
}

View File

@ -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 <base/printf.h>
#include <base/env.h>
#include <util/misc_math.h>
#include <io_port_session/connection.h>
#include <timer_session/connection.h>
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<Genode::Lock *>(h);
lock->lock();
return AE_OK;
}
void AcpiOsReleaseLock (ACPI_SPINLOCK h, ACPI_CPU_FLAGS flags)
{
Genode::Lock *lock = reinterpret_cast<Genode::Lock *>(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<Genode::Semaphore *>(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<Genode::Semaphore *>(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::addr_t>(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()
}

View File

@ -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 <base/printf.h>
#include <platform_session/connection.h>
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;
}

View File

@ -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 <io_mem_session/connection.h>
#include <os/attached_io_mem_dataspace.h>
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<uint8_t>());
if (local)
return BIOS_BASE + (local - io_mem.local_addr<uint8_t>());
}
/* search EBDA (BIOS addr + 0x40e) */
try {
unsigned short base = 0;
{
Genode::Attached_io_mem_dataspace io_mem(0, 0x1000);
local = io_mem.local_addr<uint8_t>();
if (local)
base = (*reinterpret_cast<unsigned short *>(local + 0x40e)) << 4;
}
if (!base)
return 0;
Genode::Attached_io_mem_dataspace io_mem(base, 1024);
local = _search_rsdp(io_mem.local_addr<uint8_t>());
if (local)
return base + (local - io_mem.local_addr<uint8_t>());
} 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;
}