diff --git a/base-nova/src/base/thread/thread_nova.cc b/base-nova/src/base/thread/thread_nova.cc
index 048d5c2bd7..f4b94e7452 100644
--- a/base-nova/src/base/thread/thread_nova.cc
+++ b/base-nova/src/base/thread/thread_nova.cc
@@ -129,10 +129,8 @@ void Thread_base::start()
/* request exception portals for normal threads */
if (!_tid.is_vcpu) {
- request_event_portal(pager_cap, _tid.exc_pt_sel,
- PT_SEL_STARTUP);
- request_event_portal(pager_cap, _tid.exc_pt_sel,
- PT_SEL_PAGE_FAULT);
+ request_event_portal(pager_cap, _tid.exc_pt_sel, PT_SEL_STARTUP);
+ request_event_portal(pager_cap, _tid.exc_pt_sel, PT_SEL_PAGE_FAULT);
request_event_portal(pager_cap, _tid.exc_pt_sel, SM_SEL_EC);
}
diff --git a/ports/src/vancouver/main.cc b/ports/src/vancouver/main.cc
index ac81b4e6e9..5a58d6ace5 100644
--- a/ports/src/vancouver/main.cc
+++ b/ports/src/vancouver/main.cc
@@ -32,9 +32,12 @@
#include
#include
#include
+#include
#include
#include
+#include
#include
+#include
/* NOVA includes that come with Genode */
#include
@@ -52,7 +55,8 @@
enum {
PAGE_SIZE_LOG2 = 12UL,
- PAGE_SIZE = 1UL << PAGE_SIZE_LOG2
+ PAGE_SIZE = 1UL << PAGE_SIZE_LOG2,
+ STACK_SIZE = 4096,
};
@@ -65,7 +69,7 @@ enum { verbose_io = false };
*
* Used for startup synchronization and coarse-grained locking.
*/
-Semaphore _lock;
+Genode::Lock global_lock(Genode::Lock::LOCKED);
/**
@@ -165,22 +169,84 @@ class Guest_memory
}
};
+class Vcpu_thread : Genode::Thread {
-class Vcpu_dispatcher : Genode::Thread_base,
+ private:
+
+ /**
+ * Log2 size of portal window used for virtualization events
+ */
+ enum { VCPU_EXC_BASE_LOG2 = 8 };
+
+ public:
+
+ Vcpu_thread(char const * name) : Thread(name)
+ {
+ /* release pre-allocated selectors of Thread */
+ Genode::cap_selector_allocator()->
+ free(tid().exc_pt_sel,
+ Nova::NUM_INITIAL_PT_LOG2);
+
+ /* allocate correct number of selectors */
+ tid().exc_pt_sel = Genode::cap_selector_allocator()->
+ alloc(VCPU_EXC_BASE_LOG2);
+
+ /* tell generic thread code that this becomes a vCPU */
+ tid().is_vcpu = true;
+
+ }
+
+ ~Vcpu_thread()
+ {
+ using namespace Nova;
+
+ revoke(Obj_crd(tid().exc_pt_sel, VCPU_EXC_BASE_LOG2));
+ Genode::cap_selector_allocator()->
+ free(tid().exc_pt_sel, VCPU_EXC_BASE_LOG2);
+
+ /* allocate selectors for ~Thread */
+ tid().exc_pt_sel = Genode::cap_selector_allocator()->
+ alloc(NUM_INITIAL_PT_LOG2);
+ }
+
+ Genode::addr_t exc_base() { return tid().exc_pt_sel; }
+
+ void start() {
+ this->Thread_base::start();
+
+ using namespace Genode;
+ /*
+ * Request native EC thread cap and put it next to the
+ * SM cap - see Vcpu_dispatcher->sel_sm_ec description
+ */
+ addr_t sel_ec = tid().exc_pt_sel + Nova::SM_SEL_EC + 1;
+
+ Nova_cpu_connection cpu;
+ /* Use selector next to SM cap to retrieve EC cap */
+ cpu.rcv_window(sel_ec);
+
+ Native_capability ec_cap = cpu.native_cap(_thread_cap);
+
+ if (!ec_cap.valid() || sel_ec != ec_cap.local_name())
+ Logging::panic("Could not collocate EC cap");
+
+ }
+
+ void entry() { }
+};
+
+class Vcpu_dispatcher : public Genode::Thread,
public StaticReceiver
{
private:
- /**
- * Stack size of vCPU event handler
- */
- enum { STACK_SIZE = 4096 };
-
/**
* Pointer to corresponding VCPU model
*/
VCpu * const _vcpu;
+ Vcpu_thread _vcpu_thread;
+
/**
* Guest-physical memory
*/
@@ -191,29 +257,6 @@ class Vcpu_dispatcher : Genode::Thread_base,
*/
Motherboard &_motherboard;
- /**
- * Base of portal window where the handlers for virtualization events
- * will be registered. This window will be passed to the vCPU EC at
- * creation time.
- */
- Nova::mword_t const _ev_pt_sel_base;
-
- /**
- * Size of portal window used for virtualization events, in log2
- */
- enum { EV_PT_WINDOW_SIZE_LOG2 = 8 };
-
- /**
- * Capability selector used for the VCPU's execution context
- */
- Genode::addr_t const _sm_sel;
- Genode::addr_t const _ec_sel;
-
- /**
- * Capability selector used for the VCPU's scheduling context
- */
- Genode::addr_t const _sc_sel;
-
/***************
** Shortcuts **
@@ -225,11 +268,10 @@ class Vcpu_dispatcher : Genode::Thread_base,
static void _free_sel(Nova::mword_t sel, Genode::size_t num_caps_log2 = 0) {
Genode::cap_selector_allocator()->free(sel, num_caps_log2); }
- static Nova::mword_t _pd_sel() {
- return Genode::cap_selector_allocator()->pd_sel(); }
+ static Nova::mword_t _pd_sel() { return 0; }
static ::Utcb *_utcb_of_myself() {
- return (::Utcb *)Thread_base::myself()->utcb(); }
+ return (::Utcb *)Genode::Thread_base::myself()->utcb(); }
/***********************************
@@ -262,7 +304,7 @@ class Vcpu_dispatcher : Genode::Thread_base,
if (skip == SKIP)
_skip_instruction(msg);
- SemaphoreGuard guard(_lock);
+ Genode::Lock::Guard guard(global_lock);
/**
* Send the message to the VCpu.
@@ -333,14 +375,15 @@ class Vcpu_dispatcher : Genode::Thread_base,
Logging::printf("NPT mapping (base=0x%lx, order=%d hotspot=0x%lx)\n",
crd.base(), crd.order(), hotspot);
- Nova::Utcb * u = (Nova::Utcb *)utcb;
- u->set_msg_word(0);
- u->append_item(crd, hotspot, false, true);
-
/* EPT violation during IDT vectoring? */
if (utcb->inj_info & 0x80000000)
Logging::panic("EPT violation during IDT vectoring - not handled\n");
+ Nova::Utcb * u = (Nova::Utcb *)utcb;
+ u->set_msg_word(0);
+ if (!u->append_item(crd, hotspot, false, true))
+ Logging::printf("Could not map everything");
+
return true;
}
@@ -354,7 +397,7 @@ class Vcpu_dispatcher : Genode::Thread_base,
CpuMessage msg(is_in, static_cast(utcb), io_order, port, &utcb->eax, utcb->mtd);
_skip_instruction(msg);
{
- SemaphoreGuard l(_lock);
+ Genode::Lock::Guard l(global_lock);
if (!_vcpu->executor.send(msg, true))
Logging::panic("nobody to execute %s at %x:%x\n", __func__, msg.cpu->cs.sel, msg.cpu->eip);
}
@@ -430,7 +473,7 @@ class Vcpu_dispatcher : Genode::Thread_base,
static void _portal_entry()
{
/* obtain this pointer of the event handler */
- Thread_base *myself = Thread_base::myself();
+ Genode::Thread_base *myself = Genode::Thread_base::myself();
Vcpu_dispatcher *vd = static_cast(myself);
/* call event-specific handler function */
@@ -444,73 +487,108 @@ class Vcpu_dispatcher : Genode::Thread_base,
* Register virtualization event handler
*/
template
- void _register_handler(Nova::Mtd mtd)
+ void _register_handler(Genode::addr_t exc_base, Nova::Mtd mtd)
{
- /* let the compiler generate an instance of a portal entry */
+ /*
+ * Let the compiler generate an instance of a portal
+ * entry
+ */
void (*portal_entry)() = &_portal_entry;
- Nova::create_pt(_ev_pt_sel_base + EV, _pd_sel(),
- _tid.ec_sel, mtd, (Nova::mword_t)portal_entry);
+ using namespace Genode;
+
+ /* Create the portal at the desired selector index */
+ Cap_connection conn;
+ conn.rcv_window(exc_base + EV);
+
+ Native_capability thread(tid().ec_sel);
+ Native_capability handler =
+ conn.alloc(thread, (Nova::mword_t)portal_entry,
+ mtd.value());
+
+ if (!handler.valid() ||
+ exc_base + EV != handler.local_name())
+ Logging::panic("Could not get EC cap");
}
public:
- Vcpu_dispatcher(VCpu *vcpu,
- Guest_memory &guest_memory,
- Motherboard &motherboard,
- bool has_svm,
- unsigned cpu_number)
+ Vcpu_dispatcher(VCpu *vcpu,
+ Guest_memory &guest_memory,
+ Motherboard &motherboard,
+ Genode::Cap_connection &cap_session,
+ bool has_svm,
+ unsigned cpu_number)
:
- Genode::Thread_base("vcpu_handler", STACK_SIZE),
_vcpu(vcpu),
+ _vcpu_thread("vCPU thread"),
_guest_memory(guest_memory),
- _motherboard(motherboard),
- _ev_pt_sel_base(_alloc_sel(EV_PT_WINDOW_SIZE_LOG2)),
- _sm_sel(_alloc_sel(1)),
- _ec_sel(_sm_sel + 1),
- _sc_sel(_alloc_sel())
+ _motherboard(motherboard)
{
+
+ using namespace Genode;
+
+ /* create new pager object and assign it to the new thread */
+ Pager_capability pager_cap =
+ env()->rm_session()->add_client(_thread_cap);
+ if (!pager_cap.valid())
+ throw Cpu_session::Thread_creation_failed();
+
+ if (env()->cpu_session()->set_pager(_thread_cap, pager_cap))
+ throw Cpu_session::Thread_creation_failed();
+
+ addr_t thread_sp = (addr_t)&_context->stack[-4];
+ Genode::Nova_cpu_connection cpu;
+ cpu.start_exc_base_vcpu(_thread_cap, 0, thread_sp,
+ _tid.exc_pt_sel);
+
+ request_event_portal(pager_cap, _tid.exc_pt_sel,
+ Nova::PT_SEL_PAGE_FAULT);
+ request_event_portal(pager_cap, _tid.exc_pt_sel,
+ Nova::SM_SEL_EC);
+
+ /**
+ * Request native thread cap, _thread_cap only a token.
+ * The native thread cap is required to attach new rpc objects
+ * (to create portals bound to the ec)
+ */
+ Native_capability ec_cap = cpu.native_cap(_thread_cap);
+ _tid.ec_sel = ec_cap.local_name();
+
+ /* init utcb of dispatcher thread */
+ Nova::Utcb * utcb = reinterpret_cast(&_context->utcb);
+ utcb->set_msg_word(0);
+ utcb->crd_xlt = Nova::Crd(0);
+ utcb->crd_rcv = Nova::Crd(0);
+
using namespace Nova;
- /*
- * Stack and UTCB of local EC used as VCPU event handler
- */
- mword_t *const sp = (mword_t * const)_context->stack;
- mword_t const utcb = (mword_t) &_context->utcb;
-
- /*
- * XXX: Propagate the specified CPU number. For now, we ignore
- * the 'cpu_number' argument.
- */
-
- /*
- * Create local EC used handling virtualization events. This EC has
- * is executed with the CPU time donated by the guest OS.
- */
- enum { CPU_NUMBER = 0, GLOBAL_NO = false };
- if (create_ec(_tid.ec_sel, _pd_sel(),
- CPU_NUMBER, utcb, (mword_t)sp,
- _tid.exc_pt_sel, GLOBAL_NO))
- PERR("create_ec failed while creating the handler EC");
-
-
/* shortcuts for common message-transfer descriptors */
Mtd const mtd_all(Mtd::ALL);
Mtd const mtd_cpuid(Mtd::EIP | Mtd::ACDB | Mtd::IRQ);
Mtd const mtd_irq(Mtd::IRQ);
/*
- * Register VCPU SVM event handlers
+ * Register vCPU event handlers
*/
typedef Vcpu_dispatcher This;
if (has_svm) {
+ Genode::addr_t exc_base =
+ _vcpu_thread.exc_base();
- _register_handler<0x72, &This::_svm_cpuid> (mtd_cpuid);
- _register_handler<0x7b, &This::_svm_ioio> (mtd_all);
- _register_handler<0x7c, &This::_svm_msr> (mtd_all);
- _register_handler<0xfc, &This::_svm_npt> (mtd_all);
- _register_handler<0xfd, &This::_svm_invalid> (mtd_all);
- _register_handler<0xfe, &This::_svm_startup> (mtd_all);
- _register_handler<0xff, &This::_recall> (mtd_irq);
+ _register_handler<0x72, &This::_svm_cpuid>
+ (exc_base, mtd_cpuid);
+ _register_handler<0x7b, &This::_svm_ioio>
+ (exc_base, mtd_all);
+ _register_handler<0x7c, &This::_svm_msr>
+ (exc_base, mtd_all);
+ _register_handler<0xfc, &This::_svm_npt>
+ (exc_base, mtd_all);
+ _register_handler<0xfd, &This::_svm_invalid>
+ (exc_base, mtd_all);
+ _register_handler<0xfe, &This::_svm_startup>
+ (exc_base, mtd_all);
+ _register_handler<0xff, &This::_recall>
+ (exc_base, mtd_irq);
} else {
@@ -520,27 +598,8 @@ class Vcpu_dispatcher : Genode::Thread_base,
Logging::panic("no SVM available, sorry");
}
- /*
- * Create semaphore used as mechanism for blocking the VCPU
- */
- if (create_sm(_sm_sel, _pd_sel(), 0))
- PERR("create_rm returned failed while creating VCPU");
-
- /*
- * Create EC for virtual CPU. The virtual CPU is a global EC
- * because it executes on an own budget of CPU time.
- */
- enum { GLOBAL_YES = true, UTCB_VCPU = 0 };
- if (create_ec(_ec_sel, _pd_sel(), CPU_NUMBER, UTCB_VCPU,
- 0, _ev_pt_sel_base, GLOBAL_YES))
- PERR("create_ec failed while creating VCPU");
-
- /*
- * Fuel the VCPU with CPU time by attaching a scheduling context to
- * it.
- */
- if (create_sc(_sc_sel, _pd_sel(), _ec_sel, Nova::Qpd()))
- PERR("create_sc failed while creating VCPU");
+ /* let vCPU run */
+ _vcpu_thread.start();
/* handle cpuid overrides */
vcpu->executor.add(this, receive_static);
@@ -549,37 +608,32 @@ class Vcpu_dispatcher : Genode::Thread_base,
/**
* Destructor
*/
- ~Vcpu_dispatcher()
- {
- _free_sel(_ev_pt_sel_base, EV_PT_WINDOW_SIZE_LOG2);
- _free_sel(_ec_sel, 0);
- _free_sel(_sc_sel, 0);
- }
+ ~Vcpu_dispatcher() { }
/**
* Unused member of the 'Thread_base' 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. Instread, the thread's EC gets
- * associated with several portals, each for handling a specific
- * virtualization event.
+ * 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() { }
/**
* 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 identifyer of the
- * VCPU. I.e., it gets passed as arguments for 'MessageHostOp'
- * operations.
- *
- * XXX: Couldn't we just use the 'Vcpu_dispatcher' as unique
- * identifier instead?
+ * 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 sm_ec_sel_base() const { return _sm_sel; }
+ Nova::mword_t sel_sm_ec() {
+ return _vcpu_thread.exc_base() + Nova::SM_SEL_EC;
+ }
/***********************************
@@ -658,15 +712,17 @@ class Machine : public StaticReceiver
case MessageHostOp::OP_VCPU_CREATE_BACKEND:
{
Logging::printf("OP_VCPU_CREATE_BACKEND\n");
+
+ static Genode::Cap_connection cap_session;
/*
* XXX determine real CPU number, hardcoded CPU 0 for now
*/
enum { CPU_NUMBER = 0 };
Vcpu_dispatcher *vcpu_dispatcher =
- new Vcpu_dispatcher(msg.vcpu, _guest_memory, _motherboard,
+ new Vcpu_dispatcher(msg.vcpu, _guest_memory, _motherboard, cap_session,
_hip->has_svm(), CPU_NUMBER);
- msg.value = vcpu_dispatcher->sm_ec_sel_base();
+ msg.value = vcpu_dispatcher->sel_sm_ec();
return true;
}
@@ -686,11 +742,11 @@ class Machine : public StaticReceiver
{
Logging::printf("OP_VCPU_BLOCK\n");
- _lock.up();
+ global_lock.lock();
Logging::printf("going to block\n");
bool res = (Nova::sm_ctrl(msg.value, Nova::SEMAPHORE_DOWN) == 0);
- Logging::printf("woke up from vcpu sem, block on _lock\n");
- _lock.down();
+ Logging::printf("woke up from vcpu sem, block on global_lock\n");
+ global_lock.unlock();
return res;
}
@@ -966,7 +1022,7 @@ class Machine : public StaticReceiver
MessageLegacy msg2(MessageLegacy::RESET, 0);
_motherboard.bus_legacy.send_fifo(msg2);
- _lock.up();
+ global_lock.unlock();
Logging::printf("INIT done\n");
}
@@ -981,10 +1037,6 @@ int main(int argc, char **argv)
{
Genode::printf("--- Vancouver VMM started ---\n");
- _lock = Semaphore(Genode::cap_selector_allocator()->alloc());
- if (Nova::create_sm(_lock.sm(), Genode::cap_selector_allocator()->pd_sel(), 0))
- PWRN("_lock creation failed");
-
static Boot_module_provider
boot_modules(Genode::config()->xml_node().sub_node("multiboot"));
diff --git a/ports/src/vancouver/target.mk b/ports/src/vancouver/target.mk
index fca241f59e..05a0ad5652 100644
--- a/ports/src/vancouver/target.mk
+++ b/ports/src/vancouver/target.mk
@@ -10,7 +10,7 @@ ifeq ($(wildcard $(VANCOUVER_DIR)),)
REQUIRES += prepare_ports_vancouver
endif
-LIBS += cxx env thread
+LIBS += cxx env thread server
SRC_CC = main.cc nova_user_env.cc device_model_registry.cc
#