/* * \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_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; extern Genode::addr_t _mt_master_context_begin; struct Kernel::Vm_irq : Kernel::Irq { Vm_irq(unsigned const irq) : Kernel::Irq(irq, *cpu_pool()->executing_cpu()) {} /** * 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() { /* 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); }