mirror of
https://github.com/genodelabs/genode.git
synced 2025-05-31 06:31:10 +00:00
hw: simplify x86 vCPU initialization
Initializing a vCPU on base-hw involved a round-trip to the kernel to send the initial startup exit signal and setting up the vCPU on first run of the vCPU thread. Signal the vCPU handler directly from the base libary. This removes the need to call `Vm::run()` during construction of the vCPU, possibly from a remote core. Check that `Vm::run()` and `Vm::pause()` are only called from the local CPU core. Finally, move the initialization of the vCPU from `Vm::proceed()` to `Vm::run()`, so that the `Vcpu_state` can be synced from the VMM directly after initialization. Fixes #5483
This commit is contained in:
parent
dd8b78575e
commit
c3c719fce7
@ -16,6 +16,7 @@
|
||||
#define _CORE__KERNEL__VM_H_
|
||||
|
||||
/* core includes */
|
||||
#include <kernel/cpu.h>
|
||||
#include <kernel/cpu_context.h>
|
||||
#include <kernel/pd.h>
|
||||
#include <kernel/signal.h>
|
||||
@ -61,8 +62,6 @@ class Kernel::Vm : private Kernel::Object, public Cpu_context
|
||||
Scheduler_state _scheduled = INACTIVE;
|
||||
Board::Vcpu_context _vcpu_context;
|
||||
|
||||
void _sync_to_vmm();
|
||||
void _sync_from_vmm();
|
||||
void _pause_vcpu()
|
||||
{
|
||||
if (_scheduled != INACTIVE)
|
||||
@ -134,12 +133,7 @@ class Kernel::Vm : private Kernel::Object, public Cpu_context
|
||||
|
||||
void run();
|
||||
|
||||
void pause()
|
||||
{
|
||||
_pause_vcpu();
|
||||
_sync_to_vmm();
|
||||
}
|
||||
|
||||
void pause();
|
||||
|
||||
/*****************
|
||||
** Cpu_context **
|
||||
|
@ -86,15 +86,16 @@ void Vm::proceed()
|
||||
|
||||
void Vm::run()
|
||||
{
|
||||
_sync_from_vmm();
|
||||
if (_scheduled != ACTIVE) Cpu_context::_activate();
|
||||
_scheduled = ACTIVE;
|
||||
}
|
||||
|
||||
|
||||
void Vm::_sync_to_vmm()
|
||||
{}
|
||||
|
||||
|
||||
void Vm::_sync_from_vmm()
|
||||
{}
|
||||
void Vm::pause()
|
||||
{
|
||||
if (_cpu().id() != Cpu::executing_id()) {
|
||||
Genode::error("vCPU pause called from remote core.");
|
||||
return;
|
||||
}
|
||||
_pause_vcpu();
|
||||
}
|
||||
|
@ -207,18 +207,18 @@ void Kernel::Vm::proceed()
|
||||
|
||||
void Vm::run()
|
||||
{
|
||||
_sync_from_vmm();
|
||||
if (_scheduled != ACTIVE) Cpu_context::_activate();
|
||||
_scheduled = ACTIVE;
|
||||
}
|
||||
|
||||
|
||||
void Vm::_sync_to_vmm()
|
||||
{}
|
||||
|
||||
|
||||
void Vm::_sync_from_vmm()
|
||||
{}
|
||||
void Vm::pause()
|
||||
{
|
||||
if (_cpu().id() != Cpu::executing_id()) {
|
||||
Genode::error("vCPU pause called from remote core.");
|
||||
return;
|
||||
}
|
||||
_pause_vcpu();
|
||||
}
|
||||
|
||||
|
||||
void Vm::inject_irq(unsigned irq)
|
||||
|
@ -214,18 +214,19 @@ void Vm::proceed()
|
||||
|
||||
void Vm::run()
|
||||
{
|
||||
_sync_from_vmm();
|
||||
if (_scheduled != ACTIVE) Cpu_context::_activate();
|
||||
_scheduled = ACTIVE;
|
||||
}
|
||||
|
||||
|
||||
void Vm::_sync_to_vmm()
|
||||
{}
|
||||
|
||||
|
||||
void Vm::_sync_from_vmm()
|
||||
{}
|
||||
void Vm::pause()
|
||||
{
|
||||
if (_cpu().id() != Cpu::executing_id()) {
|
||||
Genode::error("vCPU pause called from remote core.");
|
||||
return;
|
||||
}
|
||||
_pause_vcpu();
|
||||
}
|
||||
|
||||
|
||||
void Vm::inject_irq(unsigned irq)
|
||||
|
@ -35,7 +35,6 @@ namespace Board {
|
||||
|
||||
enum Platform_exitcodes : uint64_t {
|
||||
EXIT_NPF = 0xfc,
|
||||
EXIT_STARTUP = 0xfe,
|
||||
EXIT_PAUSED = 0xff,
|
||||
};
|
||||
|
||||
@ -55,7 +54,6 @@ struct Board::Vcpu_context
|
||||
{
|
||||
enum class Init_state {
|
||||
CREATED,
|
||||
INITIALIZING,
|
||||
STARTED
|
||||
};
|
||||
|
||||
|
@ -55,36 +55,57 @@ Vm::~Vm()
|
||||
|
||||
void Vm::run()
|
||||
{
|
||||
if (_vcpu_context.init_state == Board::Vcpu_context::Init_state::CREATED) {
|
||||
_vcpu_context.exit_reason = Board::EXIT_STARTUP;
|
||||
_vcpu_context.init_state = Board::Vcpu_context::Init_state::INITIALIZING;
|
||||
_context.submit(1);
|
||||
if (_cpu().id() != Cpu::executing_id()) {
|
||||
error("vCPU run called from remote core.");
|
||||
return;
|
||||
}
|
||||
|
||||
_sync_from_vmm();
|
||||
/*
|
||||
* On first start, initialize the vCPU
|
||||
*/
|
||||
if (_vcpu_context.init_state == Board::Vcpu_context::Init_state::CREATED) {
|
||||
_vcpu_context.initialize(_cpu(),
|
||||
reinterpret_cast<addr_t>(_id.table));
|
||||
_vcpu_context.tsc_aux_host = _cpu().id();
|
||||
_vcpu_context.init_state = Board::Vcpu_context::Init_state::STARTED;
|
||||
}
|
||||
|
||||
_vcpu_context.read_vcpu_state(_state);
|
||||
|
||||
if (_scheduled != ACTIVE) Cpu_context::_activate();
|
||||
_scheduled = ACTIVE;
|
||||
}
|
||||
|
||||
|
||||
void Vm::proceed()
|
||||
void Vm::pause()
|
||||
{
|
||||
using namespace Board;
|
||||
|
||||
if (_vcpu_context.init_state == Board::Vcpu_context::Init_state::INITIALIZING) {
|
||||
_vcpu_context.initialize(_cpu(),
|
||||
reinterpret_cast<addr_t>(_id.table));
|
||||
_vcpu_context.tsc_aux_host = _cpu().id();
|
||||
_vcpu_context.init_state = Board::Vcpu_context::Init_state::STARTED;
|
||||
|
||||
/*
|
||||
* Sync the initial state from the VMM that was skipped due to
|
||||
* the vCPU being uninitialized.
|
||||
*/
|
||||
_sync_from_vmm();
|
||||
if (_cpu().id() != Cpu::executing_id()) {
|
||||
Genode::error("vCPU pause called from remote core.");
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* The vCPU isn't initialized yet when the VMM first queries the state.
|
||||
* Just return so that the VMM gets presented with the default startup
|
||||
* exit code set at construction.
|
||||
*/
|
||||
if (_vcpu_context.init_state != Board::Vcpu_context::Init_state::STARTED)
|
||||
return;
|
||||
|
||||
_pause_vcpu();
|
||||
|
||||
_vcpu_context.write_vcpu_state(_state);
|
||||
|
||||
/*
|
||||
* Set exit code so that if _run() was not called after an exit, the
|
||||
* next exit due to a signal will be interpreted as PAUSE request.
|
||||
*/
|
||||
_vcpu_context.exit_reason = Board::EXIT_PAUSED;
|
||||
}
|
||||
|
||||
|
||||
void Vm::proceed()
|
||||
{
|
||||
Cpu::Ia32_tsc_aux::write(
|
||||
(Cpu::Ia32_tsc_aux::access_t)_vcpu_context.tsc_aux_guest);
|
||||
|
||||
@ -165,40 +186,6 @@ void Vm::exception(Genode::Cpu_state &state)
|
||||
}
|
||||
|
||||
|
||||
void Vm::_sync_to_vmm()
|
||||
{
|
||||
/*
|
||||
* If the vCPU isn't initialized, sync instructions such as vmread may fail.
|
||||
* Just sync the startup exit and skip the rest of the synchronization.
|
||||
*/
|
||||
if (_vcpu_context.init_state != Board::Vcpu_context::Init_state::STARTED) {
|
||||
_state.exit_reason = (unsigned) Board::EXIT_STARTUP;
|
||||
return;
|
||||
}
|
||||
|
||||
_vcpu_context.write_vcpu_state(_state);
|
||||
|
||||
/*
|
||||
* Set exit code so that if _run() was not called after an exit, the
|
||||
* next exit due to a signal will be interpreted as PAUSE request.
|
||||
*/
|
||||
_vcpu_context.exit_reason = Board::EXIT_PAUSED;
|
||||
}
|
||||
|
||||
|
||||
void Vm::_sync_from_vmm()
|
||||
{
|
||||
/*
|
||||
* Syncing the state to an unitialized vCPU may fail.
|
||||
* The inial state from the VMM will be synced after vCPU initialization.
|
||||
*/
|
||||
if (_vcpu_context.init_state != Board::Vcpu_context::Init_state::STARTED)
|
||||
return;
|
||||
|
||||
_vcpu_context.read_vcpu_state(_state);
|
||||
}
|
||||
|
||||
|
||||
Board::Vcpu_context::Vcpu_context(unsigned id, Vcpu_data &vcpu_data)
|
||||
:
|
||||
regs(1),
|
||||
|
@ -19,8 +19,11 @@
|
||||
#include <base/signal.h>
|
||||
#include <base/sleep.h>
|
||||
#include <base/internal/capability_space.h>
|
||||
|
||||
#include <kernel/interface.h>
|
||||
|
||||
#include <spec/x86/cpu/vcpu_state.h>
|
||||
|
||||
#include <vm_session/connection.h>
|
||||
#include <vm_session/handler.h>
|
||||
|
||||
@ -38,6 +41,7 @@ using Exit_config = Vm_connection::Exit_config;
|
||||
struct Hw_vcpu : Rpc_client<Vm_session::Native_vcpu>, Noncopyable
|
||||
{
|
||||
private:
|
||||
enum Initial_exitcode : unsigned { EXIT_STARTUP = 0xfe };
|
||||
|
||||
Attached_dataspace _state;
|
||||
Native_capability _kernel_vcpu { };
|
||||
@ -73,6 +77,11 @@ Hw_vcpu::Hw_vcpu(Env &env, Vm_connection &vm, Vcpu_handler_base &handler)
|
||||
_kernel_vcpu = call<Rpc_native_vcpu>();
|
||||
_id = counter++;
|
||||
_ep_handler = reinterpret_cast<Thread *>(&handler.rpc_ep());
|
||||
|
||||
/*
|
||||
* Set the startup exit for the initial signal to the VMM's Vcpu_handler
|
||||
*/
|
||||
_local_state().exit_reason = EXIT_STARTUP;
|
||||
}
|
||||
|
||||
|
||||
@ -119,5 +128,8 @@ Vm_connection::Vcpu::Vcpu(Vm_connection &vm, Allocator &alloc,
|
||||
:
|
||||
_native_vcpu(*new (alloc) Hw_vcpu(vm._env, vm, handler))
|
||||
{
|
||||
static_cast<Hw_vcpu &>(_native_vcpu).run();
|
||||
/*
|
||||
* Send the initial startup signal to the vCPU handler.
|
||||
*/
|
||||
Signal_transmitter(handler.signal_cap()).submit();
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user