platform_drv/x86: support ACPI reset

Evaluate fadt xml node in report from acpi_drv. If the io ports in the range
of 0xcf8+4 are necessary for the reset than the platform driver will
react on the 'system' state 'reset' and reboot.

Issue #1962
This commit is contained in:
Alexander Boettcher 2016-05-13 16:09:20 +02:00 committed by Christian Helmuth
parent 38c5abbaad
commit 57f47db823
5 changed files with 167 additions and 16 deletions

View File

@ -51,6 +51,24 @@ proc platform_drv_policy {} {
}
proc platform_drv_priority {} { return "" }
proc platform_drv_add_routing {} {
if {[have_spec acpi]} {
return {
<service name="ROM" label="system"> <child name="acpi_report_rom"/> </service>}
}
return ""
}
proc platform_drv_config_config {} {
if {[have_spec acpi] || [have_spec arm] || [have_spec hw_x86_64_muen]} {
return {
<config>}
}
return {
<config acpi="no">}
}
proc append_platform_drv_config {} {
global config
@ -108,6 +126,8 @@ proc append_platform_drv_config {} {
</provides>
<route>}
append config "[platform_drv_add_routing]"
append_if [have_spec acpi] config {
<service name="ROM" label="acpi"> <child name="acpi_report_rom"/> </service>}
@ -118,18 +138,7 @@ proc append_platform_drv_config {} {
<any-service> <parent/> </any-service>
</route>}
if {[have_spec acpi] || [have_spec arm] || [have_spec hw_x86_64_muen]} {
append config {
<config>}
} else {
append config {
<config acpi="no">}
}
append config [platform_drv_config_config]
append config [platform_drv_policy]
append config {

View File

@ -27,6 +27,18 @@ proc platform_drv_policy {} {
<policy label="acpica"> <pci class="ALL"/> </policy>}
}
# add routing information to dynamically generate change of 'system' ROM
proc platform_drv_add_routing {} {
return {
<service name="ROM" label="system"> <child name="dynamic_rom"/> </service>}
}
# override default config to react on 'system' ROM changes for reset
proc platform_drv_config_config {} {
return {
<config acpi="yes" system="no">}
}
append_platform_drv_build_components
build $build_components

View File

@ -39,7 +39,10 @@ struct Platform::Main
Genode::Lazy_volatile_object<Genode::Attached_rom_dataspace> acpi_rom;
Genode::Lazy_volatile_object<Platform::Root> root;
Genode::Lazy_volatile_object<Genode::Attached_rom_dataspace> system_state;
Genode::Signal_handler<Platform::Main> _acpi_report;
Genode::Signal_handler<Platform::Main> _system_report;
void acpi_update()
{
@ -54,19 +57,48 @@ struct Platform::Main
_env.parent().announce(_env.ep().manage(*root));
}
void system_update()
{
system_state->update();
if (!system_state->is_valid() || !root.is_constructed())
return;
Genode::Xml_node system(system_state->local_addr<char>(),
system_state->size());
typedef Genode::String<16> Value;
const Value state = system.attribute_value("state", Value("unknown"));
if (state == "reset")
root->system_reset();
}
Main(Genode::Env &env)
:
sliced_heap(env.ram(), env.rm()),
_env(env),
_acpi_report(_env.ep(), *this, &Main::acpi_update)
_acpi_report(_env.ep(), *this, &Main::acpi_update),
_system_report(_env.ep(), *this, &Main::system_update)
{
const Genode::Xml_node &config = Genode::config()->xml_node();
typedef Genode::String<8> Value;
Value const wait_for_acpi = Genode::config()->xml_node().attribute_value("acpi", Value("yes"));
Value const wait_for_acpi = config.attribute_value("acpi", Value("yes"));
if (wait_for_acpi == "yes") {
bool system_reset = config.attribute_value("system", false);
if (system_reset) {
/* wait for system state changes and react upon, e.g. reset */
system_state.construct("system");
system_state->sigh(_system_report);
}
/* for ACPI support, wait for the first valid acpi report */
acpi_rom.construct("acpi");
acpi_rom->sigh(_acpi_report);
/* check if already valid */
acpi_update();
return;
}

View File

@ -24,7 +24,7 @@ namespace Platform {
{
private:
enum { REG_ADDR = 0xcf8, REG_DATA = 0xcfc };
enum { REG_ADDR = 0xcf8, REG_DATA = 0xcfc, REG_SIZE = 4 };
/**
* Request interface to access an I/O port
@ -47,7 +47,7 @@ namespace Platform {
* Once created, each I/O-port session persists until
* the PCI driver gets killed by its parent.
*/
static Genode::Io_port_connection io_port(port, 4);
static Genode::Io_port_connection io_port(port, REG_SIZE);
return &io_port;
}
@ -177,6 +177,32 @@ namespace Platform {
return true;
}
}
bool reset_support(unsigned reg, unsigned reg_size) const
{
return (REG_ADDR <= reg) &&
reg + reg_size <= REG_ADDR + REG_SIZE;
}
bool system_reset(unsigned reg, unsigned long long value,
const Device::Access_size &access_size)
{
switch (access_size) {
case Device::ACCESS_8BIT:
_io_port<REG_ADDR>()->outb(reg, value);
break;
case Device::ACCESS_16BIT:
_io_port<REG_ADDR>()->outw(reg, value);
break;
case Device::ACCESS_32BIT:
_io_port<REG_ADDR>()->outl(reg, value);
break;
default:
return false;
}
return true;
}
};
}

