nova: remove legacy vCPU creation support

Vbox5 was the last user. Seoul and VBox6 using the Genode VM interface.

Issue #5492
This commit is contained in:
Alexander Boettcher 2025-04-10 10:56:43 +02:00 committed by Norman Feske
parent 016d63703d
commit b72a6b3400
9 changed files with 21 additions and 534 deletions

View File

@ -57,35 +57,4 @@ inline void request_signal_sm_cap(Genode::addr_t const cap,
}
inline void translate_remote_pager(Genode::addr_t const cap,
Genode::addr_t const sel)
{
Genode::Thread * myself = Genode::Thread::myself();
Nova::Utcb *utcb = reinterpret_cast<Nova::Utcb *>(myself->utcb());
/* save original receive window */
Nova::Crd orig_crd = utcb->crd_rcv;
utcb->crd_rcv = Nova::Obj_crd();
Genode::uint8_t res = Nova::NOVA_OK;
enum {
TRANSLATE = true, THIS_PD = false, NON_GUEST = false, HOTSPOT = 0
};
/* translate one item */
utcb->msg()[0] = 0xaffe;
utcb->set_msg_word(1);
Nova::Obj_crd obj_crd(sel, 0);
if (utcb->append_item(obj_crd, HOTSPOT, THIS_PD, NON_GUEST, TRANSLATE))
/* trigger the translation */
res = Nova::call(cap);
/* restore original receive window */
utcb->crd_rcv = orig_crd;
if (res != Nova::NOVA_OK)
Genode::error("setting exception portals for vCPU failed res=", res);
}
#endif /* _INCLUDE__NOVA__UTIL_H_ */

View File

