ports: common utilities for building VMMs on NOVA

To ease the creation of custom virtual machine monitors on top of
NOVA, this patch moves generic utilities from vancouver resp. seoul to the
public include location 'ports/include/vmm'. As a nice side effect,
this change simplifies 'vancouver/main.cc'.

Issue #949
This commit is contained in:
Norman Feske 2013-08-20 21:24:52 +02:00
parent 07aa56fffb
commit 1df48b8331
16 changed files with 660 additions and 275 deletions

View File

@ -0,0 +1,95 @@
/*
* \brief Utilities for implementing VMMs on Genode/NOVA
* \author Norman Feske
* \date 2013-08-20
*
* The VMM and the guest share the same PD. However, the guest's view on the PD
* is restricted to the guest-physical-to-VMM-local mappings installed by the
* VMM for the VCPU's EC.
*
* The guest memory is shadowed at the lower portion of the VMM's address
* space. If the guest (the VCPU EC) tries to access a page that has no mapping
* in the VMM's PD, NOVA does not generate a page-fault (which would be
* delivered to the pager of the VMM, i.e., core) but it produces a NPT
* virtualization event handled locally by the VMM. The NPT event handler is
* the '_svm_npt' function.
*/
/*
* Copyright (C) 2013 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#ifndef _INCLUDE__VMM__GUEST_MEMORY_H_
#define _INCLUDE__VMM__GUEST_MEMORY_H_
/* Genode includes */
#include <os/attached_ram_dataspace.h>
#include <rm_session/connection.h>
/* VMM utilities includes */
#include <vmm/types.h>
namespace Vmm {
using namespace Genode;
class Virtual_reservation;
class Guest_memory;
}
/**
* The 'Virtual_reservation' is a managed dataspace that occupies the lower
* part of the address space, which contains the shadow of the VCPU's physical
* memory.
*/
struct Vmm::Virtual_reservation : Rm_connection
{
Virtual_reservation(addr_t vm_size)
:
Rm_connection(0, vm_size)
{
try {
/*
* Attach reservation to the beginning of the local address
* space. We leave out the very first page because core denies
* the attachment of anything at the zero page.
*/
env()->rm_session()->attach_at(Rm_connection::dataspace(),
PAGE_SIZE, 0, PAGE_SIZE);
} catch (Rm_session::Region_conflict) {
PERR("region conflict while attaching guest-physical memory");
}
}
~Virtual_reservation()
{
env()->rm_session()->detach((void *)PAGE_SIZE);
}
};
/**
* Representation of guest memory
*
*/
struct Vmm::Guest_memory : Attached_ram_dataspace
{
/**
* Constructor
*
* \param backing_store_size number of bytes of physical RAM to be
* used as guest-physical and device memory,
* allocated from core's RAM service
*/
Guest_memory(size_t backing_store_size)
:
Attached_ram_dataspace(env()->ram_session(), backing_store_size)
{ }
};
#endif /* _INCLUDE__VMM__GUEST_MEMORY_H_ */

View File

@ -0,0 +1,53 @@
/*
* \brief Utilities for implementing VMMs on Genode/NOVA
* \author Norman Feske
* \date 2013-08-20
*/
/*
* Copyright (C) 2013 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#ifndef _INCLUDE__VMM__PRINTF_H_
#define _INCLUDE__VMM__PRINTF_H_
/* Genode includes */
#include <base/thread.h>
#include <base/lock.h>
#include <base/printf.h>
namespace Vmm {
using namespace Genode;
void printf(const char *format, ...);
}
/**
* Print message while preserving the UTCB content
*/
void Vmm::printf(const char *format, ...)
{
va_list list;
va_start(list, format);
static Lock lock;
static Native_utcb utcb_backup;
Lock::Guard guard(lock);
utcb_backup = *Thread_base::myself()->utcb();
Genode::printf("VMM: ");
Genode::vprintf(format, list);
*Thread_base::myself()->utcb() = utcb_backup;
va_end(list);
}
#endif /* _INCLUDE__VMM__PRINTF_H_ */

25
ports/include/vmm/types.h Normal file
View File

@ -0,0 +1,25 @@
/*
* \brief Utilities for implementing VMMs on Genode/NOVA
* \author Norman Feske
* \date 2013-08-20
*/
/*
* Copyright (C) 2013 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#ifndef _INCLUDE__VMM__TYPES_H_
#define _INCLUDE__VMM__TYPES_H_
namespace Vmm {
enum {
PAGE_SIZE_LOG2 = 12UL,
PAGE_SIZE = PAGE_SIZE_LOG2 << 12
};
}
#endif /* _INCLUDE__VMM__TYPES_H_ */

View File

