mirror of
https://github.com/genodelabs/genode.git
synced 2025-02-20 09:46:20 +00:00
hw/x86: add suspend kernel syscall
using the ACPI mechanism. The syscall can be triggered solely via core's RPC managing_system call. Issue #4669
This commit is contained in:
parent
30c6feb86e
commit
df27cc87b5
@ -46,6 +46,7 @@ namespace Kernel {
|
||||
constexpr Call_arg call_id_time() { return 21; }
|
||||
constexpr Call_arg call_id_run_vm() { return 22; }
|
||||
constexpr Call_arg call_id_pause_vm() { return 23; }
|
||||
constexpr Call_arg call_id_suspend() { return 24; }
|
||||
|
||||
|
||||
/*****************************************************************
|
||||
@ -431,6 +432,21 @@ namespace Kernel {
|
||||
{
|
||||
call(call_id_pause_vm(), cap);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Suspend hardware
|
||||
*
|
||||
* \param sleep_type The intended sleep state S0 ... S5. The values are
|
||||
* read out by an ACPI AML component and are of type
|
||||
* TYP_SLPx as described in the ACPI specification,
|
||||
* e.g. TYP_SLPa and TYP_SLPb. The values differ
|
||||
* between different PC systems/boards.
|
||||
*/
|
||||
inline bool suspend(unsigned const sleep_type)
|
||||
{
|
||||
return bool(call(call_id_suspend(), sleep_type));
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* _INCLUDE__KERNEL__INTERFACE_H_ */
|
||||
|
@ -37,6 +37,10 @@ SRC_CC += spec/x86_64/platform_support_common.cc
|
||||
|
||||
SRC_CC += spec/64bit/memory_map.cc
|
||||
|
||||
PD_SESSION_SUPPORT_CC_PATH := \
|
||||
$(call select_from_repositories,src/core/spec/x86_64/pd_session_support.cc)
|
||||
|
||||
vpath pd_session_support.cc $(dir $(PD_SESSION_SUPPORT_CC_PATH))
|
||||
vpath spec/64bit/memory_map.cc $(call select_from_repositories,src/lib/hw)
|
||||
|
||||
# include less specific configuration
|
||||
|
@ -870,6 +870,7 @@ void Thread::_call()
|
||||
case call_id_ack_irq(): _call_ack_irq(); return;
|
||||
case call_id_new_obj(): _call_new_obj(); return;
|
||||
case call_id_delete_obj(): _call_delete_obj(); return;
|
||||
case call_id_suspend(): _call_suspend(); return;
|
||||
default:
|
||||
Genode::raw(*this, ": unknown kernel call");
|
||||
_die();
|
||||
|
@ -295,6 +295,7 @@ class Kernel::Thread : private Kernel::Object, public Cpu_job, private Timeout
|
||||
void _call_timeout();
|
||||
void _call_timeout_max_us();
|
||||
void _call_time();
|
||||
void _call_suspend();
|
||||
|
||||
template <typename T, typename... ARGS>
|
||||
void _call_new(ARGS &&... args)
|
||||
|
@ -150,6 +150,12 @@ class Genode::Platform : public Genode::Platform_generic
|
||||
size_t max_caps() const override { return Kernel::Pd::max_cap_ids; }
|
||||
|
||||
static addr_t core_main_thread_phys_utcb();
|
||||
|
||||
template <typename T>
|
||||
static void apply_with_boot_info(T const &fn)
|
||||
{
|
||||
fn(_boot_info());
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* _CORE__PLATFORM_H_ */
|
||||
|
@ -26,6 +26,9 @@ using namespace Kernel;
|
||||
extern "C" void kernel_to_user_context_switch(Cpu::Context*, Cpu::Fpu_context*);
|
||||
|
||||
|
||||
void Thread::_call_suspend() { }
|
||||
|
||||
|
||||
void Thread::exception(Cpu & cpu)
|
||||
{
|
||||
switch (regs->cpu_exception) {
|
||||
|
@ -24,6 +24,9 @@ extern "C" void kernel_to_user_context_switch(void *, void *);
|
||||
using namespace Kernel;
|
||||
|
||||
|
||||
void Thread::_call_suspend() { }
|
||||
|
||||
|
||||
void Thread::exception(Cpu & cpu)
|
||||
{
|
||||
switch (regs->exception_type) {
|
||||
|
@ -95,6 +95,9 @@ void Thread::exception(Cpu & cpu)
|
||||
}
|
||||
|
||||
|
||||
void Thread::_call_suspend() { }
|
||||
|
||||
|
||||
void Thread::_call_cache_coherent_region() { }
|
||||
|
||||
|
||||
|
@ -7,7 +7,7 @@
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2013-2017 Genode Labs GmbH
|
||||
* Copyright (C) 2013-2023 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
@ -18,6 +18,11 @@
|
||||
#include <kernel/thread.h>
|
||||
#include <kernel/pd.h>
|
||||
|
||||
#include <hw/spec/x86_64/acpi.h>
|
||||
#include <platform_pd.h>
|
||||
|
||||
#include <kernel/main.h>
|
||||
|
||||
|
||||
void Kernel::Thread::Tlb_invalidation::execute(Cpu &)
|
||||
{
|
||||
@ -29,13 +34,132 @@ void Kernel::Thread::Tlb_invalidation::execute(Cpu &)
|
||||
global_work_list.remove(&_le);
|
||||
caller._restart();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
void Kernel::Thread::Flush_and_stop_cpu::execute(Cpu &) { }
|
||||
void Kernel::Thread::Flush_and_stop_cpu::execute(Cpu &cpu)
|
||||
{
|
||||
if (--cpus_left == 0) {
|
||||
/* last CPU triggers final ACPI suspend outside kernel lock */
|
||||
cpu.suspend.typ_a = suspend.typ_a;
|
||||
cpu.suspend.typ_b = suspend.typ_b;
|
||||
cpu.next_state_suspend();
|
||||
return;
|
||||
}
|
||||
|
||||
/* halt CPU outside kernel lock */
|
||||
cpu.next_state_halt();
|
||||
|
||||
/* adhere to ACPI specification */
|
||||
asm volatile ("wbinvd" : : : "memory");
|
||||
}
|
||||
|
||||
|
||||
void Kernel::Cpu::Halt_job::proceed(Kernel::Cpu &) { }
|
||||
void Kernel::Cpu::Halt_job::Halt_job::proceed(Kernel::Cpu &cpu)
|
||||
{
|
||||
switch (cpu.state()) {
|
||||
case HALT:
|
||||
while (true) {
|
||||
asm volatile ("hlt"); }
|
||||
break;
|
||||
case SUSPEND:
|
||||
using Genode::Platform;
|
||||
|
||||
Platform::apply_with_boot_info([&](auto const &boot_info) {
|
||||
auto table = boot_info.plat_info.acpi_fadt;
|
||||
auto acpi_fadt_table = reinterpret_cast<Hw::Acpi_generic *>(Platform::mmio_to_virt(table));
|
||||
|
||||
/* paranoia */
|
||||
if (!acpi_fadt_table)
|
||||
return;
|
||||
|
||||
/* all CPUs signaled that they are stopped, trigger ACPI suspend */
|
||||
Hw::Acpi_fadt fadt(acpi_fadt_table);
|
||||
|
||||
/* ack all GPEs, otherwise we may wakeup immediately */
|
||||
fadt.clear_gpe0_status();
|
||||
fadt.clear_gpe1_status();
|
||||
|
||||
/* adhere to ACPI specification */
|
||||
asm volatile ("wbinvd" : : : "memory");
|
||||
|
||||
fadt.suspend(cpu.suspend.typ_a, cpu.suspend.typ_b);
|
||||
|
||||
Genode::raw("kernel: unexpected resume");
|
||||
});
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
Genode::raw("unknown cpu state");
|
||||
while (true) {
|
||||
asm volatile ("hlt");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Kernel::Thread::_call_suspend()
|
||||
{
|
||||
using Genode::uint8_t;
|
||||
using Genode::Platform;
|
||||
|
||||
Hw::Acpi_generic * acpi_fadt_table { };
|
||||
unsigned cpu_count { };
|
||||
|
||||
Platform::apply_with_boot_info([&](auto const &boot_info) {
|
||||
auto table = boot_info.plat_info.acpi_fadt;
|
||||
if (table)
|
||||
acpi_fadt_table = reinterpret_cast<Hw::Acpi_generic *>(Platform::mmio_to_virt(table));
|
||||
|
||||
cpu_count = boot_info.cpus;
|
||||
});
|
||||
|
||||
if (!acpi_fadt_table || !cpu_count) {
|
||||
user_arg_0(0 /* fail */);
|
||||
return;
|
||||
}
|
||||
|
||||
if (_stop_cpu.constructed()) {
|
||||
if (_stop_cpu->cpus_left) {
|
||||
Genode::raw("kernel: resume still ongoing");
|
||||
user_arg_0(0 /* fail */);
|
||||
return;
|
||||
}
|
||||
|
||||
/* remove & destruct Flush_and_stop_cpu object */
|
||||
_stop_cpu.destruct();
|
||||
user_arg_0(1 /* success */);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
auto const sleep_typ_a = uint8_t(user_arg_1());
|
||||
auto const sleep_typ_b = uint8_t(user_arg_1() >> 8);
|
||||
|
||||
_stop_cpu.construct(_cpu_pool.work_list(), cpu_count - 1,
|
||||
Hw::Suspend_type { sleep_typ_a, sleep_typ_b });
|
||||
|
||||
/* single core CPU case */
|
||||
if (cpu_count == 1) {
|
||||
auto &cpu = _cpu_pool.executing_cpu();
|
||||
/* this CPU triggers final ACPI suspend outside kernel lock */
|
||||
cpu.next_state_suspend();
|
||||
return;
|
||||
}
|
||||
|
||||
/* trigger IPIs to all beside current CPU */
|
||||
_cpu_pool.for_each_cpu([&] (Cpu &cpu) {
|
||||
|
||||
if (cpu.id() == Cpu::executing_id()) {
|
||||
/* halt CPU outside kernel lock */
|
||||
cpu.next_state_halt();
|
||||
return;
|
||||
}
|
||||
|
||||
cpu.trigger_ip_interrupt();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
void Kernel::Thread::_call_cache_coherent_region() { }
|
||||
|
60
repos/base-hw/src/core/spec/x86_64/pd_session_support.cc
Normal file
60
repos/base-hw/src/core/spec/x86_64/pd_session_support.cc
Normal file
@ -0,0 +1,60 @@
|
||||
/*
|
||||
* \brief Core implementation of the PD session interface
|
||||
* \author Alexander Boettcher
|
||||
* \date 2022-12-02
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2022 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#include <cpu/cpu_state.h>
|
||||
#include <pd_session_component.h>
|
||||
|
||||
using namespace Genode;
|
||||
using State = Genode::Pd_session::Managing_system_state;
|
||||
|
||||
|
||||
State Pd_session_component::managing_system(State const &request)
|
||||
{
|
||||
bool const suspend = (_managing_system == Managing_system::PERMITTED) &&
|
||||
(request.trapno == State::ACPI_SUSPEND_REQUEST);
|
||||
State respond { };
|
||||
|
||||
if (!suspend) {
|
||||
/* report failed attempt */
|
||||
respond.trapno = 0;
|
||||
return respond;
|
||||
}
|
||||
|
||||
/*
|
||||
* The trapno/ip/sp registers used below are just convention to transfer
|
||||
* the intended sleep state S0 ... S5. The values are read out by an
|
||||
* ACPI AML component and are of type TYP_SLPx as described in the
|
||||
* ACPI specification, e.g. TYP_SLPa and TYP_SLPb. The values differ
|
||||
* between different PC systems/boards.
|
||||
*
|
||||
* \note trapno/ip/sp registers are chosen because they exist in
|
||||
* Managing_system_state for x86_32 and x86_64.
|
||||
*/
|
||||
unsigned const sleep_type_a = request.ip & 0xffu;
|
||||
unsigned const sleep_type_b = request.sp & 0xffu;
|
||||
|
||||
respond.trapno = Kernel::suspend((sleep_type_b << 8) | sleep_type_a);
|
||||
|
||||
return respond;
|
||||
}
|
||||
|
||||
|
||||
/***************************
|
||||
** Dummy implementations **
|
||||
***************************/
|
||||
|
||||
bool Pd_session_component::assign_pci(addr_t, uint16_t) { return true; }
|
||||
|
||||
|
||||
void Pd_session_component::map(addr_t, addr_t) { }
|
||||
|
@ -10,7 +10,14 @@
|
||||
#
|
||||
|
||||
assert_spec x86
|
||||
assert_spec nova
|
||||
|
||||
if {
|
||||
![have_spec hw] &&
|
||||
![have_spec nova]
|
||||
} {
|
||||
puts "Platform is unsupported."
|
||||
exit 0
|
||||
}
|
||||
|
||||
# non Intel machines has no GPU support, e.g. Qemu and AMD
|
||||
set board_non_intel [expr [have_include "power_on/qemu"]]
|
||||
|
Loading…
x
Reference in New Issue
Block a user