diff --git a/repos/base-hw/include/platform/arndale/vm_state.h b/repos/base-hw/include/platform/arndale/vm_state.h new file mode 100644 index 000000000..0926abac4 --- /dev/null +++ b/repos/base-hw/include/platform/arndale/vm_state.h @@ -0,0 +1,80 @@ +/* + * \brief CPU, PIC, and timer context of a virtual machine + * \author Stefan Kalkowski + * \date 2015-02-10 + */ + +/* + * Copyright (C) 2015 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__PLATFORM__ARNDALE__VM_STATE_H_ +#define _INCLUDE__PLATFORM__ARNDALE__VM_STATE_H_ + +/* Genode includes */ +#include + +namespace Genode +{ + /** + * CPU context of a virtual machine + */ + struct Vm_state; +} + +struct Genode::Vm_state : Genode::Cpu_state_modes +{ + Genode::uint64_t vttbr; + Genode::uint32_t sctrl; + Genode::uint32_t hsr; + Genode::uint32_t hpfar; + Genode::uint32_t hdfar; + Genode::uint32_t hifar; + Genode::uint32_t ttbcr; + Genode::uint32_t ttbr0; + Genode::uint32_t ttbr1; + Genode::uint32_t prrr; + Genode::uint32_t nmrr; + Genode::uint32_t dacr; + Genode::uint32_t dfsr; + Genode::uint32_t ifsr; + Genode::uint32_t adfsr; + Genode::uint32_t aifsr; + Genode::uint32_t dfar; + Genode::uint32_t ifar; + Genode::uint32_t cidr; + Genode::uint32_t tls1; + Genode::uint32_t tls2; + Genode::uint32_t tls3; + Genode::uint32_t cpacr; + + + /** + * Timer related registers + */ + + Genode::uint32_t timer_ctrl; + Genode::uint32_t timer_val; + bool timer_irq; + + + /** + * PIC related registers + */ + + enum { NR_IRQ = 4 }; + + Genode::uint32_t gic_hcr; + Genode::uint32_t gic_vmcr; + Genode::uint32_t gic_misr; + Genode::uint32_t gic_apr; + Genode::uint32_t gic_eisr; + Genode::uint32_t gic_elrsr0; + Genode::uint32_t gic_lr[4]; + unsigned gic_irq; +}; + +#endif /* _INCLUDE__PLATFORM__ARNDALE__VM_STATE_H_ */ diff --git a/repos/base-hw/src/core/include/spec/arm/kernel/vm_state.h b/repos/base-hw/include/platform/imx53/vm_state.h similarity index 50% rename from repos/base-hw/src/core/include/spec/arm/kernel/vm_state.h rename to repos/base-hw/include/platform/imx53/vm_state.h index 223352f54..ba8bce4e5 100644 --- a/repos/base-hw/src/core/include/spec/arm/kernel/vm_state.h +++ b/repos/base-hw/include/platform/imx53/vm_state.h @@ -1,5 +1,5 @@ /* - * \brief CPU context of a virtual machine + * \brief CPU context of a virtual machine for TrustZone * \author Stefan Kalkowski * \author Martin Stein * \date 2013-10-30 @@ -12,18 +12,25 @@ * under the terms of the GNU General Public License version 2. */ -#ifndef _KERNEL__VM_STATE_H_ -#define _KERNEL__VM_STATE_H_ +#ifndef _INCLUDE__PLATFORM__IMX53__VM_STATE_H_ +#define _INCLUDE__PLATFORM__IMX53__VM_STATE_H_ /* Genode includes */ #include -namespace Kernel +namespace Genode { /** * CPU context of a virtual machine */ - struct Vm_state : Genode::Cpu_state_modes { Genode::addr_t dfar; }; + struct Vm_state; } -#endif /* _KERNEL__VM_STATE_H_ */ \ No newline at end of file +struct Genode::Vm_state : Genode::Cpu_state_modes +{ + Genode::addr_t dfar; + Genode::addr_t ttbr[2]; + Genode::addr_t ttbrc; +}; + +#endif /* _INCLUDE__PLATFORM__IMX53__VM_STATE_H_ */ diff --git a/repos/base-hw/include/vm_session/client.h b/repos/base-hw/include/vm_session/client.h index b5f84a83b..f920e5052 100644 --- a/repos/base-hw/include/vm_session/client.h +++ b/repos/base-hw/include/vm_session/client.h @@ -31,6 +31,7 @@ namespace Genode explicit Vm_session_client(Vm_session_capability session) : Rpc_client(session) { } + /************************** ** Vm_session interface ** **************************/ @@ -43,6 +44,15 @@ namespace Genode void run() { call(); } void pause() { call(); } + + void attach(Dataspace_capability ds,addr_t vm_addr) { + call(ds, vm_addr); } + + void detach(addr_t vm_addr, size_t size) { + call(vm_addr, size); } + + void attach_pic(addr_t vm_addr) { + call(vm_addr); } }; } diff --git a/repos/base-hw/include/vm_session/vm_session.h b/repos/base-hw/include/vm_session/vm_session.h index 51a8fa488..636bb8750 100644 --- a/repos/base-hw/include/vm_session/vm_session.h +++ b/repos/base-hw/include/vm_session/vm_session.h @@ -26,6 +26,8 @@ namespace Genode { { static const char *service_name() { return "VM"; } + class Invalid_dataspace : Exception { }; + /** * Destructor */ @@ -51,6 +53,33 @@ namespace Genode { */ virtual void pause(void) {} + /** + * Attach dataspace to the guest-physical memory address space + * + * \param ds dataspace to be attached + * \param vm_addr address in guest-physical memory address space + */ + virtual void attach(Dataspace_capability ds, addr_t vm_addr) = 0; + + /** + * Invalidate region of the guest-physical memory address space + * + * \param vm_addr address in guest-physical memory address space + * \param size size of the region to invalidate + */ + virtual void detach(addr_t vm_addr, size_t size) = 0; + + /** + * Attach cpu-local interrupt-controller's interface to + * guest-physical memory address space. + * + * \param vm_addr address in guest-physical memory address space + * + * Note: this is currently only support for ARM interrupt-controller + * hardware virtualization + */ + virtual void attach_pic(addr_t vm_addr) = 0; + /********************* ** RPC declaration ** @@ -61,8 +90,14 @@ namespace Genode { Signal_context_capability); GENODE_RPC(Rpc_run, void, run); GENODE_RPC(Rpc_pause, void, pause); + GENODE_RPC_THROW(Rpc_attach, void, attach, + GENODE_TYPE_LIST(Invalid_dataspace), + Dataspace_capability, addr_t); + GENODE_RPC(Rpc_detach, void, detach, addr_t, size_t); + GENODE_RPC(Rpc_attach_pic, void, attach_pic, addr_t); GENODE_RPC_INTERFACE(Rpc_cpu_state, Rpc_exception_handler, - Rpc_run, Rpc_pause); + Rpc_run, Rpc_pause, Rpc_attach, Rpc_detach, + Rpc_attach_pic); }; } diff --git a/repos/base-hw/lib/mk/arm/core.inc b/repos/base-hw/lib/mk/arm/core.inc index 309b16949..a0fc63e85 100644 --- a/repos/base-hw/lib/mk/arm/core.inc +++ b/repos/base-hw/lib/mk/arm/core.inc @@ -11,7 +11,6 @@ INC_DIR += $(REP_DIR)/src/core/include/spec/arm SRC_CC += spec/arm/kernel/thread_base.cc SRC_CC += spec/arm/kernel/thread.cc SRC_CC += spec/arm/kernel/cpu.cc -SRC_CC += spec/arm/kernel/vm.cc # add assembly sources SRC_S += spec/arm/crt0.s diff --git a/repos/base-hw/lib/mk/arm_v6/core.inc b/repos/base-hw/lib/mk/arm_v6/core.inc index f0a6fee3e..81daf9426 100644 --- a/repos/base-hw/lib/mk/arm_v6/core.inc +++ b/repos/base-hw/lib/mk/arm_v6/core.inc @@ -9,6 +9,8 @@ INC_DIR += $(REP_DIR)/src/core/include/spec/arm_v6 # add C++ sources SRC_CC += spec/arm/cpu.cc +SRC_CC += spec/arm/kernel/cpu_context.cc +SRC_CC += kernel/vm_thread.cc # add assembly sources SRC_S += spec/arm_v6/mode_transition.s diff --git a/repos/base-hw/lib/mk/core.inc b/repos/base-hw/lib/mk/core.inc index c6f4eb576..014c024ff 100644 --- a/repos/base-hw/lib/mk/core.inc +++ b/repos/base-hw/lib/mk/core.inc @@ -50,7 +50,6 @@ SRC_CC += pager.cc SRC_CC += _main.cc SRC_CC += kernel/kernel.cc SRC_CC += kernel/thread.cc -SRC_CC += kernel/vm.cc SRC_CC += kernel/signal_receiver.cc SRC_CC += kernel/irq.cc SRC_CC += kernel/pd.cc diff --git a/repos/base-hw/lib/mk/cortex_a8/core.inc b/repos/base-hw/lib/mk/cortex_a8/core.inc index cb68abc26..a0f444745 100644 --- a/repos/base-hw/lib/mk/cortex_a8/core.inc +++ b/repos/base-hw/lib/mk/cortex_a8/core.inc @@ -9,6 +9,7 @@ INC_DIR += $(REP_DIR)/src/core/include/spec/cortex_a8 # add C++ sources SRC_CC += spec/arm/cpu.cc +SRC_CC += spec/arm/kernel/cpu_context.cc # include less specific configuration include $(REP_DIR)/lib/mk/arm_v7/core.inc diff --git a/repos/base-hw/lib/mk/cortex_a9/core.inc b/repos/base-hw/lib/mk/cortex_a9/core.inc index 75c914308..16ec2003a 100644 --- a/repos/base-hw/lib/mk/cortex_a9/core.inc +++ b/repos/base-hw/lib/mk/cortex_a9/core.inc @@ -11,6 +11,8 @@ INC_DIR += $(REP_DIR)/src/core/include/spec/arm_gic # add C++ sources SRC_CC += spec/arm/cpu.cc SRC_CC += spec/arm_gic/pic.cc +SRC_CC += spec/arm/kernel/cpu_context.cc +SRC_CC += kernel/vm_thread.cc # include less specific configuration include $(REP_DIR)/lib/mk/arm_v7/core.inc diff --git a/repos/base-hw/lib/mk/exynos5/core.inc b/repos/base-hw/lib/mk/exynos5/core.inc index 7deee252b..2f7078a50 100644 --- a/repos/base-hw/lib/mk/exynos5/core.inc +++ b/repos/base-hw/lib/mk/exynos5/core.inc @@ -10,7 +10,6 @@ INC_DIR += $(REP_DIR)/src/core/include/spec/exynos5 # add C++ sources SRC_CC += spec/exynos5/platform_support.cc SRC_CC += spec/exynos5/cpu.cc -SRC_CC += platform_services.cc # include less specific configuration include $(REP_DIR)/lib/mk/cortex_a15/core.inc diff --git a/repos/base-hw/lib/mk/platform_arndale/core.mk b/repos/base-hw/lib/mk/platform_arndale/core.mk index 543f2548c..3a8945c32 100644 --- a/repos/base-hw/lib/mk/platform_arndale/core.mk +++ b/repos/base-hw/lib/mk/platform_arndale/core.mk @@ -4,9 +4,22 @@ # \date 2015-02-09 # +# add include paths +INC_DIR += $(REP_DIR)/src/core/include/spec/arm_v7/virtualization + # add C++ sources SRC_CC += spec/arndale/board.cc SRC_CC += spec/arndale/pic.cc +SRC_CC += spec/arndale/platform_services.cc +SRC_CC += spec/arm_v7/kernel/vm_thread.cc +SRC_CC += spec/arm_v7/virtualization/kernel/vm.cc +SRC_CC += spec/arm_v7/virtualization/kernel/vm_thread.cc +SRC_CC += spec/arm_v7/virtualization/kernel/cpu_context.cc +SRC_CC += spec/arm_v7/vm_session_component.cc +SRC_CC += spec/arm_v7/virtualization/vm_session_component.cc + +# add assembly sources +SRC_S += spec/arm_v7/virtualization/mode_transition.s # include less specific configuration include $(REP_DIR)/lib/mk/exynos5/core.inc diff --git a/repos/base-hw/lib/mk/platform_imx53/core-trustzone.inc b/repos/base-hw/lib/mk/platform_imx53/core-trustzone.inc new file mode 100644 index 000000000..453f74334 --- /dev/null +++ b/repos/base-hw/lib/mk/platform_imx53/core-trustzone.inc @@ -0,0 +1,13 @@ +# +# \brief Build config for Genodes core process +# \author Stefan Kalkowski +# \author Martin Stein +# \date 2012-10-24 +# + +# add include paths +INC_DIR += $(REP_DIR)/src/core/include/spec/imx53 +INC_DIR += $(REP_DIR)/src/core/include/spec/imx + +# include less specific configuration +include $(REP_DIR)/lib/mk/cortex_a8/core.inc diff --git a/repos/base-hw/lib/mk/platform_imx53/core-trustzone_off.mk b/repos/base-hw/lib/mk/platform_imx53/core-trustzone_off.mk index f9954033e..fe14eaac1 100644 --- a/repos/base-hw/lib/mk/platform_imx53/core-trustzone_off.mk +++ b/repos/base-hw/lib/mk/platform_imx53/core-trustzone_off.mk @@ -5,14 +5,12 @@ # \date 2012-10-24 # -# add include paths -INC_DIR += $(REP_DIR)/src/core/include/spec/imx53 -INC_DIR += $(REP_DIR)/src/core/include/spec/cortex_a8 - # add C++ sources +SRC_CC += kernel/vm_thread.cc SRC_CC += spec/imx53/platform_support.cc SRC_CC += spec/imx53/pic.cc SRC_CC += platform_services.cc # include less specific configuration +include $(REP_DIR)/lib/mk/platform_imx53/core-trustzone.inc include $(REP_DIR)/lib/mk/core-trustzone.inc diff --git a/repos/base-hw/lib/mk/platform_imx53/core-trustzone_on.mk b/repos/base-hw/lib/mk/platform_imx53/core-trustzone_on.mk index 57215c0d7..1e4cb90a1 100644 --- a/repos/base-hw/lib/mk/platform_imx53/core-trustzone_on.mk +++ b/repos/base-hw/lib/mk/platform_imx53/core-trustzone_on.mk @@ -6,15 +6,22 @@ # # add include paths +INC_DIR += $(REP_DIR)/src/core/include/spec/arm_v7/trustzone INC_DIR += $(REP_DIR)/src/core/include/spec/imx53/trustzone -INC_DIR += $(REP_DIR)/src/core/include/spec/imx53 -INC_DIR += $(REP_DIR)/src/core/include/spec/cortex_a8 # add C++ sources SRC_CC += spec/imx53/trustzone/platform_support.cc SRC_CC += spec/imx53/trustzone/platform_services.cc SRC_CC += spec/imx53/trustzone/pic.cc -SRC_CC += vm_session_component.cc +SRC_CC += spec/arm_v7/kernel/vm_thread.cc +SRC_CC += spec/arm_v7/trustzone/kernel/vm.cc +SRC_CC += spec/arm_v7/trustzone/kernel/vm_thread.cc +SRC_CC += spec/arm_v7/vm_session_component.cc +SRC_CC += spec/arm_v7/trustzone/vm_session_component.cc + +# add assembly sources +SRC_S += spec/arm_v7/trustzone/mode_transition.s # include less specific configuration +include $(REP_DIR)/lib/mk/platform_imx53/core-trustzone.inc include $(REP_DIR)/lib/mk/core-trustzone.inc diff --git a/repos/base-hw/lib/mk/platform_imx53/core.mk b/repos/base-hw/lib/mk/platform_imx53/core.mk index 240305e08..60d13212b 100644 --- a/repos/base-hw/lib/mk/platform_imx53/core.mk +++ b/repos/base-hw/lib/mk/platform_imx53/core.mk @@ -7,11 +7,3 @@ # add library dependencies LIBS += core-trustzone - -# add include paths -INC_DIR += $(REP_DIR)/src/core/include/spec/imx53 -INC_DIR += $(REP_DIR)/src/core/include/spec/imx -INC_DIR += $(REP_DIR)/src/core/include/spec/cortex_a8 - -# include less specific configuration -include $(REP_DIR)/lib/mk/cortex_a8/core.inc diff --git a/repos/base-hw/lib/mk/platform_odroid_xu/core.mk b/repos/base-hw/lib/mk/platform_odroid_xu/core.mk index 3d83cc3a4..f2cec1fe3 100644 --- a/repos/base-hw/lib/mk/platform_odroid_xu/core.mk +++ b/repos/base-hw/lib/mk/platform_odroid_xu/core.mk @@ -7,6 +7,9 @@ # add C++ sources SRC_CC += spec/exynos5/board.cc SRC_CC += spec/arm_gic/pic.cc +SRC_CC += platform_services.cc +SRC_CC += kernel/vm_thread.cc +SRC_CC += spec/arm/kernel/cpu_context.cc # include less specific configuration include $(REP_DIR)/lib/mk/exynos5/core.inc diff --git a/repos/base-hw/src/core/include/kernel/core_interface.h b/repos/base-hw/src/core/include/kernel/core_interface.h index e58848cdc..023dbabf2 100644 --- a/repos/base-hw/src/core/include/kernel/core_interface.h +++ b/repos/base-hw/src/core/include/kernel/core_interface.h @@ -26,7 +26,6 @@ namespace Kernel unsigned pd_alignment_log2(); size_t signal_context_size(); size_t signal_receiver_size(); - size_t vm_size(); /** * Kernel names of the kernel calls @@ -48,6 +47,7 @@ namespace Kernel constexpr Call_arg call_id_run_vm() { return 28; } constexpr Call_arg call_id_pause_vm() { return 29; } constexpr Call_arg call_id_pause_thread() { return 30; } + constexpr Call_arg call_id_bin_vm() { return 31; } /** * Create a domain @@ -293,6 +293,8 @@ namespace Kernel * \param dst memory donation for the VM object * \param state location of the CPU state of the VM * \param signal_context_id kernel name of the signal context for VM events + * \param table guest-physical to host-physical translation + * table pointer * * \retval >0 kernel name of the new VM * \retval 0 failed @@ -300,10 +302,11 @@ namespace Kernel * Regaining of the supplied memory is not supported by now. */ inline unsigned new_vm(void * const dst, void * const state, - unsigned const signal_context_id) + unsigned const signal_context_id, + void * const table) { return call(call_id_new_vm(), (Call_arg)dst, (Call_arg)state, - signal_context_id); + (Call_arg)table, signal_context_id); } @@ -311,10 +314,27 @@ namespace Kernel * Execute a virtual-machine (again) * * \param vm_id kernel name of the targeted VM + * + * \retval 0 suceeded + * \retval -1 failed */ - inline void run_vm(unsigned const vm_id) + inline int run_vm(unsigned const vm_id) { - call(call_id_run_vm(), vm_id); + return call(call_id_run_vm(), vm_id); + } + + + /** + * Destruct a virtual-machine + * + * \param vm_id kernel name of the targeted VM + * + * \retval 0 suceeded + * \retval -1 failed + */ + inline int bin_vm(unsigned const vm_id) + { + return call(call_id_bin_vm(), vm_id); } @@ -322,10 +342,13 @@ namespace Kernel * Stop execution of a virtual-machine * * \param vm_id kernel name of the targeted VM + * + * \retval 0 suceeded + * \retval -1 failed */ - inline void pause_vm(unsigned const vm_id) + inline int pause_vm(unsigned const vm_id) { - call(call_id_pause_vm(), vm_id); + return call(call_id_pause_vm(), vm_id); } } diff --git a/repos/base-hw/src/core/include/kernel/irq.h b/repos/base-hw/src/core/include/kernel/irq.h index 08939437a..22cd996b1 100644 --- a/repos/base-hw/src/core/include/kernel/irq.h +++ b/repos/base-hw/src/core/include/kernel/irq.h @@ -49,16 +49,6 @@ class Kernel::Irq : public Object_pool::Item { protected: - /** - * Prevent interrupt from occurring - */ - void _disable() const; - - /** - * Allow interrupt to occur - */ - void _enable() const; - /** * Get kernel name of the interrupt */ @@ -96,6 +86,16 @@ class Kernel::Irq : public Object_pool::Item * Handle occurence of the interrupt */ virtual void occurred() { } + + /** + * Prevent interrupt from occurring + */ + void disable() const; + + /** + * Allow interrupt to occur + */ + void enable() const; }; @@ -128,7 +128,7 @@ class Kernel::User_irq ** Signal_ack_handler ** ************************/ - void _signal_acknowledged() { _enable(); } + void _signal_acknowledged() { enable(); } public: @@ -141,7 +141,7 @@ class Kernel::User_irq : Irq(irq_id), Signal_context(this, 0) { _pool()->insert(this); - _disable(); + disable(); Signal_context::ack_handler(this); } @@ -151,7 +151,7 @@ class Kernel::User_irq void occurred() { Signal_context::submit(1); - _disable(); + disable(); } /** diff --git a/repos/base-hw/src/core/include/kernel/kernel.h b/repos/base-hw/src/core/include/kernel/kernel.h index 0b582d899..f3e5490d7 100644 --- a/repos/base-hw/src/core/include/kernel/kernel.h +++ b/repos/base-hw/src/core/include/kernel/kernel.h @@ -15,11 +15,13 @@ #ifndef _KERNEL__KERNEL_H_ #define _KERNEL__KERNEL_H_ +#include #include namespace Kernel { Pd * core_pd(); Mode_transition_control * mtc(); + Pic * pic(); } #endif /* _KERNEL__KERNEL_H_ */ diff --git a/repos/base-hw/src/core/include/kernel/pd.h b/repos/base-hw/src/core/include/kernel/pd.h index 17fa55c84..c815099dc 100644 --- a/repos/base-hw/src/core/include/kernel/pd.h +++ b/repos/base-hw/src/core/include/kernel/pd.h @@ -24,14 +24,14 @@ #include #include #include -#include #include +#include +#include /* structure of the mode transition */ extern int _mt_begin; extern int _mt_end; extern int _mt_user_entry_pic; -extern int _mt_vm_entry_pic; extern Genode::addr_t _mt_client_context_ptr; extern Genode::addr_t _mt_master_context_begin; extern Genode::addr_t _mt_master_context_end; @@ -148,31 +148,6 @@ class Kernel::Mode_transition_control return VIRT_BASE + (phys - phys_base); } - /** - * Continue execution of client context - * - * \param context targeted CPU context - * \param cpu kernel name of targeted CPU - * \param entry_raw raw pointer to assembly entry-code - */ - void _continue_client(void * const context, unsigned const cpu, - addr_t const entry_raw) - { - /* override client-context pointer of the executing CPU */ - addr_t const context_ptr_base = (addr_t)&_mt_client_context_ptr; - size_t const context_ptr_offset = cpu * sizeof(context); - addr_t const context_ptr = context_ptr_base + context_ptr_offset; - *(void * *)context_ptr = context; - - /* unlock kernel data */ - data_lock().unlock(); - - /* call assembly code that applies the virtual-machine context */ - typedef void (* Entry)(); - Entry __attribute__((noreturn)) const entry = (Entry)entry_raw; - entry(); - } - public: enum { @@ -208,17 +183,44 @@ class Kernel::Mode_transition_control } /** - * Continue execution of 'user' at 'cpu' + * Continue execution of client context + * + * \param context targeted CPU context + * \param cpu kernel name of targeted CPU + * \param entry_raw raw pointer to assembly entry-code + * \param context_ptr_base base address of client-context pointer region */ - void continue_user(Cpu::Context * const user, unsigned const cpu) { - _continue_client(user, cpu, _virt_user_entry()); } + void switch_to(Cpu::Context * const context, + unsigned const cpu, + addr_t const entry_raw, + addr_t const context_ptr_base) + { + /* override client-context pointer of the executing CPU */ + size_t const context_ptr_offset = cpu * sizeof(context); + addr_t const context_ptr = context_ptr_base + context_ptr_offset; + *(void * *)context_ptr = context; + + /* unlock kernel data */ + data_lock().unlock(); + + /* call assembly code that applies the virtual-machine context */ + typedef void (* Entry)(); + Entry __attribute__((noreturn)) const entry = (Entry)entry_raw; + entry(); + } /** - * Continue execution of 'vm' at 'cpu' + * Continue execution of user context + * + * \param context targeted CPU context + * \param cpu kernel name of targeted CPU */ - void continue_vm(Vm_state * const vm, unsigned const cpu) { - _continue_client(vm, cpu, (addr_t)&_mt_vm_entry_pic); } - + void switch_to_user(Cpu::Context * const context, + unsigned const cpu) + { + switch_to(context, cpu, _virt_user_entry(), + (addr_t)&_mt_client_context_ptr); + } } __attribute__((aligned(Mode_transition_control::ALIGN))); class Kernel::Pd : public Object diff --git a/repos/base-hw/src/core/include/kernel/thread.h b/repos/base-hw/src/core/include/kernel/thread.h index f6e8738fc..721dc336a 100644 --- a/repos/base-hw/src/core/include/kernel/thread.h +++ b/repos/base-hw/src/core/include/kernel/thread.h @@ -235,6 +235,7 @@ class Kernel::Thread void _call_bin_signal_context(); void _call_bin_signal_receiver(); void _call_new_vm(); + void _call_bin_vm(); void _call_run_vm(); void _call_pause_vm(); void _call_access_thread_regs(); diff --git a/repos/base-hw/src/core/include/kernel/vm.h b/repos/base-hw/src/core/include/kernel/vm.h index 72fa7b30d..1530c62f3 100644 --- a/repos/base-hw/src/core/include/kernel/vm.h +++ b/repos/base-hw/src/core/include/kernel/vm.h @@ -14,8 +14,9 @@ #ifndef _KERNEL__VM_H_ #define _KERNEL__VM_H_ +#include + /* core includes */ -#include #include #include #include @@ -34,13 +35,18 @@ namespace Kernel Vm_pool * vm_pool(); } + class Kernel::Vm : public Object, public Cpu_job { private: - Vm_state * const _state; - Signal_context * const _context; + enum State { ACTIVE, INACTIVE }; + + Genode::Vm_state * const _state; + Signal_context * const _context; + void * const _table; + State _scheduled = INACTIVE; public: @@ -49,20 +55,35 @@ class Kernel::Vm : public Object, * * \param state initial CPU state * \param context signal for VM exceptions other than interrupts + * \param table translation table for guest to host physical memory */ - Vm(void * const state, Signal_context * const context) - : - Cpu_job(Cpu_priority::min, 0), _state((Vm_state * const)state), - _context(context) - { affinity(cpu_pool()->primary_cpu()); } + Vm(void * const state, + Signal_context * const context, + void * const table); + + /** + * Inject an interrupt to this VM + * + * \param irq interrupt number to inject + */ + void inject_irq(unsigned irq); /**************** ** Vm_session ** ****************/ - void run() { Cpu_job::_activate_own_share(); } - void pause() { Cpu_job::_deactivate_own_share(); } + void run() + { + if (_scheduled != ACTIVE) Cpu_job::_activate_own_share(); + _scheduled = ACTIVE; + } + + void pause() + { + if (_scheduled != INACTIVE) Cpu_job::_deactivate_own_share(); + _scheduled = INACTIVE; + } /************* @@ -70,7 +91,7 @@ class Kernel::Vm : public Object, *************/ void exception(unsigned const cpu); - void proceed(unsigned const cpu) { mtc()->continue_vm(_state, cpu); } + void proceed(unsigned const cpu); Cpu_job * helping_sink() { return this; } }; diff --git a/repos/base-hw/src/core/include/spec/arm/cpu_support.h b/repos/base-hw/src/core/include/spec/arm/cpu_support.h index 84ce8d418..b7ad2d83d 100644 --- a/repos/base-hw/src/core/include/spec/arm/cpu_support.h +++ b/repos/base-hw/src/core/include/spec/arm/cpu_support.h @@ -345,27 +345,21 @@ class Genode::Arm */ struct Context : Cpu_state { - /** - * TODO: currently all non-Cortex A15 platforms use the - * short translation table format and thereby the Context ID - * register to store the ASID, and the TTBR0 for the table - * address. Cortex A15 uses the long translation format and - * a 64-bit wide TTBR0 that holds all information. - * The current Cortex A15 implementation stores TTBR0 in both - * members stated below. - */ - uint32_t cidr; - uint32_t ttbr0; + Cidr::access_t cidr; + Ttbr0::access_t ttbr0; /** * Return base of assigned translation table */ - addr_t translation_table() const; + addr_t translation_table() const { + return Ttbr0::Ba::masked(ttbr0); } + /** * Assign translation-table base 'table' */ - void translation_table(addr_t const table); + void translation_table(addr_t const table) { + ttbr0 = Arm::Ttbr0::init(table); } /** * Assign protection domain @@ -421,7 +415,41 @@ class Genode::Arm * \param va holds the virtual fault-address if call returns 1 * \param w holds wether it's a write fault if call returns 1 */ - bool in_fault(addr_t & va, addr_t & w) const; + bool in_fault(addr_t & va, addr_t & w) const + { + switch (cpu_exception) { + + case PREFETCH_ABORT: + { + /* check if fault was caused by a translation miss */ + Ifsr::access_t const fs = Ifsr::Fs::get(Ifsr::read()); + if (fs != Ifsr::section && fs != Ifsr::page) + return false; + + /* fetch fault data */ + w = 0; + va = ip; + return true; + } + case DATA_ABORT: + { + /* check if fault was caused by translation miss */ + Dfsr::access_t const fs = Dfsr::Fs::get(Dfsr::read()); + if (fs != Dfsr::section && fs != Dfsr::page) + return false; + + /* fetch fault data */ + Dfsr::access_t const dfsr = Dfsr::read(); + w = Dfsr::Wnr::get(dfsr); + va = Dfar::read(); + return true; + } + + default: + return false; + }; + } + }; /** diff --git a/repos/base-hw/src/core/include/spec/arm/macros_support.s b/repos/base-hw/src/core/include/spec/arm/macros_support.s index 87d440a0c..838447619 100644 --- a/repos/base-hw/src/core/include/spec/arm/macros_support.s +++ b/repos/base-hw/src/core/include/spec/arm/macros_support.s @@ -86,6 +86,8 @@ .set TRANSIT_TTBR0_OFFSET, 17 * 4 .set CIDR_OFFSET, 18 * 4 .set TTBR0_OFFSET, 19 * 4 +.set TTBCR_OFFSET, 20 * 4 +.set MAIR0_OFFSET, 21 * 4 /* size of local variables */ .set CONTEXT_PTR_SIZE, 1 * 4 diff --git a/repos/base-hw/src/core/include/spec/arm_v7/cpu_support.h b/repos/base-hw/src/core/include/spec/arm_v7/cpu_support.h index bec851aa8..070f64bcc 100644 --- a/repos/base-hw/src/core/include/spec/arm_v7/cpu_support.h +++ b/repos/base-hw/src/core/include/spec/arm_v7/cpu_support.h @@ -238,6 +238,22 @@ class Genode::Arm_v7 : public Arm } }; + + /** + * Memory attribute indirection register 0 + */ + struct Mair0 : Register<32> + { + struct Attr0 : Bitfield<0, 8> { }; + struct Attr1 : Bitfield<8, 8> { }; + struct Attr2 : Bitfield<16, 8> { }; + struct Attr3 : Bitfield<24, 8> { }; + + static void write(access_t v) { + asm volatile ("mcr p15, 0, %[v], c10, c2, 0" :: [v]"r"(v) : ); } + }; + + /** * Invalidate all branch predictions */ diff --git a/repos/base-hw/src/core/include/spec/arm_v7/macros_support.s b/repos/base-hw/src/core/include/spec/arm_v7/macros_support.s index e3d504134..c5626e724 100644 --- a/repos/base-hw/src/core/include/spec/arm_v7/macros_support.s +++ b/repos/base-hw/src/core/include/spec/arm_v7/macros_support.s @@ -25,3 +25,62 @@ /* get the affinity-0 bitfield from the read register value */ and \r, \r, #0xff .endm + + +/** + * Determine the base of the client context of the executing CPU + * + * \param target_reg register that shall receive the base pointer + * \param buf_reg register that can be polluted by the macro + * \param client_context_ptr label of the client context pointer base + */ +.macro _get_client_context_ptr target_reg, buf_reg, client_context_ptr + + /* get kernel name of CPU */ + _get_cpu_id \buf_reg + + /* multiply CPU name with pointer size to get offset of pointer */ + mov \target_reg, #CONTEXT_PTR_SIZE + mul \buf_reg, \buf_reg, \target_reg + + /* get base of the pointer array */ + adr \target_reg, \client_context_ptr + + /* add offset and base to get CPU-local pointer */ + add \target_reg, \target_reg, \buf_reg + ldr \target_reg, [\target_reg] +.endm + + +/** + * Save sp, lr and spsr register banks of specified exception mode + */ +.macro _save_bank mode + cps #\mode /* switch to given mode */ + mrs r1, spsr /* store mode-specific spsr */ + stmia r0!, {r1,sp,lr} /* store mode-specific sp and lr */ +.endm /* _save_bank mode */ + + +/** + * Restore sp, lr and spsr register banks of specified exception mode + */ +.macro _restore_bank mode + cps #\mode /* switch to given mode */ + ldmia r0!, {r1,sp,lr} /* load mode-specific sp, lr, and spsr into r1 */ + msr spsr_cxfs, r1 /* load mode-specific spsr */ +.endm + + +/*************** + ** Constants ** + ***************/ + +/* hardware names of CPU modes */ +.set USR_MODE, 16 +.set FIQ_MODE, 17 +.set IRQ_MODE, 18 +.set SVC_MODE, 19 +.set ABT_MODE, 23 +.set UND_MODE, 27 + diff --git a/repos/base-hw/src/core/include/spec/arm_v7/trustzone/vm_session_component.h b/repos/base-hw/src/core/include/spec/arm_v7/trustzone/vm_session_component.h new file mode 100644 index 000000000..0b600a146 --- /dev/null +++ b/repos/base-hw/src/core/include/spec/arm_v7/trustzone/vm_session_component.h @@ -0,0 +1,76 @@ +/* + * \brief Core-specific instance of the VM session interface + * \author Stefan Kalkowski + * \date 2012-10-08 + */ + +/* + * Copyright (C) 2012-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 _CORE__INCLUDE__VM_SESSION_COMPONENT_H_ +#define _CORE__INCLUDE__VM_SESSION_COMPONENT_H_ + +/* Genode includes */ +#include +#include +#include +#include + +/* Core includes */ +#include +#include + +namespace Genode { + class Vm_session_component; +} + +class Genode::Vm_session_component : + public Genode::Rpc_object +{ + private: + + Rpc_entrypoint *_ds_ep; + Range_allocator *_ram_alloc; + unsigned _vm_id; + char _vm[sizeof(Kernel::Vm)]; + Dataspace_component _ds; + Dataspace_capability _ds_cap; + addr_t _ds_addr; + + static size_t _ds_size() { + return align_addr(sizeof(Cpu_state_modes), + get_page_size_log2()); } + + addr_t _alloc_ds(size_t &ram_quota); + + public: + + Vm_session_component(Rpc_entrypoint *ds_ep, + size_t ram_quota); + ~Vm_session_component(); + + + /************************** + ** Vm session interface ** + **************************/ + + Dataspace_capability cpu_state(void) { return _ds_cap; } + void exception_handler(Signal_context_capability handler); + void run(void); + void pause(void); + + void attach(Dataspace_capability ds_cap, addr_t vm_addr) { + PWRN("Not implemented for TrustZone case"); } + + void attach_pic(addr_t vm_addr) { + PWRN("Not implemented for TrustZone case"); } + + void detach(addr_t vm_addr, size_t size) { + PWRN("Not implemented for TrustZone case"); } +}; + +#endif /* _CORE__INCLUDE__VM_SESSION_COMPONENT_H_ */ diff --git a/repos/base-hw/src/core/include/spec/arm_v7/virtualization/vm_session_component.h b/repos/base-hw/src/core/include/spec/arm_v7/virtualization/vm_session_component.h new file mode 100644 index 000000000..a36584339 --- /dev/null +++ b/repos/base-hw/src/core/include/spec/arm_v7/virtualization/vm_session_component.h @@ -0,0 +1,77 @@ +/* + * \brief Core-specific instance of the VM session interface + * \author Stefan Kalkowski + * \date 2012-10-08 + */ + +/* + * Copyright (C) 2012-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 _CORE__INCLUDE__VM_SESSION_COMPONENT_H_ +#define _CORE__INCLUDE__VM_SESSION_COMPONENT_H_ + +/* Genode includes */ +#include +#include +#include +#include +#include + +/* Core includes */ +#include +#include + +namespace Genode { + class Vm_session_component; +} + +class Genode::Vm_session_component : + public Genode::Rpc_object +{ + private: + + using Translation_table = + Genode::Level_1_stage_2_translation_table; + + Rpc_entrypoint *_ds_ep; + Range_allocator *_ram_alloc; + unsigned _vm_id; + char _vm[sizeof(Kernel::Vm)]; + Dataspace_component _ds; + Dataspace_capability _ds_cap; + addr_t _ds_addr; + Translation_table *_table; + Page_slab *_pslab; + + static size_t _ds_size() { + return align_addr(sizeof(Cpu_state_modes), + get_page_size_log2()); } + + addr_t _alloc_ds(size_t &ram_quota); + void _attach(addr_t phys_addr, addr_t vm_addr, size_t size); + + public: + + Vm_session_component(Rpc_entrypoint *ds_ep, + size_t ram_quota); + ~Vm_session_component(); + + + /************************** + ** Vm session interface ** + **************************/ + + Dataspace_capability cpu_state(void) { return _ds_cap; } + void exception_handler(Signal_context_capability handler); + void run(void); + void pause(void); + void attach(Dataspace_capability ds_cap, addr_t vm_addr); + void attach_pic(addr_t vm_addr); + void detach(addr_t vm_addr, size_t size); +}; + +#endif /* _CORE__INCLUDE__VM_SESSION_COMPONENT_H_ */ diff --git a/repos/base-hw/src/core/include/spec/cortex_a15/cpu.h b/repos/base-hw/src/core/include/spec/cortex_a15/cpu.h index 1de217781..e1157d7d4 100644 --- a/repos/base-hw/src/core/include/spec/cortex_a15/cpu.h +++ b/repos/base-hw/src/core/include/spec/cortex_a15/cpu.h @@ -57,12 +57,25 @@ class Genode::Cpu : public Arm_v7 } }; - struct Mair0 : Register<32> + /** + * Memory attribute indirection register 0 + */ + struct Mair0 : Arm_v7::Mair0 { - static void init() + enum Attr { + DEVICE_MEMORY = 0x04, + NORMAL_MEMORY_UNCACHED = 0x44, + NORMAL_MEMORY_CACHED = 0xff, + }; + + static access_t init_virt_kernel() { - access_t v = 0xff0044; - asm volatile ("mcr p15, 0, %[v], c10, c2, 0" :: [v]"r"(v) : ); + access_t v = 0; + Attr0::set(v, NORMAL_MEMORY_UNCACHED); + Attr1::set(v, DEVICE_MEMORY); + Attr2::set(v, NORMAL_MEMORY_CACHED); + Attr3::set(v, DEVICE_MEMORY); + return v; } }; @@ -112,7 +125,285 @@ class Genode::Cpu : public Arm_v7 static Genode::uint32_t init(addr_t const table) { return table; } + }; + + /********************************* + ** Virtualization extensions ** + *********************************/ + + /** + * Hypervisor translation table base register + */ + struct Httbr : Register<64> + { + static void translation_table(addr_t const table) + { + asm volatile ("mcrr p15, 4, %[v0], %[v1], c2" + :: [v0]"r"(table), [v1]"r"(0)); + } + }; + + /** + * Hypervisor translation control register + */ + struct Htcr : Register<32> + { + static void write(access_t const v) { + asm volatile ("mcr p15, 4, %[v], c2, c0, 2" :: [v] "r" (v)); } + }; + + /** + * Hypervisor coprocessor trap register + */ + struct Hcptr : Register<32> + { + /* Coprocessor access trap */ + template + struct Tcp : Bitfield {}; + + struct Tase : Bitfield<15, 1> { }; + struct Tta : Bitfield<20, 1> { }; + struct Tcpac : Bitfield<31, 1> { }; + + static access_t init() + { + /* don't trap on cporocessor 10 + 11, but all others */ + access_t v = 0; + Tcp<0>::set(v, 1); + Tcp<1>::set(v, 1); + Tcp<2>::set(v, 1); + Tcp<3>::set(v, 1); + Tcp<4>::set(v, 1); + Tcp<5>::set(v, 1); + Tcp<6>::set(v, 1); + Tcp<7>::set(v, 1); + Tcp<8>::set(v, 1); + Tcp<9>::set(v, 1); + Tcp<12>::set(v, 1); + Tcp<13>::set(v, 1); + Tta::set(v, 1); + Tcpac::set(v, 1); + return v; + } + + static void write(access_t const v) { + asm volatile ("mcr p15, 4, %[v], c1, c1, 2" :: [v] "r" (v)); } + }; + + /** + * Hypervisor Memory attribute indirection register 0 + */ + struct Hmair0 : Register<32> + { + static void write(access_t const v) { + asm volatile ("mcr p15, 4, %[v], c10, c2, 0" :: [v] "r" (v)); } + }; + + /** + * Hypervisor system control register + */ + struct Hsctlr : Arm_v7::Sctlr + { + static void write(access_t const v) { + asm volatile ("mcr p15, 4, %[v], c1, c0, 0" :: [v] "r" (v)); } + }; + + /** + * Hypervisor system trap register + */ + struct Hstr : Register<32> + { + /* System coprocessor primary register access trap */ + template + struct T : Bitfield {}; + + static access_t init() + { + /* + * allow cache (7), TLB (8) maintenance, and performance + * monitor (9), process/thread ID register (13) and timer (14) + * access. + */ + access_t v = 0; + T<0>::set(v, 1); + T<1>::set(v, 1); + T<2>::set(v, 1); + T<3>::set(v, 1); + T<5>::set(v, 1); + T<6>::set(v, 1); + T<10>::set(v, 1); + T<11>::set(v, 1); + T<12>::set(v, 1); + T<15>::set(v, 1); + return v; + }; + }; + + /** + * Hypervisor control register + */ + struct Hcr : Register<32> + { + struct Vm : Bitfield<0, 1> {}; /* VT MMU enabled */ + struct Fmo : Bitfield<3, 1> {}; /* FIQ cannot been masked */ + struct Imo : Bitfield<4, 1> {}; /* IRQ cannot been masked */ + struct Amo : Bitfield<5, 1> {}; /* A bit cannot been masked */ + struct Twi : Bitfield<13, 1> {}; /* trap on WFI instruction */ + struct Twe : Bitfield<14, 1> {}; /* trap on WFE instruction */ + struct Tidcp : Bitfield<20, 1> {}; /* trap lockdown */ + struct Tac : Bitfield<21, 1> {}; /* trap ACTLR accesses */ + struct Tvm : Bitfield<26, 1> {}; /* trap virtual memory ctrls */ + + static access_t init() + { + access_t v = 0; + Vm::set(v, 1); + Fmo::set(v, 1); + Imo::set(v, 1); + Amo::set(v, 1); + Twi::set(v, 1); + Twe::set(v, 1); + Tidcp::set(v, 1); + Tac::set(v, 1); + Tvm::set(v, 1); + return v; + }; + }; + + /** + * Virtualization translation control register + */ + struct Vtcr : Ttbcr + { + struct Sl0 : Bitfield<6,2> {}; + + static access_t init() + { + access_t v = Ttbcr::init_virt_kernel(); + Sl0::set(v, 1); /* set to starting level 1 */ + return v; + } + + static void write(access_t const v) { + asm volatile ("mcr p15, 4, %[v], c2, c1, 2" :: [v] "r" (v)); } + }; + + /** + * Extend basic CPU state by members relevant for 'base-hw' only + * + * Note: this class redefines Genode::Arm::Context + */ + struct Context : Genode::Cpu_state + { + Ttbr0::access_t ttbr0 = 0; + Sctlr::access_t sctlr = 0; + Ttbcr::access_t ttbrc = 0; + Mair0::access_t mair0 = 0; + + /** + * Return base of assigned translation table + */ + addr_t translation_table() const { + return Ttbr0::Ba::masked(ttbr0); } + + /** + * Assign translation-table base 'table' + */ + void translation_table(addr_t const table) { + Ttbr0::Ba::set(ttbr0, (Ttbr0::access_t)(table >> 5)); } + + /** + * Assign protection domain + */ + void protection_domain(unsigned const id) { + Ttbr0::Asid::set(ttbr0, id); } + }; + + + /** + * An usermode execution state + * + * FIXME: this class largely overlaps with Genode::Arm::User_context + */ + struct User_context : Context + { + /** + * Constructor + */ + User_context() { cpsr = Psr::init_user(); } + + /** + * Support for kernel calls + */ + void user_arg_0(unsigned const arg) { r0 = arg; } + void user_arg_1(unsigned const arg) { r1 = arg; } + void user_arg_2(unsigned const arg) { r2 = arg; } + void user_arg_3(unsigned const arg) { r3 = arg; } + void user_arg_4(unsigned const arg) { r4 = arg; } + void user_arg_5(unsigned const arg) { r5 = arg; } + void user_arg_6(unsigned const arg) { r6 = arg; } + void user_arg_7(unsigned const arg) { r7 = arg; } + unsigned user_arg_0() const { return r0; } + unsigned user_arg_1() const { return r1; } + unsigned user_arg_2() const { return r2; } + unsigned user_arg_3() const { return r3; } + unsigned user_arg_4() const { return r4; } + unsigned user_arg_5() const { return r5; } + unsigned user_arg_6() const { return r6; } + unsigned user_arg_7() const { return r7; } + + /** + * Initialize thread context + * + * \param table physical base of appropriate translation table + * \param pd_id kernel name of appropriate protection domain + */ + void init_thread(addr_t const table, unsigned const pd_id) + { + protection_domain(pd_id); + translation_table(table); + } + + /** + * Return if the context is in a page fault due to translation miss + * + * \param va holds the virtual fault-address if call returns 1 + * \param w holds wether it's a write fault if call returns 1 + */ + bool in_fault(addr_t & va, addr_t & w) const + { + switch (cpu_exception) { + + case PREFETCH_ABORT: + { + /* check if fault was caused by a translation miss */ + Ifsr::access_t const fs = Ifsr::Fs::get(Ifsr::read()); + if ((fs & 0b11100) != 0b100) return false; + + /* fetch fault data */ + w = 0; + va = ip; + return true; + } + + case DATA_ABORT: + { + /* check if fault was caused by translation miss */ + Dfsr::access_t const fs = Dfsr::Fs::get(Dfsr::read()); + if ((fs & 0b11100) != 0b100) return false; + + /* fetch fault data */ + Dfsr::access_t const dfsr = Dfsr::read(); + w = Dfsr::Wnr::get(dfsr); + va = Dfar::read(); + return true; + } + + default: + return false; + }; + } }; @@ -140,7 +431,7 @@ class Genode::Cpu : public Arm_v7 static void init_virt_kernel(addr_t const table, unsigned const process_id) { - Mair0::init(); + Mair0::write(Mair0::init_virt_kernel()); Cidr::write(process_id); Dacr::write(Dacr::init_virt_kernel()); Ttbr0::write(Ttbr0::init(table, 1)); diff --git a/repos/base-hw/src/core/include/spec/cortex_a15/macros.s b/repos/base-hw/src/core/include/spec/cortex_a15/macros.s index 712e2dab6..134483372 100644 --- a/repos/base-hw/src/core/include/spec/cortex_a15/macros.s +++ b/repos/base-hw/src/core/include/spec/cortex_a15/macros.s @@ -22,15 +22,14 @@ * base address in the one 64-bit TTBR0 register, like in Armv7 cpus without * LPAE extensions. Therefore, we don't have to use a transition table. * - * \param transit_ttbr0 ignored parameter - * \param new_cidr new CIDR value, read reg - * \param new_ttbr0 new TTBR0 value, read/write reg + * \param ignored ignored parameter + * \param ttbr0_low low word of TTBR0 64-bit register + * \param ttbr0_high high word of TTBR0 64-bit register */ -.macro _switch_protection_domain transit_ttbr0, new_cidr, new_ttbr0 +.macro _switch_protection_domain ignored, ttbr0_low, ttbr0_high /* write translation-table-base register 0 */ - lsl \new_cidr, \new_cidr, #16 - mcrr p15, 0, \new_ttbr0, \new_cidr, c2 + mcrr p15, 0, \ttbr0_low, \ttbr0_high, c2 /* instruction and data synchronization barrier */ isb diff --git a/repos/base-hw/src/core/include/vm_session_component.h b/repos/base-hw/src/core/include/vm_session_component.h deleted file mode 100644 index 4ee9c229a..000000000 --- a/repos/base-hw/src/core/include/vm_session_component.h +++ /dev/null @@ -1,64 +0,0 @@ -/* - * \brief Core-specific instance of the VM session interface - * \author Stefan Kalkowski - * \date 2012-10-08 - */ - -/* - * Copyright (C) 2012-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 _CORE__INCLUDE__VM_SESSION_COMPONENT_H_ -#define _CORE__INCLUDE__VM_SESSION_COMPONENT_H_ - -/* Genode includes */ -#include -#include -#include -#include - -/* Core includes */ -#include - -namespace Genode { - - class Vm_session_component : public Rpc_object - { - private: - - Rpc_entrypoint *_ds_ep; - Range_allocator *_ram_alloc; - unsigned _vm_id; - void *_vm; - Dataspace_component _ds; - Dataspace_capability _ds_cap; - addr_t _ds_addr; - - static size_t _ds_size() { - return align_addr(sizeof(Cpu_state_modes), - get_page_size_log2()); } - - addr_t _alloc_ds(size_t &ram_quota); - - public: - - Vm_session_component(Rpc_entrypoint *ds_ep, - size_t ram_quota); - ~Vm_session_component(); - - - /************************** - ** Vm session interface ** - **************************/ - - Dataspace_capability cpu_state(void) { return _ds_cap; } - void exception_handler(Signal_context_capability handler); - void run(void); - void pause(void); - }; -} - -#endif /* _CORE__INCLUDE__VM_SESSION_COMPONENT_H_ */ diff --git a/repos/base-hw/src/core/kernel/cpu.cc b/repos/base-hw/src/core/kernel/cpu.cc index f1efe15d8..ba15f963b 100644 --- a/repos/base-hw/src/core/kernel/cpu.cc +++ b/repos/base-hw/src/core/kernel/cpu.cc @@ -32,7 +32,6 @@ namespace Kernel */ class Cpu_domain_update_list; - Pic * pic(); Timer * timer(); Cpu_pool * cpu_pool() { return unmanaged_singleton(); } @@ -119,7 +118,7 @@ void Cpu_job::affinity(Cpu * const cpu) ** Cpu_idle ** **************/ -void Cpu_idle::proceed(unsigned const cpu) { mtc()->continue_user(this, cpu); } +void Cpu_idle::proceed(unsigned const cpu) { mtc()->switch_to_user(this, cpu); } /********* diff --git a/repos/base-hw/src/core/kernel/irq.cc b/repos/base-hw/src/core/kernel/irq.cc index ff5b8eb07..89aabb57a 100644 --- a/repos/base-hw/src/core/kernel/irq.cc +++ b/repos/base-hw/src/core/kernel/irq.cc @@ -12,19 +12,19 @@ */ /* core includes */ +#include #include #include #include -using namespace Kernel; -namespace Kernel { Pic * pic(); } +void Kernel::Irq::disable() const { pic()->mask(_id()); } -void Irq::_disable() const { pic()->mask(_id()); } -void Irq::_enable() const { pic()->unmask(_id(), Cpu::executing_id()); } +void Kernel::Irq::enable() const { pic()->unmask(_id(), Cpu::executing_id()); } -Irq::Pool * User_irq::_pool() + +Kernel::Irq::Pool * Kernel::User_irq::_pool() { static Irq::Pool p; return &p; diff --git a/repos/base-hw/src/core/kernel/kernel.cc b/repos/base-hw/src/core/kernel/kernel.cc index e52cc55ed..bb6b9aa6f 100644 --- a/repos/base-hw/src/core/kernel/kernel.cc +++ b/repos/base-hw/src/core/kernel/kernel.cc @@ -25,7 +25,6 @@ /* core includes */ #include -#include #include #include #include @@ -51,18 +50,9 @@ Genode::Native_utcb * _main_thread_utcb; namespace Kernel { - /** - * Return interrupt-controller singleton - */ - Pic * pic() { return unmanaged_singleton(); } - /* import Genode types */ - typedef Genode::umword_t umword_t; typedef Genode::Core_thread_id Core_thread_id; -} -namespace Kernel -{ Pd_ids * pd_ids() { return unmanaged_singleton(); } Thread_ids * thread_ids() { return unmanaged_singleton(); } Signal_context_ids * signal_context_ids() { return unmanaged_singleton(); } @@ -166,11 +156,8 @@ namespace Kernel if (irq == Pic::IPI) return true; return false; } -} -namespace Kernel -{ /** * Get attributes of the mode transition region in every PD */ @@ -183,7 +170,6 @@ namespace Kernel size_t thread_size() { return sizeof(Thread); } size_t signal_context_size() { return sizeof(Signal_context); } size_t signal_receiver_size() { return sizeof(Signal_receiver); } - size_t vm_size() { return sizeof(Vm); } unsigned pd_alignm_log2() { return Genode::Translation_table::ALIGNM_LOG2; } size_t pd_size() { return sizeof(Genode::Translation_table) + sizeof(Pd); } @@ -203,6 +189,9 @@ namespace Kernel } +Pic * Kernel::pic() { return unmanaged_singleton(); } + + /** * Enable kernel-entry assembly to get an exclusive stack for every CPU */ diff --git a/repos/base-hw/src/core/kernel/thread.cc b/repos/base-hw/src/core/kernel/thread.cc index 53c3881b6..4771846e0 100644 --- a/repos/base-hw/src/core/kernel/thread.cc +++ b/repos/base-hw/src/core/kernel/thread.cc @@ -19,7 +19,6 @@ /* core includes */ #include #include -#include #include #include #include @@ -198,7 +197,7 @@ void Thread::_receive_yielded_cpu() } -void Thread::proceed(unsigned const cpu) { mtc()->continue_user(this, cpu); } +void Thread::proceed(unsigned const cpu) { mtc()->switch_to_user(this, cpu); } char const * Kernel::Thread::pd_label() const @@ -524,6 +523,7 @@ void Thread::_call_update_data_region() auto base = (addr_t)user_arg_1(); auto const size = (size_t)user_arg_2(); Cpu::flush_data_caches_by_virt_region(base, size); + Cpu::invalidate_instr_caches(); } @@ -781,32 +781,6 @@ void Thread::_call_bin_signal_receiver() } -void Thread::_call_run_vm() -{ - /* lookup virtual machine */ - Vm * const vm = Vm::pool()->object(user_arg_1()); - if (!vm) { - PWRN("failed to lookup virtual machine"); - return; - } - /* run virtual machine */ - vm->run(); -} - - -void Thread::_call_pause_vm() -{ - /* lookup virtual machine */ - Vm * const vm = Vm::pool()->object(user_arg_1()); - if (!vm) { - PWRN("failed to lookup virtual machine"); - return; - } - /* pause virtual machine */ - vm->pause(); -} - - int Thread::_read_reg(addr_t const id, addr_t & value) const { addr_t Thread::* const reg = _reg(id); @@ -874,6 +848,7 @@ void Thread::_call() case call_id_bin_signal_context(): _call_bin_signal_context(); return; case call_id_bin_signal_receiver(): _call_bin_signal_receiver(); return; case call_id_new_vm(): _call_new_vm(); return; + case call_id_bin_vm(): _call_bin_vm(); return; case call_id_run_vm(): _call_run_vm(); return; case call_id_pause_vm(): _call_pause_vm(); return; case call_id_pause_thread(): _call_pause_thread(); return; diff --git a/repos/base-hw/src/core/kernel/vm.cc b/repos/base-hw/src/core/kernel/vm.cc deleted file mode 100644 index 1cc85ea4a..000000000 --- a/repos/base-hw/src/core/kernel/vm.cc +++ /dev/null @@ -1,21 +0,0 @@ -/* - * \brief Kernel backend for virtual machines - * \author Martin Stein - * \date 2013-09-15 - */ - -/* - * 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. - */ - -/* core includes */ -#include - -namespace Kernel -{ - Vm_ids * vm_ids() { return unmanaged_singleton(); } - Vm_pool * vm_pool() { return unmanaged_singleton(); } -} diff --git a/repos/base-hw/src/core/kernel/vm_thread.cc b/repos/base-hw/src/core/kernel/vm_thread.cc new file mode 100644 index 000000000..19d88cadd --- /dev/null +++ b/repos/base-hw/src/core/kernel/vm_thread.cc @@ -0,0 +1,20 @@ +/* + * \brief Kernel backend for VMs when having no virtualization + * \author Martin Stein + * \date 2013-09-15 + */ + +/* + * 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. + */ + +/* core includes */ +#include + +void Kernel::Thread::_call_new_vm() { user_arg_0(0); } +void Kernel::Thread::_call_bin_vm() { user_arg_0(-1); } +void Kernel::Thread::_call_run_vm() { user_arg_0(-1); } +void Kernel::Thread::_call_pause_vm() { user_arg_0(-1); } diff --git a/repos/base-hw/src/core/spec/arm/cpu.cc b/repos/base-hw/src/core/spec/arm/cpu.cc index e3b76b0f6..8bfa89a8f 100644 --- a/repos/base-hw/src/core/spec/arm/cpu.cc +++ b/repos/base-hw/src/core/spec/arm/cpu.cc @@ -20,47 +20,3 @@ unsigned Cpu::primary_id() { return 0; } unsigned Cpu::executing_id() { return primary_id(); } - - -addr_t Cpu::Context::translation_table() const { - return Ttbr0::Ba::masked(ttbr0); } - - -void Cpu::Context::translation_table(addr_t const t) { - ttbr0 = Arm::Ttbr0::init(t); } - - -bool Cpu::User_context::in_fault(addr_t & va, addr_t & w) const -{ - switch (cpu_exception) { - - case PREFETCH_ABORT: - { - /* check if fault was caused by a translation miss */ - Ifsr::access_t const fs = Ifsr::Fs::get(Ifsr::read()); - if (fs != Ifsr::section && fs != Ifsr::page) - return false; - - /* fetch fault data */ - w = 0; - va = ip; - return true; - } - case DATA_ABORT: - { - /* check if fault was caused by translation miss */ - Dfsr::access_t const fs = Dfsr::Fs::get(Dfsr::read()); - if (fs != Dfsr::section && fs != Dfsr::page) - return false; - - /* fetch fault data */ - Dfsr::access_t const dfsr = Dfsr::read(); - w = Dfsr::Wnr::get(dfsr); - va = Dfar::read(); - return true; - } - - default: - return false; - }; -} diff --git a/repos/base-hw/src/core/spec/arm/kernel/cpu.cc b/repos/base-hw/src/core/spec/arm/kernel/cpu.cc index 998381e52..eb4f0409b 100644 --- a/repos/base-hw/src/core/spec/arm/kernel/cpu.cc +++ b/repos/base-hw/src/core/spec/arm/kernel/cpu.cc @@ -19,13 +19,6 @@ using namespace Kernel; -void Cpu_context::_init(size_t const stack_size, addr_t const table) -{ - r12 = stack_size; - cpu_exception = Genode::Cpu::Ttbr0::init(table); -} - - Cpu_idle::Cpu_idle(Cpu * const cpu) : Cpu_job(Cpu_priority::min, 0) { Cpu_job::cpu(cpu); diff --git a/repos/base-hw/src/core/spec/arm/kernel/cpu_context.cc b/repos/base-hw/src/core/spec/arm/kernel/cpu_context.cc new file mode 100644 index 000000000..b2f24df76 --- /dev/null +++ b/repos/base-hw/src/core/spec/arm/kernel/cpu_context.cc @@ -0,0 +1,21 @@ +/* + * \brief Kernel cpu context specific implementation + * \author Stefan Kalkowski + * \date 2015-02-11 + */ + +/* + * Copyright (C) 2015 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. + */ + +/* core includes */ +#include + +void Kernel::Cpu_context::_init(size_t const stack_size, addr_t const table) +{ + r12 = stack_size; + cpu_exception = Genode::Cpu::Ttbr0::init(table); +} diff --git a/repos/base-hw/src/core/spec/arm/kernel/thread.cc b/repos/base-hw/src/core/spec/arm/kernel/thread.cc index 7064e6fff..fa29d97c1 100644 --- a/repos/base-hw/src/core/spec/arm/kernel/thread.cc +++ b/repos/base-hw/src/core/spec/arm/kernel/thread.cc @@ -15,7 +15,6 @@ /* core includes */ #include #include -#include #include using namespace Kernel; @@ -29,26 +28,6 @@ Thread::Thread(unsigned const priority, unsigned const quota, { cpu_exception = RESET; } -void Thread::_call_new_vm() -{ - /* lookup signal context */ - auto const context = Signal_context::pool()->object(user_arg_3()); - if (!context) { - PWRN("failed to lookup signal context"); - user_arg_0(0); - return; - } - /* create virtual machine */ - typedef Genode::Cpu_state_modes Cpu_state_modes; - auto const allocator = reinterpret_cast(user_arg_1()); - auto const state = reinterpret_cast(user_arg_2()); - Vm * const vm = new (allocator) Vm(state, context); - - /* return kernel name of virtual machine */ - user_arg_0(vm->id()); -} - - void Thread::exception(unsigned const cpu) { switch (cpu_exception) { diff --git a/repos/base-hw/src/core/spec/arm/kernel/vm.cc b/repos/base-hw/src/core/spec/arm/kernel/vm.cc deleted file mode 100644 index ad04f0f46..000000000 --- a/repos/base-hw/src/core/spec/arm/kernel/vm.cc +++ /dev/null @@ -1,33 +0,0 @@ -/* - * \brief Kernel backend for virtual machines - * \author Martin Stein - * \date 2013-10-30 - */ - -/* - * 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. - */ - -/* core includes */ -#include - -using namespace Kernel; - - -void Vm::exception(unsigned const cpu) -{ - switch(_state->cpu_exception) { - case Genode::Cpu_state::INTERRUPT_REQUEST: - case Genode::Cpu_state::FAST_INTERRUPT_REQUEST: - _interrupt(cpu); - return; - case Genode::Cpu_state::DATA_ABORT: - _state->dfar = Cpu::Dfar::read(); - default: - Cpu_job::_deactivate_own_share(); - _context->submit(1); - } -} diff --git a/repos/base-hw/src/core/spec/arm_v7/kernel/vm_thread.cc b/repos/base-hw/src/core/spec/arm_v7/kernel/vm_thread.cc new file mode 100644 index 000000000..56a3406ae --- /dev/null +++ b/repos/base-hw/src/core/spec/arm_v7/kernel/vm_thread.cc @@ -0,0 +1,38 @@ +/* + * \brief Kernel backend for virtual machines + * \author Stefan Kalkowski + * \date 2015-02-10 + */ + +/* + * Copyright (C) 2015 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. + */ + +/* core includes */ +#include +#include + +void Kernel::Thread::_call_bin_vm() +{ + Vm * const vm = Vm::pool()->object(user_arg_1()); + if (vm) vm->~Vm(); + user_arg_0(vm ? 0 : -1); +} + + +void Kernel::Thread::_call_run_vm() { + Vm * const vm = Vm::pool()->object(user_arg_1()); + if (vm) vm->run(); + user_arg_0(vm ? 0 : -1); +} + + +void Kernel::Thread::_call_pause_vm() +{ + Vm * const vm = Vm::pool()->object(user_arg_1()); + if (vm) vm->pause(); + user_arg_0(vm ? 0 : -1); +} diff --git a/repos/base-hw/src/core/spec/arm_v7/mode_transition.s b/repos/base-hw/src/core/spec/arm_v7/mode_transition.s index b8a46d29c..0b1771a5d 100644 --- a/repos/base-hw/src/core/spec/arm_v7/mode_transition.s +++ b/repos/base-hw/src/core/spec/arm_v7/mode_transition.s @@ -15,19 +15,6 @@ /* core includes */ .include "macros.s" - -/*************** - ** Constants ** - ***************/ - -/* hardware names of CPU modes */ -.set USR_MODE, 16 -.set FIQ_MODE, 17 -.set IRQ_MODE, 18 -.set SVC_MODE, 19 -.set ABT_MODE, 23 -.set UND_MODE, 27 - /* size of local variables */ .set BUFFER_SIZE, 3 * 4 @@ -36,30 +23,6 @@ ** Macros ** ************/ -/** - * Determine the base of the client context of the executing CPU - * - * \param target_reg register that shall receive the base pointer - * \param buf_reg register that can be polluted by the macro - */ -.macro _get_client_context_ptr target_reg, buf_reg - - /* get kernel name of CPU */ - _get_cpu_id \buf_reg - - /* multiply CPU name with pointer size to get offset of pointer */ - mov \target_reg, #CONTEXT_PTR_SIZE - mul \buf_reg, \buf_reg, \target_reg - - /* get base of the pointer array */ - adr \target_reg, _mt_client_context_ptr - - /* add offset and base to get CPU-local pointer */ - add \target_reg, \target_reg, \buf_reg - ldr \target_reg, [\target_reg] -.endm - - /** * Determine the base of the globally mapped buffer of the executing CPU * @@ -131,7 +94,7 @@ _switch_protection_domain r1, r2, sp /* get user context-pointer */ - _get_client_context_ptr sp, r1 + _get_client_context_ptr sp, r1, _mt_client_context_ptr /* adjust and save user pc */ .if \pc_adjust != 0 @@ -157,95 +120,6 @@ .endm /* _user_to_kernel_pic */ -/** - * Save sp, lr and spsr register banks of specified exception mode - */ -.macro _save_bank mode - cps #\mode /* switch to given mode */ - mrs r1, spsr /* store mode-specific spsr */ - stmia r0!, {r1,sp,lr} /* store mode-specific sp and lr */ -.endm /* _save_bank mode */ - - -/** - * Switch from an interrupted VM to the kernel context - * - * \param exception_type immediate exception type ID - * \param pc_adjust immediate value that gets subtracted from the - * vm's PC before it gets saved - */ -.macro _vm_to_kernel exception_type, pc_adjust - ldr sp, _mt_client_context_ptr /* load context pointer */ - stmia sp, {r0-lr}^ /* save user regs r0-r12,sp,lr */ - add r0, sp, #15*4 - .if \pc_adjust != 0 /* adjust pc if necessary */ - sub lr, lr, #\pc_adjust - .endif - stmia r0!, {lr} /* save pc */ - mrs r1, spsr /* spsr to r0 */ - mov r2, #\exception_type /* exception reason to r1 */ - stmia r0!, {r1-r2} /* save spsr, and exception reason */ - mrc p15, 0, r3, c6, c0, 0 /* move DFAR to r3 */ - mrc p15, 0, r4, c2, c0, 0 /* move TTBR0 to r4 */ - mrc p15, 0, r5, c2, c0, 1 /* move TTBR1 to r5 */ - mrc p15, 0, r6, c2, c0, 2 /* move TTBRC to r6 */ - mov r1, #0 - mcr p15, 0, r1, c1, c1, 0 /* disable non-secure bit */ - _save_bank 27 /* save undefined banks */ - _save_bank 19 /* save supervisor banks */ - _save_bank 23 /* save abort banks */ - _save_bank 18 /* save irq banks */ - _save_bank 17 /* save fiq banks */ - stmia r0!, {r8-r12} /* save fiq r8-r12 */ - stmia r0!, {r3-r6} /* save MMU registers */ - b _common_client_to_kernel_pic -.endm /* _vm_to_kernel */ - - -/** - * Restore sp, lr and spsr register banks of specified exception mode - */ -.macro _restore_bank mode - cps #\mode /* switch to given mode */ - ldmia r0!, {r1,sp,lr} /* load mode-specific sp, lr, and spsr into r1 */ - msr spsr_cxfs, r1 /* load mode-specific spsr */ -.endm - - -/** - * Switch from kernel context to a VM - */ -.macro _kernel_to_vm - ldr r0, _mt_client_context_ptr /* get vm context pointer */ - add r0, r0, #18*4 /* add offset of banked modes */ - _restore_bank 27 /* load undefined banks */ - _restore_bank 19 /* load supervisor banks */ - _restore_bank 23 /* load abort banks */ - _restore_bank 18 /* load irq banks */ - _restore_bank 17 /* load fiq banks */ - ldmia r0!, {r8 - r12} /* load fiq r8-r12 */ - cps #22 /* switch to monitor mode */ - ldr sp, _mt_client_context_ptr /* get vm context pointer */ - ldmia sp, {r0-lr}^ /* load user r0-r12,sp,lr */ - ldr lr, [sp, #16*4] /* load vm's cpsr to lr */ - msr spsr_cxfs, lr /* save cpsr to be load when switching */ - mov lr, #13 - mcr p15, 0, lr, c1, c1, 0 /* enable EA, FIQ, and NS bit in SCTRL */ - ldr lr, [sp, #15*4] /* load vm's ip */ - subs pc, lr, #0 -.endm /* _kernel_to_vm */ - - -/** - * Enter kernel after hypervisor call - */ -.macro _hyp_to_kernel exception_type - cps #SVC_MODE - mov r0, #\exception_type - 1: b 1b -.endm /* _hyp_to_kernel */ - - /********************************** ** Linked into the text section ** **********************************/ @@ -348,12 +222,6 @@ */ clrex - /********************************************************* - ** Kernel-entry code that is common for all exceptions ** - *********************************************************/ - - _common_client_to_kernel_pic: - /* * Switch to supervisor mode to circumvent incorrect behavior of * kernel high-level code in fast interrupt mode and to ensure that @@ -382,7 +250,7 @@ _mt_user_entry_pic: /* get user context and globally mapped buffer of this CPU */ - _get_client_context_ptr lr, r0 + _get_client_context_ptr lr, r0, _mt_client_context_ptr _get_buffer_ptr sp, r0 /* load user psr in spsr */ @@ -409,60 +277,6 @@ /* apply user r0-r1 and user pc which implies application of spsr */ ldm sp, {r0, r1, pc}^ - /* - * On TrustZone exceptions the CPU has to jump to one of the following - * 7 entry vectors to switch to a kernel context. - */ - .p2align 5 - .global _mon_kernel_entry - _mon_kernel_entry: - b _mon_rst_entry /* reset */ - b _mon_und_entry /* undefined instruction */ - b _mon_svc_entry /* supervisor call */ - b _mon_pab_entry /* prefetch abort */ - b _mon_dab_entry /* data abort */ - nop /* reserved */ - b _mon_irq_entry /* interrupt request */ - _vm_to_kernel FIQ_TYPE, 4 /* fast interrupt request */ - - /* PICs that switch from a vm exception to the kernel */ - _mon_rst_entry: _vm_to_kernel RST_TYPE, 0 - _mon_und_entry: _vm_to_kernel UND_TYPE, 4 - _mon_svc_entry: _vm_to_kernel SVC_TYPE, 0 - _mon_pab_entry: _vm_to_kernel PAB_TYPE, 4 - _mon_dab_entry: _vm_to_kernel DAB_TYPE, 8 - _mon_irq_entry: _vm_to_kernel IRQ_TYPE, 4 - - /* kernel must jump to this point to switch to a vm */ - .p2align 2 - .global _mt_vm_entry_pic - _mt_vm_entry_pic: - _kernel_to_vm - - /* - * On virtualization exceptions the CPU has to jump to one of the following - * 7 entry vectors to switch to a kernel context. - */ - .p2align 4 - .global _hyp_kernel_entry - _hyp_kernel_entry: - b _hyp_rst_entry - b _hyp_und_entry /* undefined instruction */ - b _hyp_svc_entry /* hypervisor call */ - b _hyp_pab_entry /* prefetch abort */ - b _hyp_dab_entry /* data abort */ - b _hyp_trp_entry /* hypervisor trap */ - b _hyp_irq_entry /* interrupt request */ - _hyp_to_kernel 7 /* fast interrupt request */ - - _hyp_rst_entry: _hyp_to_kernel 0 - _hyp_und_entry: _hyp_to_kernel 1 - _hyp_svc_entry: _hyp_to_kernel 2 - _hyp_pab_entry: _hyp_to_kernel 3 - _hyp_dab_entry: _hyp_to_kernel 4 - _hyp_trp_entry: _hyp_to_kernel 5 - _hyp_irq_entry: _hyp_to_kernel 6 - /* end of the mode transition code */ .global _mt_end _mt_end: diff --git a/repos/base-hw/src/core/spec/arm_v7/trustzone/kernel/vm.cc b/repos/base-hw/src/core/spec/arm_v7/trustzone/kernel/vm.cc new file mode 100644 index 000000000..3585cd014 --- /dev/null +++ b/repos/base-hw/src/core/spec/arm_v7/trustzone/kernel/vm.cc @@ -0,0 +1,67 @@ +/* + * \brief Kernel backend for virtual machines + * \author Martin Stein + * \author Stefan Kalkowski + * \date 2013-10-30 + */ + +/* + * 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. + */ + +/* core includes */ +#include + +extern void * _mt_nonsecure_entry_pic; +extern Genode::addr_t _tz_client_context; +extern Genode::addr_t _mt_master_context_begin; +extern Genode::addr_t _tz_master_context; + +namespace Kernel +{ + Vm_ids * vm_ids() { return unmanaged_singleton(); } + Vm_pool * vm_pool() { return unmanaged_singleton(); } +} + +using namespace Kernel; + + +Kernel::Vm::Vm(void * const state, + Kernel::Signal_context * const context, + void * const table) +: Cpu_job(Cpu_priority::min, 0), + _state((Genode::Vm_state * const)state), + _context(context), _table(0) +{ + affinity(cpu_pool()->primary_cpu()); + + Genode::memcpy(&_tz_master_context, &_mt_master_context_begin, + sizeof(Cpu_context)); +} + + +void Vm::exception(unsigned const cpu) +{ + switch(_state->cpu_exception) { + case Genode::Cpu_state::INTERRUPT_REQUEST: + case Genode::Cpu_state::FAST_INTERRUPT_REQUEST: + _interrupt(cpu); + return; + case Genode::Cpu_state::DATA_ABORT: + _state->dfar = Cpu::Dfar::read(); + default: + pause(); + _context->submit(1); + } +} + + +void Vm::proceed(unsigned const cpu) +{ + mtc()->switch_to(reinterpret_cast(_state), cpu, + (addr_t)&_mt_nonsecure_entry_pic, + (addr_t)&_tz_client_context); +} diff --git a/repos/base-hw/src/core/spec/arm_v7/trustzone/kernel/vm_thread.cc b/repos/base-hw/src/core/spec/arm_v7/trustzone/kernel/vm_thread.cc new file mode 100644 index 000000000..d2fe2743e --- /dev/null +++ b/repos/base-hw/src/core/spec/arm_v7/trustzone/kernel/vm_thread.cc @@ -0,0 +1,38 @@ +/* + * \brief Kernel backend for thread-syscalls related to VMs + * \author Stefan Kalkowski + * \date 2015-02-23 + */ + +/* + * Copyright (C) 2015 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. + */ + +/* core includes */ +#include +#include + +void Kernel::Thread::_call_new_vm() +{ + /* lookup signal context */ + auto const context = Signal_context::pool()->object(user_arg_4()); + if (!context) { + PWRN("failed to lookup signal context"); + user_arg_0(0); + return; + } + + /* create virtual machine */ + typedef Genode::Cpu_state_modes Cpu_state_modes; + void * const allocator = reinterpret_cast(user_arg_1()); + void * const table = reinterpret_cast(user_arg_3()); + Cpu_state_modes * const state = + reinterpret_cast(user_arg_2()); + Vm * const vm = new (allocator) Vm(state, context, table); + + /* return kernel name of virtual machine */ + user_arg_0(vm->id()); +} diff --git a/repos/base-hw/src/core/spec/arm_v7/trustzone/mode_transition.s b/repos/base-hw/src/core/spec/arm_v7/trustzone/mode_transition.s new file mode 100644 index 000000000..103560322 --- /dev/null +++ b/repos/base-hw/src/core/spec/arm_v7/trustzone/mode_transition.s @@ -0,0 +1,125 @@ +/* + * \brief Transition between secure/normal worl + * \author Stefan Kalkowski + * \date 2015-02-16 + */ + +/* + * Copyright (C) 2015 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. + */ + +/* core includes */ +.include "macros.s" + +/** + * Switch from nonsecure into secure world + * + * \param exception_type immediate exception type ID + * \param pc_adjust immediate value that gets subtracted from the + * vm's PC before it gets saved + */ +.macro _nonsecure_to_secure exception_type, pc_adjust + ldr sp, _tz_client_context /* load context pointer */ + stmia sp, {r0-lr}^ /* save user regs r0-r12,sp,lr */ + add r0, sp, #15*4 + .if \pc_adjust != 0 /* adjust pc if necessary */ + sub lr, lr, #\pc_adjust + .endif + stmia r0!, {lr} /* save pc */ + mrs r1, spsr /* spsr to r0 */ + mov r2, #\exception_type /* exception reason to r1 */ + b _nonsecure_kernel_entry +.endm /* _non_to_secure */ + + +/** + * Switch from secure into nonsecure world + */ +.macro _secure_to_nonsecure + ldr r0, _tz_client_context /* get vm context pointer */ + add r0, r0, #18*4 /* add offset of banked modes */ + _restore_bank 27 /* load undefined banks */ + _restore_bank 19 /* load supervisor banks */ + _restore_bank 23 /* load abort banks */ + _restore_bank 18 /* load irq banks */ + _restore_bank 17 /* load fiq banks */ + ldmia r0!, {r8 - r12} /* load fiq r8-r12 */ + cps #22 /* switch to monitor mode */ + ldr sp, _tz_client_context /* get vm context pointer */ + ldmia sp, {r0-lr}^ /* load user r0-r12,sp,lr */ + ldr lr, [sp, #16*4] /* load vm's cpsr to lr */ + msr spsr_cxfs, lr /* save cpsr to be load when switching */ + mov lr, #13 + mcr p15, 0, lr, c1, c1, 0 /* enable EA, FIQ, and NS bit in SCTRL */ + ldr lr, [sp, #15*4] /* load vm's ip */ + subs pc, lr, #0 +.endm /* _secure_to_nonsecure */ + + +.section .text + +/* space for a copy of the kernel context */ +.p2align 2 +.global _tz_master_context +_tz_master_context: +.space 32 * 4 + +/* space for a client context-pointer */ +.p2align 2 +.global _tz_client_context +_tz_client_context: + .space CONTEXT_PTR_SIZE + +_nonsecure_kernel_entry: + stmia r0!, {r1-r2} /* save spsr, and exception reason */ + mrc p15, 0, r3, c6, c0, 0 /* move DFAR to r3 */ + mrc p15, 0, r4, c2, c0, 0 /* move TTBR0 to r4 */ + mrc p15, 0, r5, c2, c0, 1 /* move TTBR1 to r5 */ + mrc p15, 0, r6, c2, c0, 2 /* move TTBRC to r6 */ + mov r1, #0 + mcr p15, 0, r1, c1, c1, 0 /* disable non-secure bit */ + _save_bank 27 /* save undefined banks */ + _save_bank 19 /* save supervisor banks */ + _save_bank 23 /* save abort banks */ + _save_bank 18 /* save irq banks */ + _save_bank 17 /* save fiq banks */ + stmia r0!, {r8-r12} /* save fiq r8-r12 */ + stmia r0!, {r3-r6} /* save MMU registers */ + cps #SVC_MODE + adr r0, _tz_master_context + _restore_kernel_sp r0, r1, r2 /* apply kernel sp */ + add r1, r0, #LR_OFFSET + ldm r1, {lr, pc} + +/* kernel must jump to this point to switch to a vm */ +.global _mt_nonsecure_entry_pic +_mt_nonsecure_entry_pic: + _secure_to_nonsecure + +/* + * On TrustZone exceptions the CPU has to jump to one of the following + * 7 entry vectors to switch to a kernel context. + */ +.p2align 5 +.global _mon_kernel_entry +_mon_kernel_entry: + b _mon_rst_entry /* reset */ + b _mon_und_entry /* undefined instruction */ + b _mon_svc_entry /* supervisor call */ + b _mon_pab_entry /* prefetch abort */ + b _mon_dab_entry /* data abort */ + nop /* reserved */ + b _mon_irq_entry /* interrupt request */ + _nonsecure_to_secure FIQ_TYPE, 4 /* fast interrupt request */ + + /* PICs that switch from a vm exception to the kernel */ + _mon_rst_entry: _nonsecure_to_secure RST_TYPE, 0 + _mon_und_entry: _nonsecure_to_secure UND_TYPE, 4 + _mon_svc_entry: _nonsecure_to_secure SVC_TYPE, 0 + _mon_pab_entry: _nonsecure_to_secure PAB_TYPE, 4 + _mon_dab_entry: _nonsecure_to_secure DAB_TYPE, 8 + _mon_irq_entry: _nonsecure_to_secure IRQ_TYPE, 4 + diff --git a/repos/base-hw/src/core/spec/arm_v7/trustzone/vm_session_component.cc b/repos/base-hw/src/core/spec/arm_v7/trustzone/vm_session_component.cc new file mode 100644 index 000000000..0f2e007b4 --- /dev/null +++ b/repos/base-hw/src/core/spec/arm_v7/trustzone/vm_session_component.cc @@ -0,0 +1,38 @@ +/* + * \brief VM session component for 'base-hw' + * \author Stefan Kalkowski + * \date 2012-10-08 + */ + +/* + * Copyright (C) 2012-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. + */ + +#include +#include +#include + +using namespace Genode; + + +void Vm_session_component::exception_handler(Signal_context_capability handler) +{ + if (_vm_id) { + PWRN("Cannot register exception_handler repeatedly"); + return; + } + _vm_id = Kernel::new_vm(&_vm, (void*)_ds.core_local_addr(), handler.dst(), 0); +} + + +Vm_session_component::Vm_session_component(Rpc_entrypoint *ds_ep, + size_t ram_quota) +: _ds_ep(ds_ep), _vm_id(0), + _ds(_ds_size(), _alloc_ds(ram_quota), UNCACHED, true, 0), + _ds_cap(static_cap_cast(_ds_ep->manage(&_ds))) +{ + _ds.assign_core_local_addr(core_env()->rm_session()->attach(_ds_cap)); +} diff --git a/repos/base-hw/src/core/spec/arm_v7/virtualization/kernel/cpu_context.cc b/repos/base-hw/src/core/spec/arm_v7/virtualization/kernel/cpu_context.cc new file mode 100644 index 000000000..01f53614e --- /dev/null +++ b/repos/base-hw/src/core/spec/arm_v7/virtualization/kernel/cpu_context.cc @@ -0,0 +1,24 @@ +/* + * \brief Kernel cpu context specific implementation + * \author Stefan Kalkowski + * \date 2015-02-11 + */ + +/* + * Copyright (C) 2015 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. + */ + +/* core includes */ +#include + +void Kernel::Cpu_context::_init(size_t const stack_size, addr_t const table) +{ + r12 = stack_size; + cpu_exception = Genode::Cpu::Ttbr0::init(table); + sctlr = Cpu::Sctlr::init_virt_kernel(); + ttbrc = Cpu::Ttbcr::init_virt_kernel(); + mair0 = Cpu::Mair0::init_virt_kernel(); +} diff --git a/repos/base-hw/src/core/spec/arm_v7/virtualization/kernel/vm.cc b/repos/base-hw/src/core/spec/arm_v7/virtualization/kernel/vm.cc new file mode 100644 index 000000000..73b5d12de --- /dev/null +++ b/repos/base-hw/src/core/spec/arm_v7/virtualization/kernel/vm.cc @@ -0,0 +1,267 @@ +/* + * \brief Kernel backend for virtual machines + * \author Stefan Kalkowski + * \date 2015-02-10 + */ + +/* + * Copyright (C) 2015 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. + */ + +#include +#include + +namespace Kernel +{ + Vm_ids * vm_ids() { return unmanaged_singleton(); } + Vm_pool * vm_pool() { return unmanaged_singleton(); } + + /** + * ARM's virtual interrupt controller cpu interface + */ + struct Virtual_pic; + + /** + * ARM's virtual timer counter + */ + struct Virtual_timer; + + /** + * Kernel private virtualization interrupts, delivered to VM/VMMs + */ + struct Vm_irq; + + /** + * Cpu-specific initialization for virtualization support + */ + void prepare_hypervisor(void); +} + +using namespace Kernel; + +extern void * _vt_vm_entry; +extern void * _vt_host_entry; +extern Genode::addr_t _vt_vm_context_ptr; +extern Genode::addr_t _vt_host_context_ptr; + + +struct Kernel::Vm_irq : Kernel::Irq +{ + Vm_irq(unsigned const irq) : Kernel::Irq(irq) {} + + /** + * A VM interrupt gets injected into the VM scheduled on the current CPU + */ + void occurred() + { + Cpu_job * job = cpu_pool()->executing_cpu()->scheduled_job(); + Vm *vm = dynamic_cast(job); + if (!vm) + PERR("VM timer interrupt while VM is not runnning!"); + else + vm->inject_irq(_id()); + } +}; + + +struct Kernel::Virtual_pic : Genode::Mmio +{ + struct Gich_hcr : Register<0x00, 32> { }; + struct Gich_vmcr : Register<0x08, 32> { }; + struct Gich_misr : Register<0x10, 32> { }; + struct Gich_eisr0 : Register<0x20, 32> { }; + struct Gich_elrsr0 : Register<0x30, 32> { }; + struct Gich_apr : Register<0xf0, 32> { }; + + template + struct Gich_lr : Register<0x100 + SLOT*4, 32> { }; + + Vm_irq irq = Genode::Board::VT_MAINTAINANCE_IRQ; + + Virtual_pic() + : Genode::Mmio(Genode::Board::IRQ_CONTROLLER_VT_CTRL_BASE) { } + + static Virtual_pic& pic() + { + static Virtual_pic vgic; + return vgic; + } + + /** + * Save the virtual interrupt controller state to VM state + */ + static void save (Genode::Vm_state *s) + { + s->gic_hcr = pic().read(); + s->gic_misr = pic().read(); + s->gic_vmcr = pic().read(); + s->gic_apr = pic().read(); + s->gic_eisr = pic().read(); + s->gic_elrsr0 = pic().read(); + s->gic_lr[0] = pic().read >(); + s->gic_lr[1] = pic().read >(); + s->gic_lr[2] = pic().read >(); + s->gic_lr[3] = pic().read >(); + + /* disable virtual PIC CPU interface */ + pic().write(0); + } + + /** + * Load the virtual interrupt controller state from VM state + */ + static void load (Genode::Vm_state *s) + { + pic().write(s->gic_hcr ); + pic().write(s->gic_misr); + pic().write(s->gic_vmcr); + pic().write(s->gic_apr ); + pic().write(s->gic_elrsr0); + pic().write >(s->gic_lr[0]); + pic().write >(s->gic_lr[1]); + pic().write >(s->gic_lr[2]); + pic().write >(s->gic_lr[3]); + } +}; + + +struct Kernel::Virtual_timer +{ + Vm_irq irq = Genode::Board::VT_TIMER_IRQ; + + /** + * Return virtual timer object of currently executing cpu + * + * FIXME: remove this when re-designing the CPU (issue #1252) + */ + static Virtual_timer& timer() + { + static Virtual_timer timer[NR_OF_CPUS]; + return timer[Cpu::executing_id()]; + } + + /** + * Resets the virtual timer, thereby it disables its interrupt + */ + static void reset() + { + timer().irq.disable(); + asm volatile("mcr p15, 0, %0, c14, c3, 1 \n" + "mcr p15, 0, %0, c14, c3, 0" :: "r" (0)); + } + + /** + * Save the virtual timer state to VM state + */ + static void save(Genode::Vm_state *s) + { + asm volatile("mrc p15, 0, %0, c14, c3, 0 \n" + "mrc p15, 0, %1, c14, c3, 1" : + "=r" (s->timer_val), "=r" (s->timer_ctrl)); + } + + /** + * Load the virtual timer state from VM state + */ + static void load(Genode::Vm_state *s, unsigned const cpu_id) + { + if (s->timer_irq) timer().irq.enable(); + + asm volatile("mcr p15, 0, %0, c14, c3, 1 \n" + "mcr p15, 0, %1, c14, c3, 0 \n" + "mcr p15, 0, %2, c14, c3, 1" :: + "r" (0), + "r" (s->timer_val), "r" (s->timer_ctrl)); + } +}; + + +void Kernel::prepare_hypervisor() +{ + Cpu * cpu = cpu_pool()->executing_cpu(); + cpu->insert(&Virtual_timer::timer().irq); + cpu->insert(&Virtual_pic::pic().irq); + + /* set hypervisor exception vector */ + Cpu::hyp_exception_entry_at(&_vt_host_entry); + + /* set hypervisor's translation table */ + Genode::Translation_table * table = + core_pd()->platform_pd()->translation_table_phys(); + Cpu::Httbr::translation_table((addr_t)table); + + /* prepare MMU usage by hypervisor code */ + Cpu::Htcr::write(Cpu::Ttbcr::init_virt_kernel()); + Cpu::Hcptr::write(Cpu::Hcptr::init()); + Cpu::Hmair0::write(Cpu::Mair0::init_virt_kernel()); + Cpu::Vtcr::write(Cpu::Vtcr::init()); + Cpu::Hsctlr::write(Cpu::Sctlr::init_virt_kernel()); + + /* initialize host context used in virtualization world switch */ + *((void**)&_vt_host_context_ptr) = &_mt_master_context_begin; +} + + +Kernel::Vm::Vm(void * const state, + Kernel::Signal_context * const context, + void * const table) +: Cpu_job(Cpu_priority::min, 0), + _state((Genode::Vm_state * const)state), + _context(context), + _table(table) { + affinity(cpu_pool()->primary_cpu()); + Virtual_pic::pic().irq.enable(); +} + + +void Kernel::Vm::exception(unsigned const cpu_id) +{ + Virtual_timer::save(_state); + + switch(_state->cpu_exception) { + case Genode::Cpu_state::INTERRUPT_REQUEST: + case Genode::Cpu_state::FAST_INTERRUPT_REQUEST: + _interrupt(cpu_id); + break; + default: + pause(); + _context->submit(1); + } + + Virtual_pic::save(_state); + Virtual_timer::reset(); +} + + +void Kernel::Vm::proceed(unsigned const cpu_id) +{ + /* + * the following values have to be enforced by the hypervisor + */ + _state->vttbr = Cpu::Ttbr0::init((Genode::addr_t)_table, id()); + + /* + * use the following report fields not needed for loading the context + * to transport the HSTR and HCR register descriptions into the assembler + * path in a dense way + */ + _state->hsr = Cpu::Hstr::init(); + _state->hpfar = Cpu::Hcr::init(); + + Virtual_pic::load(_state); + Virtual_timer::load(_state, cpu_id); + + mtc()->switch_to(reinterpret_cast(_state), cpu_id, + (addr_t) &_vt_vm_entry, (addr_t)&_vt_vm_context_ptr); +} + + +void Vm::inject_irq(unsigned irq) +{ + _state->gic_irq = irq; + pause(); + _context->submit(1); +} diff --git a/repos/base-hw/src/core/spec/arm_v7/virtualization/kernel/vm_thread.cc b/repos/base-hw/src/core/spec/arm_v7/virtualization/kernel/vm_thread.cc new file mode 100644 index 000000000..737e8d2cd --- /dev/null +++ b/repos/base-hw/src/core/spec/arm_v7/virtualization/kernel/vm_thread.cc @@ -0,0 +1,38 @@ +/* + * \brief Kernel backend for thread-syscalls related to virtual machines + * \author Stefan Kalkowski + * \date 2015-02-10 + */ + +/* + * Copyright (C) 2015 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. + */ + +#include +#include + +void Kernel::Thread::_call_new_vm() +{ + /* lookup signal context */ + auto const context = Signal_context::pool()->object(user_arg_4()); + if (!context) { + PWRN("failed to lookup signal context"); + user_arg_0(0); + return; + } + + /* create virtual machine */ + typedef Genode::Cpu_state_modes Cpu_state_modes; + void * const allocator = reinterpret_cast(user_arg_1()); + void * const table = reinterpret_cast(user_arg_3()); + Cpu_state_modes * const state = + reinterpret_cast(user_arg_2()); + Vm * const vm = new (allocator) Vm(state, context, table); + + /* return kernel name of virtual machine */ + user_arg_0(vm->id()); +} + diff --git a/repos/base-hw/src/core/spec/arm_v7/virtualization/mode_transition.s b/repos/base-hw/src/core/spec/arm_v7/virtualization/mode_transition.s new file mode 100644 index 000000000..699b54f73 --- /dev/null +++ b/repos/base-hw/src/core/spec/arm_v7/virtualization/mode_transition.s @@ -0,0 +1,175 @@ +/* + * \brief Transition between virtual/host mode + * \author Stefan Kalkowski + * \date 2015-02-16 + */ + +/* + * Copyright (C) 2015 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. + */ + +/* core includes */ +.include "macros.s" + +.macro _vm_exit exception_type + str r0, [sp] + mrc p15, 4, r0, c1, c1, 0 /* read HCR register */ + tst r0, #1 /* check VM bit */ + ldreq r0, [sp] + beq _host_to_vm + mov r0, #\exception_type + str r0, [sp, #17*4] + b _vm_to_host +.endm /* _vm_exit */ + + +.section .text + +/* space for a copy of the host context */ +.p2align 2 +.global _vt_host_context_ptr +_vt_host_context_ptr: + .space CONTEXT_PTR_SIZE + +/* space for a vm context-pointer per CPU */ +.p2align 2 +.global _vt_vm_context_ptr +_vt_vm_context_ptr: +.rept NR_OF_CPUS + .space CONTEXT_PTR_SIZE +.endr + +_host_to_vm: + msr elr_hyp, r2 + msr spsr_cxfs, r3 /* load cpsr */ + mcrr p15, 6, r5, r6, c2 /* write VTTBR */ + mcr p15, 0, r7, c1, c0, 0 /* write SCTRL */ + mcr p15, 4, r8, c1, c1, 3 /* write HSTR */ + mcr p15, 4, r9, c1, c1, 0 /* write HCR register */ + mcr p15, 0, r12, c2, c0, 2 /* write TTBRC */ + sub sp, r0, #46*4 + ldm r0!, {r1-r12} + mcr p15, 0, r1, c2, c0, 0 /* write TTBR0 */ + mcr p15, 0, r2, c2, c0, 1 /* write TTBR1 */ + mcr p15, 0, r3, c10, c2, 0 /* write PRRR */ + mcr p15, 0, r4, c10, c2, 1 /* write NMRR */ + mcr p15, 0, r5, c3, c0, 0 /* write DACR */ + mcr p15, 0, r6, c5, c0, 0 /* write DFSR */ + mcr p15, 0, r7, c5, c0, 1 /* write IFSR */ + mcr p15, 0, r8, c5, c1, 0 /* write ADFSR */ + mcr p15, 0, r9, c5, c1, 1 /* write AIFSR */ + mcr p15, 0, r10, c6, c0, 0 /* write DFAR */ + mcr p15, 0, r11, c6, c0, 2 /* write IFAR */ + mcr p15, 0, r12, c13, c0, 1 /* write CIDR */ + ldm r0, {r1-r4} + mcr p15, 0, r1, c13, c0, 2 /* write TLS1 */ + mcr p15, 0, r2, c13, c0, 3 /* write TLS2 */ + mcr p15, 0, r3, c13, c0, 4 /* write TLS3 */ + mcr p15, 0, r4, c1, c0, 2 /* write CPACR */ + ldmia sp, {r0-r12} /* load vm's r0-r12 */ + eret + +_vm_to_host: + add r0, sp, #1*4 + stmia r0, {r1-r12} /* save regs r1-r12 */ + mov r1, #0 + mcrr p15, 6, r1, r1, c2 /* write VTTBR */ + mcr p15, 4, r1, c1, c1, 0 /* write HCR register */ + mcr p15, 4, r1, c1, c1, 3 /* write HSTR register */ + mcr p15, 0, r1, c1, c0, 2 /* write CPACR */ + mrs r1, ELR_hyp /* read ip */ + mrs r2, spsr /* read cpsr */ + mrc p15, 0, r3, c1, c0, 0 /* read SCTRL */ + mrc p15, 4, r4, c5, c2, 0 /* read HSR */ + mrc p15, 4, r5, c6, c0, 4 /* read HPFAR */ + mrc p15, 4, r6, c6, c0, 0 /* read HDFAR */ + mrc p15, 4, r7, c6, c0, 2 /* read HIFAR */ + mrc p15, 0, r8, c2, c0, 2 /* read TTBRC */ + mrc p15, 0, r9, c2, c0, 0 /* read TTBR0 */ + mrc p15, 0, r10, c2, c0, 1 /* read TTBR1 */ + add r0, sp, #40*4 /* offset SCTRL */ + stm r0!, {r3-r10} + add r0, r0, #3*4 + mrc p15, 0, r3, c5, c0, 0 /* read DFSR */ + mrc p15, 0, r4, c5, c0, 1 /* read IFSR */ + mrc p15, 0, r5, c5, c1, 0 /* read ADFSR */ + mrc p15, 0, r6, c5, c1, 1 /* read AIFSR */ + mrc p15, 0, r7, c6, c0, 0 /* read DFAR */ + mrc p15, 0, r8, c6, c0, 2 /* read IFAR */ + mrc p15, 0, r9, c13, c0, 1 /* read CIDR */ + mrc p15, 0, r10, c13, c0, 2 /* read TLS1 */ + mrc p15, 0, r11, c13, c0, 3 /* read TLS2 */ + mrc p15, 0, r12, c13, c0, 4 /* read TLS3 */ + stm r0, {r3-r12} + add r0, sp, #13*4 + ldr r3, _vt_host_context_ptr + _restore_kernel_sp r3, r4, r5 + add r3, r3, #CIDR_OFFSET + ldmia r3, {r4-r9} + _switch_protection_domain r0, r4, r5 + mcr p15, 0, r6, c1, c0, 0 /* write SCTRL */ + mcr p15, 0, r7, c2, c0, 2 /* write TTBRC */ + mcr p15, 0, r8, c10, c2, 0 /* write MAIR0 */ + mcr p15, 0, r9, c3, c0, 0 /* write DACR */ + cps #SVC_MODE + stmia r0, {r13-r14}^ /* save user regs sp,lr */ + add r0, r0, #2*4 + stmia r0!, {r1-r2} /* save ip, cpsr */ + add r0, r0, #1*4 + _save_bank UND_MODE /* save undefined banks */ + _save_bank SVC_MODE /* save supervisor banks */ + _save_bank ABT_MODE /* save abort banks */ + _save_bank IRQ_MODE /* save irq banks */ + _save_bank FIQ_MODE /* save fiq banks */ + stmia r0!, {r8-r12} /* save fiq r8-r12 */ + cps #SVC_MODE + ldr r0, _vt_host_context_ptr + _restore_kernel_sp r0, r1, r2 /* apply host kernel sp */ + add r1, r0, #LR_OFFSET /* apply host kernel lr */ + ldm r1, {lr, pc} + +/* host kernel must jump to this point to switch to a vm */ +.global _vt_vm_entry +_vt_vm_entry: + _get_client_context_ptr r0, lr, _vt_vm_context_ptr + add r0, r0, #SP_OFFSET + ldm r0, {r13 - r14}^ + add r0, r0, #2*4 + ldmia r0!, {r2 - r4} + _restore_bank UND_MODE + _restore_bank SVC_MODE + _restore_bank ABT_MODE + _restore_bank IRQ_MODE + _restore_bank FIQ_MODE + ldmia r0!, {r8 - r12} + cps #SVC_MODE + ldm r0!, {r5 - r12} + hvc #0 + +/* + * On virtualization exceptions the CPU has to jump to one of the following + * 7 entry vectors to switch to a kernel context. + */ +.p2align 5 +.global _vt_host_entry +_vt_host_entry: + b _vt_rst_entry + b _vt_und_entry /* undefined instruction */ + b _vt_svc_entry /* hypervisor call */ + b _vt_pab_entry /* prefetch abort */ + b _vt_dab_entry /* data abort */ + b _vt_trp_entry /* hypervisor trap */ + b _vt_irq_entry /* interrupt request */ + _vm_exit 7 /* fast interrupt request */ + +_vt_rst_entry: _vm_exit 1 +_vt_und_entry: _vm_exit 2 +_vt_svc_entry: _vm_exit 3 +_vt_pab_entry: _vm_exit 4 +_vt_dab_entry: _vm_exit 5 +_vt_irq_entry: _vm_exit 6 +_vt_trp_entry: _vm_exit 8 + diff --git a/repos/base-hw/src/core/spec/arm_v7/virtualization/vm_session_component.cc b/repos/base-hw/src/core/spec/arm_v7/virtualization/vm_session_component.cc new file mode 100644 index 000000000..0357b4d6a --- /dev/null +++ b/repos/base-hw/src/core/spec/arm_v7/virtualization/vm_session_component.cc @@ -0,0 +1,102 @@ +/* + * \brief VM session component for 'base-hw' + * \author Stefan Kalkowski + * \date 2015-02-17 + */ + +/* + * Copyright (C) 2015 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. + */ + +/* Genode includes */ +#include + +/* core includes */ +#include +#include +#include +#include + +using namespace Genode; + + +void Vm_session_component::exception_handler(Signal_context_capability handler) +{ + if (_vm_id) { + PWRN("Cannot register exception_handler repeatedly"); + return; + } + Core_mem_allocator * cma = + static_cast(platform()->core_mem_alloc()); + _vm_id = Kernel::new_vm(&_vm, (void*)_ds.core_local_addr(), handler.dst(), + cma->phys_addr(_table)); +} + + +void Vm_session_component::_attach(addr_t phys_addr, addr_t vm_addr, size_t size) +{ + Page_flags pflags = Page_flags::apply_mapping(true, CACHED, false); + + try { + for (;;) + try { + _table->insert_translation(vm_addr, phys_addr, size, pflags, _pslab); + return; + } catch(Page_slab::Out_of_slabs) { + _pslab->alloc_slab_block(); + } + } catch(Allocator::Out_of_memory) { + PERR("Translation table needs to much RAM"); + } catch(...) { + PERR("Invalid mapping %p -> %p (%zx)", (void*)phys_addr, + (void*)vm_addr, size); + } +} + + +void Vm_session_component::attach(Dataspace_capability ds_cap, addr_t vm_addr) +{ + /* check dataspace validity */ + Object_pool::Guard dsc(_ds_ep->lookup_and_lock(ds_cap)); + if (!dsc) throw Invalid_dataspace(); + + _attach(dsc->phys_addr(), vm_addr, dsc->size()); +} + + +void Vm_session_component::attach_pic(addr_t vm_addr) +{ + _attach(Board::IRQ_CONTROLLER_VT_CPU_BASE, vm_addr, + Board::IRQ_CONTROLLER_VT_CPU_SIZE); +} + + +void Vm_session_component::detach(addr_t vm_addr, size_t size) { + _table->remove_translation(vm_addr, size, _pslab); } + + +Vm_session_component::Vm_session_component(Rpc_entrypoint *ds_ep, + size_t ram_quota) +: _ds_ep(ds_ep), _vm_id(0), + _ds(_ds_size(), _alloc_ds(ram_quota), UNCACHED, true, 0), + _ds_cap(static_cap_cast(_ds_ep->manage(&_ds))) +{ + _ds.assign_core_local_addr(core_env()->rm_session()->attach(_ds_cap)); + + Core_mem_allocator * cma = + static_cast(platform()->core_mem_alloc()); + void *tt; + + /* get some aligned space for the translation table */ + if (!cma->alloc_aligned(sizeof(Translation_table), (void**)&tt, + Translation_table::ALIGNM_LOG2).is_ok()) { + PERR("failed to allocate kernel object"); + throw Root::Quota_exceeded(); + } + + _table = construct_at(tt); + _pslab = new (cma) Page_slab(cma); +} diff --git a/repos/base-hw/src/core/spec/arm_v7/vm_session_component.cc b/repos/base-hw/src/core/spec/arm_v7/vm_session_component.cc new file mode 100644 index 000000000..9a989e068 --- /dev/null +++ b/repos/base-hw/src/core/spec/arm_v7/vm_session_component.cc @@ -0,0 +1,57 @@ +/* + * \brief VM session component for 'base-hw' + * \author Stefan Kalkowski + * \date 2012-10-08 + */ + +/* + * Copyright (C) 2012-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. + */ + +#include +#include +#include + +using namespace Genode; + + +addr_t Vm_session_component::_alloc_ds(size_t &ram_quota) +{ + addr_t addr; + if (_ds_size() > ram_quota || + platform()->ram_alloc()->alloc_aligned(_ds_size(), (void**)&addr, + get_page_size_log2()).is_error()) + throw Root::Quota_exceeded(); + ram_quota -= _ds_size(); + return addr; +} + + +void Vm_session_component::run(void) +{ + if (!_vm_id || Kernel::run_vm(_vm_id)) + PWRN("Unknown VM: is the exception handler registered?"); +} + + +void Vm_session_component::pause(void) +{ + if (!_vm_id || Kernel::pause_vm(_vm_id)) + PWRN("Unknown VM: is the exception handler registered?"); +} + + +Vm_session_component::~Vm_session_component() +{ + /* dissolve VM dataspace from service entry point */ + _ds_ep->dissolve(&_ds); + + if (Kernel::bin_vm(_vm_id)) PERR("Cannot destruct unknown VM"); + + /* free region in allocator */ + core_env()->rm_session()->detach(_ds.core_local_addr()); + platform()->ram_alloc()->free((void*)_ds.phys_addr()); +} diff --git a/repos/base-hw/src/core/spec/arndale/board.cc b/repos/base-hw/src/core/spec/arndale/board.cc index 70722df3b..c023420d9 100644 --- a/repos/base-hw/src/core/spec/arndale/board.cc +++ b/repos/base-hw/src/core/spec/arndale/board.cc @@ -14,10 +14,17 @@ /* core includes */ #include #include +#include +#include +#include +#include +#include -/* hypervisor exception vector address */ -extern void* _hyp_kernel_entry; +namespace Kernel { + void prepare_hypervisor(void); +} +static unsigned char hyp_mode_stack[1024]; static inline void prepare_nonsecure_world() { @@ -29,6 +36,11 @@ static inline void prepare_nonsecure_world() if (Cpsr::M::get(Cpsr::read()) == Cpsr::M::HYP) return; + /* ARM generic timer counter freq needs to be set in secure mode */ + volatile unsigned long * mct_control = (unsigned long*) 0x101C0240; + *mct_control = 0x100; + asm volatile ("mcr p15, 0, %0, c14, c0, 0" :: "r" (24000000)); + /* * enable coprocessor 10 + 11 access and SMP bit access in auxiliary control * register for non-secure world @@ -69,15 +81,17 @@ static inline void switch_to_supervisor_mode() "msr sp_svc, sp \n" /* copy current mode's sp */ "msr lr_svc, lr \n" /* copy current mode's lr */ "msr elr_hyp, lr \n" /* copy current mode's lr to hyp lr */ + "msr sp_hyp, %[stack] \n" /* copy to hyp stack pointer */ "msr spsr_cxfs, %[psr] \n" /* set psr for supervisor mode */ + "adr lr, 1f \n" /* load exception return address */ "eret \n" /* exception return */ - :: [psr] "r" (psr)); + "1:":: [psr] "r" (psr), [stack] "r" (&hyp_mode_stack)); } void Genode::Board::prepare_kernel() { prepare_nonsecure_world(); - Genode::Cpu::hyp_exception_entry_at(&_hyp_kernel_entry); + Kernel::prepare_hypervisor(); switch_to_supervisor_mode(); } diff --git a/repos/base-hw/src/core/spec/arndale/platform_services.cc b/repos/base-hw/src/core/spec/arndale/platform_services.cc new file mode 100644 index 000000000..08e9a9dd8 --- /dev/null +++ b/repos/base-hw/src/core/spec/arndale/platform_services.cc @@ -0,0 +1,34 @@ +/* + * \brief Platform specific services for Arndale + * \author Stefan Kalkowski + * \date 2014-07-08 + */ + +/* + * Copyright (C) 2014 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. + */ + +/* Genode includes */ +#include + +/* Core includes */ +#include +#include + + +/* + * Add ARM virtualization specific vm service + */ +void Genode::platform_add_local_services(Genode::Rpc_entrypoint *ep, + Genode::Sliced_heap *sh, + Genode::Service_registry *ls) +{ + using namespace Genode; + + static Vm_root vm_root(ep, sh); + static Local_service vm_ls(Vm_session::service_name(), &vm_root); + ls->insert(&vm_ls); +} diff --git a/repos/base-hw/src/core/spec/exynos5/cpu.cc b/repos/base-hw/src/core/spec/exynos5/cpu.cc index 4fea1e1e5..f78246429 100644 --- a/repos/base-hw/src/core/spec/exynos5/cpu.cc +++ b/repos/base-hw/src/core/spec/exynos5/cpu.cc @@ -22,46 +22,3 @@ unsigned Cpu::executing_id() { return Mpidr::Aff_0::get(Mpidr::read()); } unsigned Cpu::primary_id() { return Board::PRIMARY_MPIDR_AFF_0; } - - -addr_t Cpu::Context::translation_table() const { return ttbr0; } - - -void Cpu::Context::translation_table(addr_t const t) { ttbr0 = t; } - - -bool Cpu::User_context::in_fault(addr_t & va, addr_t & w) const -{ - switch (cpu_exception) { - - case PREFETCH_ABORT: - { - /* check if fault was caused by a translation miss */ - Ifsr::access_t const fs = Ifsr::Fs::get(Ifsr::read()); - if ((fs & 0b11100) != 0b100) - return false; - - /* fetch fault data */ - w = 0; - va = ip; - return true; - } - - case DATA_ABORT: - { - /* check if fault was caused by translation miss */ - Dfsr::access_t const fs = Dfsr::Fs::get(Dfsr::read()); - if ((fs & 0b11100) != 0b100) - return false; - - /* fetch fault data */ - Dfsr::access_t const dfsr = Dfsr::read(); - w = Dfsr::Wnr::get(dfsr); - va = Dfar::read(); - return true; - } - - default: - return false; - }; -} diff --git a/repos/base-hw/src/core/spec/exynos5/platform_support.cc b/repos/base-hw/src/core/spec/exynos5/platform_support.cc index 6455faa15..237445c53 100644 --- a/repos/base-hw/src/core/spec/exynos5/platform_support.cc +++ b/repos/base-hw/src/core/spec/exynos5/platform_support.cc @@ -51,6 +51,3 @@ Native_region * Platform::_core_only_mmio_regions(unsigned const i) }; return i < sizeof(_regions)/sizeof(_regions[0]) ? &_regions[i] : 0; } - - -Cpu::User_context::User_context() { cpsr = Psr::init_user(); } diff --git a/repos/base-hw/src/core/spec/imx53/trustzone/platform_support.cc b/repos/base-hw/src/core/spec/imx53/trustzone/platform_support.cc index 297265da9..b56bd5277 100644 --- a/repos/base-hw/src/core/spec/imx53/trustzone/platform_support.cc +++ b/repos/base-hw/src/core/spec/imx53/trustzone/platform_support.cc @@ -47,17 +47,17 @@ void Kernel::init_trustzone(Pic * pic) /* configure non-secure interrupts */ for (unsigned i = 0; i < Pic::NR_OF_IRQ; i++) { - if ((i != Imx53::Board::EPIT_1_IRQ) && - (i != Imx53::Board::EPIT_2_IRQ) && - (i != Imx53::Board::I2C_2_IRQ) && - (i != Imx53::Board::I2C_3_IRQ) && - (i < Imx53::Board::GPIO1_IRQL || i > Imx53::Board::GPIO4_IRQH) && - (i < Imx53::Board::GPIO5_IRQL || i > Imx53::Board::GPIO7_IRQH)) + if ((i != Board::EPIT_1_IRQ) && + (i != Board::EPIT_2_IRQ) && + (i != Board::I2C_2_IRQ) && + (i != Board::I2C_3_IRQ) && + (i < Board::GPIO1_IRQL || i > Board::GPIO4_IRQH) && + (i < Board::GPIO5_IRQL || i > Board::GPIO7_IRQH)) pic->unsecure(i); } /* configure central security unit */ - Genode::Csu csu(Imx53::Board::CSU_BASE); + Genode::Csu csu(Board::CSU_BASE); } diff --git a/repos/base-hw/src/core/spec/panda/platform_support.cc b/repos/base-hw/src/core/spec/panda/platform_support.cc index 65c51ab49..f1012a417 100644 --- a/repos/base-hw/src/core/spec/panda/platform_support.cc +++ b/repos/base-hw/src/core/spec/panda/platform_support.cc @@ -62,9 +62,6 @@ Native_region * Platform::_core_only_mmio_regions(unsigned const i) } -Cpu::User_context::User_context() { cpsr = Psr::init_user(); } - - static Board::Pl310 * l2_cache() { return unmanaged_singleton(Board::PL310_MMIO_BASE); } @@ -72,3 +69,6 @@ static Board::Pl310 * l2_cache() { void Board::outer_cache_invalidate() { l2_cache()->invalidate(); } void Board::outer_cache_flush() { l2_cache()->flush(); } void Board::prepare_kernel() { l2_cache()->invalidate(); } + + +Cpu::User_context::User_context() { cpsr = Psr::init_user(); } diff --git a/repos/base-hw/src/core/vm_session_component.cc b/repos/base-hw/src/core/vm_session_component.cc deleted file mode 100644 index 37d873744..000000000 --- a/repos/base-hw/src/core/vm_session_component.cc +++ /dev/null @@ -1,95 +0,0 @@ -/* - * \brief VM session component for 'base-hw' - * \author Stefan Kalkowski - * \date 2012-10-08 - */ - -/* - * Copyright (C) 2012-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. - */ - -/* Genode includes */ -#include -#include -#include -#include - -/* core includes */ -#include -#include -#include -#include - -using namespace Genode; - - -addr_t Vm_session_component::_alloc_ds(size_t &ram_quota) -{ - addr_t addr; - if (_ds_size() > ram_quota || - platform()->ram_alloc()->alloc_aligned(_ds_size(), (void**)&addr, - get_page_size_log2()).is_error()) - throw Root::Quota_exceeded(); - ram_quota -= _ds_size(); - return addr; -} - - -void Vm_session_component::exception_handler(Signal_context_capability handler) -{ - if (_vm_id) { - PWRN("Cannot register exception_handler repeatedly"); - return; - } - _vm_id = Kernel::new_vm(_vm, (void*)_ds.core_local_addr(), handler.dst()); -} - - -void Vm_session_component::run(void) -{ - if (!_vm_id) { - PWRN("No exception handler registered!"); - return; - } - Kernel::run_vm(_vm_id); -} - - -void Vm_session_component::pause(void) -{ - if (!_vm_id) { - PWRN("No exception handler registered!"); - return; - } - Kernel::pause_vm(_vm_id); -} - - -Vm_session_component::Vm_session_component(Rpc_entrypoint *ds_ep, - size_t ram_quota) -: _ds_ep(ds_ep), _vm_id(0), - _ds(_ds_size(), _alloc_ds(ram_quota), UNCACHED, true, 0), - _ds_cap(static_cap_cast(_ds_ep->manage(&_ds))) -{ - _ds.assign_core_local_addr(core_env()->rm_session()->attach(_ds_cap)); - - /* alloc needed memory */ - if (Kernel::vm_size() > ram_quota || - !platform()->core_mem_alloc()->alloc(Kernel::vm_size(), &_vm)) - throw Root::Quota_exceeded(); -} - - -Vm_session_component::~Vm_session_component() -{ - /* dissolve VM dataspace from service entry point */ - _ds_ep->dissolve(&_ds); - - /* free region in allocator */ - core_env()->rm_session()->detach(_ds.core_local_addr()); - platform()->ram_alloc()->free((void*)_ds.phys_addr()); - platform()->core_mem_alloc()->free(_vm); -} diff --git a/repos/base/include/platform/vea9x4/drivers/board_base.h b/repos/base/include/platform/vea9x4/drivers/board_base.h index f979319ca..3dba12142 100644 --- a/repos/base/include/platform/vea9x4/drivers/board_base.h +++ b/repos/base/include/platform/vea9x4/drivers/board_base.h @@ -11,8 +11,8 @@ * under the terms of the GNU General Public License version 2. */ -#ifndef _INCLUDE__DRIVERS__BOARD_BASE_H_ -#define _INCLUDE__DRIVERS__BOARD_BASE_H_ +#ifndef _INCLUDE__PLATFORM__VEA9X4__DRIVERS__BOARD_BASE_H_ +#define _INCLUDE__PLATFORM__VEA9X4__DRIVERS__BOARD_BASE_H_ namespace Genode { @@ -79,5 +79,5 @@ namespace Genode }; } -#endif /* _INCLUDE__DRIVERS__BOARD_BASE_H_ */ +#endif /* _INCLUDE__PLATFORM__VEA9X4__DRIVERS__BOARD_BASE_H_ */ diff --git a/repos/base/include/platform_exynos5/board_base.h b/repos/base/include/platform_exynos5/board_base.h index 7fb0e32c4..8564c3871 100644 --- a/repos/base/include/platform_exynos5/board_base.h +++ b/repos/base/include/platform_exynos5/board_base.h @@ -36,8 +36,15 @@ class Genode::Exynos5 MMIO_0_SIZE = 0x10000000, /* interrupt controller */ - IRQ_CONTROLLER_BASE = 0x10480000, - IRQ_CONTROLLER_SIZE = 0x00010000, + IRQ_CONTROLLER_BASE = 0x10480000, + IRQ_CONTROLLER_SIZE = 0x00010000, + IRQ_CONTROLLER_VT_CTRL_BASE = 0x10484000, + IRQ_CONTROLLER_VT_CPU_BASE = 0x10486000, + IRQ_CONTROLLER_VT_CPU_SIZE = 0x1000, + + /* virtual interrupts */ + VT_MAINTAINANCE_IRQ = 25, + VT_TIMER_IRQ = 27, /* UART */ UART_2_MMIO_BASE = 0x12C20000, diff --git a/repos/os/run/vmm.run b/repos/os/run/vmm.run new file mode 100644 index 000000000..943b14e9a --- /dev/null +++ b/repos/os/run/vmm.run @@ -0,0 +1,112 @@ +# +# \brief Virtual-machine monitor demo +# \author Stefan Kalkowski +# \date 2015-06-25 +# + +assert_spec hw_arndale + +set build_components { + core init + drivers/timer + drivers/platform + drivers/uart + server/vmm +} +build $build_components +create_boot_directory + +install_config { + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +} + + +# +# This test uses a Linux kernel built from unmodified vanilla kernel sources +# but using a slightly simplified kernel configuration, as well as device tree +# for a minimal Versatile Express Cortex A15 like emulated board. +# +# The used sources, including the modified device tree and configuration file +# can be found in the following git repository/branch: +# +# https://github.com/skalk/linux/tree/vexpress-vt +# +# To compile the kernel and device tree blob used in this script, do the +# following steps: +# +# ! git checkout https://github.com/skalk/linux.git +# ! cd linux +# ! git checkout origin/vexpress-vt +# ! make ARCH=arm CROSS_COMPILE= vexpress_config +# ! make ARCH=arm CROSS_COMPILE= -j8 Image +# ! make ARCH=arm CROSS_COMPILE= vexpress-v2p-ca15-tc1.dtb +# + +if {![file exists bin/linux]} { + puts "Download linux kernel ..." + exec >& /dev/null wget -c -O bin/linux http://genode.org/files/release-15.02/arm_vt/linux +} + +if {![file exists bin/dtb]} { + puts "Download device tree blob ..." + exec >& /dev/null wget -c -O bin/dtb http://genode.org/files/release-15.02/arm_vt/dtb +} + +set boot_modules { + core init + platform_drv + uart_drv + timer + vmm + linux + dtb +} +build_boot_image $boot_modules + +# +# Execute test case +# +run_genode_until {.*\/ #.*} 220 +set serial_id [output_spawn_id] +send -i $serial_id "ls\n" +run_genode_until "var" 30 $serial_id +exec rm bin/linux bin/dtb diff --git a/repos/os/src/server/tz_vmm/imx53/main.cc b/repos/os/src/server/tz_vmm/imx53/main.cc index a53fbb051..21588ceae 100644 --- a/repos/os/src/server/tz_vmm/imx53/main.cc +++ b/repos/os/src/server/tz_vmm/imx53/main.cc @@ -17,6 +17,7 @@ #include #include #include +#include /* local includes */ #include diff --git a/repos/os/src/server/tz_vmm/include/mmu.h b/repos/os/src/server/tz_vmm/include/mmu.h index cd1d4cf0e..4b72cc8f1 100644 --- a/repos/os/src/server/tz_vmm/include/mmu.h +++ b/repos/os/src/server/tz_vmm/include/mmu.h @@ -22,8 +22,8 @@ class Mmu { private: - Vm_state *_state; - Ram *_ram; + Genode::Vm_state *_state; + Ram *_ram; unsigned _n_bits() { return _state->ttbrc & 0x7; } @@ -68,7 +68,7 @@ class Mmu public: - Mmu(Vm_state *state, Ram *ram) + Mmu(Genode::Vm_state *state, Ram *ram) : _state(state), _ram(ram) {} diff --git a/repos/os/src/server/tz_vmm/include/vm.h b/repos/os/src/server/tz_vmm/include/vm.h index 896978a84..25a380862 100644 --- a/repos/os/src/server/tz_vmm/include/vm.h +++ b/repos/os/src/server/tz_vmm/include/vm.h @@ -40,7 +40,7 @@ class Vm { Genode::Dataspace_client _kernel_cap; Genode::Dataspace_client _initrd_cap; const char* _cmdline; - Vm_state *_state; + Genode::Vm_state *_state; Genode::Io_mem_connection _ram_iomem; Ram _ram; Genode::addr_t _kernel_offset; @@ -90,7 +90,7 @@ class Vm { _kernel_cap(_kernel_rom.dataspace()), _initrd_cap(_initrd_rom.dataspace()), _cmdline(cmdline), - _state((Vm_state*)Genode::env()->rm_session()->attach(_vm_con.cpu_state())), + _state((Genode::Vm_state*)Genode::env()->rm_session()->attach(_vm_con.cpu_state())), _ram_iomem(ram_base, ram_size), _ram(ram_base, ram_size, (Genode::addr_t)Genode::env()->rm_session()->attach(_ram_iomem.dataspace())), _kernel_offset(kernel_offset), @@ -99,7 +99,7 @@ class Vm { void start() { - Genode::memset((void*)_state, 0, sizeof(Vm_state)); + Genode::memset((void*)_state, 0, sizeof(Genode::Vm_state)); _load_kernel(); _load_initrd(); _prepare_atag(); @@ -161,7 +161,7 @@ class Vm { _state->ip, va_to_pa(_state->ip)); printf(" cpsr = %08lx\n", _state->cpsr); for (unsigned i = 0; - i < Vm_state::Mode_state::MAX; i++) { + i < Genode::Vm_state::Mode_state::MAX; i++) { printf(" sp_%s = %08lx [%08lx]\n", modes[i], _state->mode[i].sp, va_to_pa(_state->mode[i].sp)); printf(" lr_%s = %08lx [%08lx]\n", modes[i], @@ -186,8 +186,8 @@ class Vm { return 0; } - Vm_state *state() const { return _state; } - Ram *ram() { return &_ram; } + Genode::Vm_state *state() const { return _state; } + Ram *ram() { return &_ram; } }; #endif /* _SRC__SERVER__VMM__INCLUDE__VM_H_ */ diff --git a/repos/os/src/server/tz_vmm/include/vm_state.h b/repos/os/src/server/tz_vmm/include/vm_state.h deleted file mode 100644 index ec1e4f751..000000000 --- a/repos/os/src/server/tz_vmm/include/vm_state.h +++ /dev/null @@ -1,27 +0,0 @@ -/* - * \brief Virtual Machine Monitor VM state definition - * \author Stefan Kalkowski - * \date 2012-06-25 - */ - -/* - * Copyright (C) 2012-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 _SRC__SERVER__VMM__INCLUDE__VM_STATE_H_ -#define _SRC__SERVER__VMM__INCLUDE__VM_STATE_H_ - -/* Genode includes */ -#include - -struct Vm_state : Genode::Cpu_state_modes -{ - Genode::addr_t dfar; /* data fault address */ - Genode::addr_t ttbr[2]; /* translation table base regs */ - Genode::addr_t ttbrc; /* translation table base control */ -}; - -#endif /* _SRC__SERVER__VMM__INCLUDE__VM_STATE_H_ */ diff --git a/repos/os/src/server/vmm/main.cc b/repos/os/src/server/vmm/main.cc new file mode 100644 index 000000000..ec3100025 --- /dev/null +++ b/repos/os/src/server/vmm/main.cc @@ -0,0 +1,1330 @@ +/* + * \brief VMM example for ARM Virtualization + * \author Stefan Kalkowski + * \date 2014-07-08 + */ + +/* + * Copyright (C) 2014 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. + */ + +/* Genode includes */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Vea9x4 { +#include +} + +struct State : Genode::Vm_state +{ + Genode::uint32_t midr; + Genode::uint32_t mpidr; + Genode::uint32_t ctr; + Genode::uint32_t ccsidr; + Genode::uint32_t clidr; + Genode::uint32_t pfr0; + Genode::uint32_t mmfr0; + Genode::uint32_t isar0; + Genode::uint32_t isar3; + Genode::uint32_t isar4; + Genode::uint32_t csselr; + Genode::uint32_t actrl; + + class Invalid_register : Genode::Exception {}; + + struct Gp_register { Genode::addr_t r[16]; }; + + struct Psr : Genode::Register<32> + { + struct Mode : Bitfield<0,5> + { + enum { + USR = 16, + FIQ = 17, + IRQ = 18, + SVC = 19, + ABORT = 23, + UND = 27 + }; + }; + + static int mode_offset(access_t v) + { + switch(Mode::get(v)) { + case Mode::FIQ: return Mode_state::Mode::FIQ; + case Mode::IRQ: return Mode_state::Mode::IRQ; + case Mode::SVC: return Mode_state::Mode::SVC; + case Mode::ABORT: return Mode_state::Mode::ABORT; + case Mode::UND: return Mode_state::Mode::UND; + default: return -1; + }; + } + }; + + Genode::addr_t * r(unsigned i) + { + unsigned mo = Psr::mode_offset(cpsr); + switch (i) { + case 13: return (mo < 0) ? &sp : &(mode[mo].sp); + case 14: return (mo < 0) ? &lr : &(mode[mo].lr); + default: return &(reinterpret_cast(this)->r[i]); + }; + } +}; + + +class Ram { + + private: + + Genode::addr_t const _base; + Genode::size_t const _size; + Genode::addr_t const _local; + + public: + + Ram(Genode::addr_t const addr, Genode::size_t const sz, + Genode::addr_t const local) + : _base(addr), _size(sz), _local(local) { } + + Genode::addr_t base() const { return _base; } + Genode::size_t size() const { return _size; } + Genode::addr_t local() const { return _local; } +}; + + +class Vm { + + private: + + enum { + RAM_ADDRESS = 0x80000000, + MACH_TYPE = 2272, /* ARNDALE = 4274; VEXPRESS = 2272 */ + KERNEL_OFFSET = 0x8000, + DTB_OFFSET = 64 * 1024 * 1024, + }; + + Genode::Vm_connection _vm_con; + Genode::Attached_rom_dataspace _kernel_rom; + Genode::Attached_rom_dataspace _dtb_rom; + Genode::Attached_ram_dataspace _vm_ram; + Ram _ram; + State *_state; + bool _active = true; + + void _load_kernel() + { + Genode::memcpy((void*)(_ram.local() + KERNEL_OFFSET), + _kernel_rom.local_addr(), + _kernel_rom.size()); + _state->ip = _ram.base() + KERNEL_OFFSET; + } + + void _load_dtb() + { + Genode::memcpy((void*)(_ram.local() + DTB_OFFSET), + _dtb_rom.local_addr(), + _dtb_rom.size()); + _state->r2 = _ram.base() + DTB_OFFSET; + } + + public: + + class Exception : Genode::Exception + { + private: + + enum { BUF_SIZE = 128 }; + + char _buf[BUF_SIZE]; + Genode::String_console _sc; + + public: + + Exception(const char *fmt, ...) + : _sc(_buf, BUF_SIZE) + { + va_list args; + va_start(args, fmt); + _sc.vprintf(fmt, args); + va_end(args); + } + + Exception() : Exception("undefined") {} + + void print() { PERR("%s", _buf); } + }; + + + Vm(const char *kernel, const char *dtb, Genode::size_t const ram_size, + Genode::Signal_context_capability sig_cap) + : _kernel_rom(kernel), + _dtb_rom(dtb), + _vm_ram(Genode::env()->ram_session(), ram_size, Genode::UNCACHED), + _ram(RAM_ADDRESS, ram_size, (Genode::addr_t)_vm_ram.local_addr()), + _state((State*)Genode::env()->rm_session()->attach(_vm_con.cpu_state())) + { + PINF("ram is at %lx", Genode::Dataspace_client(_vm_ram.cap()).phys_addr()); + _vm_con.exception_handler(sig_cap); + _vm_con.attach(_vm_ram.cap(), RAM_ADDRESS); + _vm_con.attach_pic(0x2C002000); + } + + void start() + { + Genode::memset((void*)_state, 0, sizeof(Genode::Cpu_state_modes)); + _load_kernel(); + _load_dtb(); + _state->r1 = MACH_TYPE; + _state->cpsr = 0x93; /* SVC mode and IRQs disabled */ + + _state->timer_ctrl = 0; + _state->timer_val = 0; + _state->timer_irq = false; + + _state->gic_hcr = 0b101; + _state->gic_vmcr = 0x4c0000; + _state->gic_apr = 0; + _state->gic_lr[0] = 0; + _state->gic_lr[1] = 0; + _state->gic_lr[2] = 0; + _state->gic_lr[3] = 0; + + PINF("ready to run"); + } + + void run() { if (_active) _vm_con.run(); } + void pause() { _vm_con.pause(); } + void wait_for_interrupt() { _active = false; } + void interrupt() { _active = true; } + bool active() { return _active; } + + void dump() + { + using namespace Genode; + + const char * const modes[] = + { "und", "svc", "abt", "irq", "fiq" }; + const char * const exc[] = + { "nope", "reset", "undefined", "svc", "pf_abort", + "data_abort", "irq", "fiq", "trap" }; + + printf("Cpu state:\n"); + printf(" r0 = %08lx\n", _state->r0); + printf(" r1 = %08lx\n", _state->r1); + printf(" r2 = %08lx\n", _state->r2); + printf(" r3 = %08lx\n", _state->r3); + printf(" r4 = %08lx\n", _state->r4); + printf(" r5 = %08lx\n", _state->r5); + printf(" r6 = %08lx\n", _state->r6); + printf(" r7 = %08lx\n", _state->r7); + printf(" r8 = %08lx\n", _state->r8); + printf(" r9 = %08lx\n", _state->r9); + printf(" r10 = %08lx\n", _state->r10); + printf(" r11 = %08lx\n", _state->r11); + printf(" r12 = %08lx\n", _state->r12); + printf(" sp = %08lx\n", _state->sp); + printf(" lr = %08lx\n", _state->lr); + printf(" ip = %08lx\n", _state->ip); + printf(" cpsr = %08lx\n", _state->cpsr); + for (unsigned i = 0; + i < State::Mode_state::MAX; i++) { + printf(" sp_%s = %08lx\n", modes[i], _state->mode[i].sp); + printf(" lr_%s = %08lx\n", modes[i], _state->mode[i].lr); + printf(" spsr_%s = %08lx\n", modes[i], _state->mode[i].spsr); + } + printf(" exception = %s\n", exc[_state->cpu_exception]); + } + + State *state() const { return _state; } +}; + + +class Vmm +{ + private: + + struct Hsr : Genode::Register<32> + { + struct Ec : Bitfield<26, 6> + { + enum { + WFI = 0x1, + CP15 = 0x3, + HVC = 0x12, + DA = 0x24 + }; + }; + }; + + class Coprocessor + { + protected: + + struct Iss : Hsr + { + struct Direction : Bitfield<0, 1> {}; + struct Crm : Bitfield<1, 4> {}; + struct Register : Bitfield<5, 4> {}; + struct Crn : Bitfield<10, 4> {}; + struct Opcode1 : Bitfield<14, 3> {}; + struct Opcode2 : Bitfield<17, 3> {}; + + static access_t value(unsigned crn, unsigned op1, + unsigned crm, unsigned op2) + { + access_t v = 0; + Crn::set(v, crn); + Crm::set(v, crm); + Opcode1::set(v, op1); + Opcode2::set(v, op2); + return v; + }; + + static access_t mask_encoding(access_t v) + { + return Crm::masked(v) | + Crn::masked(v) | + Opcode1::masked(v) | + Opcode2::masked(v); + } + }; + + + class Register : public Genode::Avl_node + { + private: + + const Iss::access_t _encoding; + const char *_name; + const bool _writeable; + Genode::uint32_t State::*_r; + const Genode::addr_t _init_value; + + public: + + Register(unsigned crn, unsigned op1, + unsigned crm, unsigned op2, + const char * name, + bool writeable, + Genode::uint32_t State::*r, + Genode::addr_t v) + : _encoding(Iss::value(crn, op1, crm, op2)), + _name(name), + _writeable(writeable), _r(r), _init_value(v) {} + + const char * name() const { return _name; } + const bool writeable() const { return _writeable; } + const Genode::addr_t init_value() const { + return _init_value; } + + Register * find_by_encoding(Iss::access_t e) + { + if (e == _encoding) return this; + + Register * r = + Avl_node::child(e > _encoding); + return r ? r->find_by_encoding(e) : 0; + } + + void write(State * state, Genode::addr_t v) { + state->*_r = (Genode::uint32_t)v; } + + Genode::addr_t read(State * state) const { + return (Genode::addr_t)(state->*_r); } + + + /************************ + ** Avl node interface ** + ************************/ + + bool higher(Register *r) { + return (r->_encoding > _encoding); } + }; + + Genode::Avl_tree _reg_tree; + + public: + + bool handle_trap(State *state) + { + Iss::access_t v = state->hsr; + Register * reg = _reg_tree.first(); + if (reg) reg = reg->find_by_encoding(Iss::mask_encoding(v)); + + if (!reg) { + PERR("Unknown cp15 access @ ip=%08lx:", state->ip); + PERR("%s: c15 %d r%d c%d c%d %d", + Iss::Direction::get(v) ? "read" : "write", + Iss::Opcode1::get(v), Iss::Register::get(v), + Iss::Crn::get(v), Iss::Crm::get(v), + Iss::Opcode2::get(v)); + return false; + } + + if (Iss::Direction::get(v)) { /* read access */ + *(state->r(Iss::Register::get(v))) = reg->read(state); + } else { /* write access */ + if (!reg->writeable()) { + PERR("Writing to cp15 register %s not allowed!", + reg->name()); + return false; + } + reg->write(state, *(state->r(Iss::Register::get(v)))); + } + state->ip += sizeof(Genode::addr_t); + return true; + } + }; + + + class Cp15 : public Coprocessor + { + private: + + Register _regs[27] { + { 0, 0, 0, 0, "MIDR", false, &State::midr, 0x412fc0f1 }, + { 0, 0, 0, 5, "MPIDR", false, &State::mpidr, 0x40000000 }, + { 0, 0, 0, 1, "CTR", false, &State::ctr, 0x8444c004 }, + { 0, 1, 0, 0, "CCSIDR", false, &State::ccsidr, 0x701fe00a }, + { 0, 1, 0, 1, "CLIDR", false, &State::clidr, 0x0a200023 }, + { 0, 0, 1, 0, "PFR0", false, &State::pfr0, 0x00001031 }, + { 0, 0, 1, 4, "MMFR0", false, &State::mmfr0, 0x10201105 }, + { 0, 0, 2, 0, "ISAR0", false, &State::isar0, 0x02101110 }, + { 0, 0, 2, 3, "ISAR3", false, &State::isar3, 0x11112131 }, + { 0, 0, 2, 4, "ISAR4", false, &State::isar4, 0x10011142 }, + { 0, 2, 0, 0, "CSSELR", true, &State::csselr, 0x00000000 }, + { 1, 0, 0, 0, "SCTRL", true, &State::sctrl, 0 /* 0xc5007a 0x00c5187a*/ }, + { 1, 0, 0, 1, "ACTRL", true, &State::actrl, 0x00000040 }, + { 1, 0, 0, 2, "CPACR", true, &State::cpacr, 0x00000000 }, + { 2, 0, 0, 0, "TTBR0", true, &State::ttbr0, 0x00000000 }, + { 2, 0, 0, 1, "TTBR1", true, &State::ttbr1, 0x00000000 }, + { 2, 0, 0, 2, "TTBCR", true, &State::ttbcr, 0x00000000 }, + { 3, 0, 0, 0, "DACR", true, &State::dacr, 0x55555555 }, + { 5, 0, 0, 0, "DFSR", true, &State::dfsr, 0x00000000 }, + { 5, 0, 0, 1, "IFSR", true, &State::ifsr, 0x00000000 }, + { 5, 0, 1, 0, "ADFSR", true, &State::adfsr, 0x00000000 }, + { 5, 0, 1, 1, "AIFSR", true, &State::aifsr, 0x00000000 }, + { 6, 0, 0, 0, "DFAR", true, &State::dfar, 0x00000000 }, + { 6, 0, 0, 2, "IFAR", true, &State::ifar, 0x00000000 }, + { 10, 0, 2, 0, "PRRR", true, &State::prrr, 0x00098aa4 }, + { 10, 0, 2, 1, "NMRR", true, &State::nmrr, 0x44e048e0 }, + { 13, 0, 0, 1, "CONTEXTIDR", true, &State::cidr, 0x00000000 } + }; + + public: + + Cp15(State *state) + { + for (unsigned i = 0; i < (sizeof(_regs) / sizeof(Register)); + i++) { + _reg_tree.insert(&_regs[i]); + _regs[i].write(state, _regs[i].init_value()); + } + } + }; + + + class Device : public Genode::Avl_node + { + protected: + + struct Iss : Hsr + { + struct Write : Bitfield<6, 1> {}; + struct Register : Bitfield<16, 4> {}; + struct Sign_extend : Bitfield<21, 1> {}; + struct Access_size : Bitfield<22, 2> { + enum { BYTE, HALFWORD, WORD }; }; + struct Valid : Bitfield<24, 1> {}; + + static bool valid(access_t v) { + return Valid::get(v) && !Sign_extend::get(v); } + + static bool write(access_t v) { return Write::get(v); } + static unsigned r(access_t v) { return Register::get(v); } + }; + + const char * const _name; + const Genode::uint64_t _addr; + const Genode::uint64_t _size; + Vm *_vm; + + using Error = Vm::Exception; + + public: + + Device(const char * const name, + const Genode::uint64_t addr, + const Genode::uint64_t size, + Vm *vm) + : _name(name), _addr(addr), _size(size), _vm(vm) { } + + Device() : Device("undefined", 0, 0, nullptr) {} + + Genode::uint64_t addr() { return _addr; } + Genode::uint64_t size() { return _size; } + const char * name() { return _name; } + + virtual void read (Genode::uint32_t * reg, + Genode::uint64_t off) { + throw Error("Device %s: word-wise read of %llx not allowed", + name(), off); } + + virtual void write (Genode::uint32_t * reg, + Genode::uint64_t off) { + throw Error("Device %s: word-wise write of %llx not allowed", + name(), off); } + + virtual void read (Genode::uint16_t * reg, + Genode::uint64_t off) { + throw Error("Device %s: halfword read of %llx not allowed", + name(), off); } + + virtual void write (Genode::uint16_t * reg, + Genode::uint64_t off) { + throw Error("Device %s: halfword write of %llx not allowed", + name(), off); } + + virtual void read (Genode::uint8_t * reg, + Genode::uint64_t off) { + throw Error("Device %s: byte-wise read of %llx not allowed", + name(), off); } + + virtual void write (Genode::uint8_t * reg, + Genode::uint64_t off) { + throw Error("Device %s: byte-wise write of %llx not allowed", + name(), off); } + + virtual void irq_enabled (unsigned irq) { } + virtual void irq_disabled(unsigned irq) { } + virtual void irq_handled (unsigned irq) { } + + void handle_memory_access(State *state) + { + using namespace Genode; + + if (!Iss::valid(state->hsr)) + throw Error("Device %s: unknown HSR=%lx", + name(), state->hsr); + + bool wr = Iss::Write::get(state->hsr); + unsigned idx = Iss::Register::get(state->hsr); + uint64_t ipa = (uint64_t)state->hpfar << 8; + uint64_t off = ipa - addr() + (state->hdfar & ((1 << 13) - 1)); + + switch (Iss::Access_size::get(state->hsr)) { + case Iss::Access_size::BYTE: + { + uint8_t * p = (uint8_t*)state->r(idx) + (off & 0b11); + wr ? write(p, off) : read(p, off); + break; + } + case Iss::Access_size::HALFWORD: + { + uint16_t * p = (uint16_t*) state->r(idx) + (off & 0b1); + wr ? write(p, off) : read(p, off); + break; + } + case Iss::Access_size::WORD: + { + uint32_t * p = (uint32_t*) state->r(idx); + wr ? write(p, off) : read(p, off); + break; + } + default: + throw Error("Device %s: invalid alignment", name()); + }; + } + + /************************ + ** Avl node interface ** + ************************/ + + bool higher(Device *d) { return d->addr() > addr(); } + + Device *find_by_addr(Genode::uint64_t a) + { + if ((a >= addr()) && (a < (addr()+size()))) + return this; + + Device *d = Avl_node::child(a > addr()); + return d ? d->find_by_addr(a) : 0; + } + }; + + + class Gic : public Device + { + enum { + GICD_CTLR = 0, + GICD_TYPER = 0x4, + GICD_ISENABLER0 = 0x100, + GICD_ISENABLERL = 0x17c, + GICD_ICENABLER0 = 0x180, + GICD_ICENABLERL = 0x1fc, + GICD_IPRIORITYR0 = 0x400, + GICD_IPRIORITYRL = 0x7f8, + GICD_ITARGETSR0 = 0x800, + GICD_ITARGETSRL = 0xbf8, + GICD_ICFGR2 = 0xc08, + GICD_ICFGRL = 0xcfc, + }; + + enum Irqs { + SGI_MAX = 15, + TIMER = Genode::Board_base::VT_TIMER_IRQ, + MAX_IRQ = 256, + }; + + struct Irq { + + enum Cpu_state { INACTIVE, PENDING }; + enum Distr_state { ENABLED, DISABLED }; + + Cpu_state cpu_state = INACTIVE; + Distr_state distr_state = DISABLED; + Device * device = nullptr; + bool eoi = false; + }; + + Irq _irqs[MAX_IRQ+1]; + bool _distr_enabled = false; + + using Error = Vm::Exception; + + + /********************** + ** GICH interface ** + **********************/ + + struct Gich_lr : Genode::Register<32> + { + struct Virt_id : Bitfield<0, 10> { }; + struct Phys_id : Bitfield<10, 10> { }; + struct Prio : Bitfield<23, 5> { }; + struct State : Bitfield<28, 2> { }; + struct Hw : Bitfield<31, 1> { }; + }; + + void _handle_eoi() + { + if (!(_vm->state()->gic_misr & 1)) return; + + for (unsigned i = 0; i < State::NR_IRQ; i++) { + if (_vm->state()->gic_eisr & (1 << i)) { + unsigned irq = Gich_lr::Virt_id::get(_vm->state()->gic_lr[i]); + if (irq > MAX_IRQ) + throw Error("IRQ out of bounds"); + _vm->state()->gic_lr[i] = 0; + _vm->state()->gic_elrsr0 |= 1 << i; + if (irq == TIMER && + _irqs[irq].distr_state == Irq::ENABLED) + _vm->state()->timer_irq = true; + _irqs[irq].cpu_state = Irq::INACTIVE; + } + } + + _vm->state()->gic_misr = 0; + } + + void _inject_irq(unsigned irq, bool eoi) + { + if (irq == TIMER) + _vm->state()->timer_irq = false; + + for (unsigned i = 0; i < State::NR_IRQ; i++) { + if (!(_vm->state()->gic_elrsr0 & (1 << i))) { + Gich_lr::access_t v = _vm->state()->gic_lr[i]; + if (Gich_lr::Virt_id::get(v) == irq) + return; + } + } + + for (unsigned i = 0; i < State::NR_IRQ; i++) { + if (!(_vm->state()->gic_elrsr0 & (1 << i))) + continue; + + _vm->state()->gic_elrsr0 &= ~(1 << i); + Gich_lr::access_t v = 0; + Gich_lr::Virt_id::set(v, irq); + Gich_lr::Phys_id::set(v, eoi ? 1 << 9 : 0); + Gich_lr::Prio::set(v, 0); + Gich_lr::State::set(v, 0b1); + _vm->state()->gic_lr[i] = v; + return; + } + + throw Error("IRQ queue full, can't inject irq %u", irq); + } + + void _enable_irq(unsigned irq) + { + if (irq > MAX_IRQ || !_irqs[irq].device) + throw Error("GIC: can't enable unknown IRQ %d", irq); + + if (_irqs[irq].distr_state == Irq::ENABLED) + return; + + _irqs[irq].distr_state = Irq::ENABLED; + _irqs[irq].device->irq_enabled(irq); + + if (irq == TIMER) + _vm->state()->timer_irq = true; + } + + void _disable_irq(unsigned irq) + { + if (irq > MAX_IRQ) + throw Error("IRQ out of bounds"); + + if (_irqs[irq].distr_state == Irq::DISABLED) + return; + + _irqs[irq].distr_state = Irq::DISABLED; + _irqs[irq].device->irq_disabled(irq); + + if (irq == TIMER) + _vm->state()->timer_irq = false; + } + + public: + + Gic(const char * const name, + const Genode::uint64_t addr, + const Genode::uint64_t size, + Vm *vm) + : Device(name, addr, size, vm) + { + for (unsigned i = 0; i <= MAX_IRQ; i++) { + _irqs[i] = Irq(); + if (i <= SGI_MAX) + _irqs[i].device = this; + } + } + + void read (Genode::uint32_t * reg, Genode::uint64_t off) + { + if (off >= GICD_ICFGR2 && off <= GICD_ICFGRL) { + *reg = 0; + return; + } + + /* read enable registers */ + if (off >= GICD_ISENABLER0 && off <= GICD_ISENABLERL) { + *reg = 0; + Genode::addr_t idx = ((Genode::addr_t)off - GICD_ISENABLER0) * 8; + for (unsigned i = 0; i < 32; i++) { + if (_irqs[idx + i].distr_state == Irq::ENABLED) + *reg |= 1 << i; + } + return; + } + + if (off >= GICD_ITARGETSR0 && off <= GICD_ITARGETSRL) { + *reg = 0x01010101; + return; + } + + switch (off) { + case GICD_CTLR: + *reg = _distr_enabled ? 1 : 0; + return; + case GICD_TYPER: + *reg = 0b101; + return; + default: + throw Error("GIC: unsupported read offset %llx", off); + }; + } + + void write(Genode::uint32_t * reg, Genode::uint64_t off) + { + using namespace Genode; + + /* only allow cpu0 as target by now */ + if (off >= GICD_ITARGETSR0 && off <= GICD_ITARGETSRL && + *reg == 0x01010101) + return; + + /* only allow level triggered && active low */ + if (off >= GICD_ICFGR2 && off <= GICD_ICFGRL && + *reg == 0) + return; + + /* ignore priority settings */ + if (off >= GICD_IPRIORITYR0 && off <= GICD_IPRIORITYRL) + return; + + /* set enable registers */ + if (off >= GICD_ISENABLER0 && off <= GICD_ISENABLERL) { + addr_t idx = ((addr_t)off - GICD_ISENABLER0) * 8; + for (unsigned i = 0; i < 32; i++) + if (((*reg >> i) & 1)) + _enable_irq(idx+i); + return; + } + + /* clear enable registers */ + if (off >= GICD_ICENABLER0 && off <= GICD_ICENABLERL) { + addr_t idx = ((addr_t)off - GICD_ICENABLER0) * 8; + for (unsigned i = 0; i < 32; i++) + if (((*reg >> i) & 1)) + _disable_irq(idx+i); + return; + } + + switch (off) { + case GICD_CTLR: + _distr_enabled = (*reg & 0b1); + return; + default: + throw Error("GIC: unsupported write offset %llx", off); + }; + } + + void register_irq(unsigned irq, Device * d, bool eoi) + { + _irqs[irq].device = d; + _irqs[irq].eoi = eoi; + } + + void inject_irq(unsigned irq) + { + if (!_irqs[irq].device) + throw Error("No device registered for IRQ %u", irq); + + if (_irqs[irq].cpu_state == Irq::PENDING) + throw Error("Pending IRQ should not trigger again");; + + if (_irqs[irq].eoi) + _irqs[irq].cpu_state = Irq::PENDING; + + if (_irqs[irq].distr_state == Irq::DISABLED) { + PWRN("Disabled irq %u injected", irq); + return; + } + + _inject_irq(irq, _irqs[irq].eoi); + _vm->interrupt(); + } + + void irq_occured() + { + switch(_vm->state()->gic_irq) { + case Genode::Board_base::VT_MAINTAINANCE_IRQ: + _handle_eoi(); + return; + case TIMER: + inject_irq(TIMER); + return; + default: + throw Error("Unknown IRQ %u occured", + _vm->state()->gic_irq); + }; + } + }; + + + class Generic_timer : public Device + { + private: + + using Board = Genode::Board_base; + + Timer::Connection _timer; + Genode::Signal_dispatcher _handler; + Gic &_gic; + + void _timeout(unsigned) + { + _vm->state()->timer_ctrl = 5; + _vm->state()->timer_val = 0xffffffff; + _gic.inject_irq(Board::VT_TIMER_IRQ); + } + + public: + + Generic_timer(const char * const name, + const Genode::uint64_t addr, + const Genode::uint64_t size, + Vm *vm, + Genode::Signal_receiver &receiver, + Gic &gic) + : Device(name, addr, size, vm), + _handler(receiver, *this, &Generic_timer::_timeout), + _gic(gic) { + _timer.sigh(_handler); + _gic.register_irq(Board::VT_TIMER_IRQ, this, true); + } + + void schedule_timeout() + { + if ((_vm->state()->timer_ctrl & 0b101) != 0b101) + _timer.trigger_once(_vm->state()->timer_val / 24); + } + }; + + + class System_register : public Device + { + private: + + enum { + SYS_LED = 0x8, + SYS_FLASH = 0x4c, + SYS_24MHZ = 0x5c, + SYS_MCI = 0x48, + SYS_MISC = 0x60, + SYS_PROCID0 = 0x84, + SYS_CFGDATA = 0xa0, + SYS_CFGCTRL = 0xa4, + SYS_CFGSTAT = 0xa8, + }; + + struct Sys_cfgctrl : Genode::Register<32> + { + struct Device : Bitfield<0,12> {}; + struct Position : Bitfield<12,4> {}; + struct Site : Bitfield<16,2> {}; + struct Function : Bitfield<20,6> {}; + struct Write : Bitfield<30,1> {}; + struct Start : Bitfield<31,1> {}; + }; + + Timer::Connection _timer; + Genode::uint32_t _spi_data = 0; + Genode::uint32_t _spi_stat = 1; + + using Error = Vm::Exception; + + void _mcc_control(unsigned device, unsigned func, bool write) + { + if (func == 1 && !write) { + switch (device) { + case 0: /* OSCCLK0 */ + _spi_data = 60000000; + return; + case 2: /* OSCCLK2 */ + _spi_data = 24000000; + return; + case 4: /* OSCCLK4 */ + _spi_data = 40000000; + return; + case 5: /* OSCCLK5 */ + _spi_data = 23750000; + return; + case 6: /* OSCCLK6 */ + _spi_data = 50000000; + return; + case 7: /* OSCCLK7 */ + _spi_data = 60000000; + return; + case 8: /* OSCCLK8 */ + _spi_data = 40000000; + return; + default: + throw Error("Sys regs: unsupported MCC device"); + }; + } + + if (func == 2 && !write) { + switch (device) { + case 0: /* VOLT0 */ + _spi_data = 900000; + return; + default: ; + }; + } + + throw Error("Unknown device %u func=%u write=%d", + device, func, write); + }; + + public: + + System_register(const char * const name, + const Genode::uint64_t addr, + const Genode::uint64_t size, + Vm *vm) + : Device(name, addr, size, vm) {} + + void read(Genode::uint32_t * reg, Genode::uint64_t off) + { + switch (off) { + case SYS_LED: + *reg = 0xff; + return; + case SYS_FLASH: + *reg = 0; + return; + case SYS_24MHZ: /* 24 MHz counter */ + *reg = _timer.elapsed_ms() * 24000; + return; + case SYS_MISC: + *reg = 1 << 12; + return; + case SYS_PROCID0: + *reg = 0x14000237; /* daughterboard ID */ + return; + case SYS_MCI: + *reg = 0; /* no mmc inside */ + return; + case SYS_CFGSTAT: + *reg = _spi_stat; + return; + case SYS_CFGCTRL: + *reg = 0; + return; + case SYS_CFGDATA: + *reg = _spi_data; + return; + }; + throw Error("Sys regs: read of offset %llx forbidden", off); + } + + void write(Genode::uint32_t * reg, Genode::uint64_t off) + { + switch (off) { + case SYS_CFGDATA: + _spi_data = *reg; + return; + case SYS_CFGSTAT: + _spi_stat = *reg; + return; + case SYS_CFGCTRL: + if (Sys_cfgctrl::Start::get(*reg)) { + _spi_stat = 1; + _mcc_control(Sys_cfgctrl::Device::get(*reg), + Sys_cfgctrl::Function::get(*reg), + Sys_cfgctrl::Write::get(*reg)); + return; + } + case SYS_24MHZ: + case SYS_MISC: + case SYS_PROCID0: + case SYS_MCI: + ; + }; + throw Error("Sys regs: write of offset %llx forbidden", off); + } + }; + + + class Pl011 : public Device + { + private: + + using Board = Vea9x4::Genode::Board_base; + using Ring_buffer = ::Ring_buffer; + + class Wrong_offset {}; + + enum { + UARTDR = 0x0, + UARTFR = 0x18, + UARTIBRD = 0x24, + UARTFBRD = 0x28, + UARTLCR_H = 0x2c, + UARTCR = 0x30, + UARTIFLS = 0x34, + UARTIMSC = 0x38, + UARTMIS = 0x40, + UARTICR = 0x44, + UARTPERIPHID0 = 0xfe0, + UARTPERIPHID1 = 0xfe4, + UARTPERIPHID2 = 0xfe8, + UARTPERIPHID3 = 0xfec, + UARTPCELLID0 = 0xff0, + UARTPCELLID1 = 0xff4, + UARTPCELLID2 = 0xff8, + UARTPCELLID3 = 0xffc, + }; + + Terminal::Connection _terminal; + Genode::Signal_dispatcher _handler; + Gic &_gic; + Ring_buffer _rx_buf; + Genode::uint16_t _ibrd = 0; + Genode::uint16_t _fbrd = 0; + Genode::uint16_t _lcr_h = 0; + Genode::uint16_t _imsc = 0b1111; + Genode::uint16_t _ris = 0; + Genode::uint16_t _cr = 0x300; + + void _out_char(unsigned char c) { + _terminal.write(&c, 1); + } + + unsigned char _get_char() + { + if (_rx_buf.empty()) return 0; + return _rx_buf.get(); + } + + Genode::uint16_t _get(Genode::uint64_t off) + { + switch (off) { + case UARTDR: return _get_char(); + case UARTPERIPHID0: return 0x11; + case UARTPERIPHID1: return 0x10; + case UARTPERIPHID2: return 0x14; + case UARTPERIPHID3: return 0x0; + case UARTPCELLID0: return 0xd; + case UARTPCELLID1: return 0xf0; + case UARTPCELLID2: return 0x5; + case UARTPCELLID3: return 0xb1; + case UARTFR: return _rx_buf.empty() ? 16 : 64; + case UARTCR: return _cr; + case UARTIMSC: return _imsc; + case UARTMIS: return _ris & _imsc; + case UARTFBRD: return _fbrd; + case UARTIBRD: return _ibrd; + case UARTLCR_H: return _lcr_h; + default: + throw Wrong_offset(); + }; + } + + void _mask_irqs(Genode::uint32_t mask) + { + /* TX IRQ unmask */ + if (mask & (1 << 5) && !(_imsc & (1 << 5))) { + _gic.inject_irq(Board::PL011_0_IRQ); + _ris |= 1 << 5; + } + + /* RX IRQ unmask */ + if (mask & (1 << 4) && !(_imsc & (1 << 4)) && + !_rx_buf.empty()) { + _gic.inject_irq(Board::PL011_0_IRQ); + _ris |= 1 << 4; + } + + _imsc = mask; + } + + void _read(unsigned) + { + if (!_terminal.avail()) return; + + unsigned char c = 0; + _terminal.read(&c, 1); + _rx_buf.add(c); + + _gic.inject_irq(Board::PL011_0_IRQ); + _ris |= 1 << 4; + } + + using Error = Vm::Exception; + + public: + + Pl011(const char * const name, + const Genode::uint64_t addr, + const Genode::uint64_t size, + Vm *vm, + Genode::Signal_receiver &receiver, + Gic &gic) + : Device(name, addr, size, vm), + _handler(receiver, *this, &Pl011::_read), + _gic(gic) { + _terminal.read_avail_sigh(_handler); + _gic.register_irq(Board::PL011_0_IRQ, this, false); + } + + void read(Genode::uint16_t * reg, Genode::uint64_t off) + { + try { + *reg = _get(off); + } catch(Wrong_offset &e) { + throw Error("UART: halfword read of offset %llx", off); + } + } + + void read(Genode::uint32_t * reg, Genode::uint64_t off) { + read((Genode::uint16_t*) reg, off); } + + void write(Genode::uint8_t * reg, Genode::uint64_t off) + { + if (off != UARTDR) + throw Error("UART: byte write %x to offset %llx", + *reg, off); + _terminal.write(reg, 1); + } + + void write(Genode::uint16_t * reg, Genode::uint64_t off) + { + switch (off) { + case UARTDR: + _terminal.write(reg, 1); + return; + case UARTFBRD: + _fbrd = *reg; + return; + case UARTIMSC: + _mask_irqs(*reg); + return; + case UARTIBRD: + _ibrd = *reg; + return; + case UARTLCR_H: + _lcr_h = *reg; + return; + case UARTICR: + _ris = _ris & ~*reg; + return; + case UARTCR: + _cr = *reg; + return; + case UARTIFLS: + return; + default: + throw Error("UART: halfword write %x to offset %llx", + *reg, off); + }; + } + }; + + + Genode::Signal_receiver _sig_rcv; + Genode::Signal_dispatcher _vm_handler; + Vm _vm; + Cp15 _cp15; + Genode::Avl_tree _device_tree; + Gic _gic; + Generic_timer _timer; + System_register _sys_regs; + Pl011 _uart; + + void _handle_hyper_call() { + throw Vm::Exception("Unknown hyper call!"); } + + void _handle_data_abort() + { + Genode::uint64_t ipa = (Genode::uint64_t)_vm.state()->hpfar << 8; + Device * device = _device_tree.first() + ? _device_tree.first()->find_by_addr(ipa) : nullptr; + if (!device) + throw Vm::Exception("No device at IPA=%llx", ipa); + device->handle_memory_access(_vm.state()); + _vm.state()->ip += sizeof(Genode::addr_t); + } + + void _handle_wfi() + { + if (_vm.state()->hsr & 1) + throw Vm::Exception("WFE not implemented yet"); + + _vm.wait_for_interrupt(); + _timer.schedule_timeout(); + _vm.state()->ip += sizeof(Genode::addr_t); + } + + void _handle_trap() + { + /* check device number*/ + switch (Hsr::Ec::get(_vm.state()->hsr)) { + case Hsr::Ec::HVC: + _handle_hyper_call(); + break; + case Hsr::Ec::CP15: + _cp15.handle_trap(_vm.state()); + break; + case Hsr::Ec::DA: + _handle_data_abort(); + break; + case Hsr::Ec::WFI: + _handle_wfi(); + return; + default: + throw Vm::Exception("Unknown trap: %x", + Hsr::Ec::get(_vm.state()->hsr)); + }; + } + + void _handle_vm(unsigned num) + { + if (_vm.active()) { + + enum { IRQ = 6, TRAP = 8 }; + + /* check exception reason */ + switch (_vm.state()->cpu_exception) { + case IRQ: + _gic.irq_occured(); + break; + case TRAP: + _handle_trap(); + break; + default: + throw Vm::Exception("Curious exception occured"); + } + } + } + + public: + + Vmm() + : _vm_handler(_sig_rcv, *this, &Vmm::_handle_vm), + _vm("linux", "dtb", 1024 * 1024 * 128, _vm_handler), + _cp15(_vm.state()), + _gic ("Gic", 0x2c001000, 0x2000, &_vm), + _timer ("Timer", 0x2a430000, 0x1000, &_vm, _sig_rcv, _gic), + _sys_regs ("System Register", 0x1c010000, 0x1000, &_vm), + _uart ("Pl011", 0x1c090000, 0x1000, &_vm, _sig_rcv, _gic) { } + + void run() + { + using Signal_dispatcher = Genode::Signal_dispatcher_base; + + _device_tree.insert(&_gic); + _device_tree.insert(&_sys_regs); + _device_tree.insert(&_uart); + _vm.start(); + + while (true) { + if (_vm.active()) _vm.run(); + Genode::Signal s = _sig_rcv.wait_for_signal(); + if (_vm.active() && &_vm_handler != static_cast(s.context())) { + _vm.pause(); + _handle_vm(1); + } + static_cast(s.context())->dispatch(s.num()); + } + }; + + void dump() { _vm.dump(); } +}; + +int main() +{ + static Vmm vmm; + + try { + PINF("Start virtual machine ..."); + vmm.run(); + } catch(Vm::Exception &e) { + e.print(); + vmm.dump(); + return -1; + } + return 0; +} diff --git a/repos/os/src/server/vmm/target.mk b/repos/os/src/server/vmm/target.mk new file mode 100644 index 000000000..55274ea85 --- /dev/null +++ b/repos/os/src/server/vmm/target.mk @@ -0,0 +1,4 @@ +TARGET = vmm +REQUIRES = hw_arndale +LIBS = base +SRC_CC = main.cc diff --git a/repos/os/src/server/vmm/test/kernel/main.s b/repos/os/src/server/vmm/test/kernel/main.s new file mode 100644 index 000000000..529c00e5b --- /dev/null +++ b/repos/os/src/server/vmm/test/kernel/main.s @@ -0,0 +1,30 @@ +.section ".text.crt0" + + .global _start + _start: + + /* idle a little initially because U-Boot likes it this way */ + mov r8, r8 + mov r8, r8 + mov r8, r8 + mov r8, r8 + mov r8, r8 + mov r8, r8 + mov r8, r8 + mov r8, r8 + + /* zero-fill BSS segment */ + ldr r0, =_bss_start + ldr r1, =_bss_end + mov r2, #0 + 1: + cmp r1, r0 + ble 2f + str r2, [r0] + add r0, r0, #4 + b 1b + 2: + + hvc #0 + + 1: b 1b diff --git a/repos/os/src/server/vmm/test/kernel/target.mk b/repos/os/src/server/vmm/test/kernel/target.mk new file mode 100644 index 000000000..884e62808 --- /dev/null +++ b/repos/os/src/server/vmm/test/kernel/target.mk @@ -0,0 +1,3 @@ +TARGET = test-kernel +REQUIRES = hw_arndale +SRC_S = main.s diff --git a/tool/autopilot.list b/tool/autopilot.list index 6119604e0..0163fface 100644 --- a/tool/autopilot.list +++ b/tool/autopilot.list @@ -45,3 +45,4 @@ pthread virtualbox_auto_disk virtualbox_auto_share tz_vmm +vmm