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_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 {} { proc append_platform_drv_config {} {
global config global config
@@ -108,6 +126,8 @@ proc append_platform_drv_config {} {
</provides> </provides>
<route>} <route>}
append config "[platform_drv_add_routing]"
append_if [have_spec acpi] config { append_if [have_spec acpi] config {
<service name="ROM" label="acpi"> <child name="acpi_report_rom"/> </service>} <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> <any-service> <parent/> </any-service>
</route>} </route>}
if {[have_spec acpi] || [have_spec arm] || [have_spec hw_x86_64_muen]} { append config [platform_drv_config_config]
append config {
<config>}
} else {
append config {
<config acpi="no">}
}
append config [platform_drv_policy] append config [platform_drv_policy]
append config { append config {

View File

@@ -27,6 +27,18 @@ proc platform_drv_policy {} {
<policy label="acpica"> <pci class="ALL"/> </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 append_platform_drv_build_components
build $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<Genode::Attached_rom_dataspace> acpi_rom;
Genode::Lazy_volatile_object<Platform::Root> root; 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> _acpi_report;
Genode::Signal_handler<Platform::Main> _system_report;
void acpi_update() void acpi_update()
{ {
@@ -54,19 +57,48 @@ struct Platform::Main
_env.parent().announce(_env.ep().manage(*root)); _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) Main(Genode::Env &env)
: :
sliced_heap(env.ram(), env.rm()), sliced_heap(env.ram(), env.rm()),
_env(env), _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; 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") { 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 */ /* for ACPI support, wait for the first valid acpi report */
acpi_rom.construct("acpi"); acpi_rom.construct("acpi");
acpi_rom->sigh(_acpi_report); acpi_rom->sigh(_acpi_report);
/* check if already valid */
acpi_update();
return; return;
} }

View File

@@ -24,7 +24,7 @@ namespace Platform {
{ {
private: 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 * Request interface to access an I/O port
@@ -47,7 +47,7 @@ namespace Platform {
* Once created, each I/O-port session persists until * Once created, each I/O-port session persists until
* the PCI driver gets killed by its parent. * 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; return &io_port;
} }
@@ -177,6 +177,32 @@ namespace Platform {
return true; 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/component.h>
#include <root/client.h> #include <root/client.h>
#include <util/mmio.h>
#include <util/retry.h> #include <util/retry.h>
#include <util/volatile_object.h> #include <util/volatile_object.h>
@@ -853,6 +854,27 @@ class Platform::Root : public Genode::Root_component<Session_component>
{ {
private: 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; Genode::Rpc_entrypoint _device_pd_ep;
void _parse_report_rom(const char * acpi_rom) 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")) if (!node.has_type("routing"))
continue; 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");
}
}; };