hw: introduce virtualization support for ARMv8

Ref #3553
This commit is contained in:
Stefan Kalkowski 2019-09-04 15:38:19 +02:00 committed by Norman Feske
parent f82714f341
commit af29dcf557
42 changed files with 1634 additions and 459 deletions

View File

@ -0,0 +1,109 @@
/*
* \brief CPU, PIC, and timer context of a virtual machine
* \author Stefan Kalkowski
* \date 2015-02-10
*/
/*
* Copyright (C) 2015-2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef _INCLUDE__SPEC__ARM_64__CPU__VM_STATE_VIRTUALIZATION_H_
#define _INCLUDE__SPEC__ARM_64__CPU__VM_STATE_VIRTUALIZATION_H_
/* Genode includes */
#include <cpu/cpu_state.h>
namespace Genode
{
/**
* CPU context of a virtual machine
*/
struct Vm_state;
using uint128_t = __uint128_t;
}
struct Genode::Vm_state : Genode::Cpu_state
{
Genode::uint64_t pstate { 0 };
Genode::uint64_t exception_type { 0 };
Genode::uint64_t esr_el2 { 0 };
/** Fpu registers **/
Genode::uint128_t q[32] { 0 };
Genode::uint32_t fpcr { 0 };
Genode::uint32_t fpsr { 0 };
Genode::uint64_t elr_el1 { 0 };
Genode::uint64_t sp_el1 { 0 };
Genode::uint32_t spsr_el1 { 0 };
Genode::uint32_t esr_el1 { 0 };
Genode::uint64_t sctlr_el1 { 0 };
Genode::uint64_t actlr_el1 { 0 };
Genode::uint64_t vbar_el1 { 0 };
Genode::uint32_t cpacr_el1 { 0 };
Genode::uint32_t afsr0_el1 { 0 };
Genode::uint32_t afsr1_el1 { 0 };
Genode::uint32_t contextidr_el1 { 0 };
Genode::uint64_t ttbr0_el1 { 0 };
Genode::uint64_t ttbr1_el1 { 0 };
Genode::uint64_t tcr_el1 { 0 };
Genode::uint64_t mair_el1 { 0 };
Genode::uint64_t amair_el1 { 0 };
Genode::uint64_t far_el1 { 0 };
Genode::uint64_t par_el1 { 0 };
Genode::uint64_t tpidrro_el0 { 0 };
Genode::uint64_t tpidr_el0 { 0 };
Genode::uint64_t tpidr_el1 { 0 };
Genode::uint64_t vmpidr_el2 { 0 };
Genode::uint64_t far_el2 { 0 };
Genode::uint64_t hpfar_el2 { 0 };
/**
* Timer related registers
*/
struct Timer {
Genode::uint64_t offset { 0 };
Genode::uint64_t compare { 0 };
Genode::uint32_t control { 0 };
Genode::uint32_t kcontrol { 0 };
bool irq { false };
} timer {};
/**
* Interrupt related values
*/
struct Pic
{
unsigned last_irq { 1023 };
unsigned virtual_irq { 1023 };
} irqs {};
/**************************
** Platform information **
**************************/
Genode::uint64_t id_aa64isar0_el1 { 0 };
Genode::uint64_t id_aa64isar1_el1 { 0 };
Genode::uint64_t id_aa64mmfr0_el1 { 0 };
Genode::uint64_t id_aa64mmfr1_el1 { 0 };
Genode::uint64_t id_aa64mmfr2_el1 { 0 };
Genode::uint64_t id_aa64pfr0_el1 { 0 };
Genode::uint64_t id_aa64pfr1_el1 { 0 };
Genode::uint64_t id_aa64zfr0_el1 { 0 };
Genode::uint32_t ccsidr_inst_el1[7] { 0 };
Genode::uint32_t ccsidr_data_el1[7] { 0 };
Genode::uint64_t clidr_el1 { 0 };
};
#endif /* _INCLUDE__SPEC__ARM_64__CPU__VM_STATE_VIRTUALIZATION_H_ */

View File

@ -14,8 +14,8 @@ SRC_CC += spec/arm/gicv2.cc
SRC_CC += spec/arm_v7/virtualization/kernel/vm.cc
SRC_CC += spec/arm/virtualization/platform_services.cc
SRC_CC += spec/arm/virtualization/vm_session_component.cc
SRC_CC += spec/arm/vm_session_component.cc
SRC_CC += vm_session_common.cc
SRC_CC += vm_session_component.cc
# add assembly sources
SRC_S += spec/arm_v7/virtualization/exception_vector.s

View File

@ -5,7 +5,8 @@ SRC_CC += kernel/vm_thread_on.cc
SRC_CC += spec/arm_v7/trustzone/kernel/vm.cc
SRC_CC += spec/arm_v7/trustzone/platform_services.cc
SRC_CC += spec/arm_v7/trustzone/vm_session_component.cc
SRC_CC += spec/arm_v7/vm_session_component.cc
SRC_CC += vm_session_common.cc
SRC_CC += vm_session_component.cc
SRC_S += spec/arm_v7/trustzone/exception_vector.s

View File

@ -13,10 +13,11 @@ SRC_CC += kernel/vm_thread_on.cc
SRC_CC += spec/arm/generic_timer.cc
SRC_CC += spec/arm/gicv2.cc
SRC_CC += spec/arm_v7/virtualization/kernel/vm.cc
SRC_CC += spec/arm/virtualization/gicv2.cc
SRC_CC += spec/arm/virtualization/platform_services.cc
SRC_CC += spec/arm/virtualization/vm_session_component.cc
SRC_CC += spec/arm/vm_session_component.cc
SRC_CC += vm_session_common.cc
SRC_CC += vm_session_component.cc
# add assembly sources
SRC_S += spec/arm_v7/virtualization/exception_vector.s

View File

@ -17,7 +17,8 @@ SRC_CC += spec/arm/imx_tzic.cc
SRC_CC += spec/arm_v7/trustzone/kernel/vm.cc
SRC_CC += spec/arm_v7/trustzone/platform_services.cc
SRC_CC += spec/arm_v7/trustzone/vm_session_component.cc
SRC_CC += spec/arm_v7/vm_session_component.cc
SRC_CC += vm_session_common.cc
SRC_CC += vm_session_component.cc
# add assembly sources
SRC_S += spec/arm_v7/trustzone/exception_vector.s

View File

@ -1,10 +1,10 @@
INC_DIR += $(REP_DIR)/src/core/spec/imx8q_evk
INC_DIR += $(REP_DIR)/src/core/spec/arm_v8
INC_DIR += $(REP_DIR)/src/core/spec/arm/virtualization
# add C++ sources
SRC_CC += kernel/cpu_mp.cc
SRC_CC += kernel/vm_thread_off.cc
SRC_CC += platform_services.cc
SRC_CC += kernel/vm_thread_on.cc
SRC_CC += spec/64bit/memory_map.cc
SRC_CC += spec/arm/generic_timer.cc
SRC_CC += spec/arm/gicv3.cc
@ -13,10 +13,16 @@ SRC_CC += spec/arm/platform_support.cc
SRC_CC += spec/arm_v8/cpu.cc
SRC_CC += spec/arm_v8/kernel/cpu.cc
SRC_CC += spec/arm_v8/kernel/thread.cc
SRC_CC += spec/arm_v8/virtualization/kernel/vm.cc
SRC_CC += spec/arm/virtualization/platform_services.cc
SRC_CC += spec/arm/virtualization/vm_session_component.cc
SRC_CC += vm_session_common.cc
SRC_CC += vm_session_component.cc
#add assembly sources
SRC_S += spec/arm_v8/exception_vector.s
SRC_S += spec/arm_v8/crt0.s
SRC_S += spec/arm_v8/virtualization/exception_vector.s
vpath spec/64bit/memory_map.cc $(BASE_DIR)/../base-hw/src/lib/hw

View File

@ -35,7 +35,10 @@ SRC_CC += spec/x86_64/muen/platform_services.cc
SRC_CC += spec/x86_64/muen/platform_support.cc
SRC_CC += spec/x86_64/muen/sinfo_instance.cc
SRC_CC += spec/x86_64/muen/timer.cc
SRC_CC += spec/x86_64/muen/vm_session_component.cc
SRC_CC += spec/x86_64/platform_support_common.cc
SRC_CC += vm_session_common.cc
SRC_CC += vm_session_component.cc
SRC_CC += spec/64bit/memory_map.cc

View File

