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:
committed by
Christian Helmuth
parent
38c5abbaad
commit
57f47db823
@@ -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 {
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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");
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user