mirror of
https://github.com/genodelabs/genode.git
synced 2025-05-29 13:44:26 +00:00
parent
5136883ded
commit
1515a0a51e
@ -1,11 +1,12 @@
|
|||||||
/*
|
/*
|
||||||
* \brief Client-side VM session interface
|
* \brief Client-side VM session interface
|
||||||
* \author Alexander Boettcher
|
* \author Alexander Boettcher
|
||||||
|
* \author Benjamin Lamowski
|
||||||
* \date 2018-08-27
|
* \date 2018-08-27
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2018-2021 Genode Labs GmbH
|
* Copyright (C) 2018-2023 Genode Labs GmbH
|
||||||
*
|
*
|
||||||
* This file is part of the Genode OS framework, which is distributed
|
* This file is part of the Genode OS framework, which is distributed
|
||||||
* under the terms of the GNU Affero General Public License version 3.
|
* under the terms of the GNU Affero General Public License version 3.
|
||||||
@ -16,6 +17,7 @@
|
|||||||
#include <base/attached_dataspace.h>
|
#include <base/attached_dataspace.h>
|
||||||
#include <base/env.h>
|
#include <base/env.h>
|
||||||
#include <base/registry.h>
|
#include <base/registry.h>
|
||||||
|
#include <base/sleep.h>
|
||||||
|
|
||||||
#include <vm_session/connection.h>
|
#include <vm_session/connection.h>
|
||||||
#include <vm_session/handler.h>
|
#include <vm_session/handler.h>
|
||||||
@ -36,6 +38,7 @@
|
|||||||
using namespace Genode;
|
using namespace Genode;
|
||||||
|
|
||||||
using Exit_config = Vm_connection::Exit_config;
|
using Exit_config = Vm_connection::Exit_config;
|
||||||
|
using Call_with_state = Vm_connection::Call_with_state;
|
||||||
|
|
||||||
struct Sel4_vcpu;
|
struct Sel4_vcpu;
|
||||||
|
|
||||||
@ -71,11 +74,16 @@ struct Sel4_vcpu : Genode::Thread, Noncopyable
|
|||||||
private:
|
private:
|
||||||
|
|
||||||
Vcpu_handler_base &_vcpu_handler;
|
Vcpu_handler_base &_vcpu_handler;
|
||||||
|
Vcpu_handler<Sel4_vcpu> _exit_handler;
|
||||||
Vcpu_state _state __attribute__((aligned(0x10))) { };
|
Vcpu_state _state __attribute__((aligned(0x10))) { };
|
||||||
Semaphore _wake_up { 0 };
|
Semaphore _wake_up { 0 };
|
||||||
Blockade _startup { };
|
Blockade _startup { };
|
||||||
addr_t _recall { 0 };
|
addr_t _recall { 0 };
|
||||||
uint64_t _tsc_offset { 0 };
|
uint64_t _tsc_offset { 0 };
|
||||||
|
Semaphore _state_ready { 0 };
|
||||||
|
bool _dispatching { false };
|
||||||
|
bool _extra_dispatch_up { false };
|
||||||
|
void *_ep_handler { nullptr };
|
||||||
|
|
||||||
Constructible<Sel4_native_rpc> _rpc { };
|
Constructible<Sel4_native_rpc> _rpc { };
|
||||||
|
|
||||||
@ -133,7 +141,7 @@ struct Sel4_vcpu : Genode::Thread, Noncopyable
|
|||||||
/* get selector to call back a vCPU into VMM */
|
/* get selector to call back a vCPU into VMM */
|
||||||
_recall = _stack->utcb().lock_sel();
|
_recall = _stack->utcb().lock_sel();
|
||||||
|
|
||||||
Vcpu_state &state = this->state();
|
Vcpu_state &state = _state;
|
||||||
state.discharge();
|
state.discharge();
|
||||||
|
|
||||||
/* wait for first user resume() */
|
/* wait for first user resume() */
|
||||||
@ -148,9 +156,10 @@ struct Sel4_vcpu : Genode::Thread, Noncopyable
|
|||||||
state.exit_reason = VMEXIT_STARTUP;
|
state.exit_reason = VMEXIT_STARTUP;
|
||||||
_read_sel4_state(service, state);
|
_read_sel4_state(service, state);
|
||||||
|
|
||||||
Genode::Signal_transmitter(_vcpu_handler.signal_cap()).submit();
|
_state_ready.up();
|
||||||
|
Signal_transmitter(_exit_handler.signal_cap()).submit();
|
||||||
|
|
||||||
_vcpu_handler.ready_semaphore().down();
|
_exit_handler.ready_semaphore().down();
|
||||||
_wake_up.down();
|
_wake_up.down();
|
||||||
|
|
||||||
State local_state { NONE };
|
State local_state { NONE };
|
||||||
@ -163,7 +172,7 @@ struct Sel4_vcpu : Genode::Thread, Noncopyable
|
|||||||
{
|
{
|
||||||
Mutex::Guard guard(_remote_mutex);
|
Mutex::Guard guard(_remote_mutex);
|
||||||
|
|
||||||
local_state = _remote;
|
local_state = _remote;
|
||||||
_remote = NONE;
|
_remote = NONE;
|
||||||
|
|
||||||
if (local_state == PAUSE) {
|
if (local_state == PAUSE) {
|
||||||
@ -192,14 +201,13 @@ struct Sel4_vcpu : Genode::Thread, Noncopyable
|
|||||||
|
|
||||||
_read_sel4_state(service, state);
|
_read_sel4_state(service, state);
|
||||||
|
|
||||||
/* notify VM handler */
|
_state_ready.up();
|
||||||
Genode::Signal_transmitter(_vcpu_handler.signal_cap()).submit();
|
|
||||||
|
if (_extra_dispatch_up) {
|
||||||
|
_extra_dispatch_up = false;
|
||||||
|
_exit_handler.ready_semaphore().down();
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Wait until VM handler is really really done,
|
|
||||||
* otherwise we lose state.
|
|
||||||
*/
|
|
||||||
_vcpu_handler.ready_semaphore().down();
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -219,8 +227,18 @@ struct Sel4_vcpu : Genode::Thread, Noncopyable
|
|||||||
|
|
||||||
state.discharge();
|
state.discharge();
|
||||||
|
|
||||||
if (res != SEL4_VMENTER_RESULT_FAULT)
|
/*
|
||||||
|
* If a VMEXIT_RECALL is dispatched here, it comes from a
|
||||||
|
* pause request sent by an already running asynchronous signal
|
||||||
|
* handler.
|
||||||
|
* In that case, don't dispatch an extra exit signal.
|
||||||
|
*/
|
||||||
|
bool skip_dispatch = false;
|
||||||
|
|
||||||
|
if (res != SEL4_VMENTER_RESULT_FAULT) {
|
||||||
state.exit_reason = VMEXIT_RECALL;
|
state.exit_reason = VMEXIT_RECALL;
|
||||||
|
skip_dispatch = true;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
state.exit_reason = (unsigned)seL4_GetMR(SEL4_VMENTER_FAULT_REASON_MR);
|
state.exit_reason = (unsigned)seL4_GetMR(SEL4_VMENTER_FAULT_REASON_MR);
|
||||||
|
|
||||||
@ -233,15 +251,18 @@ struct Sel4_vcpu : Genode::Thread, Noncopyable
|
|||||||
_wake_up.down();
|
_wake_up.down();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
_state_ready.up();
|
||||||
|
|
||||||
|
if (skip_dispatch)
|
||||||
|
continue;
|
||||||
/* notify VM handler */
|
/* notify VM handler */
|
||||||
Genode::Signal_transmitter(_vcpu_handler.signal_cap()).submit();
|
Genode::Signal_transmitter(_exit_handler.signal_cap()).submit();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Wait until VM handler is really really done,
|
* Wait until VM handler is really really done,
|
||||||
* otherwise we lose state.
|
* otherwise we lose state.
|
||||||
*/
|
*/
|
||||||
_vcpu_handler.ready_semaphore().down();
|
_exit_handler.ready_semaphore().down();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -747,6 +768,13 @@ struct Sel4_vcpu : Genode::Thread, Noncopyable
|
|||||||
return ep->affinity();
|
return ep->affinity();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _wrapper_dispatch()
|
||||||
|
{
|
||||||
|
_dispatching = true;
|
||||||
|
_vcpu_handler.dispatch(1);
|
||||||
|
_dispatching = false;
|
||||||
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
Sel4_vcpu(Env &env, Vm_connection &vm,
|
Sel4_vcpu(Env &env, Vm_connection &vm,
|
||||||
@ -754,12 +782,14 @@ struct Sel4_vcpu : Genode::Thread, Noncopyable
|
|||||||
:
|
:
|
||||||
Thread(env, "vcpu_thread", STACK_SIZE, _location(handler),
|
Thread(env, "vcpu_thread", STACK_SIZE, _location(handler),
|
||||||
Weight(), env.cpu()),
|
Weight(), env.cpu()),
|
||||||
_vcpu_handler(handler)
|
_vcpu_handler(handler),
|
||||||
|
_exit_handler(handler.ep(), *this, &Sel4_vcpu::_wrapper_dispatch)
|
||||||
{
|
{
|
||||||
Thread::start();
|
Thread::start();
|
||||||
|
|
||||||
/* wait until thread is alive, e.g. Thread::cap() is valid */
|
/* wait until thread is alive, e.g. Thread::cap() is valid */
|
||||||
_startup.block();
|
_startup.block();
|
||||||
|
_ep_handler = reinterpret_cast<Thread *>(&handler.rpc_ep());
|
||||||
|
|
||||||
_rpc.construct(vm, this->cap(), *this);
|
_rpc.construct(vm, this->cap(), *this);
|
||||||
|
|
||||||
@ -767,6 +797,9 @@ struct Sel4_vcpu : Genode::Thread, Noncopyable
|
|||||||
_wake_up.up();
|
_wake_up.up();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const Sel4_vcpu& operator=(const Sel4_vcpu &) = delete;
|
||||||
|
Sel4_vcpu(const Sel4_vcpu&) = delete;
|
||||||
|
|
||||||
void resume()
|
void resume()
|
||||||
{
|
{
|
||||||
Mutex::Guard guard(_remote_mutex);
|
Mutex::Guard guard(_remote_mutex);
|
||||||
@ -778,21 +811,52 @@ struct Sel4_vcpu : Genode::Thread, Noncopyable
|
|||||||
_wake_up.up();
|
_wake_up.up();
|
||||||
}
|
}
|
||||||
|
|
||||||
void pause()
|
void with_state(Call_with_state &cw)
|
||||||
{
|
{
|
||||||
Mutex::Guard guard(_remote_mutex);
|
if (!_dispatching) {
|
||||||
|
if (Thread::myself() != _ep_handler) {
|
||||||
|
error("vCPU state requested outside of vcpu_handler EP");
|
||||||
|
sleep_forever();
|
||||||
|
}
|
||||||
|
|
||||||
if (_remote == PAUSE)
|
_remote_mutex.acquire();
|
||||||
return;
|
|
||||||
|
|
||||||
_remote = PAUSE;
|
/* Trigger pause exit */
|
||||||
|
_remote = PAUSE;
|
||||||
|
seL4_Signal(_recall);
|
||||||
|
_wake_up.up();
|
||||||
|
|
||||||
seL4_Signal(_recall);
|
_remote_mutex.release();
|
||||||
|
_state_ready.down();
|
||||||
|
|
||||||
_wake_up.up();
|
/*
|
||||||
|
* We're in the async dispatch, yet processing a non-pause exit.
|
||||||
|
* Signal that we have to wrap the dispatch loop around.
|
||||||
|
*/
|
||||||
|
if (_state.exit_reason != VMEXIT_RECALL) {
|
||||||
|
_extra_dispatch_up = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
_state_ready.down();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cw.call_with_state(_state)
|
||||||
|
|| _extra_dispatch_up)
|
||||||
|
resume();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The regular exit was handled by the asynchronous dispatch handler
|
||||||
|
* triggered by the pause request.
|
||||||
|
*
|
||||||
|
* Fake finishing the exit dispatch so that the vCPU loop
|
||||||
|
* processes the asynchronously dispatched exit and provides
|
||||||
|
* the VMEXIT_RECALL to the already pending dispatch function
|
||||||
|
* for the exit code.
|
||||||
|
*/
|
||||||
|
if (!_dispatching && _extra_dispatch_up)
|
||||||
|
_exit_handler.ready_semaphore().up();
|
||||||
}
|
}
|
||||||
|
|
||||||
Vcpu_state & state() { return _state; }
|
|
||||||
Sel4_native_rpc * rpc() { return &*_rpc; }
|
Sel4_native_rpc * rpc() { return &*_rpc; }
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -800,13 +864,13 @@ struct Sel4_vcpu : Genode::Thread, Noncopyable
|
|||||||
** vCPU API **
|
** vCPU API **
|
||||||
**************/
|
**************/
|
||||||
|
|
||||||
void Vm_connection::Vcpu::run() { static_cast<Sel4_native_rpc &>(_native_vcpu).vcpu.resume(); }
|
void Vm_connection::Vcpu::_with_state(Call_with_state &cw) { static_cast<Sel4_native_rpc &>(_native_vcpu).vcpu.with_state(cw); }
|
||||||
void Vm_connection::Vcpu::pause() { static_cast<Sel4_native_rpc &>(_native_vcpu).vcpu.pause(); }
|
|
||||||
Vcpu_state & Vm_connection::Vcpu::state() { return static_cast<Sel4_native_rpc &>(_native_vcpu).vcpu.state(); }
|
|
||||||
|
|
||||||
|
|
||||||
Vm_connection::Vcpu::Vcpu(Vm_connection &vm, Allocator &alloc,
|
Vm_connection::Vcpu::Vcpu(Vm_connection &vm, Allocator &alloc,
|
||||||
Vcpu_handler_base &handler, Exit_config const &exit_config)
|
Vcpu_handler_base &handler, Exit_config const &exit_config)
|
||||||
:
|
:
|
||||||
_native_vcpu(*((new (alloc) Sel4_vcpu(vm._env, vm, handler, exit_config))->rpc()))
|
_native_vcpu(*((new (alloc) Sel4_vcpu(vm._env, vm, handler, exit_config))->rpc()))
|
||||||
{ }
|
{
|
||||||
|
static_cast<Sel4_native_rpc &>(_native_vcpu).vcpu.resume();
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user