@ -11,6 +11,7 @@
* under the terms of the GNU Affero General Public License version 3.
*/
#include <hw/spec/arm_64/memory_map.h>
#include <platform.h>
using Board::Cpu;
@ -57,11 +58,54 @@ static inline void prepare_non_secure_world()
}
static inline void prepare_hypervisor()
static inline void prepare_hypervisor(Cpu::Ttbr::access_t const ttbr)
{
Cpu::Hcr::access_t scr = Cpu::Hcr::read();
Cpu::Hcr::Rw::set(scr, 1); /* exec in aarch64 */
Cpu::Hcr::write(scr);
using namespace Hw::Mm;
/* forbid trace access */
Cpu::Cptr_el2::access_t cptr = Cpu::Cptr_el2::read();
Cpu::Cptr_el2::Tta::set(cptr, 1);
Cpu::Cptr_el2::write(cptr);
/* allow physical counter/timer access without trapping */
Cpu::Cnthctl_el2::write(0b111);
/* forbid any 32bit access to coprocessor/sysregs */
Cpu::Hstr_el2::write(0xffff);
Cpu::Hcr_el2::access_t hcr = Cpu::Hcr_el2::read();
Cpu::Hcr_el2::Rw::set(hcr, 1); /* exec in aarch64 */
Cpu::Hcr_el2::write(hcr);
/* set hypervisor exception vector */
Cpu::Vbar_el2::write(el2_addr(hypervisor_exception_vector().base));
Genode::addr_t const stack_el2 = el2_addr(hypervisor_stack().base +
hypervisor_stack().size);
/* set hypervisor's translation table */
Cpu::Ttbr0_el2::write(ttbr);
Cpu::Tcr_el2::access_t tcr_el2 = 0;
Cpu::Tcr_el2::T0sz::set(tcr_el2, 25);
Cpu::Tcr_el2::Irgn0::set(tcr_el2, 1);
Cpu::Tcr_el2::Orgn0::set(tcr_el2, 1);
Cpu::Tcr_el2::Sh0::set(tcr_el2, 0b10);
/* prepare MMU usage by hypervisor code */
Cpu::Tcr_el2::write(tcr_el2);
/* set memory attributes in indirection register */
Cpu::Mair::access_t mair = 0;
Cpu::Mair::Attr0::set(mair, Cpu::Mair::NORMAL_MEMORY_UNCACHED);
Cpu::Mair::Attr1::set(mair, Cpu::Mair::DEVICE_MEMORY);
Cpu::Mair::Attr2::set(mair, Cpu::Mair::NORMAL_MEMORY_CACHED);
Cpu::Mair::Attr3::set(mair, Cpu::Mair::DEVICE_MEMORY);
Cpu::Mair_el2::write(mair);
Cpu::Vtcr_el2::access_t vtcr = 0;
Cpu::Vtcr_el2::T0sz::set(vtcr, 25);
Cpu::Vtcr_el2::Sl0::set(vtcr, 1); /* set to starting level 1 */
Cpu::Vtcr_el2::write(vtcr);
Cpu::Spsr::access_t pstate = 0;
Cpu::Spsr::Sp::set(pstate, 1); /* select non-el0 stack pointer */
@ -72,12 +116,22 @@ static inline void prepare_hypervisor()
Cpu::Spsr::D::set(pstate, 1);
Cpu::Spsr_el2::write(pstate);
Cpu::Sctlr::access_t sctlr = Cpu::Sctlr_el2::read();
Cpu::Sctlr::M::set(sctlr, 1);
Cpu::Sctlr::A::set(sctlr, 0);
Cpu::Sctlr::C::set(sctlr, 1);
Cpu::Sctlr::Sa::set(sctlr, 0);
Cpu::Sctlr::I::set(sctlr, 1);
Cpu::Sctlr::Wxn::set(sctlr, 0);
Cpu::Sctlr_el2::write(sctlr);
asm volatile("mov x0, sp \n"
"msr sp_el1, x0 \n"
"adr x0, 1f \n"
"msr elr_el2, x0 \n"
"mov sp, %0 \n"
"eret \n"
"1:");
"1:": : "r"(stack_el2): "x0");
}
@ -87,13 +141,16 @@ unsigned Bootstrap::Platform::enable_mmu()
bool primary = primary_cpu;
if (primary) primary_cpu = false;
::Board::Pic pic __attribute__((unused)) {};
Cpu::Ttbr::access_t ttbr =
Cpu::Ttbr::Baddr::masked((Genode::addr_t)core_pd->table_base);
while (Cpu::current_privilege_level() > Cpu::Current_el::EL1) {
if (Cpu::current_privilege_level() == Cpu::Current_el::EL3)
if (Cpu::current_privilege_level() == Cpu::Current_el::EL3) {
prepare_non_secure_world();
else
prepare_hypervisor();
} else {
::Board::Pic pic __attribute__((unused)) {};
prepare_hypervisor(ttbr);
}
}
/* primary cpu wakes up all others */
@ -102,6 +159,9 @@ unsigned Bootstrap::Platform::enable_mmu()
/* enable performance counter for user-land */
Cpu::Pmuserenr_el0::write(0b1111);
/* enable user-level access of physical/virtual counter */
Cpu::Cntkctl_el1::write(0b11);
Cpu::Vbar_el1::write(Hw::Mm::supervisor_exception_vector().base);
/* set memory attributes in indirection register */
@ -110,9 +170,8 @@ unsigned Bootstrap::Platform::enable_mmu()
Cpu::Mair::Attr1::set(mair, Cpu::Mair::DEVICE_MEMORY);
Cpu::Mair::Attr2::set(mair, Cpu::Mair::NORMAL_MEMORY_CACHED);
Cpu::Mair::Attr3::set(mair, Cpu::Mair::DEVICE_MEMORY);
Cpu::Mair::write(mair);
Cpu::Mair_el1::write(mair);
Cpu::Ttbr::access_t ttbr = Cpu::Ttbr::Baddr::masked((Genode::addr_t)core_pd->table_base);
Cpu::Ttbr0_el1::write(ttbr);
Cpu::Ttbr1_el1::write(ttbr);
@ -129,13 +188,14 @@ unsigned Bootstrap::Platform::enable_mmu()
Cpu::Tcr_el1::As::set(tcr, 1);
Cpu::Tcr_el1::write(tcr);
Cpu::Sctlr_el1::access_t sctlr = Cpu::Sctlr_el1::read();
Cpu::Sctlr_el1::C::set(sctlr, 1);
Cpu::Sctlr_el1::I::set(sctlr, 1);
Cpu::Sctlr_el1::A::set(sctlr, 0);
Cpu::Sctlr_el1::M::set(sctlr, 1);
Cpu::Sctlr_el1::Sa0::set(sctlr, 1);
Cpu::Sctlr_el1::Sa::set(sctlr, 0);
Cpu::Sctlr::access_t sctlr = Cpu::Sctlr_el1::read();
Cpu::Sctlr::C::set(sctlr, 1);
Cpu::Sctlr::I::set(sctlr, 1);
Cpu::Sctlr::A::set(sctlr, 0);
Cpu::Sctlr::M::set(sctlr, 1);
Cpu::Sctlr::Sa0::set(sctlr, 1);
Cpu::Sctlr::Sa::set(sctlr, 0);
Cpu::Sctlr::Uct::set(sctlr, 1);
Cpu::Sctlr_el1::write(sctlr);
return 0;

View File

@ -22,6 +22,8 @@ namespace Genode { class Vm_state; }
#include <kernel/pd.h>
#include <kernel/signal_receiver.h>
#include <board.h>
namespace Kernel
{
/**
@ -36,19 +38,22 @@ class Kernel::Vm : public Cpu_job,
{
private:
using State = Board::Vm_state;
/*
* Noncopyable
*/
Vm(Vm const &);
Vm &operator = (Vm const &);
enum State { ACTIVE, INACTIVE };
enum Scheduler_state { ACTIVE, INACTIVE };
unsigned _id = 0;
Genode::Vm_state * const _state;
Signal_context * const _context;
void * const _table;
State _scheduled = INACTIVE;
unsigned _id = 0;
State & _state;
Signal_context & _context;
void * const _table;
Scheduler_state _scheduled = INACTIVE;
Board::Vcpu_context _vcpu_context;
public:
@ -59,9 +64,10 @@ class Kernel::Vm : public Cpu_job,
* \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,
void * const table);
Vm(unsigned cpu,
State & state,
Signal_context & context,
void * const table);
~Vm();
@ -85,12 +91,13 @@ class Kernel::Vm : public Cpu_job,
* \retval cap id when successful, otherwise invalid cap id
*/
static capid_t syscall_create(Genode::Kernel_object<Vm> & vm,
unsigned cpu,
void * const state,
capid_t const signal_context_id,
void * const table)
{
return call(call_id_new_vm(), (Call_arg)&vm, (Call_arg)state,
(Call_arg)table, signal_context_id);
return call(call_id_new_vm(), (Call_arg)&vm, (Call_arg)cpu,
(Call_arg)state, (Call_arg)table, signal_context_id);
}
/**

View File

@ -18,13 +18,14 @@
void Kernel::Thread::_call_new_vm()
{
Signal_context * context =
pd().cap_tree().find<Signal_context>(user_arg_4());
pd().cap_tree().find<Signal_context>(user_arg_5());
if (!context) {
user_arg_0(cap_id_invalid());
return;
}
_call_new<Vm>((void*)user_arg_2(), context, (void*)user_arg_3());
_call_new<Vm>((unsigned)user_arg_2(), *(Board::Vm_state*)user_arg_3(),
*context, (void*)user_arg_4());
}

View File

@ -0,0 +1,33 @@
/*
* \brief Board driver
* \author Stefan Kalkowski
* \date 2019-11-11
*/
/*
* Copyright (C) 2019 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef _CORE__SPEC__ARM_TRUSTZONE_BOARD_H_
#define _CORE__SPEC__ARM_TRUSTZONE_BOARD_H_
#include <spec/arm/cpu/vm_state_trustzone.h>
namespace Kernel { class Cpu; }
namespace Board {
using Genode::Vm_state;
enum { VCPU_MAX = 1 };
struct Vm_page_table {};
struct Vm_page_table_array {};
struct Pic : Hw::Pic { struct Virtual_context {}; };
struct Vcpu_context { Vcpu_context(Kernel::Cpu &) {} };
}
#endif /* _CORE__SPEC__ARM_TRUSTZONE_BOARD_H_ */

View File

@ -0,0 +1,62 @@
/*
* \brief Gicv2 with virtualization extensions
* \author Stefan Kalkowski
* \date 2019-09-02
*/
/*
* Copyright (C) 2019 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#include <util/mmio.h>
#include <platform.h>
#include <spec/arm/virtualization/gicv2.h>
using Board::Pic;
Pic::Gich::Gich()
: Genode::Mmio(Genode::Platform::mmio_to_virt(Board::Cpu_mmio::IRQ_CONTROLLER_VT_CTRL_BASE)) { }
bool Pic::ack_virtual_irq(Pic::Virtual_context & c)
{
c.misr = _gich.read<Gich::Gich_misr >();
c.vmcr = _gich.read<Gich::Gich_vmcr >();
c.apr = _gich.read<Gich::Gich_apr >();
c.eisr = _gich.read<Gich::Gich_eisr0 >();
c.elrsr = _gich.read<Gich::Gich_elrsr0>();
c.lr = _gich.read<Gich::Gich_lr0 >();
_gich.write<Gich::Gich_hcr>(0);
if (c.eisr & 1) {
c.lr = 0;
c.elrsr = 0xffffffff;
c.misr = 0;
c.eisr = 0;
return true;
}
return false;
}
void Pic::insert_virtual_irq(Pic::Virtual_context & c, unsigned irq)
{
enum { SPURIOUS = 1023 };
if (irq != SPURIOUS && !c.lr) {
c.elrsr &= 0x7ffffffe;
c.lr = irq | 1 << 28 | 1 << 19;
}
_gich.write<Gich::Gich_misr >(c.misr);
_gich.write<Gich::Gich_vmcr >(c.vmcr);
_gich.write<Gich::Gich_apr >(c.apr);
_gich.write<Gich::Gich_elrsr0>(c.elrsr);
_gich.write<Gich::Gich_lr0 >(c.lr);
_gich.write<Gich::Gich_hcr>(0b1);
}

View File

@ -0,0 +1,55 @@
/*
* \brief Gicv2 with virtualization extensions
* \author Stefan Kalkowski
* \date 2019-09-02
*/
/*
* Copyright (C) 2019 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef _CORE__SPEC__ARM__VIRTUALIZATION__GICV2_H_
#define _CORE__SPEC__ARM__VIRTUALIZATION__GICV2_H_
#include <hw/spec/arm/gicv2.h>
namespace Board { struct Pic; };
class Board::Pic : public Hw::Gicv2
{
private:
struct Gich : 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> { };
struct Gich_lr0 : Register<0x100, 32> { };
Gich();
} _gich {};
public:
struct Virtual_context
{
Genode::uint32_t lr { 0 };
Genode::uint32_t apr { 0 };
Genode::uint32_t vmcr { 0x4c0000 };
Genode::uint32_t misr { 0 };
Genode::uint32_t eisr { 0 };
Genode::uint32_t elrsr { 0xffffffff };
};
bool ack_virtual_irq(Virtual_context & c);
void insert_virtual_irq(Virtual_context & c, unsigned irq);
};
#endif /* _CORE__SPEC__ARM__VIRTUALIZATION__GICV2_H_ */

View File

@ -0,0 +1,56 @@
/*
* \brief Gicv2 with virtualization extensions
* \author Stefan Kalkowski
* \date 2019-09-02
*/
/*
* Copyright (C) 2019 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef _CORE__SPEC__ARM__VIRTUALIZATION__GICV3_H_
#define _CORE__SPEC__ARM__VIRTUALIZATION__GICV3_H_
#include <hw/spec/arm/gicv3.h>
namespace Board { class Pic; };
class Board::Pic : public Hw::Pic
{
public:
struct Virtual_context {
Genode::uint64_t lr { 0 };
Genode::uint32_t apr { 0 };
Genode::uint32_t vmcr { 0x4c0000 };
Genode::uint32_t misr { 0 };
Genode::uint32_t eisr { 0 };
Genode::uint32_t elrsr { 0xffffffff };
};
bool ack_virtual_irq(Virtual_context & c)
{
if (!(c.eisr & 1)) return false;
c.lr = 0;
c.elrsr = 0xffffffff;
c.misr = 0;
c.eisr = 0;
return true;
}
void insert_virtual_irq(Virtual_context & c, unsigned irq)
{
enum { SPURIOUS = 1023 };
if (irq == SPURIOUS || c.lr) return;
c.lr = irq | 1ULL << 41 | 1ULL << 60 | 1ULL << 62;
}
};
#endif /* _CORE__SPEC__ARM__VIRTUALIZATION__GICV3_H_ */

