/* * \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 #include #include namespace Vmm { using namespace Genode; template class Vcpu_dispatcher; } /** * Thread that handles virtualization events of a 'Vmm::Vcpu_thread' */ template 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 static void _portal_entry() { /* obtain this pointer of the event handler */ Genode::Thread *myself = Genode::Thread::myself(); DISPATCHER *vd = static_cast(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_session * cpu_session, 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::native_thread().ec_sel = Native_thread::INVALID_INDEX - 1; T::start(); } /** * Register virtualization event handler */ template bool register_handler(addr_t exc_base, Nova::Mtd mtd) { /* * Let the compiler generate an instance of a portal entry */ void (*entry)() = &_portal_entry; /* create the portal at the desired selector index (EV) */ Native_capability thread_cap = Capability_space::import(T::native_thread().ec_sel); Untyped_capability handler = retry( [&] () { /* manually define selector used for RPC result */ Thread::myself()->native_thread().client_rcv_sel = exc_base + EV; return _native_pd.alloc_rpc_cap(thread_cap, (addr_t)entry, mtd.value()); }, [&] () { Thread::myself()->native_thread().reset_client_rcv_sel(); _env.parent().upgrade(Parent::Env::pd(), "ram_quota=16K"); }); /* revert selector allocation to automatic mode of operation */ Thread::myself()->native_thread().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 { } /** * Return capability selector of the VCPU's SM and EC * * The returned number corresponds to the VCPU's semaphore selector. * The consecutive number corresponds to the EC. The number returned by * this function is used by the VMM code as a unique identifier of the * VCPU. I.e., it gets passed as arguments for 'MessageHostOp' * operations. */ Nova::mword_t sel_sm_ec() { return T::native_thread().exc_pt_sel + Nova::SM_SEL_EC; } }; #endif /* _INCLUDE__VMM__VCPU_DISPATCHER_H_ */