vmm: unify armv7/v8 virtualization

Fix #3638
This commit is contained in:
Stefan Kalkowski 2019-11-14 10:52:20 +01:00 committed by Christian Helmuth
parent 74e75d7fbc
commit 941e918b46
53 changed files with 1555 additions and 2648 deletions

View File

@ -27,59 +27,55 @@ namespace Genode
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;
Genode::uint64_t vttbr { 0 };
Genode::uint32_t sctrl { 0 };
Genode::uint32_t esr_el2 { 0 };
Genode::uint32_t hpfar_el2 { 0 };
Genode::uint32_t far_el2 { 0 };
Genode::uint32_t hifar { 0 };
Genode::uint32_t ttbcr { 0 };
Genode::uint32_t ttbr0 { 0 };
Genode::uint32_t ttbr1 { 0 };
Genode::uint32_t prrr { 0 };
Genode::uint32_t nmrr { 0 };
Genode::uint32_t dacr { 0 };
Genode::uint32_t dfsr { 0 };
Genode::uint32_t ifsr { 0 };
Genode::uint32_t adfsr { 0 };
Genode::uint32_t aifsr { 0 };
Genode::uint32_t dfar { 0 };
Genode::uint32_t ifar { 0 };
Genode::uint32_t cidr { 0 };
Genode::uint32_t tls1 { 0 };
Genode::uint32_t tls2 { 0 };
Genode::uint32_t tls3 { 0 };
Genode::uint32_t cpacr { 0 };
/**
* Fpu registers
*/
Genode::uint32_t fpscr;
Genode::uint64_t d0_d31[32];
Genode::uint32_t fpscr { 0 };
Genode::uint64_t d0_d31[32]{ 0 };
/**
* Timer related registers
*/
Genode::uint32_t timer_ctrl;
Genode::uint32_t timer_val;
bool timer_irq;
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 {};
/**
* PIC related registers
* Interrupt related values
*/
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;
struct Pic
{
unsigned last_irq { 1023 };
unsigned virtual_irq { 1023 };
} irqs {};
};
#endif /* _INCLUDE__SPEC__ARNDALE__VM_STATE_H_ */

View File

@ -11,6 +11,7 @@ INC_DIR += $(REP_DIR)/src/core/spec/arm/virtualization
# add C++ sources
SRC_CC += kernel/vm_thread_on.cc
SRC_CC += spec/arm/gicv2.cc
SRC_CC += spec/arm/virtualization/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

View File

@ -0,0 +1,115 @@
/*
* \brief Parts of platform that are specific to ARM virtualization
* \author Martin Stein
* \author Stefan Kalkowski
* \date 2020-04-02
*/
/*
* Copyright (C) 2020 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 <platform.h>
static inline void prepare_nonsecure_world(unsigned long timer_freq)
{
using Cpu = Hw::Arm_cpu;
/* if we are already in HYP mode we're done (depends on u-boot version) */
if (Cpu::Psr::M::get(Cpu::Cpsr::read()) == Cpu::Psr::M::HYP)
return;
/* ARM generic timer counter freq needs to be set in secure mode */
Cpu::Cntfrq::write(timer_freq);
/*
* enable coprocessor 10 + 11 access and SMP bit access in auxiliary control
* register for non-secure world
*/
Cpu::Nsacr::access_t nsacr = 0;
Cpu::Nsacr::Cpnsae10::set(nsacr, 1);
Cpu::Nsacr::Cpnsae11::set(nsacr, 1);
Cpu::Nsacr::Ns_smp::set(nsacr, 1);
Cpu::Nsacr::write(nsacr);
asm volatile (
"msr sp_mon, sp \n" /* copy current mode's sp */
"msr lr_mon, lr \n" /* copy current mode's lr */
"cps #22 \n" /* switch to monitor mode */
);
Cpu::Scr::access_t scr = 0;
Cpu::Scr::Ns::set(scr, 1);
Cpu::Scr::Fw::set(scr, 1);
Cpu::Scr::Aw::set(scr, 1);
Cpu::Scr::Scd::set(scr, 1);
Cpu::Scr::Hce::set(scr, 1);
Cpu::Scr::Sif::set(scr, 1);
Cpu::Scr::write(scr);
}
static inline void prepare_hypervisor(Genode::addr_t table)
{
using Cpu = Hw::Arm_cpu;
/* set hypervisor exception vector */
Cpu::Hvbar::write(Hw::Mm::hypervisor_exception_vector().base);
/* set hypervisor's translation table */
Cpu::Httbr_64bit::write(table);
Cpu::Ttbcr::access_t ttbcr = 0;
Cpu::Ttbcr::Irgn0::set(ttbcr, 1);
Cpu::Ttbcr::Orgn0::set(ttbcr, 1);
Cpu::Ttbcr::Sh0::set(ttbcr, 2);
Cpu::Ttbcr::Eae::set(ttbcr, 1);
/* prepare MMU usage by hypervisor code */
Cpu::Htcr::write(ttbcr);
/* don't trap on cporocessor 10 + 11, but all others */
Cpu::Hcptr::access_t hcptr = 0;
Cpu::Hcptr::Tcp<0>::set(hcptr, 1);
Cpu::Hcptr::Tcp<1>::set(hcptr, 1);
Cpu::Hcptr::Tcp<2>::set(hcptr, 1);
Cpu::Hcptr::Tcp<3>::set(hcptr, 1);
Cpu::Hcptr::Tcp<4>::set(hcptr, 1);
Cpu::Hcptr::Tcp<5>::set(hcptr, 1);
Cpu::Hcptr::Tcp<6>::set(hcptr, 1);
Cpu::Hcptr::Tcp<7>::set(hcptr, 1);
Cpu::Hcptr::Tcp<8>::set(hcptr, 1);
Cpu::Hcptr::Tcp<9>::set(hcptr, 1);
Cpu::Hcptr::Tcp<12>::set(hcptr, 1);
Cpu::Hcptr::Tcp<13>::set(hcptr, 1);
Cpu::Hcptr::Tta::set(hcptr, 1);
Cpu::Hcptr::write(hcptr);
enum Memory_attributes {
DEVICE_MEMORY = 0x04,
NORMAL_MEMORY_UNCACHED = 0x44,
NORMAL_MEMORY_CACHED = 0xff,
};
Cpu::Mair0::access_t mair0 = 0;
Cpu::Mair0::Attr0::set(mair0, NORMAL_MEMORY_UNCACHED);
Cpu::Mair0::Attr1::set(mair0, DEVICE_MEMORY);
Cpu::Mair0::Attr2::set(mair0, NORMAL_MEMORY_CACHED);
Cpu::Mair0::Attr3::set(mair0, DEVICE_MEMORY);
Cpu::Hmair0::write(mair0);
Cpu::Vtcr::access_t vtcr = ttbcr;
Cpu::Vtcr::Sl0::set(vtcr, 1); /* set to starting level 1 */
Cpu::Vtcr::write(vtcr);
Cpu::Sctlr::access_t sctlr = Cpu::Sctlr::read();
Cpu::Sctlr::C::set(sctlr, 1);
Cpu::Sctlr::I::set(sctlr, 1);
Cpu::Sctlr::V::set(sctlr, 1);
Cpu::Sctlr::M::set(sctlr, 1);
Cpu::Sctlr::Z::set(sctlr, 1);
Cpu::Hsctlr::write(sctlr);
}

View File