@ -1,5 +1,5 @@
/*
* \brief Guard to save a utcb and restore it during Guard desctruction
* \brief Guard to save a UTCB and restore it during guard destruction
* \author Alexander Boettcher
* \date 2013-07-05
*/
@ -11,24 +11,33 @@
* under the terms of the GNU General Public License version 2.
*/
#ifndef _SEOUL_UTCB_GUARD_H_
#define _SEOUL_UTCB_GUARD_H_
#ifndef _INCLUDE__VMM__UTCB_GUARD_H_
#define _INCLUDE__VMM__UTCB_GUARD_H_
/* Genode includes */
#include <base/printf.h>
#include <util/string.h>
/* NOVA syscalls */
#include <nova/syscalls.h>
class Utcb_guard {
namespace Vmm {
using namespace Genode;
class Utcb_guard;
}
class Vmm::Utcb_guard
{
private:
Genode::Native_utcb &_backup_utcb;
Native_utcb &_backup_utcb;
public:
Utcb_guard(Genode::Native_utcb &backup_utcb)
: _backup_utcb(backup_utcb)
Utcb_guard(Native_utcb &backup_utcb) : _backup_utcb(backup_utcb)
{
using namespace Genode;
Nova::Utcb *utcb =
reinterpret_cast<Nova::Utcb *>(Thread_base::myself()->utcb());
@ -37,13 +46,11 @@ class Utcb_guard {
Genode::memcpy(&_backup_utcb, utcb, len);
if (utcb->msg_items())
PWRN("Error: msg items on UTCB are not saved and restored !!!");
PWRN("Error: msg items on UTCB are not saved and restored!");
}
~Utcb_guard()
{
using namespace Genode;
Nova::Utcb *utcb = reinterpret_cast<Nova::Utcb *>(&_backup_utcb);
unsigned header_len = (char *)utcb->msg - (char *)utcb;
@ -52,4 +59,4 @@ class Utcb_guard {
}
};
#endif /* _SEOUL_UTCB_GUARD_H_ */
#endif /* _INCLUDE__VMM__UTCB_GUARD_H_ */

View File

@ -0,0 +1,118 @@
/*
* \brief Utilities for implementing VMMs on Genode/NOVA
* \author Norman Feske
* \date 2013-08-20
*/
/*
* Copyright (C) 2013 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#ifndef _INCLUDE__VMM__VCPU_DISPATCHER_H_
#define _INCLUDE__VMM__VCPU_DISPATCHER_H_
namespace Vmm {
using namespace Genode;
class Vcpu_dispatcher;
}
/**
* Thread that handles virtualization events of a 'Vmm::Vcpu_thread'
*/
class Vmm::Vcpu_dispatcher : public Genode::Thread_base
{
private:
Cap_connection &_cap;
/**
* 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_base *myself = Genode::Thread_base::myself();
DISPATCHER *vd = static_cast<DISPATCHER *>(myself);
/* call event-specific handler function */
(vd->*FUNC)();
/* continue execution of the guest */
Nova::reply(myself->stack_top());
}
public:
Vcpu_dispatcher(size_t stack_size, Cap_connection &cap)
:
Thread_base("vCPU dispatcher", stack_size),
_cap(cap)
{
using namespace Genode;
/* request creation of a 'local' EC */
_tid.ec_sel = Native_thread::INVALID_INDEX - 1;
Thread_base::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>;
/* Create the portal at the desired selector index */
_cap.rcv_window(exc_base + EV);
Native_capability thread_cap(tid().ec_sel);
Native_capability handler =
_cap.alloc(thread_cap, (Nova::mword_t)entry, mtd.value());
return handler.valid() && (exc_base + EV == handler.local_name());
}
/**
* 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. 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 identifier of the
* VCPU. I.e., it gets passed as arguments for 'MessageHostOp'
* operations.
*/
Nova::mword_t sel_sm_ec()
{
return tid().exc_pt_sel + Nova::SM_SEL_EC;
}
};
#endif /* _INCLUDE__VMM__VCPU_DISPATCHER_H_ */

View File

@ -0,0 +1,83 @@
/*
* \brief Utilities for implementing VMMs on Genode/NOVA
* \author Norman Feske
* \date 2013-08-20
*/
/*
* Copyright (C) 2013 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#ifndef _INCLUDE__VMM__VCPU_THREAD_H_
#define _INCLUDE__VMM__VCPU_THREAD_H_
/* Genode includes */
#include <base/cap_map.h>
#include <base/thread.h>
#include <cap_session/connection.h>
#include <nova_cpu_session/connection.h>
namespace Vmm {
using namespace Genode;
class Vcpu_thread;
}
class Vmm::Vcpu_thread : Genode::Thread_base
{
private:
/**
* Log2 size of portal window used for virtualization events
*/
enum { VCPU_EXC_BASE_LOG2 = 8 };
public:
Vcpu_thread(size_t stack_size)
:
Thread_base("vCPU", stack_size)
{
/* release pre-allocated selectors of Thread */
Genode::cap_map()->remove(tid().exc_pt_sel, Nova::NUM_INITIAL_PT_LOG2);
/* allocate correct number of selectors */
this->tid().exc_pt_sel = cap_map()->insert(VCPU_EXC_BASE_LOG2);
/* tell generic thread code that this becomes a vCPU */
this->tid().is_vcpu = true;
}
~Vcpu_thread()
{
using namespace Nova;
revoke(Nova::Obj_crd(this->tid().exc_pt_sel, VCPU_EXC_BASE_LOG2));
cap_map()->remove(this->tid().exc_pt_sel, VCPU_EXC_BASE_LOG2, false);
/* allocate selectors for ~Thread */
this->tid().exc_pt_sel = cap_map()->insert(Nova::NUM_INITIAL_PT_LOG2);
}
addr_t exc_base() { return this->tid().exc_pt_sel; }
void start(Genode::addr_t sel_ec)
{
this->Thread_base::start();
/*
* Request native EC thread cap and put it next to the
* SM cap - see Vcpu_dispatcher->sel_sm_ec description
*/
request_native_ec_cap(_pager_cap, sel_ec);
}
void entry() { }
};
#endif /* _INCLUDE__VMM__VCPU_THREAD_H_ */

41
ports/run/vmm_utils.run Normal file
View File

@ -0,0 +1,41 @@
build { core init drivers/timer test/vmm_utils }
create_boot_directory
install_config {
<config>
<parent-provides>
<service name="ROM"/>
<service name="RAM"/>
<service name="IRQ"/>
<service name="IO_MEM"/>
<service name="IO_PORT"/>
<service name="CAP"/>
<service name="PD"/>
<service name="RM"/>
<service name="CPU"/>
<service name="LOG"/>
<service name="SIGNAL"/>
</parent-provides>
<default-route>
<any-service><parent/><any-child/></any-service>
</default-route>
<start name="timer">
<resource name="RAM" quantum="1M"/>
<provides><service name="Timer"/></provides>
</start>
<start name="test-vmm_utils">
<resource name="RAM" quantum="1G"/>
</start>
</config>
}
build_boot_image { core init timer test-vmm_utils }
append qemu_args " -m 512 "
append qemu_args " -cpu phenom "
append qemu_args " -nographic "
run_genode_until {.*VMM: _svm_startup called} 30
puts "Test succeeded"

View File

@ -0,0 +1,97 @@
/*
* \brief Test for Genode's VMM utilities
* \author Norman Feske
* \date 2013-08-20
*/
/*
* Copyright (C) 2010-2013 Genode Labs GmbH
*
* This file is distributed under the terms of the GNU General Public License
* version 2.
*/
/* Genode includes */
#include <base/sleep.h>
/* VMM utility includes */
#include <vmm/vcpu_thread.h>
#include <vmm/vcpu_dispatcher.h>
#include <vmm/printf.h>
using Genode::Cap_connection;
using Genode::printf;
using Genode::sleep_forever;
class Vcpu_dispatcher : public Vmm::Vcpu_dispatcher
{
private:
typedef Vcpu_dispatcher This;
Vmm::Vcpu_same_pd _vcpu_thread;
/**
* Shortcut for calling 'Vmm::Vcpu_dispatcher::register_handler'
* with 'Vcpu_dispatcher' as template argument
*/
template <unsigned EV, void (This::*FUNC)()>
void _register_handler(Genode::addr_t exc_base, Nova::Mtd mtd)
{
register_handler<EV, This, FUNC>(exc_base, mtd);
}
enum { STACK_SIZE = 1024*sizeof(long) };
/***********************************
** Virtualization event handlers **
***********************************/
void _svm_startup()
{
Vmm::printf("_svm_startup called\n");
}
public:
enum Type { SVM, VTX };
Vcpu_dispatcher(Cap_connection &cap, Type type)
:
Vmm::Vcpu_dispatcher(STACK_SIZE, cap),
_vcpu_thread(STACK_SIZE)
{
using namespace Nova;
/* 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);
Genode::addr_t exc_base = _vcpu_thread.exc_base();
/* register virtualization event handlers */
if (type == SVM) {
_register_handler<0xfe, &This::_svm_startup>
(exc_base, mtd_all);
}
/* start virtual CPU */
_vcpu_thread.start(sel_sm_ec() + 1);
}
};
int main(int argc, char **argv)
{
printf("--- VBox started ---\n");
static Cap_connection cap;
static Vcpu_dispatcher vcpu_dispatcher(cap, Vcpu_dispatcher::SVM);
printf("going to sleep forever...\n");
sleep_forever();
return 0;
}

View File

@ -0,0 +1,7 @@
TARGET = test-vmm_utils
SRC_CC = main.cc
LIBS += base
REQUIRES = nova
vpath %.cc $(PRG_DIR)/..

View File

@ -139,7 +139,8 @@ class Boot_module_provider
catch (Destination_buffer_too_small) {
PERR("Boot_module_provider: destination buffer too small"); }
catch (Genode::Rm_session::Attach_failed) {
PERR("Boot_module_provider: Rm_session::Attach_failed"); }
PERR("Boot_module_provider: Rm_session::Attach_failed");
throw Module_loading_failed(); }
catch (Genode::Rom_connection::Rom_connection_failed) {
PERR("Boot_module_provider: Rom_connection_failed"); }
catch (...) {

View File

@ -343,13 +343,12 @@ void Vancouver_console::register_host_operations(Motherboard &motherboard)
Vancouver_console::Vancouver_console(Synced_motherboard &mb,
Genode::Lock &console_lock,
Genode::size_t vm_fb_size,
Genode::Dataspace_capability fb_ds)
:
Thread("vmm_console"),
_startup_lock(Genode::Lock::LOCKED),
_vm_fb_size(vm_fb_size), _motherboard(mb), _console_lock(console_lock),
_vm_fb_size(vm_fb_size), _motherboard(mb),
_fb_size(0), _pixels(0), _guest_fb(0),
_regs(0), _fb_ds(fb_ds),
_left(false), _middle(false), _right(false)

View File

@ -48,7 +48,7 @@ class Vancouver_console : public Thread<8192>, public StaticReceiver<Vancouver_c
Genode::Lock _startup_lock;
Synced_motherboard &_motherboard;
Genode::Lock &_console_lock;
Genode::Lock _console_lock;
short *_pixels;
char *_guest_fb;
unsigned long _fb_size;
@ -75,7 +75,6 @@ class Vancouver_console : public Thread<8192>, public StaticReceiver<Vancouver_c
* Constructor
*/
Vancouver_console(Synced_motherboard &,
Genode::Lock &console_lock,
Genode::size_t vm_fb_size,
Genode::Dataspace_capability fb_ds);
};

View File

@ -29,9 +29,11 @@
#include <base/heap.h>
#include <base/lock.h>
/* VMM utility includes */
#include <vmm/utcb_guard.h>
/* local includes */
#include <disk.h>
#include <utcb_guard.h>
/* Seoul includes */
#include <host/dma.h>
@ -51,12 +53,13 @@ static Genode::Heap * disk_heap() {
Vancouver_disk::Vancouver_disk(Synced_motherboard &mb,
char * backing_store_base,
char * backing_store_fb_base)
char * backing_store_base,
Genode::size_t backing_store_size)
:
Thread("vmm_disk"),
_motherboard(mb), _backing_store_base(backing_store_base),
_backing_store_fb_base(backing_store_fb_base),
_motherboard(mb),
_backing_store_base(backing_store_base),
_backing_store_size(backing_store_size),
_tslab_msg(disk_heap()), _tslab_dma(disk_heap()), _tslab_avl(disk_heap())
{
/* initialize struct with 0 size */
@ -141,8 +144,8 @@ void Vancouver_disk::_signal_dispatch_entry(unsigned disknr)
msg->dma[i].byteoffset + msg->physoffset;
// check for bounds
if (dma_addr >= _backing_store_fb_base ||
dma_addr < _backing_store_base) {
if (dma_addr >= _backing_store_base + _backing_store_size
|| dma_addr < _backing_store_base) {
PERR("dma bounds violation");
} else
memcpy(dma_addr, source_addr + sector,
@ -168,7 +171,7 @@ void Vancouver_disk::_signal_dispatch_entry(unsigned disknr)
bool Vancouver_disk::receive(MessageDisk &msg)
{
static Genode::Native_utcb utcb_backup;
Utcb_guard guard(utcb_backup);
Vmm::Utcb_guard guard(utcb_backup);
if (msg.disknr >= MAX_DISKS)
Logging::panic("You configured more disks than supported.\n");
@ -290,8 +293,8 @@ bool Vancouver_disk::receive(MessageDisk &msg)
+ msg_cpy->physoffset;
/* check for bounds */
if (dma_addr >= _backing_store_fb_base ||
dma_addr < _backing_store_base) {
if (dma_addr >= _backing_store_base + _backing_store_size
|| dma_addr < _backing_store_base) {
/* drop allocated objects not needed in error case */
if (write)
destroy(&_tslab_dma, msg_cpy->dma);

View File

@ -96,8 +96,8 @@ class Vancouver_disk : public Genode::Thread<8192>, public StaticReceiver<Vancou
} _diskcon[MAX_DISKS];
Synced_motherboard &_motherboard;
char *_backing_store_base;
char *_backing_store_fb_base;
char * const _backing_store_base;
size_t const _backing_store_size;
/* slabs for temporary holding DMADescriptor and MessageDisk objects */
typedef Genode::Tslab<MessageDisk, 128> MessageDisk_Slab;
@ -127,8 +127,8 @@ class Vancouver_disk : public Genode::Thread<8192>, public StaticReceiver<Vancou
* Constructor
*/
Vancouver_disk(Synced_motherboard &,
char * backing_store_base,
char * backing_store_fb_base);
char * backing_store_base,
Genode::size_t backing_store_size);
~Vancouver_disk();

View File

@ -33,15 +33,12 @@
/* Genode includes */
#include <util/touch.h>
#include <base/printf.h>
#include <base/sleep.h>
#include <base/thread.h>
#include <base/rpc_server.h>
#include <base/native_types.h>
#include <util/misc_math.h>
#include <rom_session/connection.h>
#include <rm_session/connection.h>
#include <cap_session/connection.h>
#include <base/allocator_avl.h>
#include <nic_session/connection.h>
#include <nic/packet_allocator.h>
@ -51,6 +48,11 @@
#include <timer_session/connection.h>
#include <rtc_session/connection.h>
/* VMM utilities includes */
#include <vmm/guest_memory.h>
#include <vmm/vcpu_thread.h>
#include <vmm/vcpu_dispatcher.h>
/* NOVA includes that come with Genode */
#include <nova/syscalls.h>
@ -67,12 +69,6 @@
#include <network.h>
#include <disk.h>
enum {
PAGE_SIZE_LOG2 = 12UL,
PAGE_SIZE = 1UL << PAGE_SIZE_LOG2,
STACK_SIZE = 1024*sizeof(Genode::addr_t),
};
enum { verbose_debug = false };
enum { verbose_npt = false };
@ -158,16 +154,10 @@ class Guest_memory
{
private:
/*
* The '_reservation' is a managed dataspace that occupies the lower
* part of the address space, which contains the shadow of the VCPU's
* physical memory.
*/
Genode::Rm_connection _reservation;
Genode::Ram_dataspace_capability _ds;
Genode::Ram_dataspace_capability _fb_ds;
Genode::size_t _backing_store_size;
Genode::size_t _fb_size;
char *_local_addr;
@ -198,8 +188,8 @@ class Guest_memory
*/
Guest_memory(Genode::size_t backing_store_size, Genode::size_t fb_size)
:
_backing_store_size(backing_store_size),
_fb_size(fb_size),
_reservation(0, backing_store_size),
_ds(Genode::env()->ram_session()->alloc(backing_store_size-fb_size)),
_fb_ds(Genode::env()->ram_session()->alloc(fb_size)),
_local_addr(0),
@ -207,17 +197,6 @@ class Guest_memory
remaining_size(backing_store_size-fb_size)
{
try {
/* free up preliminary mapping to reserve lower address space */
Genode::env()->rm_session()->detach(PAGE_SIZE);
/*
* Attach reservation to the beginning of the local address space.
* We leave out the very first page because core denies the
* attachment of anything at the zero page.
*/
Genode::env()->rm_session()->attach_at(_reservation.dataspace(),
PAGE_SIZE, 0, PAGE_SIZE);
/*
* RAM used as backing store for guest-physical memory
*/
@ -232,9 +211,6 @@ class Guest_memory
~Guest_memory()
{
/* detach reservation */
Genode::env()->rm_session()->detach((void *)PAGE_SIZE);
/* detach and free backing store */
Genode::env()->rm_session()->detach((void *)_local_addr);
Genode::env()->ram_session()->free(_ds);
@ -251,6 +227,11 @@ class Guest_memory
return _local_addr;
}
Genode::size_t backing_store_size()
{
return _backing_store_size;
}
/**
* Return pointer to lo locally mapped fb backing store
*/
@ -265,75 +246,17 @@ class Guest_memory
};
class Vcpu_thread : Genode::Thread<STACK_SIZE>
{
private:
/**
* Log2 size of portal window used for virtualization events
*/
enum { VCPU_EXC_BASE_LOG2 = 8 };
public:
Vcpu_thread(char const * name) : Thread(name)
{
using namespace Genode;
/* release pre-allocated selectors of Thread */
cap_map()->remove(tid().exc_pt_sel, Nova::NUM_INITIAL_PT_LOG2);
/* allocate correct number of selectors */
tid().exc_pt_sel = cap_map()->insert(VCPU_EXC_BASE_LOG2);
/* tell generic thread code that this becomes a vCPU */
tid().is_vcpu = true;
}
~Vcpu_thread()
{
using namespace Genode;
Nova::revoke(Nova::Obj_crd(tid().exc_pt_sel, VCPU_EXC_BASE_LOG2));
cap_map()->remove(tid().exc_pt_sel, VCPU_EXC_BASE_LOG2, false);
/* allocate selectors for ~Thread */
tid().exc_pt_sel = cap_map()->insert(Nova::NUM_INITIAL_PT_LOG2);
}
Genode::addr_t exc_base() { return tid().exc_pt_sel; }
void start(Genode::addr_t sel_ec)
{
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
*/
request_native_ec_cap(_pager_cap, sel_ec);
}
void entry() { }
};
class Vcpu_dispatcher : public Genode::Thread<STACK_SIZE>,
class Vcpu_dispatcher : public Vmm::Vcpu_dispatcher,
public StaticReceiver<Vcpu_dispatcher>
{
private:
Genode::Cap_connection _cap_session;
/**
* Pointer to corresponding VCPU model
*/
Genode::Synced_interface<VCpu> _vcpu;
Vcpu_thread _vcpu_thread;
Vmm::Vcpu_thread _vcpu_thread;
/**
* Guest-physical memory
@ -477,7 +400,7 @@ class Vcpu_dispatcher : public Genode::Thread<STACK_SIZE>,
if (verbose_npt)
Logging::printf("--> request mapping at 0x%lx\n", vm_fault_addr);
MessageMemRegion mem_region(vm_fault_addr >> PAGE_SIZE_LOG2);
MessageMemRegion mem_region(vm_fault_addr >> Vmm::PAGE_SIZE_LOG2);
if (!_motherboard()->bus_memregion.send(mem_region, false) ||
!mem_region.ptr)
@ -488,14 +411,14 @@ class Vcpu_dispatcher : public Genode::Thread<STACK_SIZE>,
" VMM area: [0x%lx:0x%lx)\n",
mem_region.page, mem_region.start_page,
mem_region.start_page + mem_region.count,
(Genode::addr_t)mem_region.ptr >> PAGE_SIZE_LOG2,
((Genode::addr_t)mem_region.ptr >> PAGE_SIZE_LOG2)
(Genode::addr_t)mem_region.ptr >> Vmm::PAGE_SIZE_LOG2,
((Genode::addr_t)mem_region.ptr >> Vmm::PAGE_SIZE_LOG2)
+ mem_region.count);
Genode::addr_t vmm_memory_base =
reinterpret_cast<Genode::addr_t>(mem_region.ptr);
Genode::addr_t vmm_memory_fault = vmm_memory_base +
(vm_fault_addr - (mem_region.start_page << PAGE_SIZE_LOG2));
(vm_fault_addr - (mem_region.start_page << Vmm::PAGE_SIZE_LOG2));
bool read=true, write=true, execute=true;
/* XXX: Not yet supported by Vancouver.
@ -506,10 +429,10 @@ class Vcpu_dispatcher : public Genode::Thread<STACK_SIZE>,
write = execute = false;
}*/
Nova::Mem_crd crd(vmm_memory_fault >> PAGE_SIZE_LOG2, 0,
Nova::Mem_crd crd(vmm_memory_fault >> Vmm::PAGE_SIZE_LOG2, 0,
Nova::Rights(read, write, execute));
if (!max_map_crd(crd, vmm_memory_base >> PAGE_SIZE_LOG2,
if (!max_map_crd(crd, vmm_memory_base >> Vmm::PAGE_SIZE_LOG2,
mem_region.start_page,
mem_region.count, mem_region.page))
Logging::panic("mapping failed");
@ -517,7 +440,7 @@ class Vcpu_dispatcher : public Genode::Thread<STACK_SIZE>,
if (need_unmap)
Logging::panic("_handle_map_memory: need_unmap not handled, yet\n");
Genode::addr_t hotspot = (mem_region.start_page << PAGE_SIZE_LOG2)
Genode::addr_t hotspot = (mem_region.start_page << Vmm::PAGE_SIZE_LOG2)
+ crd.addr() - vmm_memory_base;
if (verbose_npt)
@ -574,7 +497,7 @@ class Vcpu_dispatcher : public Genode::Thread<STACK_SIZE>,
void _svm_npt()
{
Utcb *utcb = _utcb_of_myself();
MessageMemRegion msg(utcb->qual[1] >> PAGE_SIZE_LOG2);
MessageMemRegion msg(utcb->qual[1] >> Vmm::PAGE_SIZE_LOG2);
if (!_handle_map_memory(utcb->qual[0] & 1))
_svm_invalid();
}
@ -711,7 +634,7 @@ class Vcpu_dispatcher : public Genode::Thread<STACK_SIZE>,
_handle_io(utcb->qual[0] & 8, order, utcb->qual[0] >> 16);
}
void _vmx_mmio()
void _vmx_ept()
{
Utcb *utcb = _utcb_of_myself();
@ -736,73 +659,35 @@ class Vcpu_dispatcher : public Genode::Thread<STACK_SIZE>,
}
/**
* 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 task of this function is to
* call the 'Vcpu' member function corresponding to the event type.
*/
template <unsigned EV, void (Vcpu_dispatcher::*FUNC)()>
static void _portal_entry()
{
/* obtain this pointer of the event handler */
Genode::Thread_base *myself = Genode::Thread_base::myself();
Vcpu_dispatcher *vd = static_cast<Vcpu_dispatcher *>(myself);
/* call event-specific handler function */
(vd->*FUNC)();
/* continue execution of the guest */
Nova::reply(myself->stack_top());
}
/**
* Register virtualization event handler
* Shortcut for calling 'Vmm::Vcpu_dispatcher::register_handler'
* with 'Vcpu_dispatcher' as template argument
*/
template <unsigned EV, void (Vcpu_dispatcher::*FUNC)()>
void _register_handler(Genode::addr_t exc_base, Nova::Mtd mtd)
{
/*
* Let the compiler generate an instance of a portal
* entry
*/
void (*portal_entry)() = &_portal_entry<EV, FUNC>;
using namespace Genode;
/* Create the portal at the desired selector index */
_cap_session.rcv_window(exc_base + EV);
Native_capability thread(tid().ec_sel);
Native_capability handler =
_cap_session.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");
if (!register_handler<EV, Vcpu_dispatcher, FUNC>(exc_base, mtd))
PERR("could not register handler %lx", exc_base + EV);
}
enum { STACK_SIZE = 1024*sizeof(Genode::addr_t) };
public:
Vcpu_dispatcher(Genode::Lock &vcpu_lock,
Genode::Cap_connection &cap_connection,
VCpu *unsynchronized_vcpu,
Guest_memory &guest_memory,
Synced_motherboard &motherboard,
bool has_svm,
bool has_vmx)
bool has_svm,
bool has_vmx)
:
Thread("vcpu_dispatcher"),
Vmm::Vcpu_dispatcher(STACK_SIZE, cap_connection),
_vcpu(vcpu_lock, unsynchronized_vcpu),
_vcpu_thread("vCPU thread"),
_vcpu_thread(STACK_SIZE),
_guest_memory(guest_memory),
_motherboard(motherboard)
{
using namespace Genode;
/* request creation of a 'local' EC */
_tid.ec_sel = Native_thread::INVALID_INDEX - 1;
Thread_base::start();
using namespace Nova;
/* shortcuts for common message-transfer descriptors */
@ -813,10 +698,10 @@ class Vcpu_dispatcher : public Genode::Thread<STACK_SIZE>,
/*
* Register vCPU event handlers
*/
Genode::addr_t const exc_base = _vcpu_thread.exc_base();
typedef Vcpu_dispatcher This;
if (has_svm) {
Genode::addr_t exc_base =
_vcpu_thread.exc_base();
_register_handler<0x64, &This::_vmx_irqwin>
(exc_base, MTD_IRQ);
@ -840,8 +725,6 @@ class Vcpu_dispatcher : public Genode::Thread<STACK_SIZE>,
(exc_base, MTD_IRQ);
} else if (has_vmx) {
Genode::addr_t exc_base =
_vcpu_thread.exc_base();
_register_handler<2, &This::_vmx_triple>
(exc_base, MTD_ALL);
@ -867,7 +750,7 @@ class Vcpu_dispatcher : public Genode::Thread<STACK_SIZE>,
(exc_base, MTD_ALL);
_register_handler<40, &This::_vmx_pause>
(exc_base, MTD_RIP_LEN | MTD_STATE);
_register_handler<48, &This::_vmx_mmio>
_register_handler<48, &This::_vmx_ept>
(exc_base, MTD_ALL);
_register_handler<0xfe, &This::_vmx_startup>
(exc_base, MTD_IRQ);
@ -893,32 +776,6 @@ class Vcpu_dispatcher : public Genode::Thread<STACK_SIZE>,
*/
~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. 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 identifier of the VCPU. I.e., it gets passed as
* arguments for 'MessageHostOp' operations.
*/
Nova::mword_t sel_sm_ec()
{
return tid().exc_pt_sel + Nova::SM_SEL_EC;
}
/***********************************
** Handlers for 'StaticReceiver' **
@ -964,6 +821,7 @@ class Machine : public StaticReceiver<Machine>
Genode::Rom_connection _hip_rom;
Hip * const _hip;
Genode::Cap_connection _cap;
Clock _clock;
Genode::Lock _motherboard_lock;
Motherboard _unsynchronized_motherboard;
@ -1002,7 +860,7 @@ class Machine : public StaticReceiver<Machine>
msg.len = _guest_memory.fb_size();
msg.ptr = _guest_memory.backing_store_local_base();
_alloc_fb_mem = false;
Logging::printf(" -> len=0x%lx, ptr=0x%p\n",
Logging::printf("_alloc_fb_mem -> len=0x%lx, ptr=0x%p\n",
msg.len, msg.ptr);
return true;
}
@ -1051,7 +909,9 @@ class Machine : public StaticReceiver<Machine>
Logging::printf("OP_VCPU_CREATE_BACKEND\n");
Vcpu_dispatcher *vcpu_dispatcher =
new Vcpu_dispatcher(_motherboard_lock, msg.vcpu,
new Vcpu_dispatcher(_motherboard_lock,
_cap,
msg.vcpu,
_guest_memory,
_motherboard,
_hip->has_feature_svm(), _hip->has_feature_vmx());
@ -1128,7 +988,7 @@ class Machine : public StaticReceiver<Machine>
* behind the module data, aligned on a page boundary.
*/
Genode::addr_t const cmdline_offset =
Genode::align_addr(data_len, PAGE_SIZE_LOG2);
Genode::align_addr(data_len, Vmm::PAGE_SIZE_LOG2);
if (cmdline_offset >= dst_len) {
Logging::printf("destination buffer too small for command line\n");
@ -1479,77 +1339,80 @@ class Machine : public StaticReceiver<Machine>
extern unsigned long _prog_img_beg; /* begin of program image (link address) */
extern unsigned long _prog_img_end; /* end of program image */
namespace Genode {
Rm_session *env_context_area_rm_session();
void __attribute__((constructor)) init_context_area_vmm() {
/**
* XXX Invoke env_context_rm_session to make sure the virtual region of
* the context area is reserved at core. Typically this happens
* when the first time a thread is allocated. Unfortunately,
* beforehand the VMM may try to grab the same region for
* large VM sizes.
*/
env_context_area_rm_session();
}
}
int main(int argc, char **argv)
{
/*
* Reserve complete lower address space so that nobody else can take it.
* When we found out how much memory we actually should use for the VM,
* the reservation is adjusted to the real size.
*/
Genode::Rm_connection pre_reservation(0, Genode::Native_config::context_area_virtual_base());
Genode::env()->rm_session()->attach_at(pre_reservation.dataspace(),
PAGE_SIZE, 0, PAGE_SIZE);
Genode::printf("--- Vancouver VMM starting ---\n");
/* request max available memory */
Genode::addr_t vm_size = Genode::env()->ram_session()->avail();
/* reserve some memory for the VMM */
vm_size -= 8 * 1024 * 1024;
/* calculate max memory for the VM */
vm_size = vm_size & ~((1UL << PAGE_SIZE_LOG2) - 1);
/* Find out framebuffer size (default: 4 MiB) */
Genode::addr_t fb_size = 4*1024*1024;
try {
Genode::Xml_node node = Genode::config()->xml_node().sub_node("vga");
Genode::Xml_node::Attribute arg = node.attribute("fb_size");
unsigned long val;
arg.value(&val);
fb_size = val*1024;
} catch (...) { }
Genode::addr_t vm_size;
{
/*
* Reserve complete lower address space so that nobody else can take
* it. The context area is moved as far as possible to a high virtual
* address. So we can use its base address as upper bound. The
* reservation will be dropped when this scope is left and re-acquired
* with the actual VM size which is determined below inside this scope.
*/
Vmm::Virtual_reservation
reservation(Genode::Native_config::context_area_virtual_base());
Genode::printf("--- Vancouver VMM starting ---\n");
/* request max available memory */
vm_size = Genode::env()->ram_session()->avail();
/* reserve some memory for the VMM */
vm_size -= 8 * 1024 * 1024;
/* calculate max memory for the VM */
vm_size = vm_size & ~((1UL << Vmm::PAGE_SIZE_LOG2) - 1);
/* Find out framebuffer size (default: 4 MiB) */
try {
Genode::Xml_node node = Genode::config()->xml_node().sub_node("machine").sub_node("vga");
Genode::Xml_node::Attribute arg = node.attribute("fb_size");
unsigned long val;
arg.value(&val);
fb_size = val*1024;
} catch (...) { }
}
/* re-adjust reservation to actual VM size */
Vmm::Virtual_reservation reservation(vm_size);
/* setup guest memory */
static Guest_memory guest_memory(vm_size, fb_size);
/* free up temporary rm_session */
Genode::env()->parent()->close(pre_reservation.cap());
/* diagnostic messages */
Genode::printf("[0x%08lx, 0x%08lx) - %lu MiB - guest physical memory\n",
Genode::printf("[0x%012lx, 0x%012lx) - %lu MiB - VM accessible memory\n",
0, vm_size, vm_size / 1024 / 1024);
if (guest_memory.backing_store_local_base())
Genode::printf("[0x%08p, 0x%08lx) - VMM local base of guest-physical"
" memory\n", guest_memory.backing_store_local_base(),
(Genode::addr_t)guest_memory.backing_store_local_base() +
vm_size);
Genode::printf("[0x%012p, 0x%012p) - %lu MiB - VMM accessible shadow "
"mapping of VM memory \n",
guest_memory.backing_store_local_base(),
guest_memory.backing_store_local_base() +
guest_memory.remaining_size, vm_size / 1024 / 1024);
Genode::printf("[0x%08lx, 0x%08lx) - Genode thread context area\n",
if (guest_memory.backing_store_fb_local_base())
Genode::printf("[0x%012p, 0x%012p) - %lu MiB - VMM accessible "
"framebuffer memory of VM\n",
guest_memory.backing_store_fb_local_base(),
guest_memory.backing_store_fb_local_base() + fb_size,
fb_size / 1024 / 1024);
Genode::printf("[0x%012lx, 0x%012lx) - Genode thread context area\n",
Genode::Native_config::context_area_virtual_base(),
Genode::Native_config::context_area_virtual_base() +
Genode::Native_config::context_area_virtual_size());
Genode::printf("[0x%08lx, 0x%08lx) - VMM program image\n",
Genode::printf("[0x%012lx, 0x%012lx) - VMM program image\n",
(Genode::addr_t)&_prog_img_beg,
(Genode::addr_t)&_prog_img_end);
if (!guest_memory.backing_store_local_base()) {
Genode::printf("Not enough space (0x%lx) left for VMM, VM image"
" to large\n", vm_size);
if (!guest_memory.backing_store_local_base() ||
!guest_memory.backing_store_fb_local_base()) {
PERR("Not enough space left for %s - exit",
guest_memory.backing_store_local_base() ? "framebuffer" : "VMM");
return 1;
}
@ -1560,19 +1423,15 @@ int main(int argc, char **argv)
static Machine machine(boot_modules, guest_memory);
Genode::Lock fb_lock;
/* create console thread */
Vancouver_console vcon(machine.motherboard(),
fb_lock,
fb_size, guest_memory.fb_ds());
Vancouver_console vcon(machine.motherboard(), fb_size, guest_memory.fb_ds());
vcon.register_host_operations(machine.unsynchronized_motherboard());
/* create disk thread */
Vancouver_disk vdisk(machine.motherboard(),
guest_memory.backing_store_local_base(),
guest_memory.backing_store_fb_local_base());
guest_memory.backing_store_size());
vdisk.register_host_operations(machine.unsynchronized_motherboard());

View File

@ -33,8 +33,6 @@ Vancouver_network::Vancouver_network(Synced_motherboard &mb, Nic::Session *nic)
void Vancouver_network::entry()
{
Logging::printf("Hello, this is the network receiver.\n");
while (true) {
Packet_descriptor rx_packet = _nic->rx()->get_packet();