diff --git a/ports/include/vmm/guest_memory.h b/ports/include/vmm/guest_memory.h new file mode 100644 index 000000000..962a34424 --- /dev/null +++ b/ports/include/vmm/guest_memory.h @@ -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 +#include + +/* VMM utilities includes */ +#include + +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_ */ diff --git a/ports/include/vmm/printf.h b/ports/include/vmm/printf.h new file mode 100644 index 000000000..5f3443290 --- /dev/null +++ b/ports/include/vmm/printf.h @@ -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 +#include +#include + +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_ */ diff --git a/ports/include/vmm/types.h b/ports/include/vmm/types.h new file mode 100644 index 000000000..12e705174 --- /dev/null +++ b/ports/include/vmm/types.h @@ -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_ */ diff --git a/ports/src/vancouver/utcb_guard.h b/ports/include/vmm/utcb_guard.h similarity index 64% rename from ports/src/vancouver/utcb_guard.h rename to ports/include/vmm/utcb_guard.h index 4986cf6af..c27bffb4b 100644 --- a/ports/src/vancouver/utcb_guard.h +++ b/ports/include/vmm/utcb_guard.h @@ -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 +#include + +/* NOVA syscalls */ #include -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(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(&_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_ */ diff --git a/ports/include/vmm/vcpu_dispatcher.h b/ports/include/vmm/vcpu_dispatcher.h new file mode 100644 index 000000000..43f4a6592 --- /dev/null +++ b/ports/include/vmm/vcpu_dispatcher.h @@ -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 + static void _portal_entry() + { + /* obtain this pointer of the event handler */ + Genode::Thread_base *myself = Genode::Thread_base::myself(); + DISPATCHER *vd = static_cast(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 + 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 */ + _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_ */ diff --git a/ports/include/vmm/vcpu_thread.h b/ports/include/vmm/vcpu_thread.h new file mode 100644 index 000000000..3efc3d8de --- /dev/null +++ b/ports/include/vmm/vcpu_thread.h @@ -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 +#include +#include +#include + +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_ */ diff --git a/ports/run/vmm_utils.run b/ports/run/vmm_utils.run new file mode 100644 index 000000000..2d00b6475 --- /dev/null +++ b/ports/run/vmm_utils.run @@ -0,0 +1,41 @@ +build { core init drivers/timer test/vmm_utils } + +create_boot_directory + +install_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" diff --git a/ports/src/test/vmm_utils/main.cc b/ports/src/test/vmm_utils/main.cc new file mode 100644 index 000000000..f14dbe2c6 --- /dev/null +++ b/ports/src/test/vmm_utils/main.cc @@ -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 + +/* VMM utility includes */ +#include +#include +#include + +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 + void _register_handler(Genode::addr_t exc_base, Nova::Mtd mtd) + { + register_handler(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; +} diff --git a/ports/src/test/vmm_utils/target.mk b/ports/src/test/vmm_utils/target.mk new file mode 100644 index 000000000..fd3b8f85d --- /dev/null +++ b/ports/src/test/vmm_utils/target.mk @@ -0,0 +1,7 @@ +TARGET = test-vmm_utils +SRC_CC = main.cc +LIBS += base + +REQUIRES = nova + +vpath %.cc $(PRG_DIR)/.. diff --git a/ports/src/vancouver/boot_module_provider.h b/ports/src/vancouver/boot_module_provider.h index 9b10fdb18..adea39d52 100644 --- a/ports/src/vancouver/boot_module_provider.h +++ b/ports/src/vancouver/boot_module_provider.h @@ -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 (...) { diff --git a/ports/src/vancouver/console.cc b/ports/src/vancouver/console.cc index e5a3ce90b..0df9a0590 100644 --- a/ports/src/vancouver/console.cc +++ b/ports/src/vancouver/console.cc @@ -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) diff --git a/ports/src/vancouver/console.h b/ports/src/vancouver/console.h index d1a7ca60e..1e55eaf19 100644 --- a/ports/src/vancouver/console.h +++ b/ports/src/vancouver/console.h @@ -48,7 +48,7 @@ class Vancouver_console : public Thread<8192>, public StaticReceiver, public StaticReceiver #include +/* VMM utility includes */ +#include + /* local includes */ #include -#include /* Seoul includes */ #include @@ -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); diff --git a/ports/src/vancouver/disk.h b/ports/src/vancouver/disk.h index fa579a236..f3fe1fd98 100644 --- a/ports/src/vancouver/disk.h +++ b/ports/src/vancouver/disk.h @@ -96,8 +96,8 @@ class Vancouver_disk : public Genode::Thread<8192>, public StaticReceiver MessageDisk_Slab; @@ -127,8 +127,8 @@ class Vancouver_disk : public Genode::Thread<8192>, public StaticReceiver -#include #include -#include #include #include #include #include #include -#include #include #include #include @@ -51,6 +48,11 @@ #include #include +/* VMM utilities includes */ +#include +#include +#include + /* NOVA includes that come with Genode */ #include @@ -67,12 +69,6 @@ #include #include -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 -{ - 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, +class Vcpu_dispatcher : public Vmm::Vcpu_dispatcher, public StaticReceiver { private: - Genode::Cap_connection _cap_session; - /** * Pointer to corresponding VCPU model */ Genode::Synced_interface _vcpu; - Vcpu_thread _vcpu_thread; + Vmm::Vcpu_thread _vcpu_thread; /** * Guest-physical memory @@ -477,7 +400,7 @@ class Vcpu_dispatcher : public Genode::Thread, 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, " 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(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, 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, 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, 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, _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, } /** - * 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 - static void _portal_entry() - { - /* obtain this pointer of the event handler */ - Genode::Thread_base *myself = Genode::Thread_base::myself(); - Vcpu_dispatcher *vd = static_cast(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 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; - - 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(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, /* * 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, (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, (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, */ ~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 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 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 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 * 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 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()); diff --git a/ports/src/vancouver/network.cc b/ports/src/vancouver/network.cc index 2d0483a5f..7a98fba30 100644 --- a/ports/src/vancouver/network.cc +++ b/ports/src/vancouver/network.cc @@ -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();