@ -11,6 +11,7 @@
* under the terms of the GNU Affero General Public License version 3.
*/
#include <spec/arm/cortex_a7_a15_virtualization.h>
#include <platform.h>
extern "C" void * _start_setup_stack; /* entrypoint for non-boot CPUs */
@ -25,110 +26,6 @@ Bootstrap::Platform::Board::Board()
Memory_region { UART_2_MMIO_BASE, UART_2_MMIO_SIZE }) { }
static inline void prepare_nonsecure_world()
{
using Cpu = Hw::Arm_cpu;
/* if we are already in HYP mode we're done (depends on u-boot version) */
if (Cpu::Psr::M::get(Cpu::Cpsr::read()) == Cpu::Psr::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;
Cpu::Cntfrq::write(24000000);
/*
* enable coprocessor 10 + 11 access and SMP bit access in auxiliary control
* register for non-secure world
*/
Cpu::Nsacr::access_t nsacr = 0;
Cpu::Nsacr::Cpnsae10::set(nsacr, 1);
Cpu::Nsacr::Cpnsae11::set(nsacr, 1);
Cpu::Nsacr::Ns_smp::set(nsacr, 1);
Cpu::Nsacr::write(nsacr);
asm volatile (
"msr sp_mon, sp \n" /* copy current mode's sp */
"msr lr_mon, lr \n" /* copy current mode's lr */
"cps #22 \n" /* switch to monitor mode */
);
Cpu::Scr::access_t scr = 0;
Cpu::Scr::Ns::set(scr, 1);
Cpu::Scr::Fw::set(scr, 1);
Cpu::Scr::Aw::set(scr, 1);
Cpu::Scr::Scd::set(scr, 1);
Cpu::Scr::Hce::set(scr, 1);
Cpu::Scr::Sif::set(scr, 1);
Cpu::Scr::write(scr);
}
static inline void prepare_hypervisor(Genode::addr_t table)
{
using Cpu = Hw::Arm_cpu;
/* set hypervisor exception vector */
Cpu::Hvbar::write(Hw::Mm::hypervisor_exception_vector().base);
/* set hypervisor's translation table */
Cpu::Httbr_64bit::write(table);
Cpu::Ttbcr::access_t ttbcr = 0;
Cpu::Ttbcr::Irgn0::set(ttbcr, 1);
Cpu::Ttbcr::Orgn0::set(ttbcr, 1);
Cpu::Ttbcr::Sh0::set(ttbcr, 2);
Cpu::Ttbcr::Eae::set(ttbcr, 1);
/* prepare MMU usage by hypervisor code */
Cpu::Htcr::write(ttbcr);
/* don't trap on cporocessor 10 + 11, but all others */
Cpu::Hcptr::access_t hcptr = 0;
Cpu::Hcptr::Tcp<0>::set(hcptr, 1);
Cpu::Hcptr::Tcp<1>::set(hcptr, 1);
Cpu::Hcptr::Tcp<2>::set(hcptr, 1);
Cpu::Hcptr::Tcp<3>::set(hcptr, 1);
Cpu::Hcptr::Tcp<4>::set(hcptr, 1);
Cpu::Hcptr::Tcp<5>::set(hcptr, 1);
Cpu::Hcptr::Tcp<6>::set(hcptr, 1);
Cpu::Hcptr::Tcp<7>::set(hcptr, 1);
Cpu::Hcptr::Tcp<8>::set(hcptr, 1);
Cpu::Hcptr::Tcp<9>::set(hcptr, 1);
Cpu::Hcptr::Tcp<12>::set(hcptr, 1);
Cpu::Hcptr::Tcp<13>::set(hcptr, 1);
Cpu::Hcptr::Tta::set(hcptr, 1);
Cpu::Hcptr::Tcpac::set(hcptr, 1);
Cpu::Hcptr::write(hcptr);
enum Memory_attributes {
DEVICE_MEMORY = 0x04,
NORMAL_MEMORY_UNCACHED = 0x44,
NORMAL_MEMORY_CACHED = 0xff,
};
Cpu::Mair0::access_t mair0 = 0;
Cpu::Mair0::Attr0::set(mair0, NORMAL_MEMORY_UNCACHED);
Cpu::Mair0::Attr1::set(mair0, DEVICE_MEMORY);
Cpu::Mair0::Attr2::set(mair0, NORMAL_MEMORY_CACHED);
Cpu::Mair0::Attr3::set(mair0, DEVICE_MEMORY);
Cpu::Hmair0::write(mair0);
Cpu::Vtcr::access_t vtcr = ttbcr;
Cpu::Vtcr::Sl0::set(vtcr, 1); /* set to starting level 1 */
Cpu::Vtcr::write(vtcr);
Cpu::Sctlr::access_t sctlr = Cpu::Sctlr::read();
Cpu::Sctlr::C::set(sctlr, 1);
Cpu::Sctlr::I::set(sctlr, 1);
Cpu::Sctlr::V::set(sctlr, 1);
Cpu::Sctlr::M::set(sctlr, 1);
Cpu::Sctlr::Z::set(sctlr, 1);
Cpu::Hsctlr::write(sctlr);
}
static inline void switch_to_supervisor_mode()
{
using Cpsr = Hw::Arm_cpu::Psr;
@ -152,24 +49,24 @@ static inline void switch_to_supervisor_mode()
unsigned Bootstrap::Platform::enable_mmu()
{
using namespace ::Board;
static volatile bool primary_cpu = true;
static unsigned long timer_freq = 24000000;
/* locally initialize interrupt controller */
::Board::Pic pic { };
prepare_nonsecure_world();
volatile unsigned long * mct_control = (unsigned long*) 0x101C0240;
*mct_control = 0x100;
prepare_nonsecure_world(timer_freq);
prepare_hypervisor((addr_t)core_pd->table_base);
switch_to_supervisor_mode();
Cpu::Sctlr::init();
Cpu::Cpsr::init();
Cpu::invalidate_data_cache();
/* primary cpu wakes up all others */
if (primary_cpu && NR_OF_CPUS > 1) {
Cpu::invalidate_data_cache();
primary_cpu = false;
Cpu::wake_up_all_cpus(&_start_setup_stack);
}

View File

@ -13,6 +13,7 @@
#include <platform.h>
#include <spec/arm/imx_aipstz.h>
#include <spec/arm/cortex_a7_a15_virtualization.h>
extern "C" void * _start_setup_stack; /* entrypoint for non-boot CPUs */
static unsigned char hyp_mode_stack[1024]; /* hypervisor mode's kernel stack */
@ -160,108 +161,6 @@ Bootstrap::Platform::Board::Board()
}
static inline void prepare_nonsecure_world(unsigned long timer_freq)
{
using Cpu = Hw::Arm_cpu;
/* if we are already in HYP mode we're done (depends on u-boot version) */
if (Cpu::Psr::M::get(Cpu::Cpsr::read()) == Cpu::Psr::M::HYP)
return;
/* ARM generic timer counter freq needs to be set in secure mode */
Cpu::Cntfrq::write(timer_freq);
/*
* enable coprocessor 10 + 11 access and SMP bit access in auxiliary control
* register for non-secure world
*/
Cpu::Nsacr::access_t nsacr = 0;
Cpu::Nsacr::Cpnsae10::set(nsacr, 1);
Cpu::Nsacr::Cpnsae11::set(nsacr, 1);
Cpu::Nsacr::Ns_smp::set(nsacr, 1);
Cpu::Nsacr::write(nsacr);
asm volatile (
"msr sp_mon, sp \n" /* copy current mode's sp */
"msr lr_mon, lr \n" /* copy current mode's lr */
"cps #22 \n" /* switch to monitor mode */
);
Cpu::Scr::access_t scr = 0;
Cpu::Scr::Ns::set(scr, 1);
Cpu::Scr::Fw::set(scr, 1);
Cpu::Scr::Aw::set(scr, 1);
Cpu::Scr::Scd::set(scr, 1);
Cpu::Scr::Hce::set(scr, 1);
Cpu::Scr::Sif::set(scr, 1);
Cpu::Scr::write(scr);
}
static inline void prepare_hypervisor(Genode::addr_t table)
{
using Cpu = Hw::Arm_cpu;
/* set hypervisor exception vector */
Cpu::Hvbar::write(Hw::Mm::hypervisor_exception_vector().base);
/* set hypervisor's translation table */
Cpu::Httbr_64bit::write(table);
Cpu::Ttbcr::access_t ttbcr = 0;
Cpu::Ttbcr::Irgn0::set(ttbcr, 1);
Cpu::Ttbcr::Orgn0::set(ttbcr, 1);
Cpu::Ttbcr::Sh0::set(ttbcr, 2);
Cpu::Ttbcr::Eae::set(ttbcr, 1);
/* prepare MMU usage by hypervisor code */
Cpu::Htcr::write(ttbcr);
/* don't trap on cporocessor 10 + 11, but all others */
Cpu::Hcptr::access_t hcptr = 0;
Cpu::Hcptr::Tcp<0>::set(hcptr, 1);
Cpu::Hcptr::Tcp<1>::set(hcptr, 1);
Cpu::Hcptr::Tcp<2>::set(hcptr, 1);
Cpu::Hcptr::Tcp<3>::set(hcptr, 1);
Cpu::Hcptr::Tcp<4>::set(hcptr, 1);
Cpu::Hcptr::Tcp<5>::set(hcptr, 1);
Cpu::Hcptr::Tcp<6>::set(hcptr, 1);
Cpu::Hcptr::Tcp<7>::set(hcptr, 1);
Cpu::Hcptr::Tcp<8>::set(hcptr, 1);
Cpu::Hcptr::Tcp<9>::set(hcptr, 1);
Cpu::Hcptr::Tcp<12>::set(hcptr, 1);
Cpu::Hcptr::Tcp<13>::set(hcptr, 1);
Cpu::Hcptr::Tta::set(hcptr, 1);
Cpu::Hcptr::Tcpac::set(hcptr, 1);
Cpu::Hcptr::write(hcptr);
enum Memory_attributes {
DEVICE_MEMORY = 0x04,
NORMAL_MEMORY_UNCACHED = 0x44,
NORMAL_MEMORY_CACHED = 0xff,
};
Cpu::Mair0::access_t mair0 = 0;
Cpu::Mair0::Attr0::set(mair0, NORMAL_MEMORY_UNCACHED);
Cpu::Mair0::Attr1::set(mair0, DEVICE_MEMORY);
Cpu::Mair0::Attr2::set(mair0, NORMAL_MEMORY_CACHED);
Cpu::Mair0::Attr3::set(mair0, DEVICE_MEMORY);
Cpu::Hmair0::write(mair0);
Cpu::Vtcr::access_t vtcr = ttbcr;
Cpu::Vtcr::Sl0::set(vtcr, 1); /* set to starting level 1 */
Cpu::Vtcr::write(vtcr);
Cpu::Sctlr::access_t sctlr = Cpu::Sctlr::read();
Cpu::Sctlr::C::set(sctlr, 1);
Cpu::Sctlr::I::set(sctlr, 1);
Cpu::Sctlr::V::set(sctlr, 1);
Cpu::Sctlr::M::set(sctlr, 1);
Cpu::Sctlr::Z::set(sctlr, 1);
Cpu::Hsctlr::write(sctlr);
}
static inline void switch_to_supervisor_mode()
{
using Cpsr = Hw::Arm_cpu::Psr;

View File

@ -12,6 +12,7 @@
*/
#include <platform.h>
#include <spec/arm/cortex_a7_a15_virtualization.h>
extern "C" void * _start_setup_stack; /* entrypoint for non-boot CPUs */
static unsigned char hyp_mode_stack[1024]; /* hypervisor mode's kernel stack */
@ -30,108 +31,6 @@ Bootstrap::Platform::Board::Board()
Cpu_mmio::IRQ_CONTROLLER_VT_CTRL_SIZE }) {}
static inline void prepare_nonsecure_world(unsigned long timer_freq)
{
using Cpu = Hw::Arm_cpu;
/* if we are already in HYP mode we're done (depends on u-boot version) */
if (Cpu::Psr::M::get(Cpu::Cpsr::read()) == Cpu::Psr::M::HYP)
return;
/* ARM generic timer counter freq needs to be set in secure mode */
Cpu::Cntfrq::write(timer_freq);
/*
* enable coprocessor 10 + 11 access and SMP bit access in auxiliary control
* register for non-secure world
*/
Cpu::Nsacr::access_t nsacr = 0;
Cpu::Nsacr::Cpnsae10::set(nsacr, 1);
Cpu::Nsacr::Cpnsae11::set(nsacr, 1);
Cpu::Nsacr::Ns_smp::set(nsacr, 1);
Cpu::Nsacr::write(nsacr);
asm volatile (
"msr sp_mon, sp \n" /* copy current mode's sp */
"msr lr_mon, lr \n" /* copy current mode's lr */
"cps #22 \n" /* switch to monitor mode */
);
Cpu::Scr::access_t scr = 0;
Cpu::Scr::Ns::set(scr, 1);
Cpu::Scr::Fw::set(scr, 1);
Cpu::Scr::Aw::set(scr, 1);
Cpu::Scr::Scd::set(scr, 1);
Cpu::Scr::Hce::set(scr, 1);
Cpu::Scr::Sif::set(scr, 1);
Cpu::Scr::write(scr);
}
static inline void prepare_hypervisor(Genode::addr_t table)
{
using Cpu = Hw::Arm_cpu;
/* set hypervisor exception vector */
Cpu::Hvbar::write(Hw::Mm::hypervisor_exception_vector().base);
/* set hypervisor's translation table */
Cpu::Httbr_64bit::write(table);
Cpu::Ttbcr::access_t ttbcr = 0;
Cpu::Ttbcr::Irgn0::set(ttbcr, 1);
Cpu::Ttbcr::Orgn0::set(ttbcr, 1);
Cpu::Ttbcr::Sh0::set(ttbcr, 2);
Cpu::Ttbcr::Eae::set(ttbcr, 1);
/* prepare MMU usage by hypervisor code */
Cpu::Htcr::write(ttbcr);
/* don't trap on cporocessor 10 + 11, but all others */
Cpu::Hcptr::access_t hcptr = 0;
Cpu::Hcptr::Tcp<0>::set(hcptr, 1);
Cpu::Hcptr::Tcp<1>::set(hcptr, 1);
Cpu::Hcptr::Tcp<2>::set(hcptr, 1);
Cpu::Hcptr::Tcp<3>::set(hcptr, 1);
Cpu::Hcptr::Tcp<4>::set(hcptr, 1);
Cpu::Hcptr::Tcp<5>::set(hcptr, 1);
Cpu::Hcptr::Tcp<6>::set(hcptr, 1);
Cpu::Hcptr::Tcp<7>::set(hcptr, 1);
Cpu::Hcptr::Tcp<8>::set(hcptr, 1);
Cpu::Hcptr::Tcp<9>::set(hcptr, 1);
Cpu::Hcptr::Tcp<12>::set(hcptr, 1);
Cpu::Hcptr::Tcp<13>::set(hcptr, 1);
Cpu::Hcptr::Tta::set(hcptr, 1);
Cpu::Hcptr::Tcpac::set(hcptr, 1);
Cpu::Hcptr::write(hcptr);
enum Memory_attributes {
DEVICE_MEMORY = 0x04,
NORMAL_MEMORY_UNCACHED = 0x44,
NORMAL_MEMORY_CACHED = 0xff,
};
Cpu::Mair0::access_t mair0 = 0;
Cpu::Mair0::Attr0::set(mair0, NORMAL_MEMORY_UNCACHED);
Cpu::Mair0::Attr1::set(mair0, DEVICE_MEMORY);
Cpu::Mair0::Attr2::set(mair0, NORMAL_MEMORY_CACHED);
Cpu::Mair0::Attr3::set(mair0, DEVICE_MEMORY);
Cpu::Hmair0::write(mair0);
Cpu::Vtcr::access_t vtcr = ttbcr;
Cpu::Vtcr::Sl0::set(vtcr, 1); /* set to starting level 1 */
Cpu::Vtcr::write(vtcr);
Cpu::Sctlr::access_t sctlr = Cpu::Sctlr::read();
Cpu::Sctlr::C::set(sctlr, 1);
Cpu::Sctlr::I::set(sctlr, 1);
Cpu::Sctlr::V::set(sctlr, 1);
Cpu::Sctlr::M::set(sctlr, 1);
Cpu::Sctlr::Z::set(sctlr, 1);
Cpu::Hsctlr::write(sctlr);
}
static inline void switch_to_supervisor_mode()
{
using Cpsr = Hw::Arm_cpu::Psr;
@ -146,7 +45,7 @@ static inline void switch_to_supervisor_mode()
"msr lr_svc, lr \n" /* copy current mode's lr */
"adr lr, 1f \n" /* load exception return address */
"msr elr_hyp, lr \n" /* copy current mode's lr to hyp lr */
"mov sp, %[stack] \n" /* copy to hyp stack pointer */
"mov sp, %[stack] \n" /* copy to hyp stack pointer */
"msr spsr_cxfs, %[cpsr] \n" /* set psr for supervisor mode */
"eret \n" /* exception return */
"1:":: [cpsr] "r" (cpsr), [stack] "r" (&hyp_mode_stack));
@ -156,7 +55,6 @@ static inline void switch_to_supervisor_mode()
unsigned Bootstrap::Platform::enable_mmu()
{
static volatile bool primary_cpu = true;
static unsigned long timer_freq = Cpu::Cntfrq::read();
/* locally initialize interrupt controller */
::Board::Pic pic { };
@ -168,7 +66,6 @@ unsigned Bootstrap::Platform::enable_mmu()
Cpu::wake_up_all_cpus(&_start_setup_stack);
}
if (false) prepare_nonsecure_world(timer_freq);
prepare_hypervisor((addr_t)core_pd->table_base);
switch_to_supervisor_mode();

View File

@ -0,0 +1,74 @@
/*
* \brief Board wirh ARM virtualization support
* \author Stefan Kalkowski
* \date 2019-11-12
*/
/*
* 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__BOARD_H_
#define _CORE__SPEC__ARM__VIRTUALIZATION__BOARD_H_
#include <translation_table.h>
#include <kernel/configuration.h>
#include <kernel/irq.h>
namespace Board {
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__ARM__VIRTUALIZATION__BOARD_H_ */

View File

@ -101,6 +101,11 @@ _host_to_vm:
add r1, r0, #4
vldm r1!, {d0-d15}
vldm r1!, {d16-d31}
ldm r1!, {r2-r7}
mcrr p15, 4, r2, r3, c14 /* write cntvoff */
mcrr p15, 3, r4, r5, c14 /* write cntv_cval */
mcr p15, 0, r6, c14, c3, 1 /* write cntv_ctl */
mcr p15, 0, r7, c14, c1, 0 /* write cntkctl */
ldmia sp, {r0-r12} /* load vm's r0-r12 */
eret
@ -111,9 +116,7 @@ _vm_to_host:
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 */
mov r1, #0xf
lsl r1, #20
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 */
@ -124,21 +127,29 @@ _vm_to_host:
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 */
mrc p15, 0, r11, c10, c2, 0 /* read PRRR */
mrc p15, 0, r12, c10, c2, 1 /* read NMRR */
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, r0, #4
mrc p15, 0, r3, c3, c0, 0 /* read DACR */
mrc p15, 0, r4, c5, c0, 0 /* read DFSR */
mrc p15, 0, r5, c5, c0, 1 /* read IFSR */
mrc p15, 0, r6, c5, c1, 0 /* read ADFSR */
mrc p15, 0, r7, c5, c1, 1 /* read AIFSR */
mrc p15, 0, r8, c6, c0, 0 /* read DFAR */
mrc p15, 0, r9, c6, c0, 2 /* read IFAR */
mrc p15, 0, r10, c13, c0, 1 /* read CIDR */
mrc p15, 0, r11, c13, c0, 2 /* read TLS1 */
mrc p15, 0, r12, c13, c0, 3 /* read TLS2 */
stm r0!, {r3-r12}
mrc p15, 0, r3, c13, c0, 4 /* read TLS3 */
mrc p15, 0, r4, c1, c0, 2 /* read CPACR */
stm r0!, {r3, r4}
mov r3, #0xf
lsl r3, #20
mcr p15, 0, r3, c1, c0, 2 /* write CPACR */
mov r3, #1 /* clear fpu exception state */
lsl r3, #30
vmsr fpexc, r3
@ -146,6 +157,11 @@ _vm_to_host:
stmia r0!, {r4}
vstm r0!, {d0-d15}
vstm r0!, {d16-d31}
mrrc p15, 4, r3, r4, c14 /* read cntvoff */
mrrc p15, 3, r5, r6, c14 /* read cntv_cval */
mrc p15, 0, r7, c14, c3, 1 /* write cntv_ctl */
mrc p15, 0, r8, c14, c1, 0 /* write cntkctl */
stm r0!, {r3-r8}
add r0, sp, #13*4
ldr r3, _vt_host_context_ptr
ldr sp, [r3]

View File

@ -55,139 +55,42 @@ struct Host_context {
} vt_host_context;
struct Kernel::Vm_irq : Kernel::Irq
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()
{
Vm_irq(unsigned const irq)
:
Kernel::Irq(irq, cpu_pool().executing_cpu().irq_pool())
{ }
/**
* A VM interrupt gets injected into the VM scheduled on the current CPU
*/
void occurred() override
{
Cpu_job & job = cpu_pool().executing_cpu().scheduled_job();
Vm *vm = dynamic_cast<Vm*>(&job);
if (!vm)
Genode::raw("VM timer interrupt while VM is not runnning!");
else
vm->inject_irq(_irq_nr);
}
};
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);
}
struct Kernel::Virtual_pic : Genode::Mmio
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()
{
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 <unsigned SLOT>
struct Gich_lr : Register<0x100 + SLOT*4, 32> { };
Vm_irq irq { Board::VT_MAINTAINANCE_IRQ };
Virtual_pic()
: Genode::Mmio(Genode::Platform::mmio_to_virt(Board::Cpu_mmio::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<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);
}
/**
* Load the virtual interrupt controller state from VM state
*/
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]);
}
};
struct Kernel::Virtual_timer
{
Vm_irq irq { 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)
{
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));
}
};
irq.disable();
asm volatile("mcr p15, 0, %0, c14, c3, 1" :: "r" (0));
asm volatile("mcr p15, 0, %0, c14, c1, 0" :: "r" (0b11));
}
using Vmid_allocator = Genode::Bit_allocator<256>;
@ -217,7 +120,6 @@ Kernel::Vm::Vm(unsigned, /* FIXME: smp support */
_vcpu_context(cpu_pool().primary_cpu())
{
affinity(cpu_pool().primary_cpu());
Virtual_pic::pic().irq.enable();
vt_host_context.sp = _cpu->stack_start();
vt_host_context.ttbr0 = Cpu::Ttbr0_64bit::read();
@ -235,12 +137,9 @@ Kernel::Vm::~Vm() { alloc().free(_id); }
void Kernel::Vm::exception(Cpu & cpu)
{
Virtual_timer::save(_state);
switch(_state.cpu_exception) {
case Genode::Cpu_state::INTERRUPT_REQUEST:
case Genode::Cpu_state::FAST_INTERRUPT_REQUEST:
_state.gic_irq = Board::VT_MAINTAINANCE_IRQ;
_interrupt(cpu.id());
break;
default:
@ -248,13 +147,18 @@ void Kernel::Vm::exception(Cpu & cpu)
_context.submit(1);
}
Virtual_pic::save(_state);
Virtual_timer::reset();
if (cpu.pic().ack_virtual_irq(_vcpu_context.pic))
inject_irq(Board::VT_MAINTAINANCE_IRQ);
_vcpu_context.vtimer_irq.disable();
}
void Kernel::Vm::proceed(Cpu &)
void Kernel::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
*/
@ -266,11 +170,8 @@ void Kernel::Vm::proceed(Cpu &)
* 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);
_state.esr_el2 = Cpu::Hstr::init();
_state.hpfar_el2 = Cpu::Hcr::init();
hypervisor_enter_vm(_state);
}
@ -278,7 +179,7 @@ void Kernel::Vm::proceed(Cpu &)
void Vm::inject_irq(unsigned irq)
{
_state.gic_irq = irq;
_state.irqs.last_irq = irq;
pause();
_context.submit(1);
}