View File

@ -23,6 +23,7 @@
#include <root/component.h>
#include <root/client.h>
#include <util/mmio.h>
#include <util/retry.h>
#include <util/volatile_object.h>
@ -853,6 +854,27 @@ class Platform::Root : public Genode::Root_component<Session_component>
{
private:
struct Fadt {
Genode::uint32_t features = 0, reset_type = 0, reset_value = 0;
Genode::uint64_t reset_addr = 0;
/* Table 5-35 Fixed ACPI Description Table Fixed Feature Flags */
struct Features : Genode::Register<32> {
struct Reset : Bitfield<10, 1> { };
};
/* ACPI spec - 5.2.3.2 Generic Address Structure */
struct Gas : Genode::Register<32>
{
struct Address_space : Bitfield <0, 8> {
enum { SYSTEM_IO = 1 };
};
struct Access_size : Bitfield<24,8> {
enum { UNDEFINED = 0, BYTE = 1, WORD = 2, DWORD = 3, QWORD = 4};
};
};
} fadt;
Genode::Rpc_entrypoint _device_pd_ep;
void _parse_report_rom(const char * acpi_rom)
@ -938,6 +960,13 @@ class Platform::Root : public Genode::Root_component<Session_component>
}
}
if (node.has_type("fadt")) {
node.attribute("features").value(&fadt.features);
node.attribute("reset_type").value(&fadt.reset_type);
node.attribute("reset_addr").value(&fadt.reset_addr);
node.attribute("reset_value").value(&fadt.reset_value);
}
if (!node.has_type("routing"))
continue;
@ -1029,4 +1058,47 @@ class Platform::Root : public Genode::Root_component<Session_component>
}
}
}
void system_reset()
{
const bool io_port_space = (Fadt::Gas::Address_space::get(fadt.reset_type) == Fadt::Gas::Address_space::SYSTEM_IO);
if (!io_port_space)
return;
Config_access config_access;
const unsigned raw_access_size = Fadt::Gas::Access_size::get(fadt.reset_type);
const bool reset_support = config_access.reset_support(fadt.reset_addr, raw_access_size);
if (!reset_support)
return;
const bool feature_reset = Fadt::Features::Reset::get(fadt.features);
if (!feature_reset) {
PWRN("system reset failed - feature not supported");
return;
}
Device::Access_size access_size = Device::ACCESS_8BIT;
unsigned raw_size = Fadt::Gas::Access_size::get(fadt.reset_type);
switch (raw_size) {
case Fadt::Gas::Access_size::WORD:
access_size = Device::ACCESS_16BIT;
break;
case Fadt::Gas::Access_size::DWORD:
access_size = Device::ACCESS_32BIT;
break;
case Fadt::Gas::Access_size::QWORD:
PERR("system reset failed - unsupported access size");
return;
default:
break;
}
config_access.system_reset(fadt.reset_addr, fadt.reset_value,
access_size);
/* if we are getting here - the reset failed */
PINF("system reset failed");
}
};