View File

@ -37,9 +37,19 @@ void Genode::platform_add_local_services(Rpc_entrypoint &ep,
using namespace Genode;
map_local(Platform::core_phys_addr((addr_t)&hypervisor_exception_vector),
Hw::Mm::hypervisor_exception_vector().base, 1,
Hw::Mm::hypervisor_exception_vector().base,
Hw::Mm::hypervisor_exception_vector().size / get_page_size(),
Hw::PAGE_FLAGS_KERN_TEXT);
void * stack = nullptr;
assert(platform().ram_alloc().alloc_aligned(Hw::Mm::hypervisor_stack().size,
(void**)&stack,
get_page_size_log2()).ok());
map_local((addr_t)stack,
Hw::Mm::hypervisor_stack().base,
Hw::Mm::hypervisor_stack().size / get_page_size(),
Hw::PAGE_FLAGS_KERN_DATA);
static Vm_root vm_root(ep, sh, core_env().ram_allocator(),
core_env().local_rm(), trace_sources);
static Core_service<Vm_session_component> vm_service(services, vm_root);

View File

@ -16,8 +16,9 @@
/* core includes */
#include <kernel/core_interface.h>
#include <spec/arm/virtualization/vm_session_component.h>
#include <vm_session_component.h>
#include <platform.h>
#include <cpu_thread_component.h>
#include <core_env.h>
using namespace Genode;
@ -26,14 +27,6 @@ static Core_mem_allocator & cma() {
return static_cast<Core_mem_allocator&>(platform().core_mem_alloc()); }
void Vm_session_component::_exception_handler(Signal_context_capability handler, Vcpu_id)
{
if (!_kobj.create(_ds_addr, Capability_space::capid(handler),
cma().phys_addr(&_table)))
Genode::warning("Cannot instantiate vm kernel object, invalid signal context?");
}
void Vm_session_component::_attach(addr_t phys_addr, addr_t vm_addr, size_t size)
{
using namespace Hw;
@ -78,8 +71,8 @@ void * Vm_session_component::_alloc_table()
{
void * table;
/* get some aligned space for the translation table */
if (!cma().alloc_aligned(sizeof(Table), (void**)&table,
Table::ALIGNM_LOG2).ok()) {
if (!cma().alloc_aligned(sizeof(Board::Vm_page_table), (void**)&table,
Board::Vm_page_table::ALIGNM_LOG2).ok()) {
error("failed to allocate kernel object");
throw Insufficient_ram_quota();
}
@ -101,19 +94,10 @@ Vm_session_component::Vm_session_component(Rpc_entrypoint &ds_ep,
_constrained_md_ram_alloc(ram_alloc, _ram_quota_guard(), _cap_quota_guard()),
_sliced_heap(_constrained_md_ram_alloc, region_map),
_region_map(region_map),
_table(*construct_at<Table>(_alloc_table())),
_table_array(*(new (cma()) Array([this] (void * virt) {
_table(*construct_at<Board::Vm_page_table>(_alloc_table())),
_table_array(*(new (cma()) Board::Vm_page_table_array([this] (void * virt) {
return (addr_t)cma().phys_addr(virt);})))
{
_ds_cap = _constrained_md_ram_alloc.alloc(_ds_size(), Genode::Cache_attribute::UNCACHED);
try {
_ds_addr = region_map.attach(_ds_cap);
} catch (...) {
_constrained_md_ram_alloc.free(_ds_cap);
throw;
}
/* configure managed VM area */
_map.add_range(0, 0UL - 0x1000);
_map.add_range(0UL - 0x1000, 0x1000);
@ -133,9 +117,12 @@ Vm_session_component::~Vm_session_component()
}
/* free region in allocator */
if (_ds_cap.valid()) {
_region_map.detach(_ds_addr);
_constrained_md_ram_alloc.free(_ds_cap);
for (unsigned i = 0; i < _id_alloc; i++) {
Vcpu & vcpu = _vcpus[i];
if (vcpu.ds_cap.valid()) {
_region_map.detach(vcpu.ds_addr);
_constrained_md_ram_alloc.free(vcpu.ds_cap);
}
}
/* free guest-to-host page tables */

View File

@ -1,40 +0,0 @@
/*
* \brief VM session component for 'base-hw'
* \author Stefan Kalkowski
* \date 2012-10-08
*/
/*
* Copyright (C) 2012-2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#include <vm_session_component.h>
#include <platform.h>
#include <core_env.h>
using namespace Genode;
addr_t Vm_session_component::_alloc_ds()
{
addr_t addr;
if (platform().ram_alloc().alloc_aligned(_ds_size(), (void**)&addr,
get_page_size_log2()).error())
throw Insufficient_ram_quota();
return addr;
}
void Vm_session_component::_run(Vcpu_id)
{
if (_kobj.constructed()) Kernel::run_vm(*_kobj);
}
void Vm_session_component::_pause(Vcpu_id)
{
if (_kobj.constructed()) Kernel::pause_vm(*_kobj);
}

View File

@ -20,13 +20,16 @@
using namespace Kernel;
Kernel::Vm::Vm(void * const state,
Kernel::Signal_context * const context,
void * const /* table */)
Kernel::Vm::Vm(unsigned,
Genode::Vm_state & state,
Kernel::Signal_context & context,
void * const)
:
Cpu_job(Cpu_priority::MIN, 0),
_state((Genode::Vm_state *)state),
_context(context), _table(0)
_state(state),
_context(context),
_table(0),
_vcpu_context(cpu_pool().primary_cpu())
{
affinity(cpu_pool().primary_cpu());
}
@ -37,17 +40,17 @@ Kernel::Vm::~Vm() {}
void Vm::exception(Cpu & cpu)
{
switch(_state->cpu_exception) {
case Genode::Cpu_state::INTERRUPT_REQUEST:
switch(_state.cpu_exception) {
case Genode::Cpu_state::INTERRUPT_REQUEST: [[fallthrough]]
case Genode::Cpu_state::FAST_INTERRUPT_REQUEST:
_interrupt(cpu.id());
return;
case Genode::Cpu_state::DATA_ABORT:
_state->dfar = Cpu::Dfar::read();
_state.dfar = Cpu::Dfar::read();
[[fallthrough]];
default:
pause();
_context->submit(1);
_context.submit(1);
}
}
@ -55,22 +58,21 @@ void Vm::exception(Cpu & cpu)
bool secure_irq(unsigned const i);
extern "C" void monitor_mode_enter_normal_world(Cpu::Context*, void*);
extern "C" void monitor_mode_enter_normal_world(Genode::Vm_state&, void*);
extern void * kernel_stack;
void Vm::proceed(Cpu & cpu)
{
unsigned const irq = _state->irq_injection;
unsigned const irq = _state.irq_injection;
if (irq) {
if (cpu.pic().secure(irq)) {
Genode::raw("Refuse to inject secure IRQ into VM");
} else {
cpu.pic().trigger(irq);
_state->irq_injection = 0;
_state.irq_injection = 0;
}
}
monitor_mode_enter_normal_world(reinterpret_cast<Cpu::Context*>(_state),
(void*) cpu.stack_start());
monitor_mode_enter_normal_world(_state, (void*) cpu.stack_start());
}

View File

@ -11,23 +11,44 @@
* under the terms of the GNU Affero General Public License version 3.
*/
#include <kernel/core_interface.h>
/* Genode includes */
#include <util/construct_at.h>
/* core includes */
#include <vm_session_component.h>
#include <core_env.h>
#include <platform.h>
using namespace Genode;
void Vm_session_component::_exception_handler(Signal_context_capability handler, Vcpu_id)
static Board::Vm_page_table_array & dummy_array()
{
if (!_kobj.create(_ds_addr, Capability_space::capid(handler), nullptr)) {
warning("Cannot instantiate vm kernel object twice,"
"or invalid signal context?");
}
static Board::Vm_page_table_array a;
return a;
}
Vm_session_component::Vm_session_component(Rpc_entrypoint &ds_ep,
void Vm_session_component::_attach(addr_t, addr_t, size_t) { }
void Vm_session_component::_attach_vm_memory(Dataspace_component &,
addr_t const,
Attach_attr const) { }
void Vm_session_component::attach_pic(addr_t) { }
void Vm_session_component::_detach_vm_memory(addr_t, size_t) { }
void * Vm_session_component::_alloc_table()
{
static Board::Vm_page_table table;
return (void*) &table;
}
Vm_session_component::Vm_session_component(Rpc_entrypoint &ep,
Resources resources,
Label const &,
Diag,
@ -37,26 +58,32 @@ Vm_session_component::Vm_session_component(Rpc_entrypoint &ds_ep,
:
Ram_quota_guard(resources.ram_quota),
Cap_quota_guard(resources.cap_quota),
_ds_ep(&ds_ep),
_ep(ep),
_constrained_md_ram_alloc(ram_alloc, _ram_quota_guard(), _cap_quota_guard()),
_region_map(region_map)
{
_ds_cap = _constrained_md_ram_alloc.alloc(_ds_size(), Genode::Cache_attribute::UNCACHED);
try {
_ds_addr = region_map.attach(_ds_cap);
} catch (...) {
_constrained_md_ram_alloc.free(_ds_cap);
throw;
}
}
_sliced_heap(_constrained_md_ram_alloc, region_map),
_region_map(region_map),
_table(*construct_at<Board::Vm_page_table>(_alloc_table())),
_table_array(dummy_array()) { }
Vm_session_component::~Vm_session_component()
{
/* detach all regions */
while (true) {
addr_t out_addr = 0;
if (!_map.any_block_addr(&out_addr))
break;
detach(out_addr);
}
/* free region in allocator */
if (_ds_cap.valid()) {
_region_map.detach(_ds_addr);
_constrained_md_ram_alloc.free(_ds_cap);
for (unsigned i = 0; i < _id_alloc; i++) {
Vcpu & vcpu = _vcpus[i];
if (vcpu.ds_cap.valid()) {
_region_map.detach(vcpu.ds_addr);
_constrained_md_ram_alloc.free(vcpu.ds_cap);
}
}
}

View File

@ -1,95 +0,0 @@
/*
* \brief Core-specific instance of the VM session interface
* \author Stefan Kalkowski
* \date 2012-10-08
*/
/*
* Copyright (C) 2012-2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef _CORE__SPEC__ARM_V7__TRUSTZONE__VM_SESSION_COMPONENT_H_
#define _CORE__SPEC__ARM_V7__TRUSTZONE__VM_SESSION_COMPONENT_H_
/* Genode includes */
#include <base/allocator.h>
#include <base/session_object.h>
#include <vm_session/vm_session.h>
#include <dataspace/capability.h>
#include <trace/source_registry.h>
/* Core includes */
#include <object.h>
#include <kernel/vm.h>
namespace Genode {
class Vm_session_component;
}
class Genode::Vm_session_component
:
private Ram_quota_guard,
private Cap_quota_guard,
public Rpc_object<Vm_session, Vm_session_component>
{
private:
/*
* Noncopyable
*/
Vm_session_component(Vm_session_component const &);
Vm_session_component &operator = (Vm_session_component const &);
Rpc_entrypoint *_ds_ep;
Constrained_ram_allocator _constrained_md_ram_alloc;
Region_map &_region_map;
Ram_dataspace_capability _ds_cap { };
Region_map::Local_addr _ds_addr { 0 };
Kernel_object<Kernel::Vm> _kobj {};
static size_t _ds_size() {
return align_addr(sizeof(Cpu_state_modes),
get_page_size_log2()); }
addr_t _alloc_ds();
protected:
Ram_quota_guard &_ram_quota_guard() { return *this; }
Cap_quota_guard &_cap_quota_guard() { return *this; }
public:
using Ram_quota_guard::upgrade;
using Cap_quota_guard::upgrade;
using Rpc_object<Vm_session, Vm_session_component>::cap;
Vm_session_component(Rpc_entrypoint &, Resources, Label const &,
Diag, Ram_allocator &ram, Region_map &,
unsigned priority, Trace::Source_registry &);
~Vm_session_component();
/**************************
** Vm session interface **
**************************/
Dataspace_capability _cpu_state(Vcpu_id) { return _ds_cap; }
void _exception_handler(Signal_context_capability handler, Vcpu_id);
void _run(Vcpu_id);
void _pause(Vcpu_id);
void attach(Dataspace_capability, addr_t, Attach_attr) override {
warning("Not implemented for TrustZone case"); }
void attach_pic(addr_t /* vm_addr */) override {
warning("Not implemented for TrustZone case"); }
void detach(addr_t /* vm_addr */, size_t /* size */) override {
warning("Not implemented for TrustZone case"); }
unsigned _create_vcpu(Thread_capability) { return 0; }
};
#endif /* _CORE__SPEC__ARM_V7__TRUSTZONE__VM_SESSION_COMPONENT_H_ */

View File

@ -41,7 +41,7 @@ using namespace Kernel;
extern "C" void kernel();
extern void * kernel_stack;
extern "C" void hypervisor_enter_vm(Cpu::Context*);
extern "C" void hypervisor_enter_vm(Genode::Vm_state&);
struct Host_context {
addr_t sp;
@ -103,18 +103,18 @@ struct Kernel::Virtual_pic : Genode::Mmio
/**
* Save the virtual interrupt controller state to VM state
*/
static void save (Genode::Vm_state *s)
static void save (Genode::Vm_state &s)
{
s->gic_hcr = pic().read<Gich_hcr >();
s->gic_misr = pic().read<Gich_misr >();
s->gic_vmcr = pic().read<Gich_vmcr >();
s->gic_apr = pic().read<Gich_apr >();
s->gic_eisr = pic().read<Gich_eisr0 >();
s->gic_elrsr0 = pic().read<Gich_elrsr0>();
s->gic_lr[0] = pic().read<Gich_lr<0> >();
s->gic_lr[1] = pic().read<Gich_lr<1> >();
s->gic_lr[2] = pic().read<Gich_lr<2> >();
s->gic_lr[3] = pic().read<Gich_lr<3> >();
s.gic_hcr = pic().read<Gich_hcr >();
s.gic_misr = pic().read<Gich_misr >();
s.gic_vmcr = pic().read<Gich_vmcr >();
s.gic_apr = pic().read<Gich_apr >();
s.gic_eisr = pic().read<Gich_eisr0 >();
s.gic_elrsr0 = pic().read<Gich_elrsr0>();
s.gic_lr[0] = pic().read<Gich_lr<0> >();
s.gic_lr[1] = pic().read<Gich_lr<1> >();
s.gic_lr[2] = pic().read<Gich_lr<2> >();
s.gic_lr[3] = pic().read<Gich_lr<3> >();
/* disable virtual PIC CPU interface */
pic().write<Gich_hcr>(0);
@ -123,17 +123,17 @@ struct Kernel::Virtual_pic : Genode::Mmio
/**
* Load the virtual interrupt controller state from VM state
*/
static void load (Genode::Vm_state *s)
static void load (Genode::Vm_state &s)
{
pic().write<Gich_hcr >(s->gic_hcr );
pic().write<Gich_misr >(s->gic_misr);
pic().write<Gich_vmcr >(s->gic_vmcr);
pic().write<Gich_apr >(s->gic_apr );
pic().write<Gich_elrsr0>(s->gic_elrsr0);
pic().write<Gich_lr<0> >(s->gic_lr[0]);
pic().write<Gich_lr<1> >(s->gic_lr[1]);
pic().write<Gich_lr<2> >(s->gic_lr[2]);
pic().write<Gich_lr<3> >(s->gic_lr[3]);
pic().write<Gich_hcr >(s.gic_hcr );
pic().write<Gich_misr >(s.gic_misr);
pic().write<Gich_vmcr >(s.gic_vmcr);
pic().write<Gich_apr >(s.gic_apr );
pic().write<Gich_elrsr0>(s.gic_elrsr0);
pic().write<Gich_lr<0> >(s.gic_lr[0]);
pic().write<Gich_lr<1> >(s.gic_lr[1]);
pic().write<Gich_lr<2> >(s.gic_lr[2]);
pic().write<Gich_lr<3> >(s.gic_lr[3]);
}
};
@ -166,25 +166,25 @@ struct Kernel::Virtual_timer
/**
* Save the virtual timer state to VM state
*/
static void save(Genode::Vm_state *s)
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));
"=r" (s.timer_val), "=r" (s.timer_ctrl));
}
/**
* Load the virtual timer state from VM state
*/
static void load(Genode::Vm_state *s)
static void load(Genode::Vm_state &s)
{
if (s->timer_irq) timer().irq.enable();
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));
"r" (s.timer_val), "r" (s.timer_ctrl));
}
};
@ -205,14 +205,16 @@ static Vmid_allocator &alloc()
}
Kernel::Vm::Vm(void * const state,
Kernel::Signal_context * const context,
Kernel::Vm::Vm(unsigned, /* FIXME: smp support */
Genode::Vm_state & state,
Kernel::Signal_context & context,
void * const table)
: Cpu_job(Cpu_priority::MIN, 0),
_id(alloc().alloc()),
_state((Genode::Vm_state *)state),
_state(state),
_context(context),
_table(table)
_table(table),
_vcpu_context(cpu_pool().primary_cpu())
{
affinity(cpu_pool().primary_cpu());
Virtual_pic::pic().irq.enable();
@ -235,15 +237,15 @@ void Kernel::Vm::exception(Cpu & cpu)
{
Virtual_timer::save(_state);
switch(_state->cpu_exception) {
switch(_state.cpu_exception) {
case Genode::Cpu_state::INTERRUPT_REQUEST:
case Genode::Cpu_state::FAST_INTERRUPT_REQUEST:
_state->gic_irq = Board::VT_MAINTAINANCE_IRQ;
_state.gic_irq = Board::VT_MAINTAINANCE_IRQ;
_interrupt(cpu.id());
break;
default:
pause();
_context->submit(1);
_context.submit(1);
}
Virtual_pic::save(_state);
@ -256,27 +258,27 @@ void Kernel::Vm::proceed(Cpu &)
/*
* the following values have to be enforced by the hypervisor
*/
_state->vttbr = Cpu::Ttbr_64bit::Ba::masked((Cpu::Ttbr_64bit::access_t)_table);
Cpu::Ttbr_64bit::Asid::set(_state->vttbr, _id);
_state.vttbr = Cpu::Ttbr_64bit::Ba::masked((Cpu::Ttbr_64bit::access_t)_table);
Cpu::Ttbr_64bit::Asid::set(_state.vttbr, _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();
_state.hsr = Cpu::Hstr::init();
_state.hpfar = Cpu::Hcr::init();
Virtual_pic::load(_state);
Virtual_timer::load(_state);
hypervisor_enter_vm(reinterpret_cast<Cpu::Context*>(_state));
hypervisor_enter_vm(_state);
}
void Vm::inject_irq(unsigned irq)
{
_state->gic_irq = irq;
_state.gic_irq = irq;
pause();
_context->submit(1);
_context.submit(1);
}

View File

@ -0,0 +1,369 @@
/*
* \brief Transition between virtual/host mode
* \author Alexander Boettcher
* \author Stefan Kalkowski
* \date 2019-06-25
*/
/*
* Copyright (C) 2019 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
.section .text
/*
* see D1.10.2 Exception vectors chapter
*/
.p2align 12
.global hypervisor_exception_vector
hypervisor_exception_vector:
.rept 16
add sp, sp, #-16 /* push x0, x1 to stack */
stp x0, x1, [sp]
mrs x1, hcr_el2 /* read HCR register */
tst x1, #1 /* check VM bit */
beq _host_to_vm /* if VM bit is not set, switch to VM */
ldr x0, [sp, #32] /* otherwise, load vm_state pointer */
adr x1, . /* hold exception vector offset in x1 */
and x1, x1, #0xf80
b _vm_to_host
.balign 128
.endr
_host_to_vm:
add sp, sp, #-16 /* push arg2 (vm pic state) to stack */
str x2, [sp]
msr vttbr_el2, x3 /* stage2 table pointer was arg3 */
add x0, x0, #31*8 /* skip x0...x30, loaded later */
ldr x1, [x0], #1*8 /* sp */
ldp x2, x3, [x0], #2*8 /* ip, pstate */
msr sp_el0, x1
msr elr_el2, x2
msr spsr_el2, x3
add x0, x0, #2*8 /* skip exception_type and esr_el2 */
/** FPU register **/
ldp q0, q1, [x0], #2*16
ldp q2, q3, [x0], #2*16
ldp q4, q5, [x0], #2*16
ldp q6, q7, [x0], #2*16
ldp q8, q9, [x0], #2*16
ldp q10, q11, [x0], #2*16
ldp q12, q13, [x0], #2*16
ldp q14, q15, [x0], #2*16
ldp q16, q17, [x0], #2*16
ldp q18, q19, [x0], #2*16
ldp q20, q21, [x0], #2*16
ldp q22, q23, [x0], #2*16
ldp q24, q25, [x0], #2*16
ldp q26, q27, [x0], #2*16
ldp q28, q29, [x0], #2*16
ldp q30, q31, [x0], #2*16
ldp w1, w2, [x0], #2*4
msr fpcr, x1
msr fpsr, x2
/** system register **/
ldp x1, x2, [x0], #2*8 /* elr_el1, sp_el1 */
ldp w3, w4, [x0], #2*4 /* spsr_el1, esr_el1 */
ldp x5, x6, [x0], #2*8 /* sctlr_el1, actlr_el1 */
ldr x7, [x0], #8 /* vbar_el1 */
ldp w8, w9, [x0], #2*4 /* cpacr_el1, afsr0_el1 */
ldp w10, w11, [x0], #2*4 /* afsr1_el1, contextidr_el1 */
ldp x12, x13, [x0], #2*8 /* ttbr0_el1, ttbr1_el1 */
ldp x14, x15, [x0], #2*8 /* tcr_el1, mair_el1 */
ldp x16, x17, [x0], #2*8 /* amair_el1, far_el1 */
ldp x18, x19, [x0], #2*8 /* par_el1, tpidrro_el0 */
ldp x20, x21, [x0], #2*8 /* tpidr_el0, tpidr_el1 */
ldr x22, [x0], #3*8 /* vmpidr_el2 */
msr elr_el1, x1
msr sp_el1, x2
msr spsr_el1, x3
msr esr_el1, x4
msr sctlr_el1, x5
msr actlr_el1, x6
msr vbar_el1, x7
msr cpacr_el1, x8
msr afsr0_el1, x9
msr afsr1_el1, x10
msr contextidr_el1, x11
msr ttbr0_el1, x12
msr ttbr1_el1, x13
msr tcr_el1, x14
msr mair_el1, x15
msr amair_el1, x16
msr far_el1, x17
msr par_el1, x18
msr tpidrro_el0, x19
msr tpidr_el0, x20
msr tpidr_el1, x21
msr vmpidr_el2, x22
/**********************
** load timer state **
**********************/
ldp x22, x23, [x0], #2*8 /* timer.offset, timer.compare */
ldp w24, w25, [x0] /* timer.control, kcontrol */
msr cntvoff_el2, x22
msr cntv_cval_el0, x23
msr cntv_ctl_el0, x24
msr cntkctl_el1, x25
mov x0, #0b100
msr cnthctl_el2, x0
/************************
** debug/perfm access **
************************/
mrs x0, mdcr_el2
movz x1, #0b111101100000
orr x0, x0, x1
msr mdcr_el2, x0
/**********************
** Load pic context **
**********************/
ldr x0, [sp]
ldr x1, [x0], #8 /* lr0 */
ldp w2, w3, [x0] /* apr, vmcr */
msr S3_4_C12_C12_0, x1
msr S3_4_C12_C9_0, x2
msr S3_4_C12_C11_7, x3
mov x0, #1 /* enable PIC virtualization */
msr S3_4_C12_C11_0, x0
/** enable VM mode **/
movz x1, #0b1110000000111001
movk x1, #0b10111, lsl 16
mrs x0, hcr_el2
orr x0, x0, x1
msr hcr_el2, x0
ldr x30, [sp, #16] /* load head of Vm_state again */
/** general-purpose registers **/
ldp x0, x1, [x30], #2*8
ldp x2, x3, [x30], #2*8
ldp x4, x5, [x30], #2*8
ldp x6, x7, [x30], #2*8
ldp x8, x9, [x30], #2*8
ldp x10, x11, [x30], #2*8
ldp x12, x13, [x30], #2*8
ldp x14, x15, [x30], #2*8
ldp x16, x17, [x30], #2*8
ldp x18, x19, [x30], #2*8
ldp x20, x21, [x30], #2*8
ldp x22, x23, [x30], #2*8
ldp x24, x25, [x30], #2*8
ldp x26, x27, [x30], #2*8
ldp x28, x29, [x30], #2*8
ldr x30, [x30]
eret
_vm_to_host:
/*********************
** Save vm context **
*********************/
/** general-purpose register **/
add x0, x0, #2*8 /* skip x0 and x1 for now */
stp x2, x3, [x0], #2*8
stp x4, x5, [x0], #2*8
stp x6, x7, [x0], #2*8
stp x8, x9, [x0], #2*8
stp x10, x11, [x0], #2*8
stp x12, x13, [x0], #2*8
stp x14, x15, [x0], #2*8
stp x16, x17, [x0], #2*8
stp x18, x19, [x0], #2*8
stp x20, x21, [x0], #2*8
stp x22, x23, [x0], #2*8
stp x24, x25, [x0], #2*8
stp x26, x27, [x0], #2*8
stp x28, x29, [x0], #2*8
str x30, [x0], #1*8
/** save sp, ip, pstate and exception reason **/
mrs x2, sp_el0
mrs x3, elr_el2
mrs x4, spsr_el2
mrs x5, esr_el2
stp x2, x3, [x0], #2*8
stp x4, x1, [x0], #2*8
str x5, [x0], #1*8
/** fpu registers **/
stp q0, q1, [x0], #32
stp q2, q3, [x0], #32
stp q4, q5, [x0], #32
stp q6, q7, [x0], #32
stp q8, q9, [x0], #32
stp q10, q11, [x0], #32
stp q12, q13, [x0], #32
stp q14, q15, [x0], #32
stp q16, q17, [x0], #32
stp q18, q19, [x0], #32
stp q20, q21, [x0], #32
stp q22, q23, [x0], #32
stp q24, q25, [x0], #32
stp q26, q27, [x0], #32
stp q28, q29, [x0], #32
stp q30, q31, [x0], #32
mrs x1, fpcr
mrs x2, fpsr
mrs x3, elr_el1
mrs x4, sp_el1
mrs x5, spsr_el1
mrs x6, esr_el1
mrs x7, sctlr_el1
mrs x8, actlr_el1
mrs x9, vbar_el1
mrs x10, cpacr_el1
mrs x11, afsr0_el1
mrs x12, afsr1_el1
mrs x13, contextidr_el1
mrs x14, ttbr0_el1
mrs x15, ttbr1_el1
mrs x16, tcr_el1
mrs x17, mair_el1
mrs x18, amair_el1
mrs x19, far_el1
mrs x20, par_el1
mrs x21, tpidrro_el0
mrs x22, tpidr_el0
mrs x23, tpidr_el1
mrs x24, far_el2
mrs x25, hpfar_el2
stp w1, w2, [x0], #2*4
stp x3, x4, [x0], #2*8
stp w5, w6, [x0], #2*4
stp x7, x8, [x0], #2*8
str x9, [x0], #1*8
stp w10, w11, [x0], #2*4
stp w12, w13, [x0], #2*4
stp x14, x15, [x0], #2*8
stp x16, x17, [x0], #2*8
stp x18, x19, [x0], #2*8
stp x20, x21, [x0], #2*8
stp x22, x23, [x0], #3*8
stp x24, x25, [x0], #2*8
/**********************
** save timer state **
**********************/
mrs x26, cntvoff_el2
mrs x27, cntv_cval_el0
mrs x28, cntv_ctl_el0
mrs x29, cntkctl_el1
stp x26, x27, [x0], #2*8
stp w28, w29, [x0]
mov x0, #0b111
msr cnthctl_el2, x0
ldp x0, x1, [sp], #2*8 /* pop x0, x1 from stack */
ldr x29, [sp], #2*8 /* pop vm pic state from stack */
ldp x2, x30, [sp], #2*8 /* pop vm, and host state from stack */
stp x0, x1, [x2] /* save x0, x1 to vm state */
/**********************
** Save pic context **
**********************/
mrs x10, S3_4_C12_C12_0
mrs x11, S3_4_C12_C9_0
mrs x12, S3_4_C12_C11_7
mrs x13, S3_4_C12_C11_2
mrs x14, S3_4_C12_C11_3
mrs x15, S3_4_C12_C11_5
str x10, [x29], #8 /* lr0 */
stp w11, w12, [x29], #2*4 /* apr, vmcr */
stp w13, w14, [x29], #2*4 /* misr, eisr */
str w15, [x29] /* elrsr */
msr S3_4_C12_C11_0, xzr /* disable PIC virtualization */
/***********************
** Load host context **
***********************/
add x30, x30, #32*8 /* skip general-purpose regs, sp */
ldp x0, x1, [x30] /* host state ip, and pstate */
add x30, x30, #34*16 /* skip fpu regs etc. */
ldp w2, w3, [x30], #4*4 /* fpcr and fpsr */
ldr x4, [x30], #2*8 /* sp_el1 */
ldp x5, x6, [x30], #2*8 /* sctlr_el1, actlr_el1 */
ldr x7, [x30], #1*8 /* vbar_el1 */
ldr w8, [x30], #4*4 /* cpacr_el1 */
ldp x9, x10, [x30], #2*8 /* ttbr0_el1, ttbr1_el1 */
ldp x11, x12, [x30], #2*8 /* tcr_el1, mair_el1 */
ldr x13, [x30] /* amair_el1 */
msr elr_el2, x0
msr spsr_el2, x1
msr fpcr, x2
msr fpsr, x3
msr sp_el1, x4
msr sctlr_el1, x5
msr actlr_el1, x6
msr vbar_el1, x7
msr cpacr_el1, x8
msr ttbr0_el1, x9
msr ttbr1_el1, x10
msr tcr_el1, x11
msr mair_el1, x12
msr amair_el1, x13
mrs x0, mpidr_el1
msr vmpidr_el2, x0
/************************
** debug/perfm access **
************************/
mrs x0, mdcr_el2
movz x1, #0b111101100000
bic x0, x0, x1
msr mdcr_el2, x0
/** disable VM mode **/
movz x1, #0b1110000000111001
movk x1, #0b10111, lsl 16
mrs x0, hcr_el2
msr vttbr_el2, xzr /* stage2 table pointer zeroing */
bic x0, x0, x1
msr hcr_el2, x0
eret
/* host kernel must jump to this point to switch to a vm */
.global hypervisor_enter_vm
hypervisor_enter_vm:
hvc #0

View File

@ -0,0 +1,215 @@
/*
* \brief Kernel backend for virtual machines
* \author Stefan Kalkowski
* \date 2015-02-10
*/
/*
* Copyright (C) 2015-2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#include <base/log.h>
#include <cpu/vm_state_virtualization.h>
#include <util/mmio.h>
#include <hw/assert.h>
#include <hw/spec/arm_64/memory_map.h>
#include <map_local.h>
#include <platform_pd.h>
#include <kernel/cpu.h>
#include <kernel/vm.h>
using Genode::addr_t;
using Kernel::Cpu;
using Kernel::Vm;
extern "C" void kernel();
extern void * kernel_stack;
extern "C" void hypervisor_enter_vm(addr_t vm, addr_t host,
addr_t pic, addr_t guest_table);
static Genode::Vm_state & host_context()
{
static Genode::Constructible<Genode::Vm_state> host_context;
if (!host_context.constructed()) {
host_context.construct();
host_context->ip = (addr_t) &kernel;
host_context->pstate = 0;
Cpu::Spsr::Sp::set(host_context->pstate, 1); /* select non-el0 stack pointer */
Cpu::Spsr::El::set(host_context->pstate, Cpu::Current_el::EL1);
Cpu::Spsr::F::set(host_context->pstate, 1);
Cpu::Spsr::I::set(host_context->pstate, 1);
Cpu::Spsr::A::set(host_context->pstate, 1);
Cpu::Spsr::D::set(host_context->pstate, 1);
host_context->fpcr = Cpu::Fpcr::read();
host_context->fpsr = 0;
host_context->sctlr_el1 = Cpu::Sctlr_el1::read();
host_context->actlr_el1 = Cpu::Actlr_el1::read();
host_context->vbar_el1 = Cpu::Vbar_el1::read();
host_context->cpacr_el1 = Cpu::Cpacr_el1::read();
host_context->ttbr0_el1 = Cpu::Ttbr0_el1::read();
host_context->ttbr1_el1 = Cpu::Ttbr1_el1::read();
host_context->tcr_el1 = Cpu::Tcr_el1::read();
host_context->mair_el1 = Cpu::Mair_el1::read();
host_context->amair_el1 = Cpu::Amair_el1::read();
}
return *host_context;
}
Board::Vcpu_context::Vm_irq::Vm_irq(unsigned const irq, Cpu & cpu)
: Kernel::Irq(irq, cpu.irq_pool())
{ }
void Board::Vcpu_context::Vm_irq::handle(Cpu &, Vm & vm, unsigned irq) {
vm.inject_irq(irq); }
void Board::Vcpu_context::Vm_irq::occurred()
{
Cpu & cpu = Kernel::cpu_pool().executing_cpu();
Vm *vm = dynamic_cast<Vm*>(&cpu.scheduled_job());
if (!vm) Genode::raw("VM interrupt while VM is not runnning!");
else handle(cpu, *vm, _irq_nr);
}
Board::Vcpu_context::Pic_maintainance_irq::Pic_maintainance_irq(Cpu & cpu)
: Board::Vcpu_context::Vm_irq(Board::VT_MAINTAINANCE_IRQ, cpu) {
//FIXME Irq::enable only enables caller cpu
cpu.pic().unmask(_irq_nr, cpu.id()); }
Board::Vcpu_context::Virtual_timer_irq::Virtual_timer_irq(Cpu & cpu)
: irq(Board::VT_TIMER_IRQ, cpu) {}
void Board::Vcpu_context::Virtual_timer_irq::enable() { irq.enable(); }
void Board::Vcpu_context::Virtual_timer_irq::disable()
{
irq.disable();
asm volatile("msr cntv_ctl_el0, xzr");
asm volatile("msr cntkctl_el1, %0" :: "r" (0b11));
}
using Vmid_allocator = Genode::Bit_allocator<256>;
static Vmid_allocator &alloc()
{
static Vmid_allocator * allocator = nullptr;
if (!allocator) {
allocator = unmanaged_singleton<Vmid_allocator>();
/* reserve VM ID 0 for the hypervisor */
unsigned id = allocator->alloc();
assert (id == 0);
}
return *allocator;
}
Vm::Vm(unsigned cpu,
Genode::Vm_state & state,
Kernel::Signal_context & context,
void * const table)
: Cpu_job(Cpu_priority::MIN, 0),
_id(alloc().alloc()),
_state(state),
_context(context),
_table(table),
_vcpu_context(cpu_pool().cpu(cpu))
{
affinity(cpu_pool().cpu(cpu));
_state.id_aa64isar0_el1 = Cpu::Id_aa64isar0_el1::read();
_state.id_aa64isar1_el1 = Cpu::Id_aa64isar1_el1::read();
_state.id_aa64mmfr0_el1 = Cpu::Id_aa64mmfr0_el1::read();
_state.id_aa64mmfr1_el1 = Cpu::Id_aa64mmfr1_el1::read();
_state.id_aa64mmfr2_el1 = /* FIXME Cpu::Id_aa64mmfr2_el1::read(); */ 0;
Cpu::Clidr_el1::access_t clidr = Cpu::Clidr_el1::read();
for (unsigned i = 0; i < 7; i++) {
unsigned level = clidr >> (i*3) & 0b111;
if (level == Cpu::Clidr_el1::NO_CACHE) break;
if ((level == Cpu::Clidr_el1::INSTRUCTION_CACHE) ||
(level == Cpu::Clidr_el1::SEPARATE_CACHE)) {
Cpu::Csselr_el1::access_t csselr = 0;
Cpu::Csselr_el1::Instr::set(csselr, 1);
Cpu::Csselr_el1::Level::set(csselr, level);
Cpu::Csselr_el1::write(csselr);
_state.ccsidr_inst_el1[level] = Cpu::Ccsidr_el1::read();
}
if (level != Cpu::Clidr_el1::INSTRUCTION_CACHE) {
Cpu::Csselr_el1::write(Cpu::Csselr_el1::Level::bits(level));
_state.ccsidr_data_el1[level] = Cpu::Ccsidr_el1::read();
}
}
}
Vm::~Vm() { alloc().free(_id); }
void Vm::exception(Cpu & cpu)
{
switch (_state.exception_type) {
case Cpu::IRQ_LEVEL_EL0: [[fallthrough]]
case Cpu::IRQ_LEVEL_EL1: [[fallthrough]]
case Cpu::FIQ_LEVEL_EL0: [[fallthrough]]
case Cpu::FIQ_LEVEL_EL1:
_interrupt(cpu.id());
break;
case Cpu::SYNC_LEVEL_EL0: [[fallthrough]]
case Cpu::SYNC_LEVEL_EL1: [[fallthrough]]
case Cpu::SERR_LEVEL_EL0: [[fallthrough]]
case Cpu::SERR_LEVEL_EL1:
pause();
_context.submit(1);
break;
default:
Genode::raw("Exception vector: ", (void*)_state.exception_type,
" not implemented!");
};
if (cpu.pic().ack_virtual_irq(_vcpu_context.pic))
inject_irq(Board::VT_MAINTAINANCE_IRQ);
_vcpu_context.vtimer_irq.disable();
}
void Vm::proceed(Cpu & cpu)
{
if (_state.timer.irq) _vcpu_context.vtimer_irq.enable();
cpu.pic().insert_virtual_irq(_vcpu_context.pic, _state.irqs.virtual_irq);
/*
* the following values have to be enforced by the hypervisor
*/
Cpu::Vttbr_el2::access_t vttbr_el2 =
Cpu::Vttbr_el2::Ba::masked((Cpu::Vttbr_el2::access_t)_table);
Cpu::Vttbr_el2::Asid::set(vttbr_el2, _id);
addr_t guest = Hw::Mm::el2_addr(&_state);
addr_t pic = Hw::Mm::el2_addr(&_vcpu_context.pic);
addr_t host = Hw::Mm::el2_addr(&host_context());
host_context().sp_el1 = cpu.stack_start();
hypervisor_enter_vm(guest, host, pic, vttbr_el2);
}
void Vm::inject_irq(unsigned irq)
{
_state.irqs.last_irq = irq;
pause();
_context.submit(1);
}

View File

@ -17,11 +17,25 @@
#include <hw/spec/arm/gicv2.h>
#include <hw/spec/arm/arndale_board.h>
#include <spec/arm/exynos_mct.h>
#include <spec/arm/cpu/vm_state_virtualization.h>
#include <translation_table.h>
#include <kernel/configuration.h>
namespace Kernel { class Cpu; }
namespace Board {
using namespace Hw::Arndale_board;
using Pic = Hw::Gicv2;
enum { VCPU_MAX = 1 };
using Vm_state = Genode::Vm_state;
using Vm_page_table = Hw::Level_1_stage_2_translation_table;
using Vm_page_table_array =
Vm_page_table::Allocator::Array<Kernel::DEFAULT_TRANSLATION_TABLE_MAX>;
struct Vcpu_context { Vcpu_context(Kernel::Cpu &) {} };
}
#endif /* _CORE__SPEC__ARNDALE__BOARD_H_ */

View File

@ -18,10 +18,8 @@
#include <hw/spec/arm/imx_tzic.h>
#include <hw/spec/arm/imx53_qsb_board.h>
#include <spec/arm/imx_epit.h>
#include <spec/arm/trustzone_board.h>
namespace Board {
using namespace Hw::Imx53_qsb_board;
using Hw::Pic;
}
namespace Board { using namespace Hw::Imx53_qsb_board; }
#endif /* _CORE__SPEC__IMX53_QSB__BOARD_H_ */

View File

@ -14,16 +14,28 @@
#ifndef _CORE__SPEC__IMX7D_SABRE__BOARD_H_
#define _CORE__SPEC__IMX7D_SABRE__BOARD_H_
#include <hw/spec/arm/gicv2.h>
#include <hw/spec/arm/imx7d_sabre_board.h>
#include <spec/arm/virtualization/gicv2.h>
#include <spec/arm/generic_timer.h>
#include <spec/arm/cpu/vm_state_virtualization.h>
#include <translation_table.h>
#include <kernel/configuration.h>
namespace Kernel { class Cpu; }
namespace Board {
using namespace Hw::Imx7d_sabre_board;
using Pic = Hw::Gicv2;
struct Virtual_local_pic {};
enum { TIMER_IRQ = 30 };
enum { TIMER_IRQ = 30, VCPU_MAX = 1 };
using Vm_state = Genode::Vm_state;
using Vm_page_table = Hw::Level_1_stage_2_translation_table;
using Vm_page_table_array =
Vm_page_table::Allocator::Array<Kernel::DEFAULT_TRANSLATION_TABLE_MAX>;
struct Vcpu_context { Vcpu_context(Kernel::Cpu &) {} };
}
#endif /* _CORE__SPEC__IMX7_SABRELITE__BOARD_H_ */

View File

@ -15,14 +15,73 @@
#define _CORE__SPEC__IMX8Q_EVK__BOARD_H_
#include <hw/spec/arm_64/imx8q_evk_board.h>
#include <hw/spec/arm/gicv3.h>
#include <spec/arm/generic_timer.h>
#include <spec/arm/virtualization/gicv3.h>
#include <spec/arm_64/cpu/vm_state_virtualization.h>
#include <translation_table.h>
#include <kernel/configuration.h>
#include <kernel/irq.h>
namespace Board {
using namespace Hw::Imx8q_evk_board;
using Hw::Pic;
enum { TIMER_IRQ = 30 };
enum {
TIMER_IRQ = 14 + 16,
VT_TIMER_IRQ = 11 + 16,
VT_MAINTAINANCE_IRQ = 9 + 16,
VCPU_MAX = 16
};
using Vm_page_table = Hw::Level_1_stage_2_translation_table;
using Vm_page_table_array =
Vm_page_table::Allocator::Array<Kernel::DEFAULT_TRANSLATION_TABLE_MAX>;
struct Vcpu_context;
using Vm_state = Genode::Vm_state;
};
namespace Kernel {
class Cpu;
class Vm;
};
struct Board::Vcpu_context
{
struct Vm_irq : Kernel::Irq
{
Vm_irq(unsigned const irq, Kernel::Cpu &);
virtual ~Vm_irq() {};
virtual void handle(Kernel::Cpu &, Kernel::Vm & vm, unsigned irq);
void occurred() override;
};
struct Pic_maintainance_irq : Vm_irq
{
Pic_maintainance_irq(Kernel::Cpu &);
void handle(Kernel::Cpu &, Kernel::Vm &, unsigned) override { }
};
struct Virtual_timer_irq
{
Vm_irq irq;
Virtual_timer_irq(Kernel::Cpu &);
void enable();
void disable();
};
Vcpu_context(Kernel::Cpu & cpu)
: pic_irq(cpu), vtimer_irq(cpu) {}
Pic::Virtual_context pic {};
Pic_maintainance_irq pic_irq;
Virtual_timer_irq vtimer_irq;
};
#endif /* _CORE__SPEC__IMX8Q_EVK__BOARD_H_ */

View File

@ -18,10 +18,8 @@
#include <hw/spec/arm/imx_tzic.h>
#include <hw/spec/arm/usb_armory_board.h>
#include <spec/arm/imx_epit.h>
#include <spec/arm/trustzone_board.h>
namespace Board {
using namespace Hw::Usb_armory_board;
using Hw::Pic;
}
namespace Board { using namespace Hw::Usb_armory_board; }
#endif /* _CORE__SPEC__USB_ARMORY__BOARD_H_ */

View File

@ -17,6 +17,9 @@
#include <hw/spec/x86_64/pc_board.h>
#include <spec/x86_64/muen/pic.h>
#include <spec/x86_64/muen/timer.h>
#include <cpu/cpu_state.h>
namespace Kernel { class Cpu; }
namespace Board {
using namespace Hw::Pc_board;
@ -33,6 +36,15 @@ namespace Board {
TIMER_VECTOR_KERNEL = 32,
TIMER_VECTOR_USER = 50,
};
using Vm_state = Genode::Cpu_state;
enum { VCPU_MAX = 1 };
struct Vm_page_table {};
struct Vm_page_table_array {};
struct Vcpu_context { Vcpu_context(Kernel::Cpu &) {} };
}
#endif /* _CORE__SPEC__X86_64__MUEN__BOARD_H_ */

View File

@ -17,14 +17,16 @@
#include <platform_pd.h>
#include <kernel/cpu.h>
#include <kernel/vm.h>
#include <kernel/vm_state.h>
Kernel::Vm::Vm(void * const state, Kernel::Signal_context * const context,
Kernel::Vm::Vm(unsigned,
Board::Vm_state & state,
Kernel::Signal_context & context,
void * const)
: Cpu_job(Cpu_priority::MIN, 0),
_state((Genode::Vm_state *) state),
_state(state),
_context(context),
_table(nullptr)
_table(nullptr),
_vcpu_context(cpu_pool().primary_cpu())
{
affinity(cpu_pool().primary_cpu());
}
@ -36,20 +38,20 @@ Kernel::Vm::~Vm() { }
void Kernel::Vm::exception(Cpu & cpu)
{
pause();
if (_state->trapno == 200) {
_context->submit(1);
if (_state.trapno == 200) {
_context.submit(1);
return;
}
if (_state->trapno >= Genode::Cpu_state::INTERRUPTS_START &&
_state->trapno <= Genode::Cpu_state::INTERRUPTS_END) {
cpu.pic().irq_occurred(_state->trapno);
if (_state.trapno >= Genode::Cpu_state::INTERRUPTS_START &&
_state.trapno <= Genode::Cpu_state::INTERRUPTS_END) {
cpu.pic().irq_occurred(_state.trapno);
_interrupt(cpu.id());
_context->submit(1);
_context.submit(1);
return;
}
Genode::raw("VM: triggered unknown exception ", _state->trapno,
" with error code ", _state->errcode);
Genode::raw("VM: triggered unknown exception ", _state.trapno,
" with error code ", _state.errcode);
ASSERT_NEVER_CALLED;
}
@ -57,7 +59,7 @@ void Kernel::Vm::exception(Cpu & cpu)
void Kernel::Vm::proceed(Cpu & cpu)
{
cpu.tss.ist[0] = (addr_t)_state + sizeof(Genode::Cpu_state);
cpu.tss.ist[0] = (addr_t)&_state + sizeof(Genode::Cpu_state);
asm volatile("sti \n"
"mov $1, %rax \n"

View File

@ -1,22 +0,0 @@
/*
* \brief CPU context of a virtual machine
* \author Stefan Kalkowski
* \date 2015-06-03
*/
/*
* Copyright (C) 2015-2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef _CORE__SPEC__X86_64__MUEN__VM_STATE_H_
#define _CORE__SPEC__X86_64__MUEN__VM_STATE_H_
namespace Genode
{
struct Vm_state : Cpu_state {};
}
#endif /* _CORE__SPEC__X86_64__MUEN__VM_STATE_H_ */

View File

@ -26,6 +26,8 @@ class Board::Pic
{
public:
struct Virtual_context {};
enum {
/*
* FIXME: dummy ipi value on non-SMP platform, should be removed

View File

@ -0,0 +1,89 @@
/*
* \brief Core-specific instance of the VM session interface
* \author Stefan Kalkowski
* \date 2015-06-03
*/
/*
* Copyright (C) 2015-2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
/* Genode includes */
#include <util/construct_at.h>
/* core includes */
#include <vm_session_component.h>
#include <platform.h>
using namespace Genode;
static Board::Vm_page_table_array & dummy_array()
{
static Board::Vm_page_table_array a;
return a;
}
void Vm_session_component::_attach(addr_t, addr_t, size_t) { }
void Vm_session_component::_attach_vm_memory(Dataspace_component &,
addr_t const,
Attach_attr const) { }
void Vm_session_component::attach_pic(addr_t) { }
void Vm_session_component::_detach_vm_memory(addr_t, size_t) { }
void * Vm_session_component::_alloc_table()
{
static Board::Vm_page_table table;
return (void*) &table;
}
Vm_session_component::Vm_session_component(Rpc_entrypoint &ep,
Resources resources,
Label const &,
Diag,
Ram_allocator &ram_alloc,
Region_map &region_map,
unsigned, Trace::Source_registry &)
:
Ram_quota_guard(resources.ram_quota),
Cap_quota_guard(resources.cap_quota),
_ep(ep),
_constrained_md_ram_alloc(ram_alloc, _ram_quota_guard(), _cap_quota_guard()),
_sliced_heap(_constrained_md_ram_alloc, region_map),
_region_map(region_map),
_table(*construct_at<Board::Vm_page_table>(_alloc_table())),
_table_array(dummy_array()) { }
Vm_session_component::~Vm_session_component()
{
/* detach all regions */
while (true) {
addr_t out_addr = 0;
if (!_map.any_block_addr(&out_addr))
break;
detach(out_addr);
}
/* free region in allocator */
for (unsigned i = 0; i < _id_alloc; i++) {
Vcpu & vcpu = _vcpus[i];
if (vcpu.ds_cap.valid()) {
_region_map.detach(vcpu.ds_addr);
_constrained_md_ram_alloc.free(vcpu.ds_cap);
}
}
}

View File

@ -1,88 +0,0 @@
/*
* \brief Core-specific instance of the VM session interface
* \author Stefan Kalkowski
* \date 2015-06-03
*/
/*
* Copyright (C) 2015-2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef _CORE__SPEC__X86_64__MUEN__VM_SESSION_COMPONENT_H_
#define _CORE__SPEC__X86_64__MUEN__VM_SESSION_COMPONENT_H_
/* Genode includes */
#include <base/allocator.h>
#include <base/rpc_server.h>
#include <vm_session/vm_session.h>
#include <dataspace/capability.h>
#include <trace/source_registry.h>
/* Core includes */
#include <dataspace_component.h>
#include <object.h>
#include <kernel/vm.h>
#include <kernel/vm_state.h>
namespace Genode {
class Vm_session_component;
}
class Genode::Vm_session_component
:
private Ram_quota_guard,
private Cap_quota_guard,
public Rpc_object<Vm_session, Vm_session_component>
{
private:
Kernel_object<Kernel::Vm> _kobj {};
Vm_state _state;
public:
Vm_session_component(Rpc_entrypoint &, Resources resources,
Label const &, Diag, Ram_allocator &,
Region_map &, unsigned,
Trace::Source_registry &)
:
Ram_quota_guard(resources.ram_quota),
Cap_quota_guard(resources.cap_quota),
_state()
{ }
~Vm_session_component() { }
using Ram_quota_guard::upgrade;
using Cap_quota_guard::upgrade;
using Genode::Rpc_object<Genode::Vm_session, Vm_session_component>::cap;
/**************************
** Vm session interface **
**************************/
Dataspace_capability _cpu_state(Vcpu_id) { return Dataspace_capability(); }
void _exception_handler(Signal_context_capability handler, Vcpu_id)
{
if (!_kobj.create(&_state, Capability_space::capid(handler), nullptr))
warning("Cannot instantiate vm kernel object, "
"invalid signal context?");
}
void _run(Vcpu_id) {
if (_kobj.constructed()) Kernel::run_vm(*_kobj); }
void _pause(Vcpu_id) {
if (_kobj.constructed()) Kernel::pause_vm(*_kobj); }
void attach(Dataspace_capability, addr_t, Attach_attr) override { }
void attach_pic(addr_t) override { }
void detach(addr_t, size_t) override { }
Vcpu_id _create_vcpu(Thread_capability) { return 0; }
};
#endif /* _CORE__SPEC__X86_64__MUEN__VM_SESSION_COMPONENT_H_ */

View File

@ -0,0 +1,114 @@
/*
* \brief VM session component for 'base-hw'
* \author Stefan Kalkowski
* \date 2012-10-08
*/
/*
* Copyright (C) 2012-2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
/* Genode includes */
#include <util/construct_at.h>
/* core includes */
#include <kernel/core_interface.h>
#include <vm_session_component.h>
#include <platform.h>
#include <cpu_thread_component.h>
#include <core_env.h>
using namespace Genode;
static Core_mem_allocator & cma() {
return static_cast<Core_mem_allocator&>(platform().core_mem_alloc()); }
size_t Vm_session_component::_ds_size() {
return align_addr(sizeof(Board::Vm_state), get_page_size_log2()); }
addr_t Vm_session_component::_alloc_ds()
{
addr_t addr;
if (platform().ram_alloc().alloc_aligned(_ds_size(), (void**)&addr,
get_page_size_log2()).error())
throw Insufficient_ram_quota();
return addr;
}
void Vm_session_component::_run(Vcpu_id id)
{
if (_valid_id(id) && _vcpus[id.id].kobj.constructed())
Kernel::run_vm(*_vcpus[id.id].kobj);
}
void Vm_session_component::_pause(Vcpu_id id)
{
if (_valid_id(id) && _vcpus[id.id].kobj.constructed())
Kernel::pause_vm(*_vcpus[id.id].kobj);
}
void Vm_session_component::_exception_handler(Signal_context_capability handler,
Vcpu_id id)
{
if (!_valid_id(id)) {
Genode::warning("invalid vcpu id ", id.id);
return;
}
Vcpu & vcpu = _vcpus[id.id];
if (vcpu.kobj.constructed()) {
Genode::warning("Cannot register vcpu handler twice");
return;
}
unsigned const cpu = vcpu.location.valid() ? vcpu.location.xpos() : 0;
if (!vcpu.kobj.create(cpu, vcpu.ds_addr, Capability_space::capid(handler),
cma().phys_addr(&_table)))
Genode::warning("Cannot instantiate vm kernel object, ",
"invalid signal context?");
}
Vm_session::Vcpu_id Vm_session_component::_create_vcpu(Thread_capability tcap)
{
using namespace Genode;
if (_id_alloc == Board::VCPU_MAX) return Vcpu_id{Vcpu_id::INVALID};
Affinity::Location vcpu_location;
auto lambda = [&] (Cpu_thread_component *ptr) {
if (!ptr) return;
vcpu_location = ptr->platform_thread().affinity();
};
_ep.apply(tcap, lambda);
Vcpu & vcpu = _vcpus[_id_alloc];
vcpu.ds_cap = _constrained_md_ram_alloc.alloc(_ds_size(),
Cache_attribute::UNCACHED);
try {
vcpu.ds_addr = _region_map.attach(vcpu.ds_cap);
} catch (...) {
_constrained_md_ram_alloc.free(vcpu.ds_cap);
throw;
}
vcpu.location = vcpu_location;
return Vcpu_id { _id_alloc++ };
}
Genode::Dataspace_capability
Vm_session_component::_cpu_state(Vm_session::Vcpu_id id)
{
return (_valid_id(id)) ? _vcpus[id.id].ds_cap
: Genode::Ram_dataspace_capability();
}

View File

@ -11,8 +11,8 @@
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef _CORE__SPEC__ARM_V7__VIRTUALIZATION__VM_SESSION_COMPONENT_H_
#define _CORE__SPEC__ARM_V7__VIRTUALIZATION__VM_SESSION_COMPONENT_H_
#ifndef _CORE__VM_SESSION_COMPONENT_H_
#define _CORE__VM_SESSION_COMPONENT_H_
/* Genode includes */
#include <base/allocator.h>
@ -20,14 +20,12 @@
#include <base/session_object.h>
#include <vm_session/vm_session.h>
#include <dataspace/capability.h>
#include <hw/spec/arm/lpae.h>
/* Core includes */
#include <object.h>
#include <region_map_component.h>
#include <translation_table.h>
#include <kernel/vm.h>
#include <cpu/vm_state_virtualization.h>
#include <trace/source_registry.h>
@ -39,8 +37,8 @@ class Genode::Vm_session_component
:
private Ram_quota_guard,
private Cap_quota_guard,
public Rpc_object<Vm_session, Vm_session_component>,
public Region_map_detach
public Rpc_object<Vm_session, Vm_session_component>,
public Region_map_detach
{
private:
@ -52,30 +50,31 @@ class Genode::Vm_session_component
Vm_session_component(Vm_session_component const &);
Vm_session_component &operator = (Vm_session_component const &);
using Table = Hw::Level_1_stage_2_translation_table;
using Array = Table::Allocator::Array<Kernel::DEFAULT_TRANSLATION_TABLE_MAX>;
struct Vcpu
{
Ram_dataspace_capability ds_cap { };
Region_map::Local_addr ds_addr { nullptr };
Kernel_object<Kernel::Vm> kobj {};
Affinity::Location location {};
} _vcpus[Board::VCPU_MAX];
Rpc_entrypoint &_ep;
Constrained_ram_allocator _constrained_md_ram_alloc;
Sliced_heap _sliced_heap;
Avl_region _map { &_sliced_heap };
Region_map &_region_map;
Ram_dataspace_capability _ds_cap { };
Region_map::Local_addr _ds_addr { nullptr };
Table &_table;
Array &_table_array;
Kernel_object<Kernel::Vm> _kobj {};
Rpc_entrypoint &_ep;
Constrained_ram_allocator _constrained_md_ram_alloc;
Sliced_heap _sliced_heap;
Avl_region _map { &_sliced_heap };
Region_map &_region_map;
Board::Vm_page_table &_table;
Board::Vm_page_table_array &_table_array;
unsigned _id_alloc { 0 };
static size_t _ds_size() {
return align_addr(sizeof(Vm_state),
get_page_size_log2()); }
addr_t _alloc_ds();
void * _alloc_table();
void _attach(addr_t phys_addr, addr_t vm_addr, size_t size);
void _attach_vm_memory(Dataspace_component &, addr_t, Attach_attr);
void _detach_vm_memory(addr_t, size_t);
static size_t _ds_size();
bool _valid_id(Vcpu_id id) { return id.id < Board::VCPU_MAX; }
addr_t _alloc_ds();
void * _alloc_table();
void _attach(addr_t phys_addr, addr_t vm_addr, size_t size);
void _attach_vm_memory(Dataspace_component &, addr_t,
Attach_attr);
void _detach_vm_memory(addr_t, size_t);
protected:
@ -104,14 +103,16 @@ class Genode::Vm_session_component
** Vm session interface **
**************************/
Dataspace_capability _cpu_state(Vcpu_id) { return _ds_cap; }
void _exception_handler(Signal_context_capability, Vcpu_id);
void _run(Vcpu_id);
void _pause(Vcpu_id);
void attach(Dataspace_capability, addr_t, Attach_attr) override;
void attach_pic(addr_t) override;
void detach(addr_t, size_t) override;
Vcpu_id _create_vcpu(Thread_capability) { return 0; }
Dataspace_capability _cpu_state(Vcpu_id);
Vcpu_id _create_vcpu(Thread_capability);
void _exception_handler(Signal_context_capability,
Vcpu_id);
void _run(Vcpu_id);
void _pause(Vcpu_id);
};
#endif /* _CORE__SPEC__ARM_V7__VIRTUALIZATION__VM_SESSION_COMPONENT_H_ */
#endif /* _CORE__VM_SESSION_COMPONENT_H_ */

View File

@ -30,6 +30,7 @@ namespace Hw {
Memory_region const core_heap();
Memory_region const system_exception_vector();
Memory_region const hypervisor_exception_vector();
Memory_region const hypervisor_stack();
Memory_region const supervisor_exception_vector();
Memory_region const boot_info();
}

View File

@ -35,11 +35,31 @@ namespace Hw { struct Arm_64_cpu; }
struct Hw::Arm_64_cpu
{
SYSTEM_REGISTER(64, Id_pfr0, id_aa64pfr0_el1,
struct El2 : Bitfield<8, 4> {};
struct El3 : Bitfield<8, 4> {};
SYSTEM_REGISTER(64, Actlr_el1, actlr_el1);
SYSTEM_REGISTER(64, Amair_el1, amair_el1);
SYSTEM_REGISTER(32, Ccsidr_el1, ccsidr_el1);
SYSTEM_REGISTER(64, Clidr_el1, clidr_el1,
enum Cache_type {
NO_CACHE,
INSTRUCTION_CACHE,
DATA_CACHE,
SEPARATE_CACHE,
UNIFIED_CACHE
};
);
SYSTEM_REGISTER(32, Csselr_el1, csselr_el1,
struct Instr : Bitfield<0, 1> {};
struct Level : Bitfield<1, 3> {};
);
SYSTEM_REGISTER(32, Cpacr_el1, cpacr_el1);
SYSTEM_REGISTER(32, Cptr_el2, cptr_el2,
struct Tta : Bitfield<20, 1> {}; );
SYSTEM_REGISTER(64, Current_el, currentel,
enum Level { EL0, EL1, EL2, EL3 };
struct El : Bitfield<2, 2> {};
@ -80,12 +100,27 @@ struct Hw::Arm_64_cpu
SYSTEM_REGISTER(64, Esr_el1, esr_el1);
SYSTEM_REGISTER(64, Far_el1, far_el1);
SYSTEM_REGISTER(32, Fpcr, fpcr);
SYSTEM_REGISTER(64, Hcr, hcr_el2,
SYSTEM_REGISTER(64, Hcr_el2, hcr_el2,
struct Rw : Bitfield<31, 1> {};
);
SYSTEM_REGISTER(64, Mair, mair_el1,
SYSTEM_REGISTER(32, Hstr_el2, hstr_el2);
SYSTEM_REGISTER(64, Id_aa64isar0_el1, id_aa64isar0_el1);
SYSTEM_REGISTER(64, Id_aa64isar1_el1, id_aa64isar1_el1);
SYSTEM_REGISTER(64, Id_aa64mmfr0_el1, id_aa64mmfr0_el1);
SYSTEM_REGISTER(64, Id_aa64mmfr1_el1, id_aa64mmfr1_el1);
SYSTEM_REGISTER(64, Id_aa64mmfr2_el1, id_aa64mmfr2_el1);
SYSTEM_REGISTER(64, Id_pfr0, id_aa64pfr0_el1,
struct El2 : Bitfield<8, 4> {};
struct El3 : Bitfield<8, 4> {};
);
struct Mair : Genode::Register<64>
{
enum Attributes {
DEVICE_MEMORY = 0x04,
NORMAL_MEMORY_UNCACHED = 0x44,
@ -95,7 +130,10 @@ struct Hw::Arm_64_cpu
struct Attr1 : Bitfield<8, 8> {};
struct Attr2 : Bitfield<16, 8> {};
struct Attr3 : Bitfield<24, 8> {};
);
};
SYSTEM_REGISTER(64, Mair_el1, mair_el1);
SYSTEM_REGISTER(64, Mair_el2, mair_el2);
SYSTEM_REGISTER(64, Mpidr, mpidr_el1);
@ -107,14 +145,20 @@ struct Hw::Arm_64_cpu
struct Rw : Bitfield<10, 1> {};
);
SYSTEM_REGISTER(64, Sctlr_el1, sctlr_el1,
struct Sctlr : Genode::Register<64>
{
struct M : Bitfield<0, 1> { };
struct A : Bitfield<1, 1> { };
struct C : Bitfield<2, 1> { };
struct Sa : Bitfield<3, 1> { };
struct Sa0 : Bitfield<4, 1> { };
struct I : Bitfield<12, 1> { };
);
struct Uct : Bitfield<15, 1> { };
struct Wxn : Bitfield<19, 1> { };
};
SYSTEM_REGISTER(64, Sctlr_el1, sctlr_el1);
SYSTEM_REGISTER(64, Sctlr_el2, sctlr_el2);
struct Spsr : Genode::Register<64>
{
@ -147,6 +191,13 @@ struct Hw::Arm_64_cpu
struct As : Bitfield<36, 1> { };
);
SYSTEM_REGISTER(64, Tcr_el2, tcr_el2,
struct T0sz : Bitfield<0, 6> { };
struct Irgn0 : Bitfield<8, 2> { };
struct Orgn0 : Bitfield<10, 2> { };
struct Sh0 : Bitfield<12, 2> { };
);
struct Ttbr : Genode::Register<64>
{
struct Baddr : Bitfield<0, 48> { };
@ -154,10 +205,24 @@ struct Hw::Arm_64_cpu
};
SYSTEM_REGISTER(64, Ttbr0_el1, ttbr0_el1);
SYSTEM_REGISTER(64, Ttbr0_el2, ttbr0_el2);
SYSTEM_REGISTER(64, Ttbr1_el1, ttbr1_el1);
SYSTEM_REGISTER(64, Vbar_el1, vbar_el1);
SYSTEM_REGISTER(64, Vbar_el2, vbar_el2);
SYSTEM_REGISTER(32, Vtcr_el2, vtcr_el2,
struct T0sz : Bitfield<0, 6> {};
struct Sl0 : Bitfield<6, 2> {};
);
SYSTEM_REGISTER(64, Vttbr_el2, vttbr_el2,
struct CnP : Bitfield<0, 1> { };
struct Ba : Bitfield<1, 47> { };
struct Asid : Bitfield<48, 8> { };
);
static inline unsigned current_privilege_level() {
return Current_el::El::get(Current_el::read()); }
@ -175,6 +240,8 @@ struct Hw::Arm_64_cpu
SYSTEM_REGISTER(64, Cntpct_el0, cntpct_el0);
SYSTEM_REGISTER(32, Cntp_tval_el0, cntp_tval_el0);
SYSTEM_REGISTER(32, Cntkctl_el1, cntkctl_el1);
SYSTEM_REGISTER(32, Cnthctl_el2, cnthctl_el2);
using Cntfrq = Cntfrq_el0;
using Cntp_ctl = Cntp_ctl_el0;

View File

@ -35,6 +35,8 @@ namespace Hw::Imx8q_evk_board {
enum {
IRQ_CONTROLLER_DISTR_BASE = 0x38800000,
IRQ_CONTROLLER_DISTR_SIZE = 0x10000,
IRQ_CONTROLLER_VT_CPU_BASE = 0x31020000,
IRQ_CONTROLLER_VT_CPU_SIZE = 0x2000,
IRQ_CONTROLLER_REDIST_BASE = 0x38880000,
IRQ_CONTROLLER_REDIST_SIZE = 0xc0000,
};

View File

@ -0,0 +1,31 @@
/*
* \brief Memory map specific to ARM 64
* \author Stefan Kalkowski
* \date 2019-09-02
*/
/*
* Copyright (C) 2016-2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef _SRC__LIB__HW__SPEC__ARM_64__MEMORY_MAP_H_
#define _SRC__LIB__HW__SPEC__ARM_64__MEMORY_MAP_H_
#include <hw/util.h>
namespace Hw {
namespace Mm {
template <typename T>
Genode::addr_t el2_addr(T t)
{
static constexpr Genode::addr_t OFF = 0xffffff8000000000UL;
return (Genode::addr_t)t - OFF;
}
};
};
#endif /* _SRC__LIB__HW__SPEC__ARM_64__MEMORY_MAP_H_ */

View File

@ -45,6 +45,9 @@ Memory_region const Hw::Mm::system_exception_vector() {
Memory_region const Hw::Mm::hypervisor_exception_vector() {
return Memory_region(0xfff10000UL, 0x1000UL); }
Memory_region const Hw::Mm::hypervisor_stack() {
return Memory_region(0xfff12000UL, 0x1000UL); }
Memory_region const Hw::Mm::boot_info() {
return Memory_region(0xfffe0000UL, 0x1000UL); }

View File

@ -20,12 +20,14 @@ using Hw::Memory_region;
using Genode::addr_t;
using Genode::Native_utcb;
static constexpr addr_t USER_START = Genode::user_utcb_main_thread()
+ sizeof(Native_utcb);
static constexpr addr_t USER_START = Genode::user_utcb_main_thread()
+ sizeof(Native_utcb);
static constexpr addr_t USER_END = (1ULL << (39 - 1));
static constexpr addr_t KERNEL_START = 0xffffffc000000000UL;
Memory_region const Hw::Mm::user() {
return Memory_region(USER_START, 0x800000000000 - USER_START); }
return Memory_region(USER_START, USER_END - USER_START); }
Memory_region const Hw::Mm::core_heap() {
return Memory_region(0xffffffd000000000UL, 0x1000000000UL); }
@ -45,5 +47,11 @@ Memory_region const Hw::Mm::core_mmio() {
Memory_region const Hw::Mm::boot_info() {
return Memory_region(0xffffffe040000000UL, 0x1000UL); }
Memory_region const Hw::Mm::hypervisor_exception_vector() {
return Memory_region(0xffffffe050000000UL, 0x2000UL); }
Memory_region const Hw::Mm::hypervisor_stack() {
return Memory_region(0xffffffe050003000UL, 0x1000UL); }
Memory_region const Hw::Mm::supervisor_exception_vector() {
return Memory_region(KERNEL_START, 0x1000UL); }