parent
74e75d7fbc
commit
941e918b46
|
@ -27,59 +27,55 @@ namespace Genode
|
||||||
|
|
||||||
struct Genode::Vm_state : Genode::Cpu_state_modes
|
struct Genode::Vm_state : Genode::Cpu_state_modes
|
||||||
{
|
{
|
||||||
Genode::uint64_t vttbr;
|
Genode::uint64_t vttbr { 0 };
|
||||||
Genode::uint32_t sctrl;
|
Genode::uint32_t sctrl { 0 };
|
||||||
Genode::uint32_t hsr;
|
Genode::uint32_t esr_el2 { 0 };
|
||||||
Genode::uint32_t hpfar;
|
Genode::uint32_t hpfar_el2 { 0 };
|
||||||
Genode::uint32_t hdfar;
|
Genode::uint32_t far_el2 { 0 };
|
||||||
Genode::uint32_t hifar;
|
Genode::uint32_t hifar { 0 };
|
||||||
Genode::uint32_t ttbcr;
|
Genode::uint32_t ttbcr { 0 };
|
||||||
Genode::uint32_t ttbr0;
|
Genode::uint32_t ttbr0 { 0 };
|
||||||
Genode::uint32_t ttbr1;
|
Genode::uint32_t ttbr1 { 0 };
|
||||||
Genode::uint32_t prrr;
|
Genode::uint32_t prrr { 0 };
|
||||||
Genode::uint32_t nmrr;
|
Genode::uint32_t nmrr { 0 };
|
||||||
Genode::uint32_t dacr;
|
Genode::uint32_t dacr { 0 };
|
||||||
Genode::uint32_t dfsr;
|
Genode::uint32_t dfsr { 0 };
|
||||||
Genode::uint32_t ifsr;
|
Genode::uint32_t ifsr { 0 };
|
||||||
Genode::uint32_t adfsr;
|
Genode::uint32_t adfsr { 0 };
|
||||||
Genode::uint32_t aifsr;
|
Genode::uint32_t aifsr { 0 };
|
||||||
Genode::uint32_t dfar;
|
Genode::uint32_t dfar { 0 };
|
||||||
Genode::uint32_t ifar;
|
Genode::uint32_t ifar { 0 };
|
||||||
Genode::uint32_t cidr;
|
Genode::uint32_t cidr { 0 };
|
||||||
Genode::uint32_t tls1;
|
Genode::uint32_t tls1 { 0 };
|
||||||
Genode::uint32_t tls2;
|
Genode::uint32_t tls2 { 0 };
|
||||||
Genode::uint32_t tls3;
|
Genode::uint32_t tls3 { 0 };
|
||||||
Genode::uint32_t cpacr;
|
Genode::uint32_t cpacr { 0 };
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fpu registers
|
* Fpu registers
|
||||||
*/
|
*/
|
||||||
Genode::uint32_t fpscr;
|
Genode::uint32_t fpscr { 0 };
|
||||||
Genode::uint64_t d0_d31[32];
|
Genode::uint64_t d0_d31[32]{ 0 };
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Timer related registers
|
* Timer related registers
|
||||||
*/
|
*/
|
||||||
|
struct Timer {
|
||||||
Genode::uint32_t timer_ctrl;
|
Genode::uint64_t offset { 0 };
|
||||||
Genode::uint32_t timer_val;
|
Genode::uint64_t compare { 0 };
|
||||||
bool timer_irq;
|
Genode::uint32_t control { 0 };
|
||||||
|
Genode::uint32_t kcontrol { 0 };
|
||||||
|
bool irq { false };
|
||||||
|
} timer {};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* PIC related registers
|
* Interrupt related values
|
||||||
*/
|
*/
|
||||||
|
struct Pic
|
||||||
enum { NR_IRQ = 4 };
|
{
|
||||||
|
unsigned last_irq { 1023 };
|
||||||
Genode::uint32_t gic_hcr;
|
unsigned virtual_irq { 1023 };
|
||||||
Genode::uint32_t gic_vmcr;
|
} irqs {};
|
||||||
Genode::uint32_t gic_misr;
|
|
||||||
Genode::uint32_t gic_apr;
|
|
||||||
Genode::uint32_t gic_eisr;
|
|
||||||
Genode::uint32_t gic_elrsr0;
|
|
||||||
Genode::uint32_t gic_lr[4];
|
|
||||||
unsigned gic_irq;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* _INCLUDE__SPEC__ARNDALE__VM_STATE_H_ */
|
#endif /* _INCLUDE__SPEC__ARNDALE__VM_STATE_H_ */
|
||||||
|
|
|
@ -11,6 +11,7 @@ INC_DIR += $(REP_DIR)/src/core/spec/arm/virtualization
|
||||||
# add C++ sources
|
# add C++ sources
|
||||||
SRC_CC += kernel/vm_thread_on.cc
|
SRC_CC += kernel/vm_thread_on.cc
|
||||||
SRC_CC += spec/arm/gicv2.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_v7/virtualization/kernel/vm.cc
|
||||||
SRC_CC += spec/arm/virtualization/platform_services.cc
|
SRC_CC += spec/arm/virtualization/platform_services.cc
|
||||||
SRC_CC += spec/arm/virtualization/vm_session_component.cc
|
SRC_CC += spec/arm/virtualization/vm_session_component.cc
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
|
@ -11,6 +11,7 @@
|
||||||
* under the terms of the GNU Affero General Public License version 3.
|
* under the terms of the GNU Affero General Public License version 3.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <spec/arm/cortex_a7_a15_virtualization.h>
|
||||||
#include <platform.h>
|
#include <platform.h>
|
||||||
|
|
||||||
extern "C" void * _start_setup_stack; /* entrypoint for non-boot CPUs */
|
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 }) { }
|
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()
|
static inline void switch_to_supervisor_mode()
|
||||||
{
|
{
|
||||||
using Cpsr = Hw::Arm_cpu::Psr;
|
using Cpsr = Hw::Arm_cpu::Psr;
|
||||||
|
@ -152,24 +49,24 @@ static inline void switch_to_supervisor_mode()
|
||||||
|
|
||||||
unsigned Bootstrap::Platform::enable_mmu()
|
unsigned Bootstrap::Platform::enable_mmu()
|
||||||
{
|
{
|
||||||
using namespace ::Board;
|
|
||||||
|
|
||||||
static volatile bool primary_cpu = true;
|
static volatile bool primary_cpu = true;
|
||||||
|
static unsigned long timer_freq = 24000000;
|
||||||
|
|
||||||
/* locally initialize interrupt controller */
|
/* locally initialize interrupt controller */
|
||||||
::Board::Pic pic { };
|
::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);
|
prepare_hypervisor((addr_t)core_pd->table_base);
|
||||||
switch_to_supervisor_mode();
|
switch_to_supervisor_mode();
|
||||||
|
|
||||||
Cpu::Sctlr::init();
|
Cpu::Sctlr::init();
|
||||||
Cpu::Cpsr::init();
|
Cpu::Cpsr::init();
|
||||||
|
|
||||||
Cpu::invalidate_data_cache();
|
|
||||||
|
|
||||||
/* primary cpu wakes up all others */
|
/* primary cpu wakes up all others */
|
||||||
if (primary_cpu && NR_OF_CPUS > 1) {
|
if (primary_cpu && NR_OF_CPUS > 1) {
|
||||||
|
Cpu::invalidate_data_cache();
|
||||||
primary_cpu = false;
|
primary_cpu = false;
|
||||||
Cpu::wake_up_all_cpus(&_start_setup_stack);
|
Cpu::wake_up_all_cpus(&_start_setup_stack);
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
|
|
||||||
#include <platform.h>
|
#include <platform.h>
|
||||||
#include <spec/arm/imx_aipstz.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 */
|
extern "C" void * _start_setup_stack; /* entrypoint for non-boot CPUs */
|
||||||
static unsigned char hyp_mode_stack[1024]; /* hypervisor mode's kernel stack */
|
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()
|
static inline void switch_to_supervisor_mode()
|
||||||
{
|
{
|
||||||
using Cpsr = Hw::Arm_cpu::Psr;
|
using Cpsr = Hw::Arm_cpu::Psr;
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <platform.h>
|
#include <platform.h>
|
||||||
|
#include <spec/arm/cortex_a7_a15_virtualization.h>
|
||||||
|
|
||||||
extern "C" void * _start_setup_stack; /* entrypoint for non-boot CPUs */
|
extern "C" void * _start_setup_stack; /* entrypoint for non-boot CPUs */
|
||||||
static unsigned char hyp_mode_stack[1024]; /* hypervisor mode's kernel stack */
|
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 }) {}
|
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()
|
static inline void switch_to_supervisor_mode()
|
||||||
{
|
{
|
||||||
using Cpsr = Hw::Arm_cpu::Psr;
|
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 */
|
"msr lr_svc, lr \n" /* copy current mode's lr */
|
||||||
"adr lr, 1f \n" /* load exception return address */
|
"adr lr, 1f \n" /* load exception return address */
|
||||||
"msr elr_hyp, lr \n" /* copy current mode's lr to hyp lr */
|
"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 */
|
"msr spsr_cxfs, %[cpsr] \n" /* set psr for supervisor mode */
|
||||||
"eret \n" /* exception return */
|
"eret \n" /* exception return */
|
||||||
"1:":: [cpsr] "r" (cpsr), [stack] "r" (&hyp_mode_stack));
|
"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()
|
unsigned Bootstrap::Platform::enable_mmu()
|
||||||
{
|
{
|
||||||
static volatile bool primary_cpu = true;
|
static volatile bool primary_cpu = true;
|
||||||
static unsigned long timer_freq = Cpu::Cntfrq::read();
|
|
||||||
|
|
||||||
/* locally initialize interrupt controller */
|
/* locally initialize interrupt controller */
|
||||||
::Board::Pic pic { };
|
::Board::Pic pic { };
|
||||||
|
@ -168,7 +66,6 @@ unsigned Bootstrap::Platform::enable_mmu()
|
||||||
Cpu::wake_up_all_cpus(&_start_setup_stack);
|
Cpu::wake_up_all_cpus(&_start_setup_stack);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (false) prepare_nonsecure_world(timer_freq);
|
|
||||||
prepare_hypervisor((addr_t)core_pd->table_base);
|
prepare_hypervisor((addr_t)core_pd->table_base);
|
||||||
switch_to_supervisor_mode();
|
switch_to_supervisor_mode();
|
||||||
|
|
||||||
|
|
|
@ -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_ */
|
|
@ -101,6 +101,11 @@ _host_to_vm:
|
||||||
add r1, r0, #4
|
add r1, r0, #4
|
||||||
vldm r1!, {d0-d15}
|
vldm r1!, {d0-d15}
|
||||||
vldm r1!, {d16-d31}
|
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 */
|
ldmia sp, {r0-r12} /* load vm's r0-r12 */
|
||||||
eret
|
eret
|
||||||
|
|
||||||
|
@ -111,9 +116,7 @@ _vm_to_host:
|
||||||
mcrr p15, 6, r1, r1, c2 /* write VTTBR */
|
mcrr p15, 6, r1, r1, c2 /* write VTTBR */
|
||||||
mcr p15, 4, r1, c1, c1, 0 /* write HCR register */
|
mcr p15, 4, r1, c1, c1, 0 /* write HCR register */
|
||||||
mcr p15, 4, r1, c1, c1, 3 /* write HSTR 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 r1, ELR_hyp /* read ip */
|
||||||
mrs r2, spsr /* read cpsr */
|
mrs r2, spsr /* read cpsr */
|
||||||
mrc p15, 0, r3, c1, c0, 0 /* read SCTRL */
|
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, r8, c2, c0, 2 /* read TTBRC */
|
||||||
mrc p15, 0, r9, c2, c0, 0 /* read TTBR0 */
|
mrc p15, 0, r9, c2, c0, 0 /* read TTBR0 */
|
||||||
mrc p15, 0, r10, c2, c0, 1 /* read TTBR1 */
|
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 */
|
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}
|
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 */
|
mov r3, #1 /* clear fpu exception state */
|
||||||
lsl r3, #30
|
lsl r3, #30
|
||||||
vmsr fpexc, r3
|
vmsr fpexc, r3
|
||||||
|
@ -146,6 +157,11 @@ _vm_to_host:
|
||||||
stmia r0!, {r4}
|
stmia r0!, {r4}
|
||||||
vstm r0!, {d0-d15}
|
vstm r0!, {d0-d15}
|
||||||
vstm r0!, {d16-d31}
|
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
|
add r0, sp, #13*4
|
||||||
ldr r3, _vt_host_context_ptr
|
ldr r3, _vt_host_context_ptr
|
||||||
ldr sp, [r3]
|
ldr sp, [r3]
|
||||||
|
|
|
@ -55,139 +55,42 @@ struct Host_context {
|
||||||
} vt_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)
|
Cpu & cpu = Kernel::cpu_pool().executing_cpu();
|
||||||
:
|
Vm *vm = dynamic_cast<Vm*>(&cpu.scheduled_job());
|
||||||
Kernel::Irq(irq, cpu_pool().executing_cpu().irq_pool())
|
if (!vm) Genode::raw("VM interrupt while VM is not runnning!");
|
||||||
{ }
|
else handle(cpu, *vm, _irq_nr);
|
||||||
|
}
|
||||||
/**
|
|
||||||
* 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);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
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> { };
|
irq.disable();
|
||||||
struct Gich_vmcr : Register<0x08, 32> { };
|
asm volatile("mcr p15, 0, %0, c14, c3, 1" :: "r" (0));
|
||||||
struct Gich_misr : Register<0x10, 32> { };
|
asm volatile("mcr p15, 0, %0, c14, c1, 0" :: "r" (0b11));
|
||||||
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));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
using Vmid_allocator = Genode::Bit_allocator<256>;
|
using Vmid_allocator = Genode::Bit_allocator<256>;
|
||||||
|
|
||||||
|
@ -217,7 +120,6 @@ Kernel::Vm::Vm(unsigned, /* FIXME: smp support */
|
||||||
_vcpu_context(cpu_pool().primary_cpu())
|
_vcpu_context(cpu_pool().primary_cpu())
|
||||||
{
|
{
|
||||||
affinity(cpu_pool().primary_cpu());
|
affinity(cpu_pool().primary_cpu());
|
||||||
Virtual_pic::pic().irq.enable();
|
|
||||||
|
|
||||||
vt_host_context.sp = _cpu->stack_start();
|
vt_host_context.sp = _cpu->stack_start();
|
||||||
vt_host_context.ttbr0 = Cpu::Ttbr0_64bit::read();
|
vt_host_context.ttbr0 = Cpu::Ttbr0_64bit::read();
|
||||||
|
@ -235,12 +137,9 @@ Kernel::Vm::~Vm() { alloc().free(_id); }
|
||||||
|
|
||||||
void Kernel::Vm::exception(Cpu & cpu)
|
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::INTERRUPT_REQUEST:
|
||||||
case Genode::Cpu_state::FAST_INTERRUPT_REQUEST:
|
case Genode::Cpu_state::FAST_INTERRUPT_REQUEST:
|
||||||
_state.gic_irq = Board::VT_MAINTAINANCE_IRQ;
|
|
||||||
_interrupt(cpu.id());
|
_interrupt(cpu.id());
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
@ -248,13 +147,18 @@ void Kernel::Vm::exception(Cpu & cpu)
|
||||||
_context.submit(1);
|
_context.submit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
Virtual_pic::save(_state);
|
if (cpu.pic().ack_virtual_irq(_vcpu_context.pic))
|
||||||
Virtual_timer::reset();
|
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
|
* 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
|
* to transport the HSTR and HCR register descriptions into the assembler
|
||||||
* path in a dense way
|
* path in a dense way
|
||||||
*/
|
*/
|
||||||
_state.hsr = Cpu::Hstr::init();
|
_state.esr_el2 = Cpu::Hstr::init();
|
||||||
_state.hpfar = Cpu::Hcr::init();
|
_state.hpfar_el2 = Cpu::Hcr::init();
|
||||||
|
|
||||||
Virtual_pic::load(_state);
|
|
||||||
Virtual_timer::load(_state);
|
|
||||||
|
|
||||||
hypervisor_enter_vm(_state);
|
hypervisor_enter_vm(_state);
|
||||||
}
|
}
|
||||||
|
@ -278,7 +179,7 @@ void Kernel::Vm::proceed(Cpu &)
|
||||||
|
|
||||||
void Vm::inject_irq(unsigned irq)
|
void Vm::inject_irq(unsigned irq)
|
||||||
{
|
{
|
||||||
_state.gic_irq = irq;
|
_state.irqs.last_irq = irq;
|
||||||
pause();
|
pause();
|
||||||
_context.submit(1);
|
_context.submit(1);
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,28 +14,20 @@
|
||||||
#ifndef _CORE__SPEC__ARNDALE__BOARD_H_
|
#ifndef _CORE__SPEC__ARNDALE__BOARD_H_
|
||||||
#define _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 <hw/spec/arm/arndale_board.h>
|
||||||
#include <spec/arm/exynos_mct.h>
|
#include <spec/arm/exynos_mct.h>
|
||||||
#include <spec/arm/cpu/vm_state_virtualization.h>
|
#include <spec/arm/cpu/vm_state_virtualization.h>
|
||||||
#include <translation_table.h>
|
#include <spec/arm/virtualization/board.h>
|
||||||
#include <kernel/configuration.h>
|
|
||||||
|
|
||||||
namespace Kernel { class Cpu; }
|
namespace Kernel { class Cpu; }
|
||||||
|
|
||||||
namespace Board {
|
namespace Board {
|
||||||
using namespace Hw::Arndale_board;
|
using namespace Hw::Arndale_board;
|
||||||
|
|
||||||
using Pic = Hw::Gicv2;
|
struct Virtual_local_pic {};
|
||||||
|
|
||||||
enum { VCPU_MAX = 1 };
|
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_ */
|
#endif /* _CORE__SPEC__ARNDALE__BOARD_H_ */
|
||||||
|
|
|
@ -41,18 +41,10 @@ class Genode::Cpu : public Arm_v7_cpu
|
||||||
static access_t init()
|
static access_t init()
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* allow cache (7), TLB (8) maintenance, and performance
|
* allow everything except c0, c11, c12, and c15 accesses.
|
||||||
* monitor (9), process/thread ID register (13) and timer (14)
|
|
||||||
* access.
|
|
||||||
*/
|
*/
|
||||||
access_t v = 0;
|
access_t v = 0;
|
||||||
T<0>::set(v, 1);
|
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<11>::set(v, 1);
|
||||||
T<12>::set(v, 1);
|
T<12>::set(v, 1);
|
||||||
T<15>::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 Twe : Bitfield<14, 1> {}; /* trap on WFE instruction */
|
||||||
struct Tidcp : Bitfield<20, 1> {}; /* trap lockdown */
|
struct Tidcp : Bitfield<20, 1> {}; /* trap lockdown */
|
||||||
struct Tac : Bitfield<21, 1> {}; /* trap ACTLR accesses */
|
struct Tac : Bitfield<21, 1> {}; /* trap ACTLR accesses */
|
||||||
struct Tvm : Bitfield<26, 1> {}; /* trap virtual memory ctrls */
|
|
||||||
|
|
||||||
static access_t init()
|
static access_t init()
|
||||||
{
|
{
|
||||||
|
@ -86,7 +77,6 @@ class Genode::Cpu : public Arm_v7_cpu
|
||||||
Twe::set(v, 1);
|
Twe::set(v, 1);
|
||||||
Tidcp::set(v, 1);
|
Tidcp::set(v, 1);
|
||||||
Tac::set(v, 1);
|
Tac::set(v, 1);
|
||||||
Tvm::set(v, 1);
|
|
||||||
return v;
|
return v;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -18,10 +18,7 @@
|
||||||
#include <spec/arm/virtualization/gicv2.h>
|
#include <spec/arm/virtualization/gicv2.h>
|
||||||
#include <spec/arm/generic_timer.h>
|
#include <spec/arm/generic_timer.h>
|
||||||
#include <spec/arm/cpu/vm_state_virtualization.h>
|
#include <spec/arm/cpu/vm_state_virtualization.h>
|
||||||
#include <translation_table.h>
|
#include <spec/arm/virtualization/board.h>
|
||||||
#include <kernel/configuration.h>
|
|
||||||
|
|
||||||
namespace Kernel { class Cpu; }
|
|
||||||
|
|
||||||
namespace Board {
|
namespace Board {
|
||||||
using namespace Hw::Imx7d_sabre_board;
|
using namespace Hw::Imx7d_sabre_board;
|
||||||
|
@ -29,13 +26,6 @@ namespace Board {
|
||||||
struct Virtual_local_pic {};
|
struct Virtual_local_pic {};
|
||||||
|
|
||||||
enum { TIMER_IRQ = 30, VCPU_MAX = 1 };
|
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_ */
|
#endif /* _CORE__SPEC__IMX7_SABRELITE__BOARD_H_ */
|
||||||
|
|
|
@ -18,9 +18,7 @@
|
||||||
#include <spec/arm/generic_timer.h>
|
#include <spec/arm/generic_timer.h>
|
||||||
#include <spec/arm/virtualization/gicv3.h>
|
#include <spec/arm/virtualization/gicv3.h>
|
||||||
#include <spec/arm_64/cpu/vm_state_virtualization.h>
|
#include <spec/arm_64/cpu/vm_state_virtualization.h>
|
||||||
#include <translation_table.h>
|
#include <spec/arm/virtualization/board.h>
|
||||||
#include <kernel/configuration.h>
|
|
||||||
#include <kernel/irq.h>
|
|
||||||
|
|
||||||
namespace Board {
|
namespace Board {
|
||||||
using namespace Hw::Imx8q_evk_board;
|
using namespace Hw::Imx8q_evk_board;
|
||||||
|
@ -29,59 +27,8 @@ namespace Board {
|
||||||
TIMER_IRQ = 14 + 16,
|
TIMER_IRQ = 14 + 16,
|
||||||
VT_TIMER_IRQ = 11 + 16,
|
VT_TIMER_IRQ = 11 + 16,
|
||||||
VT_MAINTAINANCE_IRQ = 9 + 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_ */
|
#endif /* _CORE__SPEC__IMX8Q_EVK__BOARD_H_ */
|
||||||
|
|
|
@ -18,8 +18,7 @@
|
||||||
#include <spec/arm/virtualization/gicv2.h>
|
#include <spec/arm/virtualization/gicv2.h>
|
||||||
#include <spec/arm/generic_timer.h>
|
#include <spec/arm/generic_timer.h>
|
||||||
#include <spec/arm/cpu/vm_state_virtualization.h>
|
#include <spec/arm/cpu/vm_state_virtualization.h>
|
||||||
#include <translation_table.h>
|
#include <spec/arm/virtualization/board.h>
|
||||||
#include <kernel/configuration.h>
|
|
||||||
|
|
||||||
namespace Kernel { class Cpu; }
|
namespace Kernel { class Cpu; }
|
||||||
|
|
||||||
|
@ -34,13 +33,6 @@ namespace Board {
|
||||||
VT_MAINTAINANCE_IRQ = 25,
|
VT_MAINTAINANCE_IRQ = 25,
|
||||||
VCPU_MAX = 1
|
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_ */
|
#endif /* _SRC__CORE__SPEC__VIRT__QEMU_H_ */
|
||||||
|
|
|
@ -250,7 +250,7 @@ class Hw::Long_translation_table
|
||||||
| Attribute_index::create(f)
|
| Attribute_index::create(f)
|
||||||
| Not_global::bits(!f.global)
|
| Not_global::bits(!f.global)
|
||||||
| Base::Shareability::bits(
|
| Base::Shareability::bits(
|
||||||
Base::Shareability::OUTER_SHAREABLE)
|
Base::Shareability::INNER_SHAREABLE)
|
||||||
| Base::Output_address::masked(pa)
|
| Base::Output_address::masked(pa)
|
||||||
| Base::Access_flag::bits(1)
|
| Base::Access_flag::bits(1)
|
||||||
| Descriptor::Valid::bits(1)
|
| Descriptor::Valid::bits(1)
|
||||||
|
@ -270,7 +270,7 @@ class Hw::Long_translation_table
|
||||||
addr_t const pa)
|
addr_t const pa)
|
||||||
{
|
{
|
||||||
return Base::Shareability::bits(
|
return Base::Shareability::bits(
|
||||||
Base::Shareability::NON_SHAREABLE)
|
Base::Shareability::INNER_SHAREABLE)
|
||||||
| Base::Output_address::masked(pa)
|
| Base::Output_address::masked(pa)
|
||||||
| Base::Access_flag::bits(1)
|
| Base::Access_flag::bits(1)
|
||||||
| Descriptor::Valid::bits(1)
|
| Descriptor::Valid::bits(1)
|
||||||
|
|
|
@ -87,33 +87,56 @@ if { [have_spec arm] } {
|
||||||
|
|
||||||
if {![file exists bin/linux]} {
|
if {![file exists bin/linux]} {
|
||||||
puts "Download linux kernel ..."
|
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]} {
|
if {![file exists bin/dtb]} {
|
||||||
puts "Download device tree blob ..."
|
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
|
# To obtain the linux kernel, do the following steps:
|
||||||
# but using a slightly simplified kernel configuration, as well as device tree
|
#
|
||||||
# for a minimal Versatile Express Cortex A15 like emulated board.
|
# 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
|
# To construct the initrd do the following:
|
||||||
# ! cd linux
|
# * get and install gcc from bootlin:
|
||||||
# ! git checkout origin/vexpress-vt
|
# (https://toolchains.bootlin.com/downloads/releases/toolchains/armv7-eabihf/tarballs/)
|
||||||
# ! make ARCH=arm CROSS_COMPILE=<cross_compiler_prefix> vexpress_config
|
# * build busybox
|
||||||
# ! make ARCH=arm CROSS_COMPILE=<cross_compiler_prefix> -j8 Image
|
# wget https://busybox.net/downloads/busybox-1.31.1.tar.bz2
|
||||||
# ! make ARCH=arm CROSS_COMPILE=<cross_compiler_prefix> vexpress-v2p-ca15-tc1.dtb
|
# 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
|
vmm
|
||||||
linux
|
linux
|
||||||
dtb
|
dtb
|
||||||
|
initrd
|
||||||
}
|
}
|
||||||
append_if [have_spec arm_64] boot_modules initrd
|
|
||||||
build_boot_image $boot_modules
|
build_boot_image $boot_modules
|
||||||
|
|
||||||
#
|
#
|
||||||
|
|
|
@ -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) { }
|
|
@ -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_ */
|
|
@ -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));
|
||||||
|
}
|
|
@ -22,7 +22,7 @@
|
||||||
#include <util/register.h>
|
#include <util/register.h>
|
||||||
|
|
||||||
namespace Vmm {
|
namespace Vmm {
|
||||||
class Cpu;
|
class Cpu_base;
|
||||||
class Generic_timer;
|
class Generic_timer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,7 +33,7 @@ class Vmm::Generic_timer : Gic::Irq::Irq_handler
|
||||||
Timer::Connection _timer;
|
Timer::Connection _timer;
|
||||||
Timer::One_shot_timeout<Generic_timer> _timeout;
|
Timer::One_shot_timeout<Generic_timer> _timeout;
|
||||||
Gic::Irq & _irq;
|
Gic::Irq & _irq;
|
||||||
Cpu & _cpu;
|
Cpu_base & _cpu;
|
||||||
|
|
||||||
struct Ctrl : Genode::Register<32>
|
struct Ctrl : Genode::Register<32>
|
||||||
{
|
{
|
||||||
|
@ -56,7 +56,7 @@ class Vmm::Generic_timer : Gic::Irq::Irq_handler
|
||||||
Generic_timer(Genode::Env & env,
|
Generic_timer(Genode::Env & env,
|
||||||
Genode::Entrypoint & ep,
|
Genode::Entrypoint & ep,
|
||||||
Gic::Irq & irq,
|
Gic::Irq & irq,
|
||||||
Cpu & cpu);
|
Cpu_base & cpu);
|
||||||
|
|
||||||
void schedule_timeout();
|
void schedule_timeout();
|
||||||
void cancel_timeout();
|
void cancel_timeout();
|
|
@ -159,13 +159,13 @@ void Gic::Gicd_banked::handle_irq()
|
||||||
|
|
||||||
irq(i).deassert();
|
irq(i).deassert();
|
||||||
|
|
||||||
_cpu.state().irqs.virtual_irq = 1023;
|
_cpu.state().irqs.virtual_irq = SPURIOUS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool Gic::Gicd_banked::pending_irq()
|
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 * i = _gic._pending_list.highest_enabled();
|
||||||
Irq * j = _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)
|
: _cpu(cpu), _gic(gic)
|
||||||
{
|
{
|
||||||
for (unsigned i = 0; i < MAX_SGI; i++)
|
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++)
|
for (unsigned i = 0; i < MAX_PPI; i++)
|
||||||
_ppi[i].construct(i+MAX_SGI, Irq::PPI, _pending_list);
|
_ppi[i].construct(i+MAX_SGI, Irq::PPI, _pending_list);
|
||||||
|
|
||||||
_cpu.state().irqs.last_irq = 1023;
|
_cpu.state().irqs.last_irq = SPURIOUS;
|
||||||
_cpu.state().irqs.virtual_irq = 1023;
|
_cpu.state().irqs.virtual_irq = SPURIOUS;
|
||||||
|
|
||||||
_rdist.construct(0x80a0000 + (cpu.cpu_id()*0x20000), 0x20000, cpu.cpu_id(), Vm::last_cpu() == cpu.cpu_id());
|
if (gic.version() >= 3) {
|
||||||
bus.add(*_rdist);
|
_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,
|
unsigned Gic::version() { return _version; }
|
||||||
const Genode::uint64_t addr,
|
|
||||||
const Genode::uint64_t size,
|
|
||||||
Mmio_bus & bus,
|
Gic::Gic(const char * const name,
|
||||||
Genode::Env & env)
|
const Genode::uint64_t addr,
|
||||||
: Mmio_device(name, addr, size)
|
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(_ctrl);
|
||||||
add(_typer);
|
add(_typer);
|
||||||
|
@ -248,4 +259,6 @@ Gic::Gic(const char * const name,
|
||||||
_spi[i].construct(i+MAX_SGI+MAX_PPI, Irq::SPI, _pending_list);
|
_spi[i].construct(i+MAX_SGI+MAX_PPI, Irq::SPI, _pending_list);
|
||||||
|
|
||||||
bus.add(*this);
|
bus.add(*this);
|
||||||
|
|
||||||
|
if (version < 3) vm.attach_pic(GICC_MMIO_START);
|
||||||
}
|
}
|
|
@ -18,11 +18,15 @@
|
||||||
|
|
||||||
#include <base/env.h>
|
#include <base/env.h>
|
||||||
#include <drivers/defs/arm_v7.h>
|
#include <drivers/defs/arm_v7.h>
|
||||||
|
#include <vm_session/connection.h>
|
||||||
#include <util/list.h>
|
#include <util/list.h>
|
||||||
#include <util/register.h>
|
#include <util/register.h>
|
||||||
#include <util/reconstructible.h>
|
#include <util/reconstructible.h>
|
||||||
|
|
||||||
namespace Vmm { class Gic; }
|
namespace Vmm {
|
||||||
|
class Cpu_base;
|
||||||
|
class Gic;
|
||||||
|
}
|
||||||
|
|
||||||
class Vmm::Gic : public Vmm::Mmio_device
|
class Vmm::Gic : public Vmm::Mmio_device
|
||||||
{
|
{
|
||||||
|
@ -120,11 +124,11 @@ class Vmm::Gic : public Vmm::Mmio_device
|
||||||
void handle_irq();
|
void handle_irq();
|
||||||
bool pending_irq();
|
bool pending_irq();
|
||||||
|
|
||||||
Gicd_banked(Cpu & cpu, Gic & gic, Mmio_bus & bus);
|
Gicd_banked(Cpu_base & cpu, Gic & gic, Mmio_bus & bus);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
Cpu & _cpu;
|
Cpu_base & _cpu;
|
||||||
Gic & _gic;
|
Gic & _gic;
|
||||||
Genode::Constructible<Irq> _sgi[MAX_SGI];
|
Genode::Constructible<Irq> _sgi[MAX_SGI];
|
||||||
Genode::Constructible<Irq> _ppi[MAX_PPI];
|
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,
|
Mmio_register gicr_ctlr { "GICR_CTLR", Mmio_register::RO,
|
||||||
0x0, 4, 0b10010 };
|
0x0, 4, 0b10010 };
|
||||||
Mmio_register gicr_typer { "GICR_TYPER", Mmio_register::RO,
|
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,
|
Mmio_register gicr_waker { "GICR_WAKER", Mmio_register::RO,
|
||||||
0x14, 4, 0 };
|
0x14, 4, 0 };
|
||||||
Mmio_register gicr_pidr2 { "GICR_PIDR2", Mmio_register::RO,
|
Mmio_register gicr_pidr2 { "GICR_PIDR2", Mmio_register::RO,
|
||||||
|
@ -242,9 +248,14 @@ class Vmm::Gic : public Vmm::Mmio_device
|
||||||
Genode::Constructible<Redistributor> _rdist;
|
Genode::Constructible<Redistributor> _rdist;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
unsigned version();
|
||||||
|
|
||||||
Gic(const char * const name,
|
Gic(const char * const name,
|
||||||
const Genode::uint64_t addr,
|
const Genode::uint64_t addr,
|
||||||
const Genode::uint64_t size,
|
const Genode::uint64_t size,
|
||||||
|
unsigned cpus,
|
||||||
|
unsigned version,
|
||||||
|
Genode::Vm_connection & vm,
|
||||||
Mmio_bus & bus,
|
Mmio_bus & bus,
|
||||||
Genode::Env & env);
|
Genode::Env & env);
|
||||||
|
|
||||||
|
@ -254,8 +265,8 @@ class Vmm::Gic : public Vmm::Mmio_device
|
||||||
|
|
||||||
Genode::Constructible<Irq> _spi[MAX_SPI];
|
Genode::Constructible<Irq> _spi[MAX_SPI];
|
||||||
Irq::List _pending_list;
|
Irq::List _pending_list;
|
||||||
unsigned _cpu_cnt { 2 }; /* FIXME: smp support */
|
unsigned _cpu_cnt;
|
||||||
unsigned _version { 3 }; /* FIXME: version support */
|
unsigned _version;
|
||||||
|
|
||||||
struct Gicd_ctlr : Genode::Register<32>, Mmio_register
|
struct Gicd_ctlr : Genode::Register<32>, Mmio_register
|
||||||
{
|
{
|
||||||
|
@ -282,7 +293,8 @@ class Vmm::Gic : public Vmm::Mmio_device
|
||||||
|
|
||||||
Gicd_typer(unsigned cpus)
|
Gicd_typer(unsigned cpus)
|
||||||
: Mmio_register("GICD_TYPER", Mmio_register::RO, 0x4, 4,
|
: 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 };
|
} _typer { _cpu_cnt };
|
||||||
|
|
||||||
struct Gicd_iidr : Genode::Register<32>, Mmio_register
|
struct Gicd_iidr : Genode::Register<32>, Mmio_register
|
|
@ -112,11 +112,9 @@ void Vmm::Mmio_bus::handle_memory_access(Vmm::Cpu & cpu)
|
||||||
Mmio_device & dev = get<Mmio_device>(bus_range);
|
Mmio_device & dev = get<Mmio_device>(bus_range);
|
||||||
Address_range dev_range(ipa - dev.start,width);
|
Address_range dev_range(ipa - dev.start,width);
|
||||||
if (wr) {
|
if (wr) {
|
||||||
dev.write(dev_range, cpu, (idx == 31) ? 0 : state.r[idx]);
|
dev.write(dev_range, cpu, state.reg(idx));
|
||||||
} else {
|
} else {
|
||||||
if (idx > 30)
|
state.reg(idx, dev.read(dev_range, cpu));
|
||||||
throw Exception("Wrong register index when reading ", bus_range);
|
|
||||||
state.r[idx] = dev.read(dev_range, cpu);
|
|
||||||
}
|
}
|
||||||
} catch(Exception & e) {
|
} catch(Exception & e) {
|
||||||
Genode::warning(e);
|
Genode::warning(e);
|
|
@ -1,77 +1,53 @@
|
||||||
/*
|
/*
|
||||||
* \brief Driver for the Versatile Express A9X4 board
|
* \brief VMM address space utility
|
||||||
* \author Martin stein
|
* \author Stefan Kalkowski
|
||||||
* \date 2011-11-03
|
* \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
|
* This file is part of the Genode OS framework, which is distributed
|
||||||
* under the terms of the GNU Affero General Public License version 3.
|
* under the terms of the GNU Affero General Public License version 3.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef _INCLUDE__PLATFORM__VEA9X4__DRIVERS__BOARD_BASE_H_
|
#ifndef _SRC__SERVER__VMM__BOARD_H_
|
||||||
#define _INCLUDE__PLATFORM__VEA9X4__DRIVERS__BOARD_BASE_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,
|
||||||
|
|
||||||
/**
|
GIC_VERSION = 2,
|
||||||
* Driver for the Versatile Express A9X4 board
|
GICD_MMIO_START = 0x8000000,
|
||||||
*
|
GICD_MMIO_SIZE = 0x10000,
|
||||||
* Implies the uATX motherboard and the CoreTile Express A9X4 daughterboard
|
GICC_MMIO_START = 0x8010000,
|
||||||
*/
|
GICR_MMIO_START = 0x80a0000,
|
||||||
struct Vea9x4::Board
|
GICR_MMIO_SIZE = 0xf60000,
|
||||||
{
|
|
||||||
enum
|
|
||||||
{
|
|
||||||
/* MMIO */
|
|
||||||
MMIO_0_BASE = 0x10000000,
|
|
||||||
MMIO_0_SIZE = 0x10000000,
|
|
||||||
MMIO_1_BASE = 0x4C000000,
|
|
||||||
MMIO_1_SIZE = 0x04000000,
|
|
||||||
|
|
||||||
/* RAM */
|
PL011_MMIO_START = 0x9000000,
|
||||||
RAM_0_BASE = 0x60000000,
|
PL011_MMIO_SIZE = 0x1000,
|
||||||
RAM_0_SIZE = 0x20000000,
|
PL011_IRQ = 33,
|
||||||
RAM_1_BASE = 0x84000000,
|
|
||||||
RAM_1_SIZE = 0x1c000000,
|
|
||||||
RAM_2_BASE = 0x48000000,
|
|
||||||
RAM_2_SIZE = 0x02000000,
|
|
||||||
|
|
||||||
/* UART */
|
VIRTIO_CONSOLE_MMIO_START = 0xa000000,
|
||||||
PL011_0_MMIO_BASE = MMIO_0_BASE + 0x9000,
|
VIRTIO_CONSOLE_MMIO_SIZE = 0x200,
|
||||||
PL011_0_MMIO_SIZE = 0x1000,
|
VIRTIO_CONSOLE_IRQ = 48,
|
||||||
PL011_0_CLOCK = 24*1000*1000,
|
|
||||||
PL011_0_IRQ = 37,
|
|
||||||
PL011_1_IRQ = 38,
|
|
||||||
PL011_2_IRQ = 39,
|
|
||||||
PL011_3_IRQ = 40,
|
|
||||||
|
|
||||||
/* timer/counter */
|
VIRTIO_NET_MMIO_START = 0xa000200,
|
||||||
SP804_0_1_MMIO_BASE = MMIO_0_BASE + 0x11000,
|
VIRTIO_NET_MMIO_SIZE = 0x200,
|
||||||
SP804_0_1_MMIO_SIZE = 0x1000,
|
VIRTIO_NET_IRQ = 49,
|
||||||
SP804_0_1_CLOCK = 1000*1000,
|
|
||||||
SP804_0_1_IRQ = 34,
|
|
||||||
|
|
||||||
/* PS2 */
|
RAM_START = 0x40000000,
|
||||||
KMI_0_IRQ = 44,
|
RAM_SIZE = 128 * 1024 *1024,
|
||||||
KMI_1_IRQ = 45,
|
|
||||||
|
|
||||||
/* LAN */
|
VTIMER_IRQ = 27,
|
||||||
LAN9118_IRQ = 47,
|
|
||||||
|
|
||||||
/* card reader */
|
MAX_CPUS = 1,
|
||||||
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,
|
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
#endif /* _INCLUDE__PLATFORM__VEA9X4__DRIVERS__BOARD_BASE_H_ */
|
|
||||||
|
|
||||||
|
#endif /* _SRC__SERVER__VMM__BOARD_H_ */
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
|
@ -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_ */
|
|
@ -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
|
@ -1,7 +1,19 @@
|
||||||
TARGET = vmm
|
TARGET = vmm
|
||||||
REQUIRES = hw arm_v7
|
REQUIRES = hw arm_v7
|
||||||
LIBS = base
|
LIBS = base
|
||||||
SRC_CC = main.cc
|
SRC_CC += spec/arm_v7/generic_timer.cc
|
||||||
INC_DIR += $(PRG_DIR)
|
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 :=
|
CC_CXX_WARN_STRICT :=
|
||||||
|
|
|
@ -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
|
|
|
@ -1,4 +0,0 @@
|
||||||
TARGET = vmm-test-kernel
|
|
||||||
REQUIRES = arm_v7
|
|
||||||
SRC_S = main.s
|
|
||||||
CC_MARCH = -mcpu=cortex-a15
|
|
|
@ -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>;
|
||||||
|
};
|
||||||
|
};
|
|
@ -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_ */
|
|
@ -14,15 +14,27 @@
|
||||||
#include <vm.h>
|
#include <vm.h>
|
||||||
#include <psci.h>
|
#include <psci.h>
|
||||||
|
|
||||||
|
using Vmm::Cpu_base;
|
||||||
using Vmm::Cpu;
|
using Vmm::Cpu;
|
||||||
using Vmm::Gic;
|
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
|
void Cpu_base::State::reg(unsigned idx, Genode::uint64_t v)
|
||||||
Cpu::System_register::Iss::value(unsigned op0, unsigned crn, unsigned op1,
|
{
|
||||||
unsigned crm, unsigned op2)
|
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;
|
access_t v = 0;
|
||||||
Crn::set(v, crn);
|
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_base::System_register::Iss::access_t
|
||||||
Cpu::System_register::Iss::mask_encoding(access_t v)
|
Cpu_base::System_register::Iss::mask_encoding(access_t v)
|
||||||
{
|
{
|
||||||
return Crm::masked(v) |
|
return Crm::masked(v) |
|
||||||
Crn::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,
|
void Cpu_base::_handle_brk()
|
||||||
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);
|
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;
|
state().reg(0, dtb);
|
||||||
|
state().ip = ip;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
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,
|
Cpu::Cpu(Vm & vm,
|
||||||
Genode::Vm_connection & vm_session,
|
Genode::Vm_connection & vm_session,
|
||||||
Mmio_bus & bus,
|
Mmio_bus & bus,
|
||||||
|
@ -356,15 +213,7 @@ Cpu::Cpu(Vm & vm,
|
||||||
Genode::Env & env,
|
Genode::Env & env,
|
||||||
Genode::Heap & heap,
|
Genode::Heap & heap,
|
||||||
Genode::Entrypoint & ep)
|
Genode::Entrypoint & ep)
|
||||||
: _vm(vm),
|
: Cpu_base(vm, vm_session, bus, gic, env, heap, ep),
|
||||||
_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
|
|
||||||
_sr_id_aa64afr0_el1 (3, 0, 0, 5, 4, "ID_AA64AFR0_EL1", false, 0x0, _reg_tree),
|
_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_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),
|
_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_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_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_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_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_csselr_el1 (3, 0, 2, 0, 0, "CSSELR_EL1", true, 0x0, _reg_tree),
|
||||||
_sr_ctr_el0 (_reg_tree),
|
_sr_ctr_el0 (_reg_tree),
|
||||||
_sr_ccsidr_el1 (_sr_csselr_el1, _state, _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_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_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),
|
_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_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_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_oslar (2, 1, 0, 0, 4, "OSLAR_EL1", true, 0x0, _reg_tree),
|
||||||
_sr_sgi1r_el1 (_reg_tree, vm),
|
_sr_sgi1r_el1 (_reg_tree, vm)
|
||||||
_gic(*this, gic, bus),
|
|
||||||
_timer(env, ep, _gic.irq(27), *this)
|
|
||||||
{
|
{
|
||||||
_state.pstate = 0b1111000101; /* el1 mode and IRQs disabled */
|
_state.pstate = 0b1111000101; /* el1 mode and IRQs disabled */
|
||||||
_state.vmpidr_el2 = cpu_id();
|
_state.vmpidr_el2 = cpu_id();
|
||||||
|
|
|
@ -14,41 +14,14 @@
|
||||||
#ifndef _SRC__SERVER__VMM__CPU_H_
|
#ifndef _SRC__SERVER__VMM__CPU_H_
|
||||||
#define _SRC__SERVER__VMM__CPU_H_
|
#define _SRC__SERVER__VMM__CPU_H_
|
||||||
|
|
||||||
#include <exception.h>
|
#include <cpu_base.h>
|
||||||
#include <generic_timer.h>
|
|
||||||
|
|
||||||
#include <base/env.h>
|
namespace Vmm { class Cpu; }
|
||||||
#include <base/heap.h>
|
|
||||||
#include <cpu/vm_state_virtualization.h>
|
|
||||||
#include <util/mmio.h>
|
|
||||||
#include <vm_session/connection.h>
|
|
||||||
|
|
||||||
namespace Vmm {
|
class Vmm::Cpu : public Vmm::Cpu_base
|
||||||
class Vm;
|
|
||||||
class Cpu;
|
|
||||||
Genode::Lock & lock();
|
|
||||||
}
|
|
||||||
|
|
||||||
class Vmm::Cpu
|
|
||||||
{
|
{
|
||||||
public:
|
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,
|
Cpu(Vm & vm,
|
||||||
Genode::Vm_connection & vm_session,
|
Genode::Vm_connection & vm_session,
|
||||||
Mmio_bus & bus,
|
Mmio_bus & bus,
|
||||||
|
@ -57,58 +30,6 @@ class Vmm::Cpu
|
||||||
Genode::Heap & heap,
|
Genode::Heap & heap,
|
||||||
Genode::Entrypoint & ep);
|
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 {
|
enum Exception_type {
|
||||||
AARCH64_SYNC = 0x400,
|
AARCH64_SYNC = 0x400,
|
||||||
AARCH64_IRQ = 0x480,
|
AARCH64_IRQ = 0x480,
|
||||||
|
@ -121,72 +42,7 @@ class Vmm::Cpu
|
||||||
NO_EXCEPTION = 0xffff
|
NO_EXCEPTION = 0xffff
|
||||||
};
|
};
|
||||||
|
|
||||||
class System_register : public Genode::Avl_node<System_register>
|
private:
|
||||||
{
|
|
||||||
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); }
|
|
||||||
};
|
|
||||||
|
|
||||||
class Id_aa64pfr0 : public System_register,
|
class Id_aa64pfr0 : public System_register,
|
||||||
public Genode::Register<64>
|
public Genode::Register<64>
|
||||||
|
@ -252,15 +108,6 @@ class Vmm::Cpu
|
||||||
virtual void write(Genode::addr_t v) override;
|
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 **
|
** Identification registers **
|
||||||
******************************/
|
******************************/
|
||||||
|
@ -308,22 +155,10 @@ class Vmm::Cpu
|
||||||
System_register _sr_oslar;
|
System_register _sr_oslar;
|
||||||
|
|
||||||
/***********************
|
/***********************
|
||||||
** Local peripherals **
|
** GIC cpu interface **
|
||||||
***********************/
|
***********************/
|
||||||
|
|
||||||
Icc_sgi1r_el1 _sr_sgi1r_el1;
|
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_ */
|
#endif /* _SRC__SERVER__VMM__CPU_H_ */
|
||||||
|
|
|
@ -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 Generic_timer::_usecs_left()
|
||||||
{
|
{
|
||||||
Genode::uint64_t count;
|
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,
|
return Genode::timer_ticks_to_us(_cpu.state().timer.compare - count,
|
||||||
_ticks_per_ms());
|
_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));
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,15 +1,19 @@
|
||||||
TARGET = vmm
|
TARGET = vmm
|
||||||
REQUIRES = hw arm_v8
|
REQUIRES = hw arm_v8
|
||||||
LIBS = base
|
LIBS = base
|
||||||
|
SRC_CC += spec/arm_v8/generic_timer.cc
|
||||||
SRC_CC += address_space.cc
|
SRC_CC += address_space.cc
|
||||||
SRC_CC += cpu.cc
|
SRC_CC += cpu.cc
|
||||||
|
SRC_CC += cpu_base.cc
|
||||||
SRC_CC += generic_timer.cc
|
SRC_CC += generic_timer.cc
|
||||||
SRC_CC += gicv2.cc
|
SRC_CC += gic.cc
|
||||||
SRC_CC += main.cc
|
SRC_CC += main.cc
|
||||||
SRC_CC += mmio.cc
|
SRC_CC += mmio.cc
|
||||||
SRC_CC += pl011.cc
|
SRC_CC += pl011.cc
|
||||||
SRC_CC += virtio_device.cc
|
SRC_CC += virtio_device.cc
|
||||||
SRC_CC += vm.cc
|
SRC_CC += vm.cc
|
||||||
INC_DIR += $(PRG_DIR)
|
INC_DIR += $(PRG_DIR)/../.. $(PRG_DIR)
|
||||||
|
|
||||||
|
vpath %.cc $(PRG_DIR)/../..
|
||||||
|
|
||||||
CC_CXX_WARN_STRICT :=
|
CC_CXX_WARN_STRICT :=
|
||||||
|
|
|
@ -48,9 +48,12 @@ struct Vmm::Virtio_queue_data
|
||||||
uint32_t ready { 0 };
|
uint32_t ready { 0 };
|
||||||
bool tx { false };
|
bool tx { false };
|
||||||
|
|
||||||
addr_t descr() const { return ((addr_t)descr_high << 32) | descr_low; }
|
uint64_t descr() const {
|
||||||
addr_t driver() const { return ((addr_t)driver_high << 32) | driver_low; }
|
return ((uint64_t)descr_high << 32) | descr_low; }
|
||||||
addr_t device() const { return ((addr_t)device_high << 32) | device_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 };
|
enum { MAX_QUEUE_SIZE = 1 << 15 };
|
||||||
};
|
};
|
||||||
|
@ -83,10 +86,10 @@ class Vmm::Virtio_descriptor : Genode::Mmio
|
||||||
return Virtio_descriptor(base() + (size() * idx));
|
return Virtio_descriptor(base() + (size() * idx));
|
||||||
}
|
}
|
||||||
|
|
||||||
addr_t address() const { return read<Address>(); }
|
uint64_t address() const { return read<Address>(); }
|
||||||
size_t length () const { return read<Length>(); }
|
size_t length () const { return read<Length>(); }
|
||||||
uint16_t flags() const { return read<Flags>(); }
|
uint16_t flags() const { return read<Flags>(); }
|
||||||
uint16_t next() const { return read<Next>(); }
|
uint16_t next() const { return read<Next>(); }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -173,7 +176,7 @@ class Vmm::Virtio_queue
|
||||||
id %= _length;
|
id %= _length;
|
||||||
|
|
||||||
Virtio_descriptor descr = _descr.index(id);
|
Virtio_descriptor descr = _descr.index(id);
|
||||||
addr_t address = descr.address();
|
uint64_t address = descr.address();
|
||||||
size_t length = descr.length();
|
size_t length = descr.length();
|
||||||
if (!address || !length) break;
|
if (!address || !length) break;
|
||||||
|
|
|
@ -48,14 +48,16 @@ Vmm::Cpu & Vm::boot_cpu()
|
||||||
|
|
||||||
Vm::Vm(Genode::Env & env)
|
Vm::Vm(Genode::Env & env)
|
||||||
: _env(env),
|
: _env(env),
|
||||||
_gic("Gicv3", 0x8000000, 0x10000, _bus, env),
|
_gic("Gicv3", GICD_MMIO_START, GICD_MMIO_SIZE,
|
||||||
_uart("Pl011", 0x9000000, 0x1000, 33, boot_cpu(), _bus, env),
|
MAX_CPUS, GIC_VERSION, _vm, _bus, env),
|
||||||
_virtio_console("HVC", 0xa000000, 0x200, 48, boot_cpu(), _bus, _ram, env),
|
_uart("Pl011", PL011_MMIO_START, PL011_MMIO_SIZE,
|
||||||
_virtio_net("Net", 0xa000200, 0x200, 49, boot_cpu(), _bus, _ram, env)
|
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);
|
_vm.attach(_vm_ram.cap(), RAM_START);
|
||||||
|
|
||||||
/* FIXME extend for gicv2 by: _vm.attach_pic(0x8010000); */
|
|
||||||
|
|
||||||
_load_kernel();
|
_load_kernel();
|
||||||
_load_dtb();
|
_load_dtb();
|
||||||
|
@ -71,7 +73,7 @@ Vm::Vm(Genode::Env & env)
|
||||||
Genode::log("Start virtual machine ...");
|
Genode::log("Start virtual machine ...");
|
||||||
|
|
||||||
Cpu & cpu = boot_cpu();
|
Cpu & cpu = boot_cpu();
|
||||||
cpu.state().ip = _ram.base() + KERNEL_OFFSET;
|
cpu.initialize_boot(_ram.base() + KERNEL_OFFSET,
|
||||||
cpu.state().r[0] = _ram.base() + DTB_OFFSET;
|
_ram.base() + DTB_OFFSET);
|
||||||
cpu.run();
|
cpu.run();
|
||||||
};
|
};
|
|
@ -14,6 +14,7 @@
|
||||||
#ifndef _SRC__SERVER__VMM__VM_H_
|
#ifndef _SRC__SERVER__VMM__VM_H_
|
||||||
#define _SRC__SERVER__VMM__VM_H_
|
#define _SRC__SERVER__VMM__VM_H_
|
||||||
|
|
||||||
|
#include <board.h>
|
||||||
#include <ram.h>
|
#include <ram.h>
|
||||||
#include <exception.h>
|
#include <exception.h>
|
||||||
#include <cpu.h>
|
#include <cpu.h>
|
||||||
|
@ -34,15 +35,7 @@ class Vmm::Vm
|
||||||
|
|
||||||
using Ep = Genode::Entrypoint;
|
using Ep = Genode::Entrypoint;
|
||||||
|
|
||||||
enum {
|
enum { STACK_SIZE = sizeof(unsigned long) * 2048, };
|
||||||
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,
|
|
||||||
};
|
|
||||||
|
|
||||||
Genode::Env & _env;
|
Genode::Env & _env;
|
||||||
Genode::Vm_connection _vm { _env };
|
Genode::Vm_connection _vm { _env };
|
||||||
|
@ -51,7 +44,7 @@ class Vmm::Vm
|
||||||
Genode::Attached_rom_dataspace _initrd_rom { _env, "initrd" };
|
Genode::Attached_rom_dataspace _initrd_rom { _env, "initrd" };
|
||||||
Genode::Attached_ram_dataspace _vm_ram { _env.ram(), _env.rm(),
|
Genode::Attached_ram_dataspace _vm_ram { _env.ram(), _env.rm(),
|
||||||
RAM_SIZE, Genode::CACHED };
|
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::addr_t)_vm_ram.local_addr<void>()};
|
||||||
Genode::Heap _heap { _env.ram(), _env.rm() };
|
Genode::Heap _heap { _env.ram(), _env.rm() };
|
||||||
Mmio_bus _bus;
|
Mmio_bus _bus;
|
Loading…
Reference in New Issue