@ -20,7 +20,7 @@
struct Genode::Cpu_session::Native_cpu : Interface
{
enum Thread_type { GLOBAL, LOCAL, VCPU };
enum Thread_type { GLOBAL, LOCAL };
/*
* Exception base of thread in caller protection domain - not in core!

View File

@ -47,10 +47,8 @@ class Core::Platform_thread
enum {
MAIN_THREAD = 0x1U,
VCPU = 0x2U,
WORKER = 0x4U,
SC_CREATED = 0x8U,
REMOTE_PD = 0x10U,
WORKER = 0x2U,
SC_CREATED = 0x4U,
};
uint8_t _features;
uint8_t _priority;
@ -63,10 +61,8 @@ class Core::Platform_thread
/* convenience function to access _feature variable */
inline bool main_thread() const { return _features & MAIN_THREAD; }
inline bool vcpu() const { return _features & VCPU; }
inline bool worker() const { return _features & WORKER; }
inline bool sc_created() const { return _features & SC_CREATED; }
inline bool remote_pd() const { return _features & REMOTE_PD; }
/*
* Noncopyable
@ -81,15 +77,6 @@ class Core::Platform_thread
public:
/* mark as vcpu in remote pd if it is a vcpu */
addr_t remote_vcpu() {
if (!vcpu())
return Native_thread::INVALID_INDEX;
_features |= Platform_thread::REMOTE_PD;
return _sel_exc_base;
}
/**
* Constructor
*/

View File

@ -387,39 +387,6 @@ void Pager_object::_invoke_handler(Pager_object &obj)
addr_t const event = utcb.msg()[0];
/* check for translated pager portals - required for vCPU in remote PDs */
if (utcb.msg_items() == 1 && utcb.msg_words() == 1 && event == 0xaffe) {
Nova::Utcb::Item const &item = *utcb.get_item(0);
Nova::Crd const cap(item.crd);
/* valid item which got translated ? */
if (!cap.is_null() && !item.is_del() && _core_ep_ptr) {
_core_ep_ptr->apply(cap.base(),
[&] (Cpu_thread_component *source) {
if (!source)
return;
Platform_thread &p = source->platform_thread();
addr_t const sel_exc_base = p.remote_vcpu();
if (sel_exc_base == Native_thread::INVALID_INDEX)
return;
/* delegate VM-exit portals */
map_vcpu_portals(p.pager(), sel_exc_base, sel_exc_base,
utcb, obj.pd_sel());
/* delegate portal to contact pager */
map_pagefault_portal(obj, p.pager().exc_pt_sel_client(),
sel_exc_base, obj.pd_sel(), utcb);
});
}
utcb.mtd = 0;
utcb.set_msg_word(0);
reply(my_stack_top());
}
utcb.mtd = 0;
utcb.set_msg_word(0);

View File

@ -67,7 +67,7 @@ void Platform_thread::affinity(Affinity::Location location)
if (!_pager)
return;
if (worker() || vcpu() || !sc_created())
if (worker() || !sc_created())
return;
_pager->migrate(platform_specific().sanitize(location));
@ -109,7 +109,7 @@ void Platform_thread::start(void *ip, void *sp)
Pager_object &pager = *_pager;
if (main_thread() && !vcpu() && (_pd.parent_pt_sel() == Native_thread::INVALID_INDEX)) {
if (main_thread() && (_pd.parent_pt_sel() == Native_thread::INVALID_INDEX)) {
error("protection domain undefined");
return;
}
@ -125,7 +125,7 @@ void Platform_thread::start(void *ip, void *sp)
if (!main_thread()) {
addr_t const initial_sp = reinterpret_cast<addr_t>(sp);
addr_t const utcb_addr = vcpu() ? 0 : round_page(initial_sp);
addr_t const utcb_addr = round_page(initial_sp);
if (_sel_exc_base == Native_thread::INVALID_INDEX) {
error("exception base not specified");
@ -144,12 +144,11 @@ void Platform_thread::start(void *ip, void *sp)
return;
}
if (!vcpu())
res = map_thread_portals(pager, _sel_exc_base, utcb);
res = map_thread_portals(pager, _sel_exc_base, utcb);
if (res != NOVA_OK) {
revoke(Obj_crd(_sel_ec(), 0));
error("creation of new thread/vcpu failed ", res);
error("creation of new thread failed ", res);
return;
}
@ -164,28 +163,26 @@ void Platform_thread::start(void *ip, void *sp)
return;
}
if (!vcpu() && _sel_exc_base != Native_thread::INVALID_INDEX) {
if (_sel_exc_base != Native_thread::INVALID_INDEX) {
error("thread already started");
return;
}
addr_t pd_utcb = 0;
if (!vcpu()) {
_sel_exc_base = 0;
_sel_exc_base = 0;
pd_utcb = stack_area_virtual_base() + stack_virtual_size() - get_page_size();
pd_utcb = stack_area_virtual_base() + stack_virtual_size() - get_page_size();
addr_t remap_src[] = { _pd.parent_pt_sel() };
addr_t remap_dst[] = { PT_SEL_PARENT };
addr_t remap_src[] = { _pd.parent_pt_sel() };
addr_t remap_dst[] = { PT_SEL_PARENT };
/* remap exception portals for first thread */
for (unsigned i = 0; i < sizeof(remap_dst)/sizeof(remap_dst[0]); i++) {
if (map_local(source_pd, utcb,
Obj_crd(remap_src[i], 0),
Obj_crd(pager.exc_pt_sel_client() + remap_dst[i], 0)))
return;
}
/* remap exception portals for first thread */
for (unsigned i = 0; i < sizeof(remap_dst)/sizeof(remap_dst[0]); i++) {
if (map_local(source_pd, utcb,
Obj_crd(remap_src[i], 0),
Obj_crd(pager.exc_pt_sel_client() + remap_dst[i], 0)))
return;
}
/* create first thread in task */
@ -202,10 +199,7 @@ void Platform_thread::start(void *ip, void *sp)
pager.initial_eip((addr_t)ip);
pager.initial_esp((addr_t)sp);
if (vcpu())
_features |= REMOTE_PD;
else
res = map_thread_portals(pager, 0, utcb);
res = map_thread_portals(pager, 0, utcb);
if (res == NOVA_OK) {
res = syscall_retry(pager,
@ -333,13 +327,11 @@ void Platform_thread::thread_type(Cpu_session::Native_cpu::Thread_type thread_ty
if (_sel_exc_base != Native_thread::INVALID_INDEX)
return;
if (!main_thread() || (thread_type == Cpu_session::Native_cpu::Thread_type::VCPU))
if (!main_thread())
_sel_exc_base = exception_base.exception_base;
if (thread_type == Cpu_session::Native_cpu::Thread_type::LOCAL)
_features |= WORKER;
else if (thread_type == Cpu_session::Native_cpu::Thread_type::VCPU)
_features |= VCPU;
}

View File

@ -1,83 +0,0 @@
/*
* \brief Utilities for implementing VMMs on Genode/NOVA
* \author Norman Feske
* \date 2013-08-20
*/
/*
* Copyright (C) 2013-2017 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.
*/
#ifndef _INCLUDE__VMM__PRINTF_H_
#define _INCLUDE__VMM__PRINTF_H_
/* Genode includes */
#include <base/thread.h>
#include <base/log.h>
/* NOVA includes */
#include <nova/syscalls.h>
namespace Vmm {
using namespace Genode;
/**
* Print message while preserving the UTCB content
*/
template <typename... ARGS>
void log(ARGS... args)
{
struct Utcb_backup { char buf[Nova::Utcb::size()]; };
static Mutex mutex;
static Utcb_backup utcb_backup;
Mutex::Guard guard(mutex);
utcb_backup = *(Utcb_backup *)Thread::myself()->utcb();
Genode::log("VMM: ", args...);
*(Utcb_backup *)Thread::myself()->utcb() = utcb_backup;
}
template <typename... ARGS>
void warning(ARGS... args)
{
struct Utcb_backup { char buf[Nova::Utcb::size()]; };
static Mutex mutex;
static Utcb_backup utcb_backup;
Mutex::Guard guard(mutex);
utcb_backup = *(Utcb_backup *)Thread::myself()->utcb();
Genode::warning("VMM: ", args...);
*(Utcb_backup *)Thread::myself()->utcb() = utcb_backup;
}
template <typename... ARGS>
void error(ARGS... args)
{
struct Utcb_backup { char buf[Nova::Utcb::size()]; };
static Mutex mutex;
static Utcb_backup utcb_backup;
Mutex::Guard guard(mutex);
utcb_backup = *(Utcb_backup *)Thread::myself()->utcb();
Genode::error("VMM: ", args...);
*(Utcb_backup *)Thread::myself()->utcb() = utcb_backup;
}
}
#endif /* _INCLUDE__VMM__PRINTF_H_ */

View File

@ -1,65 +0,0 @@
/*
* \brief Guard to save a UTCB and restore it during guard destruction
* \author Alexander Boettcher
* \date 2013-07-05
*/
/*
* Copyright (C) 2013-2017 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.
*/
#ifndef _INCLUDE__VMM__UTCB_GUARD_H_
#define _INCLUDE__VMM__UTCB_GUARD_H_
/* Genode includes */
#include <util/string.h>
/* NOVA syscalls */
#include <nova/syscalls.h>
namespace Vmm {
using namespace Genode;
class Utcb_guard;
}
class Vmm::Utcb_guard
{
public:
struct Utcb_backup { char buf[Nova::Utcb::size()]; };
private:
Utcb_backup &_backup_utcb;
public:
Utcb_guard(Utcb_backup &backup_utcb) : _backup_utcb(backup_utcb)
{
Nova::Utcb *utcb =
reinterpret_cast<Nova::Utcb *>(Thread::myself()->utcb());
unsigned header_len = (char *)utcb->msg() - (char *)utcb;
unsigned len = header_len + utcb->msg_words() * sizeof(Nova::mword_t);
Genode::memcpy(&_backup_utcb, utcb, len);
if (utcb->msg_items())
Genode::warning("Error: msg items on UTCB are not saved and restored!");
}
~Utcb_guard()
{
Nova::Utcb *utcb = reinterpret_cast<Nova::Utcb *>(&_backup_utcb);
unsigned header_len = (char *)utcb->msg() - (char *)utcb;
unsigned len = header_len + utcb->msg_words() * sizeof(Nova::mword_t);
Genode::memcpy(Thread::myself()->utcb(), utcb, len);
}
};
#endif /* _INCLUDE__VMM__UTCB_GUARD_H_ */

View File

@ -1,142 +0,0 @@
/*
* \brief Utilities for implementing VMMs on Genode/NOVA
* \author Norman Feske
* \date 2013-08-20
*/
/*
* Copyright (C) 2013-2017 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.
*/
#ifndef _INCLUDE__VMM__VCPU_DISPATCHER_H_
#define _INCLUDE__VMM__VCPU_DISPATCHER_H_
/* Genode includes */
#include <util/retry.h>
#include <nova_native_pd/client.h>
#include <nova/capability_space.h>
namespace Vmm {
using namespace Genode;
template <class T>
class Vcpu_dispatcher;
}
/**
* Thread that handles virtualization events of a 'Vmm::Vcpu_thread'
*/
template <class T>
class Vmm::Vcpu_dispatcher : public T
{
private:
enum { WEIGHT = Genode::Cpu_session::Weight::DEFAULT_WEIGHT };
Env &_env;
Nova_native_pd_client _native_pd { _env.pd().native_pd() };
/**
* Portal entry point entered on virtualization events
*
* For each event type used as argument of the 'register_handler'
* function template, the compiler automatically generates a separate
* instance of this function. The sole purpose of this function is to
* call the 'Vcpu' member function corresponding to the event type.
*/
template <unsigned EV, typename DISPATCHER, void (DISPATCHER::*FUNC)()>
static void _portal_entry()
{
/* obtain this pointer of the event handler */
Genode::Thread *myself = Genode::Thread::myself();
DISPATCHER *vd = static_cast<DISPATCHER *>(myself);
vd->exit_reason = EV;
/* call event-specific handler function */
(vd->*FUNC)();
/* continue execution of the guest */
Nova::reply(myself->stack_top());
}
public:
unsigned int exit_reason = 0;
Vcpu_dispatcher(Genode::Env &env, Genode::size_t stack_size,
Cpu_connection *,
Genode::Affinity::Location location,
const char * name = "vCPU dispatcher")
:
T(WEIGHT, name, stack_size, location), _env(env)
{
using namespace Genode;
/* request creation of a 'local' EC */
T::with_native_thread([&] (Native_thread &nt) {
nt.ec_sel = Native_thread::INVALID_INDEX - 1; });
T::start();
}
/**
* Register virtualization event handler
*/
template <unsigned EV, typename DISPATCHER, void (DISPATCHER::*FUNC)()>
bool register_handler(addr_t exc_base, Nova::Mtd mtd)
{
/*
* Let the compiler generate an instance of a portal entry
*/
void (*entry)() = &_portal_entry<EV, DISPATCHER, FUNC>;
Untyped_capability handler { };
T::with_native_thread([&] (Native_thread &nt) {
/* create the portal at the desired selector index (EV) */
Native_capability thread_cap =
Capability_space::import(nt.ec_sel);
Thread::myself()->with_native_thread([&] (Native_thread &myself_nt) {
handler = retry<Genode::Out_of_ram>(
[&] () {
/* manually define selector used for RPC result */
myself_nt.client_rcv_sel = exc_base + EV;
return _native_pd.alloc_rpc_cap(thread_cap, (addr_t)entry,
mtd.value());
},
[&] () {
myself_nt.reset_client_rcv_sel();
_env.parent().upgrade(Parent::Env::pd(), "ram_quota=16K");
});
/* revert selector allocation to automatic mode of operation */
myself_nt.reset_client_rcv_sel();
});
});
return handler.valid() && (exc_base + EV == (addr_t)handler.local_name());
}
/**
* Unused member of the 'Thread' interface
*
* Similarly to how 'Rpc_entrypoints' are handled, a 'Vcpu_dispatcher'
* comes with a custom initialization procedure, which does not call
* the thread's normal entry function. Instead, the thread's EC gets
* associated with several portals, each for handling a specific
* virtualization event.
*/
void entry() override { }
};
#endif /* _INCLUDE__VMM__VCPU_DISPATCHER_H_ */

View File

@ -1,138 +0,0 @@
/*
* \brief Utilities for implementing VMMs on Genode/NOVA
* \author Norman Feske
* \date 2013-08-20
*/
/*
* Copyright (C) 2013-2017 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.
*/
#ifndef _INCLUDE__VMM__VCPU_THREAD_H_
#define _INCLUDE__VMM__VCPU_THREAD_H_
/* Genode includes */
#include <base/thread.h>
#include <cpu_session/connection.h>
#include <pd_session/connection.h>
#include <region_map/client.h>
#include <cpu_thread/client.h>
#include <nova_native_cpu/client.h>
/* NOVA includes */
#include <nova/native_thread.h>
#include <nova/cap_map.h>
namespace Vmm {
using namespace Genode;
class Vcpu_thread;
class Vcpu_other_pd;
class Vcpu_same_pd;
}
class Vmm::Vcpu_thread
{
public:
virtual Genode::addr_t exc_base() = 0;
virtual void start(Genode::addr_t) = 0;
virtual ~Vcpu_thread() { };
};
class Vmm::Vcpu_other_pd : public Vmm::Vcpu_thread
{
private:
Genode::Capability<Genode::Pd_session> _pd_cap;
Genode::Affinity::Location _location;
Genode::Cpu_connection *_cpu_connection;
Genode::addr_t _exc_pt_sel;
/*
* Noncopyable
*/
Vcpu_other_pd(Vcpu_other_pd const &);
Vcpu_other_pd &operator = (Vcpu_other_pd const &);
public:
Vcpu_other_pd(Cpu_connection * cpu_connection,
Genode::Affinity::Location location,
Genode::Capability<Genode::Pd_session> pd_cap,
Genode::size_t = 0 /* stack_size */)
:
_pd_cap(pd_cap), _location(location), _cpu_connection(cpu_connection),
_exc_pt_sel(Genode::cap_map().insert(Nova::NUM_INITIAL_VCPU_PT_LOG2))
{ }
void start(Genode::addr_t sel_ec) override
{
using namespace Genode;
Thread_capability vcpu_vm { };
while (!vcpu_vm.valid()) {
bool denied = false;
using Error = Cpu_session::Create_thread_error;
_cpu_connection->create_thread(_pd_cap, "vCPU", _location,
Cpu_session::Weight()).with_result(
[&] (Thread_capability cap) { vcpu_vm = cap; },
[&] (Error e) {
if (e == Error::OUT_OF_RAM) _cpu_connection->upgrade_ram(8*1024);
else if (e == Error::OUT_OF_CAPS) _cpu_connection->upgrade_caps(2);
else
denied = true;
}
);
if (denied) {
error("Vcpu_other_pd: failed to create vCPU");
return;
}
}
/* tell parent that this will be a vCPU */
Cpu_session::Native_cpu::Thread_type thread_type { Cpu_session::Native_cpu::Thread_type::VCPU };
Cpu_session::Native_cpu::Exception_base exception_base { _exc_pt_sel };
Nova_native_cpu_client native_cpu(_cpu_connection->native_cpu());
native_cpu.thread_type(vcpu_vm, thread_type, exception_base);
Cpu_thread_client cpu_thread(vcpu_vm);
/*
* Translate vcpu_vm thread cap via current executing thread,
* which is used to lookup current PD to delegate VM-exit portals.
*/
Thread::myself()->with_native_thread([&] (Native_thread &nt) {
addr_t const current = nt.exc_pt_sel + Nova::PT_SEL_PAGE_FAULT;
translate_remote_pager(current, vcpu_vm.local_name()); });
/* start vCPU in separate PD */
cpu_thread.start(0, 0);
/*
* Request native EC thread cap used for recalling vCPU
*/
addr_t const pager_pt = _exc_pt_sel + Nova::PT_SEL_PAGE_FAULT;
request_native_ec_cap(pager_pt, sel_ec);
/* solely needed for vcpu to request native ec cap - drop it */
Nova::revoke(Nova::Obj_crd(pager_pt, 0));
/* request creation of SC to let vCPU run */
cpu_thread.resume();
}
Genode::addr_t exc_base() override { return _exc_pt_sel; }
};
#endif /* _INCLUDE__VMM__VCPU_THREAD_H_ */