View File

@ -14,28 +14,20 @@
#ifndef _CORE__SPEC__ARNDALE__BOARD_H_
#define _CORE__SPEC__ARNDALE__BOARD_H_
#include <hw/spec/arm/gicv2.h>
#include <spec/arm/virtualization/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>
#include <spec/arm/virtualization/board.h>
namespace Kernel { class Cpu; }
namespace Board {
using namespace Hw::Arndale_board;
using Pic = Hw::Gicv2;
struct Virtual_local_pic {};
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

@ -41,18 +41,10 @@ class Genode::Cpu : public Arm_v7_cpu
static access_t init()
{
/*
* allow cache (7), TLB (8) maintenance, and performance
* monitor (9), process/thread ID register (13) and timer (14)
* access.
* allow everything except c0, c11, c12, and c15 accesses.
*/
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);
@ -73,7 +65,6 @@ class Genode::Cpu : public Arm_v7_cpu
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()
{
@ -86,7 +77,6 @@ class Genode::Cpu : public Arm_v7_cpu
Twe::set(v, 1);
Tidcp::set(v, 1);
Tac::set(v, 1);
Tvm::set(v, 1);
return v;
};
};

View File

@ -18,10 +18,7 @@
#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; }
#include <spec/arm/virtualization/board.h>
namespace Board {
using namespace Hw::Imx7d_sabre_board;
@ -29,13 +26,6 @@ namespace Board {
struct Virtual_local_pic {};
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

@ -18,9 +18,7 @@
#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>
#include <spec/arm/virtualization/board.h>
namespace Board {
using namespace Hw::Imx8q_evk_board;
@ -29,59 +27,8 @@ namespace Board {
TIMER_IRQ = 14 + 16,
VT_TIMER_IRQ = 11 + 16,
VT_MAINTAINANCE_IRQ = 9 + 16,
VCPU_MAX = 16
VCPU_MAX = 4
};
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,8 +18,7 @@
#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>
#include <spec/arm/virtualization/board.h>
namespace Kernel { class Cpu; }
@ -34,13 +33,6 @@ namespace Board {
VT_MAINTAINANCE_IRQ = 25,
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 /* _SRC__CORE__SPEC__VIRT__QEMU_H_ */

View File

@ -250,7 +250,7 @@ class Hw::Long_translation_table
| Attribute_index::create(f)
| Not_global::bits(!f.global)
| Base::Shareability::bits(
Base::Shareability::OUTER_SHAREABLE)
Base::Shareability::INNER_SHAREABLE)
| Base::Output_address::masked(pa)
| Base::Access_flag::bits(1)
| Descriptor::Valid::bits(1)
@ -270,7 +270,7 @@ class Hw::Long_translation_table
addr_t const pa)
{
return Base::Shareability::bits(
Base::Shareability::NON_SHAREABLE)
Base::Shareability::INNER_SHAREABLE)
| Base::Output_address::masked(pa)
| Base::Access_flag::bits(1)
| Descriptor::Valid::bits(1)

View File

@ -87,33 +87,56 @@ if { [have_spec arm] } {
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
exec >& /dev/null wget -c -O bin/linux http://genode.org/files/release-20.05/linux-arm32
}
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
exec >& /dev/null wget -c -O bin/dtb http://genode.org/files/release-20.05/dtb-arm32-virt
}
if {![file exists bin/initrd]} {
puts "Download initramfs ..."
exec >& /dev/null wget -c -O bin/initrd http://genode.org/files/release-20.05/initrd-arm32
}
#
# 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.
# To obtain the linux kernel, do the following steps:
#
# wget https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.3.10.tar.xz
#
# tar -xJf linux-5.3.10.tar.xz
# cd linux-5.3.10
#
# make O=../build-linux-aarch32 ARCH=arm CROSS_COMPILE=/usr/local/genode/tool/current/bin/genode-arm- defconfig
# make O=../build-linux-aarch32 ARCH=arm CROSS_COMPILE=/usr/local/genode/tool/current/bin/genode-arm- -j32
#
# copy ../build-linux-aarch32/arch/arm/boot/zImage to your build directory in 'bin/linux'
#
# 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 get the dtb (device-tree-binary), you have to compile the file:
# repos/os/src/server/vmm/spec/arm_v7/virt.dts with the dtc compiler:
# dtc repos/os/src/server/vmm/spec/arm_v7/virt.dts > bin/dtb
#
# 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=<cross_compiler_prefix> vexpress_config
# ! make ARCH=arm CROSS_COMPILE=<cross_compiler_prefix> -j8 Image
# ! make ARCH=arm CROSS_COMPILE=<cross_compiler_prefix> vexpress-v2p-ca15-tc1.dtb
# To construct the initrd do the following:
# * get and install gcc from bootlin:
# (https://toolchains.bootlin.com/downloads/releases/toolchains/armv7-eabihf/tarballs/)
# * build busybox
# wget https://busybox.net/downloads/busybox-1.31.1.tar.bz2
# tar xjf busybox-1.31.1.tar.bz2
# mkdir build-busybox-aarch32
# cd busybox-1.31.1
# make O=../build-busybox-aarch32 defconfig
# make O=../build-busybox-aarch32 menuconfig
#
# [*] Setting -> Build static binary (no shared libs)
#
# cd ../build-busybox-aarch32
# make CROSS_COMPILE=/opt/armv7-eabihf--uclibc--stable-2020.02-1/bin/arm-buildroot-linux-uclibcgnueabihf- install -j6
# * create ramdisk
# cd _install
# find . | cpio -H newc -o | gzip > ../initrd
#
}
@ -183,8 +206,8 @@ set boot_modules {
vmm
linux
dtb
initrd
}
append_if [have_spec arm_64] boot_modules initrd
build_boot_image $boot_modules
#

View File

