base-sel4: port to new VMM library API

Ref #4968
This commit is contained in:
Benjamin Lamowski 2023-08-09 16:21:12 +02:00 committed by Christian Helmuth
parent 5136883ded
commit 1515a0a51e

View File

@ -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();
}