diff --git a/repos/gems/run/sculpt.run b/repos/gems/run/sculpt.run
index 7faa58d447..1b2a4d9e1f 100644
--- a/repos/gems/run/sculpt.run
+++ b/repos/gems/run/sculpt.run
@@ -419,6 +419,8 @@ install_config {
+
+
diff --git a/repos/gems/sculpt/drivers/pc b/repos/gems/sculpt/drivers/pc
index 6e14399319..263c85c5a1 100644
--- a/repos/gems/sculpt/drivers/pc
+++ b/repos/gems/sculpt/drivers/pc
@@ -89,6 +89,7 @@
+
diff --git a/repos/os/src/drivers/platform/common.h b/repos/os/src/drivers/platform/common.h
index db9ff7ccd4..a01d46a4d2 100644
--- a/repos/os/src/drivers/platform/common.h
+++ b/repos/os/src/drivers/platform/common.h
@@ -49,6 +49,8 @@ class Driver::Common : Device_reporter,
Constructible _dev_reporter { };
Constructible _iommu_reporter { };
+ uint64_t _resume_counter { };
+
void _handle_devices();
bool _iommu();
@@ -67,6 +69,7 @@ class Driver::Common : Device_reporter,
void handle_config(Xml_node config);
void acquire_io_mmu_devices();
+ void report_resume();
/*********************
** Device_reporter **
@@ -174,6 +177,7 @@ void Driver::Common::update_report()
{
if (_dev_reporter.constructed())
_dev_reporter->generate([&] (Xml_generator & xml) {
+ xml.attribute("resumed", _resume_counter);
_devices.generate(xml); });
if (_iommu_reporter.constructed())
_iommu_reporter->generate([&] (Xml_generator & xml) {
@@ -182,6 +186,12 @@ void Driver::Common::update_report()
}
+void Driver::Common::report_resume()
+{
+ _resume_counter ++;
+ update_report();
+}
+
void Driver::Common::disable_device(Device const & device)
{
_io_mmu_devices.for_each([&] (Io_mmu & io_mmu) {
diff --git a/repos/pc/src/drivers/platform/pc/spec/x86_64/main.cc b/repos/pc/src/drivers/platform/pc/spec/x86_64/main.cc
index 5635f175ca..98c2860694 100644
--- a/repos/pc/src/drivers/platform/pc/spec/x86_64/main.cc
+++ b/repos/pc/src/drivers/platform/pc/spec/x86_64/main.cc
@@ -24,6 +24,7 @@ struct Driver::Main
Attached_rom_dataspace _config_rom { _env, "config" };
Attached_rom_dataspace _acpi_rom { _env, "acpi" };
Attached_rom_dataspace _system_rom { _env, "system" };
+ Attached_rom_dataspace _sleep_rom { _env, "sleep_states" };
Common _common { _env, _config_rom };
Signal_handler _config_handler { _env.ep(), *this,
&Main::_handle_config };
@@ -33,6 +34,7 @@ struct Driver::Main
Intel::Io_mmu_factory _intel_iommu { _env, _common.io_mmu_factories() };
void _handle_config();
+ void _suspend();
void _reset();
void _system_update();
@@ -40,10 +42,13 @@ struct Driver::Main
: _env(e)
{
_config_rom.sigh(_config_handler);
- _acpi_rom.sigh(_system_handler);
+ _acpi_rom .sigh(_system_handler);
_system_rom.sigh(_system_handler);
+ _sleep_rom .sigh(_system_handler);
+
_handle_config();
_system_update();
+
_common.acquire_io_mmu_devices();
_common.announce_service();
}
@@ -53,13 +58,19 @@ struct Driver::Main
void Driver::Main::_handle_config()
{
_config_rom.update();
- _common.handle_config(_config_rom.xml());
+
+ if (_config_rom.valid())
+ _common.handle_config(_config_rom.xml());
}
void Driver::Main::_reset()
{
_acpi_rom.update();
+
+ if (!_acpi_rom.valid())
+ return;
+
_acpi_rom.xml().with_optional_sub_node("reset", [&] (Xml_node reset)
{
uint16_t const io_port = reset.attribute_value("io_port", 0);
@@ -79,8 +90,68 @@ void Driver::Main::_reset()
void Driver::Main::_system_update()
{
_system_rom.update();
- if (_system_rom.xml().attribute_value("state", String<16>()) == "reset")
+
+ if (!_system_rom.valid())
+ return;
+
+ auto const state = _system_rom.xml().attribute_value("state", String<16>());
+
+ if (state == "reset")
_reset();
+
+ if (state == "suspend") {
+ try { _suspend(); } catch (...) { error("suspend failed"); }
+ /* report independent of result */
+ _common.report_resume();
+ }
+}
+
+
+void Driver::Main::_suspend()
+{
+ _sleep_rom .update();
+
+ if (!_sleep_rom.valid())
+ return;
+
+ struct Client: Genode::Rpc_client
+ {
+ explicit Client(Genode::Capability cap)
+ : Rpc_client(cap) { }
+
+ Pd_session::Managing_system_state system_control(Pd_session::Managing_system_state const &state) override {
+ return call(state); }
+ } system_control { _env.pd().system_control_cap(Affinity::Location()) };
+
+ _sleep_rom.xml().with_sub_node("S3", [&] (auto const &node) {
+
+ auto const typea = "SLP_TYPa";
+ auto const typeb = "SLP_TYPb";
+ auto const valid = node.attribute_value("supported", false) &&
+ node.has_attribute(typea) &&
+ node.has_attribute(typeb);
+
+ if (!valid)
+ return;
+
+ auto s3_sleep_typea = uint8_t(node.attribute_value(typea, 0u));
+ auto s3_sleep_typeb = uint8_t(node.attribute_value(typeb, 0u));
+
+ Pd_session::Managing_system_state in { }, out { };
+
+ in.trapno = Pd_session::Managing_system_state::ACPI_SUSPEND_REQUEST;
+ in.ip = s3_sleep_typea;
+ in.sp = s3_sleep_typeb;
+
+ out = system_control.system_control(in);
+
+ if (!out.trapno)
+ error("S3 suspend failed");
+ else
+ log("resumed from S3");
+ }, [&] {
+ warning("S3 suspend not supported");
+ });
}