@ -0,0 +1,203 @@
/*
* \brief VMM cpu object
* \author Stefan Kalkowski
* \date 2019-07-18
*/
/*
* 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 <cpu.h>
#include <vm.h>
#include <psci.h>
using Vmm::Cpu_base;
using Vmm::Cpu;
using Vmm::Gic;
Genode::Lock & Vmm::lock() { static Genode::Lock l {}; return l; }
Cpu_base::System_register::System_register(unsigned op0,
unsigned crn,
unsigned op1,
unsigned crm,
unsigned op2,
const char * name,
bool writeable,
Genode::addr_t v,
Genode::Avl_tree<System_register> & tree)
: _encoding(Iss::value(op0, crn, op1, crm, op2)),
_name(name),
_writeable(writeable),
_value(v)
{
tree.insert(this);
}
bool Cpu_base::_handle_sys_reg()
{
using Iss = System_register::Iss;
Iss::access_t v = _state.esr_el2;
System_register * reg = _reg_tree.first();
if (reg) reg = reg->find_by_encoding(Iss::mask_encoding(v));
if (!reg) {
Genode::error("ignore unknown system register access @ ip=", (void*)_state.ip, ":");
Genode::error(Iss::Direction::get(v) ? "read" : "write",
": "
"op0=", Iss::Opcode0::get(v), " "
"op1=", Iss::Opcode1::get(v), " "
"r", Iss::Register::get(v), " "
"crn=", Iss::Crn::get(v), " "
"crm=", Iss::Crm::get(v), " ",
"op2=", Iss::Opcode2::get(v));
if (Iss::Direction::get(v)) _state.reg(Iss::Register::get(v), 0);
_state.ip += sizeof(Genode::uint32_t);
return false;
}
if (Iss::Direction::get(v)) { /* read access */
_state.reg(Iss::Register::get(v), reg->read());
} else { /* write access */
if (!reg->writeable()) {
Genode::error("writing to system register ",
reg->name(), " not allowed!");
return false;
}
reg->write(_state.reg(Iss::Register::get(v)));
}
_state.ip += sizeof(Genode::uint32_t);
return true;
}
void Cpu_base::_handle_wfi()
{
_state.ip += sizeof(Genode::uint32_t);
if (_state.esr_el2 & 1) return; /* WFE */
_active = false;
_timer.schedule_timeout();
}
void Cpu_base::_handle_sync()
{
/* check device number*/
switch (Esr::Ec::get(_state.esr_el2)) {
case Esr::Ec::HVC:
_handle_hyper_call();
break;
case Esr::Ec::MRC_MCR: [[fallthrough]];
case Esr::Ec::MRS_MSR:
_handle_sys_reg();
break;
case Esr::Ec::DA:
_handle_data_abort();
break;
case Esr::Ec::WFI:
_handle_wfi();
return;
case Esr::Ec::BRK:
_handle_brk();
return;
default:
throw Exception("Unknown trap: ",
Esr::Ec::get(_state.esr_el2));
};
}
void Cpu_base::_handle_irq()
{
switch (_state.irqs.last_irq) {
case VTIMER_IRQ:
_timer.handle_irq();
break;
default:
_gic.handle_irq();
};
}
void Cpu_base::_handle_hyper_call()
{
switch(_state.reg(0)) {
case Psci::PSCI_VERSION:
_state.reg(0, Psci::VERSION);
return;
case Psci::MIGRATE_INFO_TYPE:
_state.reg(0, Psci::NOT_SUPPORTED);
return;
case Psci::PSCI_FEATURES:
_state.reg(0, Psci::NOT_SUPPORTED);
return;
case Psci::CPU_ON:
_vm.cpu((unsigned)_state.reg(1), [&] (Cpu & cpu) {
cpu.state().ip = _state.reg(2);
cpu.state().reg(0, _state.reg(3));
cpu.run();
});
_state.reg(0, Psci::SUCCESS);
return;
default:
Genode::warning("unknown hypercall! ", cpu_id());
dump();
};
}
void Cpu_base::_handle_data_abort()
{
_vm.bus().handle_memory_access(*static_cast<Cpu*>(this));
_state.ip += sizeof(Genode::uint32_t);
}
void Cpu_base::_update_state()
{
if (!_gic.pending_irq()) return;
_active = true;
_timer.cancel_timeout();
}
unsigned Cpu_base::cpu_id() const { return _vcpu_id.id; }
void Cpu_base::run() { _vm_session.run(_vcpu_id); }
void Cpu_base::pause() { _vm_session.pause(_vcpu_id); }
bool Cpu_base::active() const { return _active; }
Cpu_base::State & Cpu_base::state() const { return _state; }
Gic::Gicd_banked & Cpu_base::gic() { return _gic; }
void Cpu_base::recall()
{
Genode::Signal_transmitter(_vm_handler).submit();
};
Cpu_base::Cpu_base(Vm & vm,
Genode::Vm_connection & vm_session,
Mmio_bus & bus,
Gic & gic,
Genode::Env & env,
Genode::Heap & heap,
Genode::Entrypoint & ep)
: _vm(vm),
_vm_session(vm_session),
_heap(heap),
_vm_handler(*this, ep, *this, &Cpu_base::_handle_nothing),
_vcpu_id(_vm_session.with_upgrade([&]() {
return _vm_session.create_vcpu(heap, env, _vm_handler);
})),
_state(*((State*)env.rm().attach(_vm_session.cpu_state(_vcpu_id)))),
_gic(*this, gic, bus),
_timer(env, ep, _gic.irq(VTIMER_IRQ), *this) { }

View File

@ -0,0 +1,225 @@
/*
* \brief VMM cpu object
* \author Stefan Kalkowski
* \date 2019-07-18
*/
/*
* 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 _SRC__SERVER__VMM__CPU_BASE_H_
#define _SRC__SERVER__VMM__CPU_BASE_H_
#include <exception.h>
#include <generic_timer.h>
#include <base/env.h>
#include <base/heap.h>
#include <cpu/vm_state_virtualization.h>
#include <util/mmio.h>
#include <vm_session/connection.h>
namespace Vmm {
class Vm;
class Cpu_base;
Genode::Lock & lock();
}
class Vmm::Cpu_base
{
public:
struct State : Genode::Vm_state
{
Genode::uint64_t reg(unsigned idx) const;
void reg(unsigned idx, Genode::uint64_t v);
};
struct Esr : Genode::Register<32>
{
struct Ec : Bitfield<26, 6>
{
enum {
WFI = 0x1,
MRC_MCR = 0x3,
HVC = 0x16,
MRS_MSR = 0x18,
DA = 0x24,
BRK = 0x3c
};
};
};
Cpu_base(Vm & vm,
Genode::Vm_connection & vm_session,
Mmio_bus & bus,
Gic & gic,
Genode::Env & env,
Genode::Heap & heap,
Genode::Entrypoint & ep);
unsigned cpu_id() const;
void run();
void pause();
bool active() const;
State & state() const;
Gic::Gicd_banked & gic();
void dump();
void handle_exception();
void recall();
void initialize_boot(Genode::addr_t ip,
Genode::addr_t dtb);
template <typename FUNC>
void handle_signal(FUNC handler)
{
if (active()) {
pause();
handle_exception();
}
handler();
_update_state();
if (active()) run();
}
template <typename T>
struct Signal_handler : Genode::Vm_handler<Signal_handler<T>>
{
using Base = Genode::Vm_handler<Signal_handler<T>>;
Cpu_base & cpu;
T & obj;
void (T::*member)();
void handle()
{
try {
cpu.handle_signal([this] () { (obj.*member)(); });
} catch(Exception &e) {
Genode::error(e);
cpu.dump();
}
}
Signal_handler(Cpu_base & cpu,
Genode::Entrypoint & ep,
T & o,
void (T::*f)())
: Base(ep, *this, &Signal_handler::handle),
cpu(cpu), obj(o), member(f) {}
};
protected:
class System_register : public Genode::Avl_node<System_register>
{
private:
const Esr::access_t _encoding;
const char *_name;
const bool _writeable;
Genode::uint64_t _value;
public:
struct Iss : Esr
{
struct Direction : Bitfield<0, 1> {};
struct Crm : Bitfield<1, 4> {};
struct Register : Bitfield<5, 5> {};
struct Crn : Bitfield<10, 4> {};
struct Opcode1 : Bitfield<14, 3> {};
struct Opcode2 : Bitfield<17, 3> {};
struct Opcode0 : Bitfield<20, 2> {};
static access_t value(unsigned op0,
unsigned crn,
unsigned op1,
unsigned crm,
unsigned op2);
static access_t mask_encoding(access_t v);
};
System_register(unsigned op0,
unsigned crn,
unsigned op1,
unsigned crm,
unsigned op2,
const char * name,
bool writeable,
Genode::addr_t v,
Genode::Avl_tree<System_register> & tree);
System_register(unsigned crn,
unsigned op1,
unsigned crm,
unsigned op2,
const char * name,
bool writeable,
Genode::addr_t v,
Genode::Avl_tree<System_register> & tree)
: System_register(0, crn, op1, crm, op2,
name, writeable, v, tree) {}
const char * name() const { return _name; }
const bool writeable() const { return _writeable; }
System_register * find_by_encoding(Iss::access_t e)
{
if (e == _encoding) return this;
System_register * r =
Avl_node<System_register>::child(e > _encoding);
return r ? r->find_by_encoding(e) : nullptr;
}
virtual void write(Genode::addr_t v) {
_value = (Genode::addr_t)v; }
virtual Genode::addr_t read() const {
return (Genode::addr_t)(_value); }
/************************
** Avl node interface **
************************/
bool higher(System_register *r) {
return (r->_encoding > _encoding); }
};
bool _active { true };
Vm & _vm;
Genode::Vm_connection & _vm_session;
Genode::Heap & _heap;
Signal_handler<Cpu_base> _vm_handler;
Genode::Vm_session::Vcpu_id _vcpu_id;
State & _state;
Genode::Avl_tree<System_register> _reg_tree;
/***********************
** Local peripherals **
***********************/
Gic::Gicd_banked _gic;
Generic_timer _timer;
void _handle_nothing() {}
bool _handle_sys_reg();
void _handle_brk();
void _handle_wfi();
void _handle_sync();
void _handle_irq();
void _handle_data_abort();
void _handle_hyper_call();
void _update_state();
};
#endif /* _SRC__SERVER__VMM__CPU_BASE_H_ */

View File

@ -0,0 +1,93 @@
/*
* \brief VMM ARM Generic timer device model
* \author Stefan Kalkowski
* \date 2019-08-20
*/
/*
* 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 <cpu.h>
#include <generic_timer.h>
using Vmm::Generic_timer;
bool Generic_timer::_enabled() {
return Ctrl::Enabled::get(_cpu.state().timer.control); }
bool Generic_timer::_masked() {
return Ctrl::Imask::get(_cpu.state().timer.control); }
bool Generic_timer::_pending() {
return Ctrl::Istatus::get(_cpu.state().timer.control); }
void Generic_timer::_handle_timeout(Genode::Duration)
{
_cpu.handle_signal([this] (void) {
if (_enabled() && !_masked()) handle_irq();
});
}
Generic_timer::Generic_timer(Genode::Env & env,
Genode::Entrypoint & ep,
Gic::Irq & irq,
Cpu_base & cpu)
: _timer(env, ep),
_timeout(_timer, *this, &Generic_timer::_handle_timeout),
_irq(irq),
_cpu(cpu)
{
_cpu.state().timer.irq = true;
_irq.handler(*this);
}
void Generic_timer::schedule_timeout()
{
if (_pending()) {
handle_irq();
return;
}
if (_enabled()) {
if (_usecs_left()) {
_timeout.schedule(Genode::Microseconds(_usecs_left()));
} else _handle_timeout(Genode::Duration(Genode::Microseconds(0)));
}
}
void Generic_timer::cancel_timeout()
{
if (_timeout.scheduled()) _timeout.discard();
}
void Generic_timer::handle_irq()
{
_irq.assert();
_cpu.state().timer.irq = false;
}
void Generic_timer::eoi()
{
_cpu.state().timer.irq = true;
};
void Generic_timer::dump()
{
using namespace Genode;
log(" timer.ctl = ", Hex(_cpu.state().timer.control, Hex::PREFIX, Hex::PAD));
log(" timer.cmp = ", Hex(_cpu.state().timer.compare, Hex::PREFIX, Hex::PAD));
}

View File

@ -22,7 +22,7 @@
#include <util/register.h>
namespace Vmm {
class Cpu;
class Cpu_base;
class Generic_timer;
}
@ -33,7 +33,7 @@ class Vmm::Generic_timer : Gic::Irq::Irq_handler
Timer::Connection _timer;
Timer::One_shot_timeout<Generic_timer> _timeout;
Gic::Irq & _irq;
Cpu & _cpu;
Cpu_base & _cpu;
struct Ctrl : Genode::Register<32>
{
@ -56,7 +56,7 @@ class Vmm::Generic_timer : Gic::Irq::Irq_handler
Generic_timer(Genode::Env & env,
Genode::Entrypoint & ep,
Gic::Irq & irq,
Cpu & cpu);
Cpu_base & cpu);
void schedule_timeout();
void cancel_timeout();

View File

@ -159,13 +159,13 @@ void Gic::Gicd_banked::handle_irq()
irq(i).deassert();
_cpu.state().irqs.virtual_irq = 1023;
_cpu.state().irqs.virtual_irq = SPURIOUS;
}
bool Gic::Gicd_banked::pending_irq()
{
if (_cpu.state().irqs.virtual_irq != 1023) return true;
if (_cpu.state().irqs.virtual_irq != SPURIOUS) return true;
Irq * i = _gic._pending_list.highest_enabled();
Irq * j = _pending_list.highest_enabled();
@ -178,7 +178,7 @@ bool Gic::Gicd_banked::pending_irq()
}
Gic::Gicd_banked::Gicd_banked(Cpu & cpu, Gic & gic, Mmio_bus & bus)
Gic::Gicd_banked::Gicd_banked(Cpu_base & cpu, Gic & gic, Mmio_bus & bus)
: _cpu(cpu), _gic(gic)
{
for (unsigned i = 0; i < MAX_SGI; i++)
@ -187,11 +187,16 @@ Gic::Gicd_banked::Gicd_banked(Cpu & cpu, Gic & gic, Mmio_bus & bus)
for (unsigned i = 0; i < MAX_PPI; i++)
_ppi[i].construct(i+MAX_SGI, Irq::PPI, _pending_list);
_cpu.state().irqs.last_irq = 1023;
_cpu.state().irqs.virtual_irq = 1023;
_cpu.state().irqs.last_irq = SPURIOUS;
_cpu.state().irqs.virtual_irq = SPURIOUS;
_rdist.construct(0x80a0000 + (cpu.cpu_id()*0x20000), 0x20000, cpu.cpu_id(), Vm::last_cpu() == cpu.cpu_id());
bus.add(*_rdist);
if (gic.version() >= 3) {
_rdist.construct(GICR_MMIO_START +
(cpu.cpu_id()*0x20000), 0x20000,
cpu.cpu_id(),
Vm::last_cpu() == cpu.cpu_id());
bus.add(*_rdist);
}
}
@ -218,12 +223,18 @@ void Gic::Irq_reg::write(Address_range & access, Cpu & cpu, Register value)
}
Gic::Gic(const char * const name,
const Genode::uint64_t addr,
const Genode::uint64_t size,
Mmio_bus & bus,
Genode::Env & env)
: Mmio_device(name, addr, size)
unsigned Gic::version() { return _version; }
Gic::Gic(const char * const name,
const Genode::uint64_t addr,
const Genode::uint64_t size,
unsigned cpus,
unsigned version,
Genode::Vm_connection & vm,
Mmio_bus & bus,
Genode::Env & env)
: Mmio_device(name, addr, size), _cpu_cnt(cpus), _version(version)
{
add(_ctrl);
add(_typer);
@ -248,4 +259,6 @@ Gic::Gic(const char * const name,
_spi[i].construct(i+MAX_SGI+MAX_PPI, Irq::SPI, _pending_list);
bus.add(*this);
if (version < 3) vm.attach_pic(GICC_MMIO_START);
}

View File

@ -18,11 +18,15 @@
#include <base/env.h>
#include <drivers/defs/arm_v7.h>
#include <vm_session/connection.h>
#include <util/list.h>
#include <util/register.h>
#include <util/reconstructible.h>
namespace Vmm { class Gic; }
namespace Vmm {
class Cpu_base;
class Gic;
}
class Vmm::Gic : public Vmm::Mmio_device
{
@ -120,11 +124,11 @@ class Vmm::Gic : public Vmm::Mmio_device
void handle_irq();
bool pending_irq();
Gicd_banked(Cpu & cpu, Gic & gic, Mmio_bus & bus);
Gicd_banked(Cpu_base & cpu, Gic & gic, Mmio_bus & bus);
private:
Cpu & _cpu;
Cpu_base & _cpu;
Gic & _gic;
Genode::Constructible<Irq> _sgi[MAX_SGI];
Genode::Constructible<Irq> _ppi[MAX_PPI];
@ -137,7 +141,9 @@ class Vmm::Gic : public Vmm::Mmio_device
Mmio_register gicr_ctlr { "GICR_CTLR", Mmio_register::RO,
0x0, 4, 0b10010 };
Mmio_register gicr_typer { "GICR_TYPER", Mmio_register::RO,
0x8, 8, (Genode::uint64_t)cpu_id<<32 | cpu_id<<8 | (last ? 1<<4 : 0) };
0x8, 8,
(Genode::uint64_t)cpu_id<<32 |
cpu_id<<8 | (last ? 1<<4 : 0) };
Mmio_register gicr_waker { "GICR_WAKER", Mmio_register::RO,
0x14, 4, 0 };
Mmio_register gicr_pidr2 { "GICR_PIDR2", Mmio_register::RO,
@ -242,9 +248,14 @@ class Vmm::Gic : public Vmm::Mmio_device
Genode::Constructible<Redistributor> _rdist;
};
unsigned version();
Gic(const char * const name,
const Genode::uint64_t addr,
const Genode::uint64_t size,
unsigned cpus,
unsigned version,
Genode::Vm_connection & vm,
Mmio_bus & bus,
Genode::Env & env);
@ -254,8 +265,8 @@ class Vmm::Gic : public Vmm::Mmio_device
Genode::Constructible<Irq> _spi[MAX_SPI];
Irq::List _pending_list;
unsigned _cpu_cnt { 2 }; /* FIXME: smp support */
unsigned _version { 3 }; /* FIXME: version support */
unsigned _cpu_cnt;
unsigned _version;
struct Gicd_ctlr : Genode::Register<32>, Mmio_register
{
@ -282,7 +293,8 @@ class Vmm::Gic : public Vmm::Mmio_device
Gicd_typer(unsigned cpus)
: Mmio_register("GICD_TYPER", Mmio_register::RO, 0x4, 4,
It_lines_number::bits(31) | Cpu_number::bits(cpus-1) | Id_bits::bits(9)) {}
It_lines_number::bits(31) |
Cpu_number::bits(cpus-1) | Id_bits::bits(9)) {}
} _typer { _cpu_cnt };
struct Gicd_iidr : Genode::Register<32>, Mmio_register

View File

@ -112,11 +112,9 @@ void Vmm::Mmio_bus::handle_memory_access(Vmm::Cpu & cpu)
Mmio_device & dev = get<Mmio_device>(bus_range);
Address_range dev_range(ipa - dev.start,width);
if (wr) {
dev.write(dev_range, cpu, (idx == 31) ? 0 : state.r[idx]);
dev.write(dev_range, cpu, state.reg(idx));
} else {
if (idx > 30)
throw Exception("Wrong register index when reading ", bus_range);
state.r[idx] = dev.read(dev_range, cpu);
state.reg(idx, dev.read(dev_range, cpu));
}
} catch(Exception & e) {
Genode::warning(e);

View File

@ -1,77 +1,53 @@
/*
* \brief Driver for the Versatile Express A9X4 board
* \author Martin stein
* \date 2011-11-03
* \brief VMM address space utility
* \author Stefan Kalkowski
* \date 2019-11-13
*/
/*
* Copyright (C) 2011-2017 Genode Labs GmbH
* 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 _INCLUDE__PLATFORM__VEA9X4__DRIVERS__BOARD_BASE_H_
#define _INCLUDE__PLATFORM__VEA9X4__DRIVERS__BOARD_BASE_H_
#ifndef _SRC__SERVER__VMM__BOARD_H_
#define _SRC__SERVER__VMM__BOARD_H_
namespace Vea9x4 { struct Board; }
namespace Vmm {
enum {
SIZE_1_MB = 1024 * 1024,
KERNEL_OFFSET = 54 * SIZE_1_MB,
DTB_OFFSET = 64 * SIZE_1_MB,
INITRD_OFFSET = 96 * SIZE_1_MB,
/**
* Driver for the Versatile Express A9X4 board
*
* Implies the uATX motherboard and the CoreTile Express A9X4 daughterboard
*/
struct Vea9x4::Board
{
enum
{
/* MMIO */
MMIO_0_BASE = 0x10000000,
MMIO_0_SIZE = 0x10000000,
MMIO_1_BASE = 0x4C000000,
MMIO_1_SIZE = 0x04000000,
GIC_VERSION = 2,
GICD_MMIO_START = 0x8000000,
GICD_MMIO_SIZE = 0x10000,
GICC_MMIO_START = 0x8010000,
GICR_MMIO_START = 0x80a0000,
GICR_MMIO_SIZE = 0xf60000,
/* RAM */
RAM_0_BASE = 0x60000000,
RAM_0_SIZE = 0x20000000,
RAM_1_BASE = 0x84000000,
RAM_1_SIZE = 0x1c000000,
RAM_2_BASE = 0x48000000,
RAM_2_SIZE = 0x02000000,
PL011_MMIO_START = 0x9000000,
PL011_MMIO_SIZE = 0x1000,
PL011_IRQ = 33,
/* UART */
PL011_0_MMIO_BASE = MMIO_0_BASE + 0x9000,
PL011_0_MMIO_SIZE = 0x1000,
PL011_0_CLOCK = 24*1000*1000,
PL011_0_IRQ = 37,
PL011_1_IRQ = 38,
PL011_2_IRQ = 39,
PL011_3_IRQ = 40,
VIRTIO_CONSOLE_MMIO_START = 0xa000000,
VIRTIO_CONSOLE_MMIO_SIZE = 0x200,
VIRTIO_CONSOLE_IRQ = 48,
/* timer/counter */
SP804_0_1_MMIO_BASE = MMIO_0_BASE + 0x11000,
SP804_0_1_MMIO_SIZE = 0x1000,
SP804_0_1_CLOCK = 1000*1000,
SP804_0_1_IRQ = 34,
VIRTIO_NET_MMIO_START = 0xa000200,
VIRTIO_NET_MMIO_SIZE = 0x200,
VIRTIO_NET_IRQ = 49,
/* PS2 */
KMI_0_IRQ = 44,
KMI_1_IRQ = 45,
RAM_START = 0x40000000,
RAM_SIZE = 128 * 1024 *1024,
/* LAN */
LAN9118_IRQ = 47,
VTIMER_IRQ = 27,
/* card reader */
PL180_0_IRQ = 9,
PL180_1_IRQ = 10,
/* CPU */
CORTEX_A9_PRIVATE_MEM_BASE = 0x1e000000,
CORTEX_A9_PRIVATE_MEM_SIZE = 0x2000,
CORTEX_A9_PRIVATE_TIMER_CLK = 200010000,
MAX_CPUS = 1,
};
};
#endif /* _INCLUDE__PLATFORM__VEA9X4__DRIVERS__BOARD_BASE_H_ */
}
#endif /* _SRC__SERVER__VMM__BOARD_H_ */

View File

@ -0,0 +1,189 @@
/*
* \brief VMM cpu object
* \author Stefan Kalkowski
* \date 2019-07-18
*/
/*
* 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 <cpu.h>
#include <vm.h>
#include <psci.h>
using Vmm::Cpu_base;
using Vmm::Cpu;
using Vmm::Gic;
Genode::uint64_t Cpu_base::State::reg(unsigned idx) const
{
if (idx > 15) return 0;
Genode::uint32_t * r = (Genode::uint32_t*)this;
r += idx;
return *r;
}
void Cpu_base::State::reg(unsigned idx, Genode::uint64_t v)
{
if (idx > 15) return;
Genode::uint32_t * r = (Genode::uint32_t*)this;
r += idx;
*r = v;
}
Cpu_base::System_register::Iss::access_t
Cpu_base::System_register::Iss::value(unsigned op0, 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;
};
Cpu_base::System_register::Iss::access_t
Cpu_base::System_register::Iss::mask_encoding(access_t v)
{
return Crm::masked(v) |
Crn::masked(v) |
Opcode1::masked(v) |
Opcode2::masked(v);
}
void Cpu_base::_handle_brk()
{
Genode::error(__func__, " not implemented yet");
}
void Cpu_base::handle_exception()
{
/* check exception reason */
switch (_state.cpu_exception) {
case Cpu::NO_EXCEPTION: break;
case Cpu::FIQ: [[fallthrough]];
case Cpu::IRQ: _handle_irq(); break;
case Cpu::TRAP: _handle_sync(); break;
default:
throw Exception("Curious exception ",
_state.cpu_exception, " occured");
}
_state.cpu_exception = Cpu::NO_EXCEPTION;
}
void Cpu_base::dump()
{
using namespace Genode;
auto lambda = [] (unsigned i) {
switch (i) {
case 0: return "und";
case 1: return "svc";
case 2: return "abt";
case 3: return "irq";
case 4: return "fiq";
default: return "unknown";
};
};
log("VM state (", _active ? "active" : "inactive", ") :");
for (unsigned i = 0; i < 13; i++) {
log(" r", i, " = ",
Hex(_state.reg(i), Hex::PREFIX, Hex::PAD));
}
log(" sp = ", Hex(_state.sp, Hex::PREFIX, Hex::PAD));
log(" lr = ", Hex(_state.lr, Hex::PREFIX, Hex::PAD));
log(" ip = ", Hex(_state.ip, Hex::PREFIX, Hex::PAD));
log(" cpsr = ", Hex(_state.cpsr, Hex::PREFIX, Hex::PAD));
for (unsigned i = 0; i < State::Mode_state::MAX; i++) {
log(" sp_", lambda(i), " = ",
Hex(_state.mode[i].sp, Hex::PREFIX, Hex::PAD));
log(" lr_", lambda(i), " = ",
Hex(_state.mode[i].lr, Hex::PREFIX, Hex::PAD));
log(" spsr_", lambda(i), " = ",
Hex(_state.mode[i].spsr, Hex::PREFIX, Hex::PAD));
}
log(" exception = ", _state.cpu_exception);
log(" esr_el2 = ", Hex(_state.esr_el2, Hex::PREFIX, Hex::PAD));
log(" hpfar_el2 = ", Hex(_state.hpfar_el2, Hex::PREFIX, Hex::PAD));
log(" far_el2 = ", Hex(_state.far_el2, Hex::PREFIX, Hex::PAD));
log(" hifar = ", Hex(_state.hifar, Hex::PREFIX, Hex::PAD));
log(" dfsr = ", Hex(_state.dfsr, Hex::PREFIX, Hex::PAD));
log(" ifsr = ", Hex(_state.ifsr, Hex::PREFIX, Hex::PAD));
log(" sctrl = ", Hex(_state.sctrl, Hex::PREFIX, Hex::PAD));
_timer.dump();
}
void Cpu_base::initialize_boot(Genode::addr_t ip, Genode::addr_t dtb)
{
state().reg(1, 0xffffffff); /* invalid machine type */
state().reg(2, dtb);
state().ip = ip;
}
Genode::addr_t Cpu::Ccsidr::read() const
{
struct Csselr : Genode::Register<32>
{
struct Level : Bitfield<1, 4> {};
};
enum { INVALID = 0xffffffff };
unsigned level = Csselr::Level::get(csselr.read());
if (level > 6) {
Genode::warning("Invalid Csselr value!");
return INVALID;
}
return 0;
}
Cpu::Cpu(Vm & vm,
Genode::Vm_connection & vm_session,
Mmio_bus & bus,
Gic & gic,
Genode::Env & env,
Genode::Heap & heap,
Genode::Entrypoint & ep)
: Cpu_base(vm, vm_session, bus, gic, env, heap, ep),
_sr_midr (0, 0, 0, 0, "MIDR", false, 0x412fc0f1, _reg_tree),
_sr_mpidr (0, 0, 0, 5, "MPIDR", false, 1<<31|cpu_id(), _reg_tree),
_sr_mmfr0 (0, 0, 1, 4, "MMFR0", false, 0x10201105, _reg_tree),
_sr_mmfr1 (0, 0, 1, 5, "MMFR1", false, 0x20000000, _reg_tree),
_sr_mmfr2 (0, 0, 1, 6, "MMFR2", false, 0x01240000, _reg_tree),
_sr_mmfr3 (0, 0, 1, 7, "MMFR3", false, 0x02102211, _reg_tree),
_sr_isar0 (0, 0, 2, 0, "ISAR0", false, 0x02101110, _reg_tree),
_sr_isar1 (0, 0, 2, 1, "ISAR1", false, 0x13112111, _reg_tree),
_sr_isar2 (0, 0, 2, 2, "ISAR2", false, 0x21232041, _reg_tree),
_sr_isar3 (0, 0, 2, 3, "ISAR3", false, 0x11112131, _reg_tree),
_sr_isar4 (0, 0, 2, 4, "ISAR4", false, 0x10011142, _reg_tree),
_sr_isar5 (0, 0, 2, 5, "ISAR5", false, 0x0, _reg_tree),
_sr_pfr0 (0, 0, 1, 0, "PFR0", false, 0x00001131, _reg_tree),
_sr_pfr1 (0, 0, 1, 1, "PFR1", false, 0x00011011, _reg_tree),
_sr_clidr (0, 1, 0, 1, "CLIDR", false, 0xa200023, _reg_tree),
_sr_csselr (0, 2, 0, 0, "CSSELR", true, 0x0, _reg_tree),
_sr_ctr (0, 0, 0, 1, "CTR", true, 0x8444c004, _reg_tree),
_sr_revidr (0, 0, 0, 6, "REVIDR", true, 0x0, _reg_tree),
_sr_ccsidr (_sr_csselr, _reg_tree),
_sr_actlr (1, 0, 0, 1, "ACTLR", true, 0x0, _reg_tree)
{
_state.cpsr = 0x93; /* el1 mode and IRQs disabled */
_state.sctrl = 0xc50078;
}

View File

@ -0,0 +1,91 @@
/*
* \brief VMM cpu object
* \author Stefan Kalkowski
* \date 2019-07-18
*/
/*
* 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 _SRC__SERVER__VMM__CPU_H_
#define _SRC__SERVER__VMM__CPU_H_
#include <cpu_base.h>
namespace Vmm { class Cpu; }
class Vmm::Cpu : public Vmm::Cpu_base
{
public:
Cpu(Vm & vm,
Genode::Vm_connection & vm_session,
Mmio_bus & bus,
Gic & gic,
Genode::Env & env,
Genode::Heap & heap,
Genode::Entrypoint & ep);
enum Exception_type {
NO_EXCEPTION,
RESET,
UNDEFINED,
HVC,
PF_ABORT,
DATA_ABORT,
IRQ,
FIQ,
TRAP
};
private:
struct Ccsidr : System_register
{
System_register & csselr;
Ccsidr(System_register &csselr,
Genode::Avl_tree<System_register> & tree)
: System_register(0, 1, 0, 0, "CCSIDR", false, 0x0, tree),
csselr(csselr) {}
virtual Genode::addr_t read() const override;
};
/******************************
** Identification registers **
******************************/
System_register _sr_midr;
System_register _sr_mpidr;
System_register _sr_mmfr0;
System_register _sr_mmfr1;
System_register _sr_mmfr2;
System_register _sr_mmfr3;
System_register _sr_isar0;
System_register _sr_isar1;
System_register _sr_isar2;
System_register _sr_isar3;
System_register _sr_isar4;
System_register _sr_isar5;
System_register _sr_pfr0;
System_register _sr_pfr1;
System_register _sr_clidr;
System_register _sr_csselr;
System_register _sr_ctr;
System_register _sr_revidr;
Ccsidr _sr_ccsidr;
/*********************
** System register **
*********************/
System_register _sr_actlr;
};
#endif /* _SRC__SERVER__VMM__CPU_H_ */

View File

@ -0,0 +1,41 @@
/*
* \brief VMM ARM Generic timer device model
* \author Stefan Kalkowski
* \date 2019-08-20
*/
/*
* 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 <cpu.h>
#include <generic_timer.h>
using Vmm::Generic_timer;
Genode::uint64_t Generic_timer::_ticks_per_ms()
{
static Genode::uint64_t ticks_per_ms = 0;
if (!ticks_per_ms) {
Genode::uint32_t freq = 0;
asm volatile("mrc p15, 0, %0, c14, c0, 0" : "=r" (freq));
ticks_per_ms = freq / 1000;
}
return ticks_per_ms;
}
Genode::uint64_t Generic_timer::_usecs_left()
{
Genode::uint64_t count;
Genode::uint32_t low, high;
asm volatile("mrrc p15, 0, %0, %1, c14" : "=r" (low), "=r" (high));
count = (Genode::uint64_t)high << 32 | (Genode::uint64_t)low;
count -= _cpu.state().timer.offset;
if (count > _cpu.state().timer.compare) return 0;
return Genode::timer_ticks_to_us(_cpu.state().timer.compare - count,
_ticks_per_ms());
}

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,19 @@
TARGET = vmm
REQUIRES = hw arm_v7
LIBS = base
SRC_CC = main.cc
INC_DIR += $(PRG_DIR)
SRC_CC += spec/arm_v7/generic_timer.cc
SRC_CC += address_space.cc
SRC_CC += cpu.cc
SRC_CC += cpu_base.cc
SRC_CC += generic_timer.cc
SRC_CC += gic.cc
SRC_CC += main.cc
SRC_CC += mmio.cc
SRC_CC += pl011.cc
SRC_CC += vm.cc
SRC_CC += virtio_device.cc
INC_DIR += $(PRG_DIR)/../.. $(PRG_DIR)
vpath %.cc $(PRG_DIR)/../..
CC_CXX_WARN_STRICT :=

View File

@ -1,30 +0,0 @@
.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

View File

@ -1,4 +0,0 @@
TARGET = vmm-test-kernel
REQUIRES = arm_v7
SRC_S = main.s
CC_MARCH = -mcpu=cortex-a15

View File

@ -0,0 +1,80 @@
/dts-v1/;
/ {
compatible = "linux,dummy-virt";
#address-cells = <0x02>;
#size-cells = <0x02>;
interrupt-parent = <0x8001>;
cpus {
#address-cells = <0x01>;
#size-cells = <0x00>;
cpu@0 {
compatible = "arm,cortex-a15";
reg = <0x00>;
device_type = "cpu";
};
};
timer {
interrupts = <0x01 0x0d 0x04 0x01 0x0e 0x04 0x01 0x0b 0x04 0x01 0x0a 0x04>;
compatible = "arm,armv7-timer";
always-on;
};
apb-pclk {
compatible = "fixed-clock";
phandle = <0x8000>;
clock-output-names = "clk24mhz";
clock-frequency = <0x16e3600>;
#clock-cells = <0x00>;
};
pl011@9000000 {
interrupts = <0x00 0x01 0x04>;
compatible = "arm,pl011\0arm,primecell";
clock-names = "uartclk\0apb_pclk";
reg = <0x00 0x9000000 0x00 0x1000>;
clocks = <0x8000 0x8000>;
};
memory@40000000 {
reg = <0x00 0x40000000 0x00 0x8000000>;
device_type = "memory";
};
chosen {
bootargs = "rdinit=/bin/sh ip=dhcp console=hvc0";
linux,initrd-start = <0x46000000>;
linux,initrd-end = <0x460b04b6>;
stdout-path = "/pl011@9000000";
};
intc@8000000 {
compatible = "arm,cortex-a15-gic";
phandle = <0x8001>;
reg = <0x00 0x8000000 0x00 0x10000 0x00 0x8010000 0x00 0x10000>;
ranges;
#address-cells = <0x02>;
#redistributor-regions = <0x01>;
interrupt-controller;
#interrupt-cells = <0x03>;
#size-cells = <0x02>;
};
virtio_mmio@a000000 {
interrupts = <0x00 0x10 0x01>;
compatible = "virtio,mmio";
dma-coherent;
reg = <0x00 0xa000000 0x00 0x200>;
};
virtio_mmio@a000200 {
interrupts = <0x00 0x11 0x01>;
compatible = "virtio,mmio";
dma-coherent;
reg = <0x00 0xa000200 0x00 0x200>;
};
};

View File

@ -0,0 +1,53 @@
/*
* \brief VMM address space utility
* \author Stefan Kalkowski
* \date 2019-11-13
*/
/*
* 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 _SRC__SERVER__VMM__BOARD_H_
#define _SRC__SERVER__VMM__BOARD_H_
namespace Vmm {
enum {
SIZE_1_MB = 1024 * 1024,
KERNEL_OFFSET = 0x80000,
INITRD_OFFSET = 32 * SIZE_1_MB,
DTB_OFFSET = 64 * SIZE_1_MB,
GIC_VERSION = 3,
GICD_MMIO_START = 0x8000000,
GICD_MMIO_SIZE = 0x10000,
GICC_MMIO_START = 0x8010000,
GICR_MMIO_START = 0x80a0000,
GICR_MMIO_SIZE = 0xf60000,
PL011_MMIO_START = 0x9000000,
PL011_MMIO_SIZE = 0x1000,
PL011_IRQ = 33,
VIRTIO_CONSOLE_MMIO_START = 0xa000000,
VIRTIO_CONSOLE_MMIO_SIZE = 0x200,
VIRTIO_CONSOLE_IRQ = 48,
VIRTIO_NET_MMIO_START = 0xa000200,
VIRTIO_NET_MMIO_SIZE = 0x200,
VIRTIO_NET_IRQ = 49,
RAM_START = 0x40000000,
RAM_SIZE = 128 * 1024 *1024,
VTIMER_IRQ = 27,
MAX_CPUS = 1,
};
}
#endif /* _SRC__SERVER__VMM__BOARD_H_ */

View File

@ -14,15 +14,27 @@
#include <vm.h>
#include <psci.h>
using Vmm::Cpu_base;
using Vmm::Cpu;
using Vmm::Gic;
Genode::Lock & Vmm::lock() { static Genode::Lock l {}; return l; }
Genode::uint64_t Cpu_base::State::reg(unsigned idx) const
{
if (idx > 30) return 0;
return r[idx];
}
Cpu::System_register::Iss::access_t
Cpu::System_register::Iss::value(unsigned op0, unsigned crn, unsigned op1,
unsigned crm, unsigned op2)
void Cpu_base::State::reg(unsigned idx, Genode::uint64_t v)
{
if (idx > 30) return;
r[idx] = v;
}
Cpu_base::System_register::Iss::access_t
Cpu_base::System_register::Iss::value(unsigned op0, unsigned crn, unsigned op1,
unsigned crm, unsigned op2)
{
access_t v = 0;
Crn::set(v, crn);
@ -34,8 +46,8 @@ Cpu::System_register::Iss::value(unsigned op0, unsigned crn, unsigned op1,
};
Cpu::System_register::Iss::access_t
Cpu::System_register::Iss::mask_encoding(access_t v)
Cpu_base::System_register::Iss::access_t
Cpu_base::System_register::Iss::mask_encoding(access_t v)
{
return Crm::masked(v) |
Crn::masked(v) |
@ -45,21 +57,69 @@ Cpu::System_register::Iss::mask_encoding(access_t v)
}
Cpu::System_register::System_register(unsigned op0,
unsigned crn,
unsigned op1,
unsigned crm,
unsigned op2,
const char * name,
bool writeable,
Genode::addr_t v,
Genode::Avl_tree<System_register> & tree)
: _encoding(Iss::value(op0, crn, op1, crm, op2)),
_name(name),
_writeable(writeable),
_value(v)
void Cpu_base::_handle_brk()
{
tree.insert(this);
Genode::uint64_t offset = 0x0;
if (!(_state.pstate & 0b100)) {
offset = 0x400;
} else if (_state.pstate & 0b1) {
offset = 0x200;
}
_state.esr_el1 = _state.esr_el2;
_state.spsr_el1 = _state.pstate;
_state.elr_el1 = _state.ip;
_state.ip = _state.vbar_el1 + offset;
_state.pstate = 0b1111000101;
}
void Cpu_base::handle_exception()
{
/* check exception reason */
switch (_state.exception_type) {
case Cpu::NO_EXCEPTION: break;
case Cpu::AARCH64_IRQ: _handle_irq(); break;
case Cpu::AARCH64_SYNC: _handle_sync(); break;
default:
throw Exception("Curious exception ",
_state.exception_type, " occured");
}
_state.exception_type = Cpu::NO_EXCEPTION;
}
void Cpu_base::dump()
{
using namespace Genode;
auto lambda = [] (addr_t exc) {
switch (exc) {
case Cpu::AARCH64_SYNC: return "aarch64 sync";
case Cpu::AARCH64_IRQ: return "aarch64 irq";
case Cpu::AARCH64_FIQ: return "aarch64 fiq";
case Cpu::AARCH64_SERROR: return "aarch64 serr";
case Cpu::AARCH32_SYNC: return "aarch32 sync";
case Cpu::AARCH32_IRQ: return "aarch32 irq";
case Cpu::AARCH32_FIQ: return "aarch32 fiq";
case Cpu::AARCH32_SERROR: return "aarch32 serr";
default: return "unknown";
};
};
log("VM state (", _active ? "active" : "inactive", ") :");
for (unsigned i = 0; i < 31; i++) {
log(" r", i, " = ",
Hex(_state.r[i], Hex::PREFIX, Hex::PAD));
}
log(" sp = ", Hex(_state.sp, Hex::PREFIX, Hex::PAD));
log(" ip = ", Hex(_state.ip, Hex::PREFIX, Hex::PAD));
log(" sp_el1 = ", Hex(_state.sp_el1, Hex::PREFIX, Hex::PAD));
log(" elr_el1 = ", Hex(_state.elr_el1, Hex::PREFIX, Hex::PAD));
log(" pstate = ", Hex(_state.pstate, Hex::PREFIX, Hex::PAD));
log(" exception = ", _state.exception_type, " (",
lambda(_state.exception_type), ")");
log(" esr_el2 = ", Hex(_state.esr_el2, Hex::PREFIX, Hex::PAD));
_timer.dump();
}
@ -139,216 +199,13 @@ void Cpu::Icc_sgi1r_el1::write(Genode::addr_t v)
};
bool Cpu::_handle_sys_reg()
void Cpu_base::initialize_boot(Genode::addr_t ip, Genode::addr_t dtb)
{
using Iss = System_register::Iss;
Iss::access_t v = _state.esr_el2;
System_register * reg = _reg_tree.first();
if (reg) reg = reg->find_by_encoding(Iss::mask_encoding(v));
if (!reg) {
Genode::error("ignore unknown system register access @ ip=", (void*)_state.ip, ":");
Genode::error(Iss::Direction::get(v) ? "read" : "write",
": "
"op0=", Iss::Opcode0::get(v), " "
"op1=", Iss::Opcode1::get(v), " "
"r", Iss::Register::get(v), " "
"crn=", Iss::Crn::get(v), " "
"crm=", Iss::Crm::get(v), " ",
"op2=", Iss::Opcode2::get(v));
if (Iss::Direction::get(v)) _state.r[Iss::Register::get(v)] = 0;
_state.ip += sizeof(Genode::uint32_t);
return false;
}
if (Iss::Direction::get(v)) { /* read access */
_state.r[Iss::Register::get(v)] = reg->read();
} else { /* write access */
if (!reg->writeable()) {
Genode::error("writing to system register ",
reg->name(), " not allowed!");
return false;
}
reg->write(_state.r[Iss::Register::get(v)]);
}
_state.ip += sizeof(Genode::uint32_t);
return true;
state().reg(0, dtb);
state().ip = ip;
}
void Cpu::_handle_wfi()
{
_state.ip += sizeof(Genode::uint32_t);
if (_state.esr_el2 & 1) return; /* WFE */
_active = false;
_timer.schedule_timeout();
}
void Cpu::_handle_brk()
{
Genode::uint64_t offset = 0x0;
if (!(_state.pstate & 0b100)) {
offset = 0x400;
} else if (_state.pstate & 0b1) {
offset = 0x200;
}
_state.esr_el1 = _state.esr_el2;
_state.spsr_el1 = _state.pstate;
_state.elr_el1 = _state.ip;
_state.ip = _state.vbar_el1 + offset;
_state.pstate = 0b1111000101;
}
void Cpu::_handle_sync()
{
/* check device number*/
switch (Esr::Ec::get(_state.esr_el2)) {
case Esr::Ec::HVC:
_handle_hyper_call();
break;
case Esr::Ec::MRS_MSR:
_handle_sys_reg();
break;
case Esr::Ec::DA:
_handle_data_abort();
break;
case Esr::Ec::WFI:
_handle_wfi();
return;
case Esr::Ec::BRK:
_handle_brk();
return;
default:
throw Exception("Unknown trap: %x",
Esr::Ec::get(_state.esr_el2));
};
}
void Cpu::_handle_irq()
{
enum { /* FIXME */ VT_TIMER_IRQ = 27 };
switch (_state.irqs.last_irq) {
case VT_TIMER_IRQ:
_timer.handle_irq();
break;
default:
_gic.handle_irq();
};
}
void Cpu::_handle_hyper_call()
{
switch(_state.r[0]) {
case Psci::PSCI_VERSION:
_state.r[0] = Psci::VERSION;
return;
case Psci::MIGRATE_INFO_TYPE:
_state.r[0] = Psci::NOT_SUPPORTED;
return;
case Psci::PSCI_FEATURES:
_state.r[0] = Psci::NOT_SUPPORTED;
return;
case Psci::CPU_ON:
_vm.cpu((unsigned)_state.r[1], [&] (Cpu & cpu) {
cpu.state().ip = _state.r[2];
cpu.state().r[0] = _state.r[3];
cpu.run();
});
_state.r[0] = Psci::SUCCESS;
return;
default:
Genode::warning("unknown hypercall! ", cpu_id());
dump();
};
}
void Cpu::_handle_data_abort()
{
_vm.bus().handle_memory_access(*this);
_state.ip += sizeof(Genode::uint32_t);
}
void Cpu::_update_state()
{
if (!_gic.pending_irq()) return;
_active = true;
_timer.cancel_timeout();
}
unsigned Cpu::cpu_id() const { return _vcpu_id.id; }
void Cpu::run() { _vm_session.run(_vcpu_id); }
void Cpu::pause() { _vm_session.pause(_vcpu_id); }
bool Cpu::active() const { return _active; }
Cpu::State & Cpu::state() const { return _state; }
Gic::Gicd_banked & Cpu::gic() { return _gic; }
void Cpu::handle_exception()
{
/* check exception reason */
switch (_state.exception_type) {
case NO_EXCEPTION: break;
case AARCH64_IRQ: _handle_irq(); break;
case AARCH64_SYNC: _handle_sync(); break;
default:
throw Exception("Curious exception ",
_state.exception_type, " occured");
}
_state.exception_type = NO_EXCEPTION;
}
void Cpu::dump()
{
using namespace Genode;
auto lambda = [] (addr_t exc) {
switch (exc) {
case AARCH64_SYNC: return "aarch64 sync";
case AARCH64_IRQ: return "aarch64 irq";
case AARCH64_FIQ: return "aarch64 fiq";
case AARCH64_SERROR: return "aarch64 serr";
case AARCH32_SYNC: return "aarch32 sync";
case AARCH32_IRQ: return "aarch32 irq";
case AARCH32_FIQ: return "aarch32 fiq";
case AARCH32_SERROR: return "aarch32 serr";
default: return "unknown";
};
};
log("VM state (", _active ? "active" : "inactive", ") :");
for (unsigned i = 0; i < 31; i++) {
log(" r", i, " = ",
Hex(_state.r[i], Hex::PREFIX, Hex::PAD));
}
log(" sp = ", Hex(_state.sp, Hex::PREFIX, Hex::PAD));
log(" ip = ", Hex(_state.ip, Hex::PREFIX, Hex::PAD));
log(" sp_el1 = ", Hex(_state.sp_el1, Hex::PREFIX, Hex::PAD));
log(" elr_el1 = ", Hex(_state.elr_el1, Hex::PREFIX, Hex::PAD));
log(" pstate = ", Hex(_state.pstate, Hex::PREFIX, Hex::PAD));
log(" exception = ", _state.exception_type, " (",
lambda(_state.exception_type), ")");
log(" esr_el2 = ", Hex(_state.esr_el2, Hex::PREFIX, Hex::PAD));
_timer.dump();
}
void Cpu::recall()
{
Genode::Signal_transmitter(_vm_handler).submit();
};
Cpu::Cpu(Vm & vm,
Genode::Vm_connection & vm_session,
Mmio_bus & bus,
@ -356,15 +213,7 @@ Cpu::Cpu(Vm & vm,
Genode::Env & env,
Genode::Heap & heap,
Genode::Entrypoint & ep)
: _vm(vm),
_vm_session(vm_session),
_heap(heap),
_vm_handler(*this, ep, *this, &Cpu::_handle_nothing),
_vcpu_id(_vm_session.with_upgrade([&]() {
return _vm_session.create_vcpu(heap, env, _vm_handler);
})),
_state(*((State*)env.rm().attach(_vm_session.cpu_state(_vcpu_id)))),
// op0, crn, op1, crm, op2, writeable, reset value
: Cpu_base(vm, vm_session, bus, gic, env, heap, ep),
_sr_id_aa64afr0_el1 (3, 0, 0, 5, 4, "ID_AA64AFR0_EL1", false, 0x0, _reg_tree),
_sr_id_aa64afr1_el1 (3, 0, 0, 5, 5, "ID_AA64AFR1_EL1", false, 0x0, _reg_tree),
_sr_id_aa64dfr0_el1 (3, 0, 0, 5, 0, "ID_AA64DFR0_EL1", false, 0x6, _reg_tree),
@ -379,13 +228,10 @@ Cpu::Cpu(Vm & vm,
_sr_id_aa64zfr0_el1 (3, 0, 0, 4, 4, "ID_AA64ZFR0_EL1", false, 0x0, _reg_tree),
_sr_aidr_el1 (3, 0, 1, 0, 7, "AIDR_EL1", false, 0x0, _reg_tree),
_sr_revidr_el1 (3, 0, 0, 0, 6, "REVIDR_EL1", false, 0x0, _reg_tree),
_sr_clidr_el1 (3, 0, 1, 0, 1, "CLIDR_EL1", false, _state.clidr_el1, _reg_tree),
_sr_csselr_el1 (3, 0, 2, 0, 0, "CSSELR_EL1", true, 0x0, _reg_tree),
_sr_ctr_el0 (_reg_tree),
_sr_ccsidr_el1 (_sr_csselr_el1, _state, _reg_tree),
//_sr_pmccfiltr_el0 (3, 14, 3, 15, 7, "PMCCFILTR_EL0", true, 0x0, _reg_tree),
_sr_pmuserenr_el0 (3, 9, 3, 14, 0, "PMUSEREN_EL0", true, 0x0, _reg_tree),
_sr_dbgbcr0 (2, 0, 0, 0, 5, "DBGBCR_EL1", true, 0x0, _reg_tree),
_sr_dbgbvr0 (2, 0, 0, 0, 4, "DBGBVR_EL1", true, 0x0, _reg_tree),
@ -394,9 +240,7 @@ Cpu::Cpu(Vm & vm,
_sr_mdscr (2, 0, 0, 2, 2, "MDSCR_EL1", true, 0x0, _reg_tree),
_sr_osdlr (2, 1, 0, 3, 4, "OSDLR_EL1", true, 0x0, _reg_tree),
_sr_oslar (2, 1, 0, 0, 4, "OSLAR_EL1", true, 0x0, _reg_tree),
_sr_sgi1r_el1 (_reg_tree, vm),
_gic(*this, gic, bus),
_timer(env, ep, _gic.irq(27), *this)
_sr_sgi1r_el1 (_reg_tree, vm)
{
_state.pstate = 0b1111000101; /* el1 mode and IRQs disabled */
_state.vmpidr_el2 = cpu_id();

View File

@ -14,41 +14,14 @@
#ifndef _SRC__SERVER__VMM__CPU_H_
#define _SRC__SERVER__VMM__CPU_H_
#include <exception.h>
#include <generic_timer.h>
#include <cpu_base.h>
#include <base/env.h>
#include <base/heap.h>
#include <cpu/vm_state_virtualization.h>
#include <util/mmio.h>
#include <vm_session/connection.h>
namespace Vmm { class Cpu; }
namespace Vmm {
class Vm;
class Cpu;
Genode::Lock & lock();
}
class Vmm::Cpu
class Vmm::Cpu : public Vmm::Cpu_base
{
public:
using State = Genode::Vm_state;
struct Esr : Genode::Register<32>
{
struct Ec : Bitfield<26, 6>
{
enum {
WFI = 0x1,
HVC = 0x16,
MRS_MSR = 0x18,
DA = 0x24,
BRK = 0x3c
};
};
};
Cpu(Vm & vm,
Genode::Vm_connection & vm_session,
Mmio_bus & bus,
@ -57,58 +30,6 @@ class Vmm::Cpu
Genode::Heap & heap,
Genode::Entrypoint & ep);
unsigned cpu_id() const;
void run();
void pause();
bool active() const;
State & state() const;
Gic::Gicd_banked & gic();
void dump();
void handle_exception();
void recall();
template <typename FUNC>
void handle_signal(FUNC handler)
{
if (active()) {
pause();
handle_exception();
}
handler();
_update_state();
if (active()) run();
}
template <typename T>
struct Signal_handler : Genode::Vm_handler<Signal_handler<T>>
{
using Base = Genode::Vm_handler<Signal_handler<T>>;
Cpu & cpu;
T & obj;
void (T::*member)();
void handle()
{
try {
cpu.handle_signal([this] () { (obj.*member)(); });
} catch(Exception &e) {
Genode::error(e);
cpu.dump();
}
}
Signal_handler(Cpu & cpu,
Genode::Entrypoint & ep,
T & o,
void (T::*f)())
: Base(ep, *this, &Signal_handler::handle),
cpu(cpu), obj(o), member(f) {}
};
private:
enum Exception_type {
AARCH64_SYNC = 0x400,
AARCH64_IRQ = 0x480,
@ -121,72 +42,7 @@ class Vmm::Cpu
NO_EXCEPTION = 0xffff
};
class System_register : public Genode::Avl_node<System_register>
{
private:
const Esr::access_t _encoding;
const char *_name;
const bool _writeable;
Genode::uint64_t _value;
public:
struct Iss : Esr
{
struct Direction : Bitfield<0, 1> {};
struct Crm : Bitfield<1, 4> {};
struct Register : Bitfield<5, 5> {};
struct Crn : Bitfield<10, 4> {};
struct Opcode1 : Bitfield<14, 3> {};
struct Opcode2 : Bitfield<17, 3> {};
struct Opcode0 : Bitfield<20, 2> {};
static access_t value(unsigned op0,
unsigned crn,
unsigned op1,
unsigned crm,
unsigned op2);
static access_t mask_encoding(access_t v);
};
System_register(unsigned op0,
unsigned crn,
unsigned op1,
unsigned crm,
unsigned op2,
const char * name,
bool writeable,
Genode::addr_t v,
Genode::Avl_tree<System_register> & tree);
const char * name() const { return _name; }
const bool writeable() const { return _writeable; }
System_register * find_by_encoding(Iss::access_t e)
{
if (e == _encoding) return this;
System_register * r =
Avl_node<System_register>::child(e > _encoding);
return r ? r->find_by_encoding(e) : nullptr;
}
virtual void write(Genode::addr_t v) {
_value = (Genode::addr_t)v; }
virtual Genode::addr_t read() const {
return (Genode::addr_t)(_value); }
/************************
** Avl node interface **
************************/
bool higher(System_register *r) {
return (r->_encoding > _encoding); }
};
private:
class Id_aa64pfr0 : public System_register,
public Genode::Register<64>
@ -252,15 +108,6 @@ class Vmm::Cpu
virtual void write(Genode::addr_t v) override;
};
bool _active { true };
Vm & _vm;
Genode::Vm_connection & _vm_session;
Genode::Heap & _heap;
Signal_handler<Cpu> _vm_handler;
Genode::Vm_session::Vcpu_id _vcpu_id;
State & _state;
Genode::Avl_tree<System_register> _reg_tree;
/******************************
** Identification registers **
******************************/
@ -308,22 +155,10 @@ class Vmm::Cpu
System_register _sr_oslar;
/***********************
** Local peripherals **
** GIC cpu interface **
***********************/
Icc_sgi1r_el1 _sr_sgi1r_el1;
Gic::Gicd_banked _gic;
Generic_timer _timer;
void _handle_nothing() {}
bool _handle_sys_reg();
void _handle_brk();
void _handle_wfi();
void _handle_sync();
void _handle_irq();
void _handle_data_abort();
void _handle_hyper_call();
void _update_state();
};
#endif /* _SRC__SERVER__VMM__CPU_H_ */

View File

@ -28,26 +28,6 @@ Genode::uint64_t Generic_timer::_ticks_per_ms()
}
bool Generic_timer::_enabled() {
return Ctrl::Enabled::get(_cpu.state().timer.control); }
bool Generic_timer::_masked() {
return Ctrl::Imask::get(_cpu.state().timer.control); }
bool Generic_timer::_pending() {
return Ctrl::Istatus::get(_cpu.state().timer.control); }
void Generic_timer::_handle_timeout(Genode::Duration)
{
_cpu.handle_signal([this] (void) {
if (_enabled() && !_masked()) handle_irq();
});
}
Genode::uint64_t Generic_timer::_usecs_left()
{
Genode::uint64_t count;
@ -57,60 +37,3 @@ Genode::uint64_t Generic_timer::_usecs_left()
return Genode::timer_ticks_to_us(_cpu.state().timer.compare - count,
_ticks_per_ms());
}
Generic_timer::Generic_timer(Genode::Env & env,
Genode::Entrypoint & ep,
Gic::Irq & irq,
Cpu & cpu)
: _timer(env, ep),
_timeout(_timer, *this, &Generic_timer::_handle_timeout),
_irq(irq),
_cpu(cpu)
{
_cpu.state().timer.irq = true;
_irq.handler(*this);
}
void Generic_timer::schedule_timeout()
{
if (_pending()) {
handle_irq();
return;
}
if (_enabled()) {
if (_usecs_left()) {
_timeout.schedule(Genode::Microseconds(_usecs_left()));
} else _handle_timeout(Genode::Duration(Genode::Microseconds(0)));
}
}
void Generic_timer::cancel_timeout()
{
if (_timeout.scheduled()) _timeout.discard();
}
void Generic_timer::handle_irq()
{
_irq.assert();
_cpu.state().timer.irq = false;
}
void Generic_timer::eoi()
{
_cpu.state().timer.irq = true;
};
void Generic_timer::dump()
{
using namespace Genode;
log(" timer.ctl = ", Hex(_cpu.state().timer.control, Hex::PREFIX, Hex::PAD));
log(" timer.cmp = ", Hex(_cpu.state().timer.compare, Hex::PREFIX, Hex::PAD));
}

View File

@ -1,15 +1,19 @@
TARGET = vmm
REQUIRES = hw arm_v8
LIBS = base
SRC_CC += spec/arm_v8/generic_timer.cc
SRC_CC += address_space.cc
SRC_CC += cpu.cc
SRC_CC += cpu_base.cc
SRC_CC += generic_timer.cc
SRC_CC += gicv2.cc
SRC_CC += gic.cc
SRC_CC += main.cc
SRC_CC += mmio.cc
SRC_CC += pl011.cc
SRC_CC += virtio_device.cc
SRC_CC += vm.cc
INC_DIR += $(PRG_DIR)
INC_DIR += $(PRG_DIR)/../.. $(PRG_DIR)
vpath %.cc $(PRG_DIR)/../..
CC_CXX_WARN_STRICT :=

View File

@ -48,9 +48,12 @@ struct Vmm::Virtio_queue_data
uint32_t ready { 0 };
bool tx { false };
addr_t descr() const { return ((addr_t)descr_high << 32) | descr_low; }
addr_t driver() const { return ((addr_t)driver_high << 32) | driver_low; }
addr_t device() const { return ((addr_t)device_high << 32) | device_low; }
uint64_t descr() const {
return ((uint64_t)descr_high << 32) | descr_low; }
uint64_t driver() const {
return ((uint64_t)driver_high << 32) | driver_low; }
uint64_t device() const {
return ((uint64_t)device_high << 32) | device_low; }
enum { MAX_QUEUE_SIZE = 1 << 15 };
};
@ -83,10 +86,10 @@ class Vmm::Virtio_descriptor : Genode::Mmio
return Virtio_descriptor(base() + (size() * idx));
}
addr_t address() const { return read<Address>(); }
size_t length () const { return read<Length>(); }
uint16_t flags() const { return read<Flags>(); }
uint16_t next() const { return read<Next>(); }
uint64_t address() const { return read<Address>(); }
size_t length () const { return read<Length>(); }
uint16_t flags() const { return read<Flags>(); }
uint16_t next() const { return read<Next>(); }
};
@ -173,7 +176,7 @@ class Vmm::Virtio_queue
id %= _length;
Virtio_descriptor descr = _descr.index(id);
addr_t address = descr.address();
uint64_t address = descr.address();
size_t length = descr.length();
if (!address || !length) break;

View File

@ -48,14 +48,16 @@ Vmm::Cpu & Vm::boot_cpu()
Vm::Vm(Genode::Env & env)
: _env(env),
_gic("Gicv3", 0x8000000, 0x10000, _bus, env),
_uart("Pl011", 0x9000000, 0x1000, 33, boot_cpu(), _bus, env),
_virtio_console("HVC", 0xa000000, 0x200, 48, boot_cpu(), _bus, _ram, env),
_virtio_net("Net", 0xa000200, 0x200, 49, boot_cpu(), _bus, _ram, env)
_gic("Gicv3", GICD_MMIO_START, GICD_MMIO_SIZE,
MAX_CPUS, GIC_VERSION, _vm, _bus, env),
_uart("Pl011", PL011_MMIO_START, PL011_MMIO_SIZE,
PL011_IRQ, boot_cpu(), _bus, env),
_virtio_console("HVC", VIRTIO_CONSOLE_MMIO_START, VIRTIO_CONSOLE_MMIO_SIZE,
VIRTIO_CONSOLE_IRQ, boot_cpu(), _bus, _ram, env),
_virtio_net("Net", VIRTIO_NET_MMIO_START, VIRTIO_NET_MMIO_SIZE,
VIRTIO_NET_IRQ, boot_cpu(), _bus, _ram, env)
{
_vm.attach(_vm_ram.cap(), RAM_ADDRESS);
/* FIXME extend for gicv2 by: _vm.attach_pic(0x8010000); */
_vm.attach(_vm_ram.cap(), RAM_START);
_load_kernel();
_load_dtb();
@ -71,7 +73,7 @@ Vm::Vm(Genode::Env & env)
Genode::log("Start virtual machine ...");
Cpu & cpu = boot_cpu();
cpu.state().ip = _ram.base() + KERNEL_OFFSET;
cpu.state().r[0] = _ram.base() + DTB_OFFSET;
cpu.initialize_boot(_ram.base() + KERNEL_OFFSET,
_ram.base() + DTB_OFFSET);
cpu.run();
};

View File

@ -14,6 +14,7 @@
#ifndef _SRC__SERVER__VMM__VM_H_
#define _SRC__SERVER__VMM__VM_H_
#include <board.h>
#include <ram.h>
#include <exception.h>
#include <cpu.h>
@ -34,15 +35,7 @@ class Vmm::Vm
using Ep = Genode::Entrypoint;
enum {
RAM_ADDRESS = 0x40000000,
RAM_SIZE = 128 * 1024 *1024,
KERNEL_OFFSET = 0x80000,
INITRD_OFFSET = 32 * 1024 * 1024,
DTB_OFFSET = 64 * 1024 * 1024,
MAX_CPUS = 1,
STACK_SIZE = sizeof(unsigned long) * 2048,
};
enum { STACK_SIZE = sizeof(unsigned long) * 2048, };
Genode::Env & _env;
Genode::Vm_connection _vm { _env };
@ -51,7 +44,7 @@ class Vmm::Vm
Genode::Attached_rom_dataspace _initrd_rom { _env, "initrd" };
Genode::Attached_ram_dataspace _vm_ram { _env.ram(), _env.rm(),
RAM_SIZE, Genode::CACHED };
Ram _ram { RAM_ADDRESS, RAM_SIZE,
Ram _ram { RAM_START, RAM_SIZE,
(Genode::addr_t)_vm_ram.local_addr<void>()};
Genode::Heap _heap { _env.ram(), _env.rm() };
Mmio_bus _bus;