mirror of
https://github.com/genodelabs/genode.git
synced 2025-01-18 02:40:08 +00:00
parent
74e75d7fbc
commit
941e918b46
@ -27,59 +27,55 @@ namespace Genode
|
||||
|
||||
struct Genode::Vm_state : Genode::Cpu_state_modes
|
||||
{
|
||||
Genode::uint64_t vttbr;
|
||||
Genode::uint32_t sctrl;
|
||||
Genode::uint32_t hsr;
|
||||
Genode::uint32_t hpfar;
|
||||
Genode::uint32_t hdfar;
|
||||
Genode::uint32_t hifar;
|
||||
Genode::uint32_t ttbcr;
|
||||
Genode::uint32_t ttbr0;
|
||||
Genode::uint32_t ttbr1;
|
||||
Genode::uint32_t prrr;
|
||||
Genode::uint32_t nmrr;
|
||||
Genode::uint32_t dacr;
|
||||
Genode::uint32_t dfsr;
|
||||
Genode::uint32_t ifsr;
|
||||
Genode::uint32_t adfsr;
|
||||
Genode::uint32_t aifsr;
|
||||
Genode::uint32_t dfar;
|
||||
Genode::uint32_t ifar;
|
||||
Genode::uint32_t cidr;
|
||||
Genode::uint32_t tls1;
|
||||
Genode::uint32_t tls2;
|
||||
Genode::uint32_t tls3;
|
||||
Genode::uint32_t cpacr;
|
||||
Genode::uint64_t vttbr { 0 };
|
||||
Genode::uint32_t sctrl { 0 };
|
||||
Genode::uint32_t esr_el2 { 0 };
|
||||
Genode::uint32_t hpfar_el2 { 0 };
|
||||
Genode::uint32_t far_el2 { 0 };
|
||||
Genode::uint32_t hifar { 0 };
|
||||
Genode::uint32_t ttbcr { 0 };
|
||||
Genode::uint32_t ttbr0 { 0 };
|
||||
Genode::uint32_t ttbr1 { 0 };
|
||||
Genode::uint32_t prrr { 0 };
|
||||
Genode::uint32_t nmrr { 0 };
|
||||
Genode::uint32_t dacr { 0 };
|
||||
Genode::uint32_t dfsr { 0 };
|
||||
Genode::uint32_t ifsr { 0 };
|
||||
Genode::uint32_t adfsr { 0 };
|
||||
Genode::uint32_t aifsr { 0 };
|
||||
Genode::uint32_t dfar { 0 };
|
||||
Genode::uint32_t ifar { 0 };
|
||||
Genode::uint32_t cidr { 0 };
|
||||
Genode::uint32_t tls1 { 0 };
|
||||
Genode::uint32_t tls2 { 0 };
|
||||
Genode::uint32_t tls3 { 0 };
|
||||
Genode::uint32_t cpacr { 0 };
|
||||
|
||||
/**
|
||||
* Fpu registers
|
||||
*/
|
||||
Genode::uint32_t fpscr;
|
||||
Genode::uint64_t d0_d31[32];
|
||||
Genode::uint32_t fpscr { 0 };
|
||||
Genode::uint64_t d0_d31[32]{ 0 };
|
||||
|
||||
/**
|
||||
* Timer related registers
|
||||
*/
|
||||
|
||||
Genode::uint32_t timer_ctrl;
|
||||
Genode::uint32_t timer_val;
|
||||
bool timer_irq;
|
||||
|
||||
struct Timer {
|
||||
Genode::uint64_t offset { 0 };
|
||||
Genode::uint64_t compare { 0 };
|
||||
Genode::uint32_t control { 0 };
|
||||
Genode::uint32_t kcontrol { 0 };
|
||||
bool irq { false };
|
||||
} timer {};
|
||||
|
||||
/**
|
||||
* PIC related registers
|
||||
* Interrupt related values
|
||||
*/
|
||||
|
||||
enum { NR_IRQ = 4 };
|
||||
|
||||
Genode::uint32_t gic_hcr;
|
||||
Genode::uint32_t gic_vmcr;
|
||||
Genode::uint32_t gic_misr;
|
||||
Genode::uint32_t gic_apr;
|
||||
Genode::uint32_t gic_eisr;
|
||||
Genode::uint32_t gic_elrsr0;
|
||||
Genode::uint32_t gic_lr[4];
|
||||
unsigned gic_irq;
|
||||
struct Pic
|
||||
{
|
||||
unsigned last_irq { 1023 };
|
||||
unsigned virtual_irq { 1023 };
|
||||
} irqs {};
|
||||
};
|
||||
|
||||
#endif /* _INCLUDE__SPEC__ARNDALE__VM_STATE_H_ */
|
||||
|
@ -11,6 +11,7 @@ INC_DIR += $(REP_DIR)/src/core/spec/arm/virtualization
|
||||
# add C++ sources
|
||||
SRC_CC += kernel/vm_thread_on.cc
|
||||
SRC_CC += spec/arm/gicv2.cc
|
||||
SRC_CC += spec/arm/virtualization/gicv2.cc
|
||||
SRC_CC += spec/arm_v7/virtualization/kernel/vm.cc
|
||||
SRC_CC += spec/arm/virtualization/platform_services.cc
|
||||
SRC_CC += spec/arm/virtualization/vm_session_component.cc
|
||||
|
@ -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.
|
||||
*/
|
||||
|
||||
#include <spec/arm/cortex_a7_a15_virtualization.h>
|
||||
#include <platform.h>
|
||||
|
||||
extern "C" void * _start_setup_stack; /* entrypoint for non-boot CPUs */
|
||||
@ -25,110 +26,6 @@ Bootstrap::Platform::Board::Board()
|
||||
Memory_region { UART_2_MMIO_BASE, UART_2_MMIO_SIZE }) { }
|
||||
|
||||
|
||||
static inline void prepare_nonsecure_world()
|
||||
{
|
||||
using Cpu = Hw::Arm_cpu;
|
||||
|
||||
/* if we are already in HYP mode we're done (depends on u-boot version) */
|
||||
if (Cpu::Psr::M::get(Cpu::Cpsr::read()) == Cpu::Psr::M::HYP)
|
||||
return;
|
||||
|
||||
/* ARM generic timer counter freq needs to be set in secure mode */
|
||||
volatile unsigned long * mct_control = (unsigned long*) 0x101C0240;
|
||||
*mct_control = 0x100;
|
||||
Cpu::Cntfrq::write(24000000);
|
||||
|
||||
/*
|
||||
* enable coprocessor 10 + 11 access and SMP bit access in auxiliary control
|
||||
* register for non-secure world
|
||||
*/
|
||||
Cpu::Nsacr::access_t nsacr = 0;
|
||||
Cpu::Nsacr::Cpnsae10::set(nsacr, 1);
|
||||
Cpu::Nsacr::Cpnsae11::set(nsacr, 1);
|
||||
Cpu::Nsacr::Ns_smp::set(nsacr, 1);
|
||||
Cpu::Nsacr::write(nsacr);
|
||||
|
||||
asm volatile (
|
||||
"msr sp_mon, sp \n" /* copy current mode's sp */
|
||||
"msr lr_mon, lr \n" /* copy current mode's lr */
|
||||
"cps #22 \n" /* switch to monitor mode */
|
||||
);
|
||||
|
||||
Cpu::Scr::access_t scr = 0;
|
||||
Cpu::Scr::Ns::set(scr, 1);
|
||||
Cpu::Scr::Fw::set(scr, 1);
|
||||
Cpu::Scr::Aw::set(scr, 1);
|
||||
Cpu::Scr::Scd::set(scr, 1);
|
||||
Cpu::Scr::Hce::set(scr, 1);
|
||||
Cpu::Scr::Sif::set(scr, 1);
|
||||
Cpu::Scr::write(scr);
|
||||
}
|
||||
|
||||
|
||||
static inline void prepare_hypervisor(Genode::addr_t table)
|
||||
{
|
||||
using Cpu = Hw::Arm_cpu;
|
||||
|
||||
/* set hypervisor exception vector */
|
||||
Cpu::Hvbar::write(Hw::Mm::hypervisor_exception_vector().base);
|
||||
|
||||
/* set hypervisor's translation table */
|
||||
Cpu::Httbr_64bit::write(table);
|
||||
|
||||
Cpu::Ttbcr::access_t ttbcr = 0;
|
||||
Cpu::Ttbcr::Irgn0::set(ttbcr, 1);
|
||||
Cpu::Ttbcr::Orgn0::set(ttbcr, 1);
|
||||
Cpu::Ttbcr::Sh0::set(ttbcr, 2);
|
||||
Cpu::Ttbcr::Eae::set(ttbcr, 1);
|
||||
|
||||
/* prepare MMU usage by hypervisor code */
|
||||
Cpu::Htcr::write(ttbcr);
|
||||
|
||||
/* don't trap on cporocessor 10 + 11, but all others */
|
||||
Cpu::Hcptr::access_t hcptr = 0;
|
||||
Cpu::Hcptr::Tcp<0>::set(hcptr, 1);
|
||||
Cpu::Hcptr::Tcp<1>::set(hcptr, 1);
|
||||
Cpu::Hcptr::Tcp<2>::set(hcptr, 1);
|
||||
Cpu::Hcptr::Tcp<3>::set(hcptr, 1);
|
||||
Cpu::Hcptr::Tcp<4>::set(hcptr, 1);
|
||||
Cpu::Hcptr::Tcp<5>::set(hcptr, 1);
|
||||
Cpu::Hcptr::Tcp<6>::set(hcptr, 1);
|
||||
Cpu::Hcptr::Tcp<7>::set(hcptr, 1);
|
||||
Cpu::Hcptr::Tcp<8>::set(hcptr, 1);
|
||||
Cpu::Hcptr::Tcp<9>::set(hcptr, 1);
|
||||
Cpu::Hcptr::Tcp<12>::set(hcptr, 1);
|
||||
Cpu::Hcptr::Tcp<13>::set(hcptr, 1);
|
||||
Cpu::Hcptr::Tta::set(hcptr, 1);
|
||||
Cpu::Hcptr::Tcpac::set(hcptr, 1);
|
||||
Cpu::Hcptr::write(hcptr);
|
||||
|
||||
enum Memory_attributes {
|
||||
DEVICE_MEMORY = 0x04,
|
||||
NORMAL_MEMORY_UNCACHED = 0x44,
|
||||
NORMAL_MEMORY_CACHED = 0xff,
|
||||
};
|
||||
|
||||
Cpu::Mair0::access_t mair0 = 0;
|
||||
Cpu::Mair0::Attr0::set(mair0, NORMAL_MEMORY_UNCACHED);
|
||||
Cpu::Mair0::Attr1::set(mair0, DEVICE_MEMORY);
|
||||
Cpu::Mair0::Attr2::set(mair0, NORMAL_MEMORY_CACHED);
|
||||
Cpu::Mair0::Attr3::set(mair0, DEVICE_MEMORY);
|
||||
Cpu::Hmair0::write(mair0);
|
||||
|
||||
Cpu::Vtcr::access_t vtcr = ttbcr;
|
||||
Cpu::Vtcr::Sl0::set(vtcr, 1); /* set to starting level 1 */
|
||||
Cpu::Vtcr::write(vtcr);
|
||||
|
||||
Cpu::Sctlr::access_t sctlr = Cpu::Sctlr::read();
|
||||
Cpu::Sctlr::C::set(sctlr, 1);
|
||||
Cpu::Sctlr::I::set(sctlr, 1);
|
||||
Cpu::Sctlr::V::set(sctlr, 1);
|
||||
Cpu::Sctlr::M::set(sctlr, 1);
|
||||
Cpu::Sctlr::Z::set(sctlr, 1);
|
||||
Cpu::Hsctlr::write(sctlr);
|
||||
}
|
||||
|
||||
|
||||
static inline void switch_to_supervisor_mode()
|
||||
{
|
||||
using Cpsr = Hw::Arm_cpu::Psr;
|
||||
@ -152,24 +49,24 @@ static inline void switch_to_supervisor_mode()
|
||||
|
||||
unsigned Bootstrap::Platform::enable_mmu()
|
||||
{
|
||||
using namespace ::Board;
|
||||
|
||||
static volatile bool primary_cpu = true;
|
||||
static unsigned long timer_freq = 24000000;
|
||||
|
||||
/* locally initialize interrupt controller */
|
||||
::Board::Pic pic { };
|
||||
|
||||
prepare_nonsecure_world();
|
||||
volatile unsigned long * mct_control = (unsigned long*) 0x101C0240;
|
||||
*mct_control = 0x100;
|
||||
prepare_nonsecure_world(timer_freq);
|
||||
prepare_hypervisor((addr_t)core_pd->table_base);
|
||||
switch_to_supervisor_mode();
|
||||
|
||||
Cpu::Sctlr::init();
|
||||
Cpu::Cpsr::init();
|
||||
|
||||
Cpu::invalidate_data_cache();
|
||||
|
||||
/* primary cpu wakes up all others */
|
||||
if (primary_cpu && NR_OF_CPUS > 1) {
|
||||
Cpu::invalidate_data_cache();
|
||||
primary_cpu = false;
|
||||
Cpu::wake_up_all_cpus(&_start_setup_stack);
|
||||
}
|
||||
|
@ -13,6 +13,7 @@
|
||||
|
||||
#include <platform.h>
|
||||
#include <spec/arm/imx_aipstz.h>
|
||||
#include <spec/arm/cortex_a7_a15_virtualization.h>
|
||||
|
||||
extern "C" void * _start_setup_stack; /* entrypoint for non-boot CPUs */
|
||||
static unsigned char hyp_mode_stack[1024]; /* hypervisor mode's kernel stack */
|
||||
@ -160,108 +161,6 @@ Bootstrap::Platform::Board::Board()
|
||||
}
|
||||
|
||||
|
||||
static inline void prepare_nonsecure_world(unsigned long timer_freq)
|
||||
{
|
||||
using Cpu = Hw::Arm_cpu;
|
||||
|
||||
/* if we are already in HYP mode we're done (depends on u-boot version) */
|
||||
if (Cpu::Psr::M::get(Cpu::Cpsr::read()) == Cpu::Psr::M::HYP)
|
||||
return;
|
||||
|
||||
/* ARM generic timer counter freq needs to be set in secure mode */
|
||||
Cpu::Cntfrq::write(timer_freq);
|
||||
|
||||
/*
|
||||
* enable coprocessor 10 + 11 access and SMP bit access in auxiliary control
|
||||
* register for non-secure world
|
||||
*/
|
||||
Cpu::Nsacr::access_t nsacr = 0;
|
||||
Cpu::Nsacr::Cpnsae10::set(nsacr, 1);
|
||||
Cpu::Nsacr::Cpnsae11::set(nsacr, 1);
|
||||
Cpu::Nsacr::Ns_smp::set(nsacr, 1);
|
||||
Cpu::Nsacr::write(nsacr);
|
||||
|
||||
asm volatile (
|
||||
"msr sp_mon, sp \n" /* copy current mode's sp */
|
||||
"msr lr_mon, lr \n" /* copy current mode's lr */
|
||||
"cps #22 \n" /* switch to monitor mode */
|
||||
);
|
||||
|
||||
Cpu::Scr::access_t scr = 0;
|
||||
Cpu::Scr::Ns::set(scr, 1);
|
||||
Cpu::Scr::Fw::set(scr, 1);
|
||||
Cpu::Scr::Aw::set(scr, 1);
|
||||
Cpu::Scr::Scd::set(scr, 1);
|
||||
Cpu::Scr::Hce::set(scr, 1);
|
||||
Cpu::Scr::Sif::set(scr, 1);
|
||||
Cpu::Scr::write(scr);
|
||||
}
|
||||
|
||||
|
||||
static inline void prepare_hypervisor(Genode::addr_t table)
|
||||
{
|
||||
using Cpu = Hw::Arm_cpu;
|
||||
|
||||
/* set hypervisor exception vector */
|
||||
Cpu::Hvbar::write(Hw::Mm::hypervisor_exception_vector().base);
|
||||
|
||||
/* set hypervisor's translation table */
|
||||
Cpu::Httbr_64bit::write(table);
|
||||
|
||||
Cpu::Ttbcr::access_t ttbcr = 0;
|
||||
Cpu::Ttbcr::Irgn0::set(ttbcr, 1);
|
||||
Cpu::Ttbcr::Orgn0::set(ttbcr, 1);
|
||||
Cpu::Ttbcr::Sh0::set(ttbcr, 2);
|
||||
Cpu::Ttbcr::Eae::set(ttbcr, 1);
|
||||
|
||||
/* prepare MMU usage by hypervisor code */
|
||||
Cpu::Htcr::write(ttbcr);
|
||||
|
||||
/* don't trap on cporocessor 10 + 11, but all others */
|
||||
Cpu::Hcptr::access_t hcptr = 0;
|
||||
Cpu::Hcptr::Tcp<0>::set(hcptr, 1);
|
||||
Cpu::Hcptr::Tcp<1>::set(hcptr, 1);
|
||||
Cpu::Hcptr::Tcp<2>::set(hcptr, 1);
|
||||
Cpu::Hcptr::Tcp<3>::set(hcptr, 1);
|
||||
Cpu::Hcptr::Tcp<4>::set(hcptr, 1);
|
||||
Cpu::Hcptr::Tcp<5>::set(hcptr, 1);
|
||||
Cpu::Hcptr::Tcp<6>::set(hcptr, 1);
|
||||
Cpu::Hcptr::Tcp<7>::set(hcptr, 1);
|
||||
Cpu::Hcptr::Tcp<8>::set(hcptr, 1);
|
||||
Cpu::Hcptr::Tcp<9>::set(hcptr, 1);
|
||||
Cpu::Hcptr::Tcp<12>::set(hcptr, 1);
|
||||
Cpu::Hcptr::Tcp<13>::set(hcptr, 1);
|
||||
Cpu::Hcptr::Tta::set(hcptr, 1);
|
||||
Cpu::Hcptr::Tcpac::set(hcptr, 1);
|
||||
Cpu::Hcptr::write(hcptr);
|
||||
|
||||
enum Memory_attributes {
|
||||
DEVICE_MEMORY = 0x04,
|
||||
NORMAL_MEMORY_UNCACHED = 0x44,
|
||||
NORMAL_MEMORY_CACHED = 0xff,
|
||||
};
|
||||
|
||||
Cpu::Mair0::access_t mair0 = 0;
|
||||
Cpu::Mair0::Attr0::set(mair0, NORMAL_MEMORY_UNCACHED);
|
||||
Cpu::Mair0::Attr1::set(mair0, DEVICE_MEMORY);
|
||||
Cpu::Mair0::Attr2::set(mair0, NORMAL_MEMORY_CACHED);
|
||||
Cpu::Mair0::Attr3::set(mair0, DEVICE_MEMORY);
|
||||
Cpu::Hmair0::write(mair0);
|
||||
|
||||
Cpu::Vtcr::access_t vtcr = ttbcr;
|
||||
Cpu::Vtcr::Sl0::set(vtcr, 1); /* set to starting level 1 */
|
||||
Cpu::Vtcr::write(vtcr);
|
||||
|
||||
Cpu::Sctlr::access_t sctlr = Cpu::Sctlr::read();
|
||||
Cpu::Sctlr::C::set(sctlr, 1);
|
||||
Cpu::Sctlr::I::set(sctlr, 1);
|
||||
Cpu::Sctlr::V::set(sctlr, 1);
|
||||
Cpu::Sctlr::M::set(sctlr, 1);
|
||||
Cpu::Sctlr::Z::set(sctlr, 1);
|
||||
Cpu::Hsctlr::write(sctlr);
|
||||
}
|
||||
|
||||
|
||||
static inline void switch_to_supervisor_mode()
|
||||
{
|
||||
using Cpsr = Hw::Arm_cpu::Psr;
|
||||
|
@ -12,6 +12,7 @@
|
||||
*/
|
||||
|
||||
#include <platform.h>
|
||||
#include <spec/arm/cortex_a7_a15_virtualization.h>
|
||||
|
||||
extern "C" void * _start_setup_stack; /* entrypoint for non-boot CPUs */
|
||||
static unsigned char hyp_mode_stack[1024]; /* hypervisor mode's kernel stack */
|
||||
@ -30,108 +31,6 @@ Bootstrap::Platform::Board::Board()
|
||||
Cpu_mmio::IRQ_CONTROLLER_VT_CTRL_SIZE }) {}
|
||||
|
||||
|
||||
static inline void prepare_nonsecure_world(unsigned long timer_freq)
|
||||
{
|
||||
using Cpu = Hw::Arm_cpu;
|
||||
|
||||
/* if we are already in HYP mode we're done (depends on u-boot version) */
|
||||
if (Cpu::Psr::M::get(Cpu::Cpsr::read()) == Cpu::Psr::M::HYP)
|
||||
return;
|
||||
|
||||
/* ARM generic timer counter freq needs to be set in secure mode */
|
||||
Cpu::Cntfrq::write(timer_freq);
|
||||
|
||||
/*
|
||||
* enable coprocessor 10 + 11 access and SMP bit access in auxiliary control
|
||||
* register for non-secure world
|
||||
*/
|
||||
Cpu::Nsacr::access_t nsacr = 0;
|
||||
Cpu::Nsacr::Cpnsae10::set(nsacr, 1);
|
||||
Cpu::Nsacr::Cpnsae11::set(nsacr, 1);
|
||||
Cpu::Nsacr::Ns_smp::set(nsacr, 1);
|
||||
Cpu::Nsacr::write(nsacr);
|
||||
|
||||
asm volatile (
|
||||
"msr sp_mon, sp \n" /* copy current mode's sp */
|
||||
"msr lr_mon, lr \n" /* copy current mode's lr */
|
||||
"cps #22 \n" /* switch to monitor mode */
|
||||
);
|
||||
|
||||
Cpu::Scr::access_t scr = 0;
|
||||
Cpu::Scr::Ns::set(scr, 1);
|
||||
Cpu::Scr::Fw::set(scr, 1);
|
||||
Cpu::Scr::Aw::set(scr, 1);
|
||||
Cpu::Scr::Scd::set(scr, 1);
|
||||
Cpu::Scr::Hce::set(scr, 1);
|
||||
Cpu::Scr::Sif::set(scr, 1);
|
||||
Cpu::Scr::write(scr);
|
||||
}
|
||||
|
||||
|
||||
static inline void prepare_hypervisor(Genode::addr_t table)
|
||||
{
|
||||
using Cpu = Hw::Arm_cpu;
|
||||
|
||||
/* set hypervisor exception vector */
|
||||
Cpu::Hvbar::write(Hw::Mm::hypervisor_exception_vector().base);
|
||||
|
||||
/* set hypervisor's translation table */
|
||||
Cpu::Httbr_64bit::write(table);
|
||||
|
||||
Cpu::Ttbcr::access_t ttbcr = 0;
|
||||
Cpu::Ttbcr::Irgn0::set(ttbcr, 1);
|
||||
Cpu::Ttbcr::Orgn0::set(ttbcr, 1);
|
||||
Cpu::Ttbcr::Sh0::set(ttbcr, 2);
|
||||
Cpu::Ttbcr::Eae::set(ttbcr, 1);
|
||||
|
||||
/* prepare MMU usage by hypervisor code */
|
||||
Cpu::Htcr::write(ttbcr);
|
||||
|
||||
/* don't trap on cporocessor 10 + 11, but all others */
|
||||
Cpu::Hcptr::access_t hcptr = 0;
|
||||
Cpu::Hcptr::Tcp<0>::set(hcptr, 1);
|
||||
Cpu::Hcptr::Tcp<1>::set(hcptr, 1);
|
||||
Cpu::Hcptr::Tcp<2>::set(hcptr, 1);
|
||||
Cpu::Hcptr::Tcp<3>::set(hcptr, 1);
|
||||
Cpu::Hcptr::Tcp<4>::set(hcptr, 1);
|
||||
Cpu::Hcptr::Tcp<5>::set(hcptr, 1);
|
||||
Cpu::Hcptr::Tcp<6>::set(hcptr, 1);
|
||||
Cpu::Hcptr::Tcp<7>::set(hcptr, 1);
|
||||
Cpu::Hcptr::Tcp<8>::set(hcptr, 1);
|
||||
Cpu::Hcptr::Tcp<9>::set(hcptr, 1);
|
||||
Cpu::Hcptr::Tcp<12>::set(hcptr, 1);
|
||||
Cpu::Hcptr::Tcp<13>::set(hcptr, 1);
|
||||
Cpu::Hcptr::Tta::set(hcptr, 1);
|
||||
Cpu::Hcptr::Tcpac::set(hcptr, 1);
|
||||
Cpu::Hcptr::write(hcptr);
|
||||
|
||||
enum Memory_attributes {
|
||||
DEVICE_MEMORY = 0x04,
|
||||
NORMAL_MEMORY_UNCACHED = 0x44,
|
||||
NORMAL_MEMORY_CACHED = 0xff,
|
||||
};
|
||||
|
||||
Cpu::Mair0::access_t mair0 = 0;
|
||||
Cpu::Mair0::Attr0::set(mair0, NORMAL_MEMORY_UNCACHED);
|
||||
Cpu::Mair0::Attr1::set(mair0, DEVICE_MEMORY);
|
||||
Cpu::Mair0::Attr2::set(mair0, NORMAL_MEMORY_CACHED);
|
||||
Cpu::Mair0::Attr3::set(mair0, DEVICE_MEMORY);
|
||||
Cpu::Hmair0::write(mair0);
|
||||
|
||||
Cpu::Vtcr::access_t vtcr = ttbcr;
|
||||
Cpu::Vtcr::Sl0::set(vtcr, 1); /* set to starting level 1 */
|
||||
Cpu::Vtcr::write(vtcr);
|
||||
|
||||
Cpu::Sctlr::access_t sctlr = Cpu::Sctlr::read();
|
||||
Cpu::Sctlr::C::set(sctlr, 1);
|
||||
Cpu::Sctlr::I::set(sctlr, 1);
|
||||
Cpu::Sctlr::V::set(sctlr, 1);
|
||||
Cpu::Sctlr::M::set(sctlr, 1);
|
||||
Cpu::Sctlr::Z::set(sctlr, 1);
|
||||
Cpu::Hsctlr::write(sctlr);
|
||||
}
|
||||
|
||||
|
||||
static inline void switch_to_supervisor_mode()
|
||||
{
|
||||
using Cpsr = Hw::Arm_cpu::Psr;
|
||||
@ -146,7 +45,7 @@ static inline void switch_to_supervisor_mode()
|
||||
"msr lr_svc, lr \n" /* copy current mode's lr */
|
||||
"adr lr, 1f \n" /* load exception return address */
|
||||
"msr elr_hyp, lr \n" /* copy current mode's lr to hyp lr */
|
||||
"mov sp, %[stack] \n" /* copy to hyp stack pointer */
|
||||
"mov sp, %[stack] \n" /* copy to hyp stack pointer */
|
||||
"msr spsr_cxfs, %[cpsr] \n" /* set psr for supervisor mode */
|
||||
"eret \n" /* exception return */
|
||||
"1:":: [cpsr] "r" (cpsr), [stack] "r" (&hyp_mode_stack));
|
||||
@ -156,7 +55,6 @@ static inline void switch_to_supervisor_mode()
|
||||
unsigned Bootstrap::Platform::enable_mmu()
|
||||
{
|
||||
static volatile bool primary_cpu = true;
|
||||
static unsigned long timer_freq = Cpu::Cntfrq::read();
|
||||
|
||||
/* locally initialize interrupt controller */
|
||||
::Board::Pic pic { };
|
||||
@ -168,7 +66,6 @@ unsigned Bootstrap::Platform::enable_mmu()
|
||||
Cpu::wake_up_all_cpus(&_start_setup_stack);
|
||||
}
|
||||
|
||||
if (false) prepare_nonsecure_world(timer_freq);
|
||||
prepare_hypervisor((addr_t)core_pd->table_base);
|
||||
switch_to_supervisor_mode();
|
||||
|
||||
|
74
repos/base-hw/src/core/spec/arm/virtualization/board.h
Normal file
74
repos/base-hw/src/core/spec/arm/virtualization/board.h
Normal file
@ -0,0 +1,74 @@
|
||||
/*
|
||||
* \brief Board wirh ARM virtualization support
|
||||
* \author Stefan Kalkowski
|
||||
* \date 2019-11-12
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2019 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#ifndef _CORE__SPEC__ARM__VIRTUALIZATION__BOARD_H_
|
||||
#define _CORE__SPEC__ARM__VIRTUALIZATION__BOARD_H_
|
||||
|
||||
#include <translation_table.h>
|
||||
#include <kernel/configuration.h>
|
||||
#include <kernel/irq.h>
|
||||
|
||||
namespace Board {
|
||||
using Vm_page_table = Hw::Level_1_stage_2_translation_table;
|
||||
using Vm_page_table_array =
|
||||
Vm_page_table::Allocator::Array<Kernel::DEFAULT_TRANSLATION_TABLE_MAX>;
|
||||
|
||||
struct Vcpu_context;
|
||||
|
||||
using Vm_state = Genode::Vm_state;
|
||||
};
|
||||
|
||||
namespace Kernel {
|
||||
class Cpu;
|
||||
class Vm;
|
||||
};
|
||||
|
||||
struct Board::Vcpu_context
|
||||
{
|
||||
struct Vm_irq : Kernel::Irq
|
||||
{
|
||||
Vm_irq(unsigned const irq, Kernel::Cpu &);
|
||||
virtual ~Vm_irq() {};
|
||||
|
||||
virtual void handle(Kernel::Cpu &, Kernel::Vm & vm, unsigned irq);
|
||||
void occurred() override;
|
||||
};
|
||||
|
||||
|
||||
struct Pic_maintainance_irq : Vm_irq
|
||||
{
|
||||
Pic_maintainance_irq(Kernel::Cpu &);
|
||||
|
||||
void handle(Kernel::Cpu &, Kernel::Vm &, unsigned) override { }
|
||||
};
|
||||
|
||||
|
||||
struct Virtual_timer_irq
|
||||
{
|
||||
Vm_irq irq;
|
||||
|
||||
Virtual_timer_irq(Kernel::Cpu &);
|
||||
|
||||
void enable();
|
||||
void disable();
|
||||
};
|
||||
|
||||
Vcpu_context(Kernel::Cpu & cpu)
|
||||
: pic_irq(cpu), vtimer_irq(cpu) {}
|
||||
|
||||
Pic::Virtual_context pic {};
|
||||
Pic_maintainance_irq pic_irq;
|
||||
Virtual_timer_irq vtimer_irq;
|
||||
};
|
||||
|
||||
#endif /* _CORE__SPEC__ARM__VIRTUALIZATION__BOARD_H_ */
|
@ -101,6 +101,11 @@ _host_to_vm:
|
||||
add r1, r0, #4
|
||||
vldm r1!, {d0-d15}
|
||||
vldm r1!, {d16-d31}
|
||||
ldm r1!, {r2-r7}
|
||||
mcrr p15, 4, r2, r3, c14 /* write cntvoff */
|
||||
mcrr p15, 3, r4, r5, c14 /* write cntv_cval */
|
||||
mcr p15, 0, r6, c14, c3, 1 /* write cntv_ctl */
|
||||
mcr p15, 0, r7, c14, c1, 0 /* write cntkctl */
|
||||
ldmia sp, {r0-r12} /* load vm's r0-r12 */
|
||||
eret
|
||||
|
||||
@ -111,9 +116,7 @@ _vm_to_host:
|
||||
mcrr p15, 6, r1, r1, c2 /* write VTTBR */
|
||||
mcr p15, 4, r1, c1, c1, 0 /* write HCR register */
|
||||
mcr p15, 4, r1, c1, c1, 3 /* write HSTR register */
|
||||
mov r1, #0xf
|
||||
lsl r1, #20
|
||||
mcr p15, 0, r1, c1, c0, 2 /* write CPACR */
|
||||
|
||||
mrs r1, ELR_hyp /* read ip */
|
||||
mrs r2, spsr /* read cpsr */
|
||||
mrc p15, 0, r3, c1, c0, 0 /* read SCTRL */
|
||||
@ -124,21 +127,29 @@ _vm_to_host:
|
||||
mrc p15, 0, r8, c2, c0, 2 /* read TTBRC */
|
||||
mrc p15, 0, r9, c2, c0, 0 /* read TTBR0 */
|
||||
mrc p15, 0, r10, c2, c0, 1 /* read TTBR1 */
|
||||
mrc p15, 0, r11, c10, c2, 0 /* read PRRR */
|
||||
mrc p15, 0, r12, c10, c2, 1 /* read NMRR */
|
||||
add r0, sp, #40*4 /* offset SCTRL */
|
||||
stm r0!, {r3-r10}
|
||||
add r0, r0, #3*4
|
||||
mrc p15, 0, r3, c5, c0, 0 /* read DFSR */
|
||||
mrc p15, 0, r4, c5, c0, 1 /* read IFSR */
|
||||
mrc p15, 0, r5, c5, c1, 0 /* read ADFSR */
|
||||
mrc p15, 0, r6, c5, c1, 1 /* read AIFSR */
|
||||
mrc p15, 0, r7, c6, c0, 0 /* read DFAR */
|
||||
mrc p15, 0, r8, c6, c0, 2 /* read IFAR */
|
||||
mrc p15, 0, r9, c13, c0, 1 /* read CIDR */
|
||||
mrc p15, 0, r10, c13, c0, 2 /* read TLS1 */
|
||||
mrc p15, 0, r11, c13, c0, 3 /* read TLS2 */
|
||||
mrc p15, 0, r12, c13, c0, 4 /* read TLS3 */
|
||||
stm r0!, {r3-r12}
|
||||
add r0, r0, #4
|
||||
mrc p15, 0, r3, c3, c0, 0 /* read DACR */
|
||||
mrc p15, 0, r4, c5, c0, 0 /* read DFSR */
|
||||
mrc p15, 0, r5, c5, c0, 1 /* read IFSR */
|
||||
mrc p15, 0, r6, c5, c1, 0 /* read ADFSR */
|
||||
mrc p15, 0, r7, c5, c1, 1 /* read AIFSR */
|
||||
mrc p15, 0, r8, c6, c0, 0 /* read DFAR */
|
||||
mrc p15, 0, r9, c6, c0, 2 /* read IFAR */
|
||||
mrc p15, 0, r10, c13, c0, 1 /* read CIDR */
|
||||
mrc p15, 0, r11, c13, c0, 2 /* read TLS1 */
|
||||
mrc p15, 0, r12, c13, c0, 3 /* read TLS2 */
|
||||
stm r0!, {r3-r12}
|
||||
mrc p15, 0, r3, c13, c0, 4 /* read TLS3 */
|
||||
mrc p15, 0, r4, c1, c0, 2 /* read CPACR */
|
||||
stm r0!, {r3, r4}
|
||||
|
||||
mov r3, #0xf
|
||||
lsl r3, #20
|
||||
mcr p15, 0, r3, c1, c0, 2 /* write CPACR */
|
||||
|
||||
mov r3, #1 /* clear fpu exception state */
|
||||
lsl r3, #30
|
||||
vmsr fpexc, r3
|
||||
@ -146,6 +157,11 @@ _vm_to_host:
|
||||
stmia r0!, {r4}
|
||||
vstm r0!, {d0-d15}
|
||||
vstm r0!, {d16-d31}
|
||||
mrrc p15, 4, r3, r4, c14 /* read cntvoff */
|
||||
mrrc p15, 3, r5, r6, c14 /* read cntv_cval */
|
||||
mrc p15, 0, r7, c14, c3, 1 /* write cntv_ctl */
|
||||
mrc p15, 0, r8, c14, c1, 0 /* write cntkctl */
|
||||
stm r0!, {r3-r8}
|
||||
add r0, sp, #13*4
|
||||
ldr r3, _vt_host_context_ptr
|
||||
ldr sp, [r3]
|
||||
|
@ -55,139 +55,42 @@ struct Host_context {
|
||||
} vt_host_context;
|
||||
|
||||
|
||||
struct Kernel::Vm_irq : Kernel::Irq
|
||||
Board::Vcpu_context::Vm_irq::Vm_irq(unsigned const irq, Cpu & cpu)
|
||||
: Kernel::Irq(irq, cpu.irq_pool())
|
||||
{ }
|
||||
|
||||
|
||||
void Board::Vcpu_context::Vm_irq::handle(Cpu &, Vm & vm, unsigned irq) {
|
||||
vm.inject_irq(irq); }
|
||||
|
||||
|
||||
void Board::Vcpu_context::Vm_irq::occurred()
|
||||
{
|
||||
Vm_irq(unsigned const irq)
|
||||
:
|
||||
Kernel::Irq(irq, cpu_pool().executing_cpu().irq_pool())
|
||||
{ }
|
||||
|
||||
/**
|
||||
* A VM interrupt gets injected into the VM scheduled on the current CPU
|
||||
*/
|
||||
void occurred() override
|
||||
{
|
||||
Cpu_job & job = cpu_pool().executing_cpu().scheduled_job();
|
||||
Vm *vm = dynamic_cast<Vm*>(&job);
|
||||
if (!vm)
|
||||
Genode::raw("VM timer interrupt while VM is not runnning!");
|
||||
else
|
||||
vm->inject_irq(_irq_nr);
|
||||
}
|
||||
};
|
||||
Cpu & cpu = Kernel::cpu_pool().executing_cpu();
|
||||
Vm *vm = dynamic_cast<Vm*>(&cpu.scheduled_job());
|
||||
if (!vm) Genode::raw("VM interrupt while VM is not runnning!");
|
||||
else handle(cpu, *vm, _irq_nr);
|
||||
}
|
||||
|
||||
|
||||
struct Kernel::Virtual_pic : Genode::Mmio
|
||||
Board::Vcpu_context::Pic_maintainance_irq::Pic_maintainance_irq(Cpu & cpu)
|
||||
: Board::Vcpu_context::Vm_irq(Board::VT_MAINTAINANCE_IRQ, cpu) {
|
||||
//FIXME Irq::enable only enables caller cpu
|
||||
cpu.pic().unmask(_irq_nr, cpu.id()); }
|
||||
|
||||
Board::Vcpu_context::Virtual_timer_irq::Virtual_timer_irq(Cpu & cpu)
|
||||
: irq(Board::VT_TIMER_IRQ, cpu) {}
|
||||
|
||||
|
||||
void Board::Vcpu_context::Virtual_timer_irq::enable() { irq.enable(); }
|
||||
|
||||
|
||||
void Board::Vcpu_context::Virtual_timer_irq::disable()
|
||||
{
|
||||
struct Gich_hcr : Register<0x00, 32> { };
|
||||
struct Gich_vmcr : Register<0x08, 32> { };
|
||||
struct Gich_misr : Register<0x10, 32> { };
|
||||
struct Gich_eisr0 : Register<0x20, 32> { };
|
||||
struct Gich_elrsr0 : Register<0x30, 32> { };
|
||||
struct Gich_apr : Register<0xf0, 32> { };
|
||||
|
||||
template <unsigned SLOT>
|
||||
struct Gich_lr : Register<0x100 + SLOT*4, 32> { };
|
||||
|
||||
Vm_irq irq { Board::VT_MAINTAINANCE_IRQ };
|
||||
|
||||
Virtual_pic()
|
||||
: Genode::Mmio(Genode::Platform::mmio_to_virt(Board::Cpu_mmio::IRQ_CONTROLLER_VT_CTRL_BASE)) { }
|
||||
|
||||
static Virtual_pic& pic()
|
||||
{
|
||||
static Virtual_pic vgic;
|
||||
return vgic;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the virtual interrupt controller state to VM state
|
||||
*/
|
||||
static void save (Genode::Vm_state &s)
|
||||
{
|
||||
s.gic_hcr = pic().read<Gich_hcr >();
|
||||
s.gic_misr = pic().read<Gich_misr >();
|
||||
s.gic_vmcr = pic().read<Gich_vmcr >();
|
||||
s.gic_apr = pic().read<Gich_apr >();
|
||||
s.gic_eisr = pic().read<Gich_eisr0 >();
|
||||
s.gic_elrsr0 = pic().read<Gich_elrsr0>();
|
||||
s.gic_lr[0] = pic().read<Gich_lr<0> >();
|
||||
s.gic_lr[1] = pic().read<Gich_lr<1> >();
|
||||
s.gic_lr[2] = pic().read<Gich_lr<2> >();
|
||||
s.gic_lr[3] = pic().read<Gich_lr<3> >();
|
||||
|
||||
/* disable virtual PIC CPU interface */
|
||||
pic().write<Gich_hcr>(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the virtual interrupt controller state from VM state
|
||||
*/
|
||||
static void load (Genode::Vm_state &s)
|
||||
{
|
||||
pic().write<Gich_hcr >(s.gic_hcr );
|
||||
pic().write<Gich_misr >(s.gic_misr);
|
||||
pic().write<Gich_vmcr >(s.gic_vmcr);
|
||||
pic().write<Gich_apr >(s.gic_apr );
|
||||
pic().write<Gich_elrsr0>(s.gic_elrsr0);
|
||||
pic().write<Gich_lr<0> >(s.gic_lr[0]);
|
||||
pic().write<Gich_lr<1> >(s.gic_lr[1]);
|
||||
pic().write<Gich_lr<2> >(s.gic_lr[2]);
|
||||
pic().write<Gich_lr<3> >(s.gic_lr[3]);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct Kernel::Virtual_timer
|
||||
{
|
||||
Vm_irq irq { Board::VT_TIMER_IRQ };
|
||||
|
||||
/**
|
||||
* Return virtual timer object of currently executing cpu
|
||||
*
|
||||
* FIXME: remove this when re-designing the CPU (issue #1252)
|
||||
*/
|
||||
static Virtual_timer& timer()
|
||||
{
|
||||
static Virtual_timer timer[NR_OF_CPUS];
|
||||
return timer[Cpu::executing_id()];
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the virtual timer, thereby it disables its interrupt
|
||||
*/
|
||||
static void reset()
|
||||
{
|
||||
timer().irq.disable();
|
||||
asm volatile("mcr p15, 0, %0, c14, c3, 1 \n"
|
||||
"mcr p15, 0, %0, c14, c3, 0" :: "r" (0));
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the virtual timer state to VM state
|
||||
*/
|
||||
static void save(Genode::Vm_state &s)
|
||||
{
|
||||
asm volatile("mrc p15, 0, %0, c14, c3, 0 \n"
|
||||
"mrc p15, 0, %1, c14, c3, 1" :
|
||||
"=r" (s.timer_val), "=r" (s.timer_ctrl));
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the virtual timer state from VM state
|
||||
*/
|
||||
static void load(Genode::Vm_state &s)
|
||||
{
|
||||
if (s.timer_irq) timer().irq.enable();
|
||||
|
||||
asm volatile("mcr p15, 0, %0, c14, c3, 1 \n"
|
||||
"mcr p15, 0, %1, c14, c3, 0 \n"
|
||||
"mcr p15, 0, %2, c14, c3, 1" ::
|
||||
"r" (0),
|
||||
"r" (s.timer_val), "r" (s.timer_ctrl));
|
||||
}
|
||||
};
|
||||
|
||||
irq.disable();
|
||||
asm volatile("mcr p15, 0, %0, c14, c3, 1" :: "r" (0));
|
||||
asm volatile("mcr p15, 0, %0, c14, c1, 0" :: "r" (0b11));
|
||||
}
|
||||
|
||||
using Vmid_allocator = Genode::Bit_allocator<256>;
|
||||
|
||||
@ -217,7 +120,6 @@ Kernel::Vm::Vm(unsigned, /* FIXME: smp support */
|
||||
_vcpu_context(cpu_pool().primary_cpu())
|
||||
{
|
||||
affinity(cpu_pool().primary_cpu());
|
||||
Virtual_pic::pic().irq.enable();
|
||||
|
||||
vt_host_context.sp = _cpu->stack_start();
|
||||
vt_host_context.ttbr0 = Cpu::Ttbr0_64bit::read();
|
||||
@ -235,12 +137,9 @@ Kernel::Vm::~Vm() { alloc().free(_id); }
|
||||
|
||||
void Kernel::Vm::exception(Cpu & cpu)
|
||||
{
|
||||
Virtual_timer::save(_state);
|
||||
|
||||
switch(_state.cpu_exception) {
|
||||
case Genode::Cpu_state::INTERRUPT_REQUEST:
|
||||
case Genode::Cpu_state::FAST_INTERRUPT_REQUEST:
|
||||
_state.gic_irq = Board::VT_MAINTAINANCE_IRQ;
|
||||
_interrupt(cpu.id());
|
||||
break;
|
||||
default:
|
||||
@ -248,13 +147,18 @@ void Kernel::Vm::exception(Cpu & cpu)
|
||||
_context.submit(1);
|
||||
}
|
||||
|
||||
Virtual_pic::save(_state);
|
||||
Virtual_timer::reset();
|
||||
if (cpu.pic().ack_virtual_irq(_vcpu_context.pic))
|
||||
inject_irq(Board::VT_MAINTAINANCE_IRQ);
|
||||
_vcpu_context.vtimer_irq.disable();
|
||||
}
|
||||
|
||||
|
||||
void Kernel::Vm::proceed(Cpu &)
|
||||
void Kernel::Vm::proceed(Cpu & cpu)
|
||||
{
|
||||
if (_state.timer.irq) _vcpu_context.vtimer_irq.enable();
|
||||
|
||||
cpu.pic().insert_virtual_irq(_vcpu_context.pic, _state.irqs.virtual_irq);
|
||||
|
||||
/*
|
||||
* the following values have to be enforced by the hypervisor
|
||||
*/
|
||||
@ -266,11 +170,8 @@ void Kernel::Vm::proceed(Cpu &)
|
||||
* to transport the HSTR and HCR register descriptions into the assembler
|
||||
* path in a dense way
|
||||
*/
|
||||
_state.hsr = Cpu::Hstr::init();
|
||||
_state.hpfar = Cpu::Hcr::init();
|
||||
|
||||
Virtual_pic::load(_state);
|
||||
Virtual_timer::load(_state);
|
||||
_state.esr_el2 = Cpu::Hstr::init();
|
||||
_state.hpfar_el2 = Cpu::Hcr::init();
|
||||
|
||||
hypervisor_enter_vm(_state);
|
||||
}
|
||||
@ -278,7 +179,7 @@ void Kernel::Vm::proceed(Cpu &)
|
||||
|
||||
void Vm::inject_irq(unsigned irq)
|
||||
{
|
||||
_state.gic_irq = irq;
|
||||
_state.irqs.last_irq = irq;
|
||||
pause();
|
||||
_context.submit(1);
|
||||
}
|
||||
|
@ -14,28 +14,20 @@
|
||||
#ifndef _CORE__SPEC__ARNDALE__BOARD_H_
|
||||
#define _CORE__SPEC__ARNDALE__BOARD_H_
|
||||
|
||||
#include <hw/spec/arm/gicv2.h>
|
||||
#include <spec/arm/virtualization/gicv2.h>
|
||||
#include <hw/spec/arm/arndale_board.h>
|
||||
#include <spec/arm/exynos_mct.h>
|
||||
#include <spec/arm/cpu/vm_state_virtualization.h>
|
||||
#include <translation_table.h>
|
||||
#include <kernel/configuration.h>
|
||||
#include <spec/arm/virtualization/board.h>
|
||||
|
||||
namespace Kernel { class Cpu; }
|
||||
|
||||
namespace Board {
|
||||
using namespace Hw::Arndale_board;
|
||||
|
||||
using Pic = Hw::Gicv2;
|
||||
struct Virtual_local_pic {};
|
||||
|
||||
enum { VCPU_MAX = 1 };
|
||||
|
||||
using Vm_state = Genode::Vm_state;
|
||||
using Vm_page_table = Hw::Level_1_stage_2_translation_table;
|
||||
using Vm_page_table_array =
|
||||
Vm_page_table::Allocator::Array<Kernel::DEFAULT_TRANSLATION_TABLE_MAX>;
|
||||
|
||||
struct Vcpu_context { Vcpu_context(Kernel::Cpu &) {} };
|
||||
}
|
||||
|
||||
#endif /* _CORE__SPEC__ARNDALE__BOARD_H_ */
|
||||
|
@ -41,18 +41,10 @@ class Genode::Cpu : public Arm_v7_cpu
|
||||
static access_t init()
|
||||
{
|
||||
/*
|
||||
* allow cache (7), TLB (8) maintenance, and performance
|
||||
* monitor (9), process/thread ID register (13) and timer (14)
|
||||
* access.
|
||||
* allow everything except c0, c11, c12, and c15 accesses.
|
||||
*/
|
||||
access_t v = 0;
|
||||
T<0>::set(v, 1);
|
||||
T<1>::set(v, 1);
|
||||
T<2>::set(v, 1);
|
||||
T<3>::set(v, 1);
|
||||
T<5>::set(v, 1);
|
||||
T<6>::set(v, 1);
|
||||
T<10>::set(v, 1);
|
||||
T<11>::set(v, 1);
|
||||
T<12>::set(v, 1);
|
||||
T<15>::set(v, 1);
|
||||
@ -73,7 +65,6 @@ class Genode::Cpu : public Arm_v7_cpu
|
||||
struct Twe : Bitfield<14, 1> {}; /* trap on WFE instruction */
|
||||
struct Tidcp : Bitfield<20, 1> {}; /* trap lockdown */
|
||||
struct Tac : Bitfield<21, 1> {}; /* trap ACTLR accesses */
|
||||
struct Tvm : Bitfield<26, 1> {}; /* trap virtual memory ctrls */
|
||||
|
||||
static access_t init()
|
||||
{
|
||||
@ -86,7 +77,6 @@ class Genode::Cpu : public Arm_v7_cpu
|
||||
Twe::set(v, 1);
|
||||
Tidcp::set(v, 1);
|
||||
Tac::set(v, 1);
|
||||
Tvm::set(v, 1);
|
||||
return v;
|
||||
};
|
||||
};
|
||||
|
@ -18,10 +18,7 @@
|
||||
#include <spec/arm/virtualization/gicv2.h>
|
||||
#include <spec/arm/generic_timer.h>
|
||||
#include <spec/arm/cpu/vm_state_virtualization.h>
|
||||
#include <translation_table.h>
|
||||
#include <kernel/configuration.h>
|
||||
|
||||
namespace Kernel { class Cpu; }
|
||||
#include <spec/arm/virtualization/board.h>
|
||||
|
||||
namespace Board {
|
||||
using namespace Hw::Imx7d_sabre_board;
|
||||
@ -29,13 +26,6 @@ namespace Board {
|
||||
struct Virtual_local_pic {};
|
||||
|
||||
enum { TIMER_IRQ = 30, VCPU_MAX = 1 };
|
||||
|
||||
using Vm_state = Genode::Vm_state;
|
||||
using Vm_page_table = Hw::Level_1_stage_2_translation_table;
|
||||
using Vm_page_table_array =
|
||||
Vm_page_table::Allocator::Array<Kernel::DEFAULT_TRANSLATION_TABLE_MAX>;
|
||||
|
||||
struct Vcpu_context { Vcpu_context(Kernel::Cpu &) {} };
|
||||
}
|
||||
|
||||
#endif /* _CORE__SPEC__IMX7_SABRELITE__BOARD_H_ */
|
||||
|
@ -18,9 +18,7 @@
|
||||
#include <spec/arm/generic_timer.h>
|
||||
#include <spec/arm/virtualization/gicv3.h>
|
||||
#include <spec/arm_64/cpu/vm_state_virtualization.h>
|
||||
#include <translation_table.h>
|
||||
#include <kernel/configuration.h>
|
||||
#include <kernel/irq.h>
|
||||
#include <spec/arm/virtualization/board.h>
|
||||
|
||||
namespace Board {
|
||||
using namespace Hw::Imx8q_evk_board;
|
||||
@ -29,59 +27,8 @@ namespace Board {
|
||||
TIMER_IRQ = 14 + 16,
|
||||
VT_TIMER_IRQ = 11 + 16,
|
||||
VT_MAINTAINANCE_IRQ = 9 + 16,
|
||||
VCPU_MAX = 16
|
||||
VCPU_MAX = 4
|
||||
};
|
||||
|
||||
using Vm_page_table = Hw::Level_1_stage_2_translation_table;
|
||||
using Vm_page_table_array =
|
||||
Vm_page_table::Allocator::Array<Kernel::DEFAULT_TRANSLATION_TABLE_MAX>;
|
||||
|
||||
struct Vcpu_context;
|
||||
|
||||
using Vm_state = Genode::Vm_state;
|
||||
};
|
||||
|
||||
namespace Kernel {
|
||||
class Cpu;
|
||||
class Vm;
|
||||
};
|
||||
|
||||
struct Board::Vcpu_context
|
||||
{
|
||||
struct Vm_irq : Kernel::Irq
|
||||
{
|
||||
Vm_irq(unsigned const irq, Kernel::Cpu &);
|
||||
virtual ~Vm_irq() {};
|
||||
|
||||
virtual void handle(Kernel::Cpu &, Kernel::Vm & vm, unsigned irq);
|
||||
void occurred() override;
|
||||
};
|
||||
|
||||
|
||||
struct Pic_maintainance_irq : Vm_irq
|
||||
{
|
||||
Pic_maintainance_irq(Kernel::Cpu &);
|
||||
|
||||
void handle(Kernel::Cpu &, Kernel::Vm &, unsigned) override { }
|
||||
};
|
||||
|
||||
|
||||
struct Virtual_timer_irq
|
||||
{
|
||||
Vm_irq irq;
|
||||
|
||||
Virtual_timer_irq(Kernel::Cpu &);
|
||||
|
||||
void enable();
|
||||
void disable();
|
||||
};
|
||||
|
||||
Vcpu_context(Kernel::Cpu & cpu)
|
||||
: pic_irq(cpu), vtimer_irq(cpu) {}
|
||||
|
||||
Pic::Virtual_context pic {};
|
||||
Pic_maintainance_irq pic_irq;
|
||||
Virtual_timer_irq vtimer_irq;
|
||||
};
|
||||
|
||||
#endif /* _CORE__SPEC__IMX8Q_EVK__BOARD_H_ */
|
||||
|
@ -18,8 +18,7 @@
|
||||
#include <spec/arm/virtualization/gicv2.h>
|
||||
#include <spec/arm/generic_timer.h>
|
||||
#include <spec/arm/cpu/vm_state_virtualization.h>
|
||||
#include <translation_table.h>
|
||||
#include <kernel/configuration.h>
|
||||
#include <spec/arm/virtualization/board.h>
|
||||
|
||||
namespace Kernel { class Cpu; }
|
||||
|
||||
@ -34,13 +33,6 @@ namespace Board {
|
||||
VT_MAINTAINANCE_IRQ = 25,
|
||||
VCPU_MAX = 1
|
||||
};
|
||||
|
||||
using Vm_state = Genode::Vm_state;
|
||||
using Vm_page_table = Hw::Level_1_stage_2_translation_table;
|
||||
using Vm_page_table_array =
|
||||
Vm_page_table::Allocator::Array<Kernel::DEFAULT_TRANSLATION_TABLE_MAX>;
|
||||
|
||||
struct Vcpu_context { Vcpu_context(Kernel::Cpu &) {} };
|
||||
};
|
||||
|
||||
#endif /* _SRC__CORE__SPEC__VIRT__QEMU_H_ */
|
||||
|
@ -250,7 +250,7 @@ class Hw::Long_translation_table
|
||||
| Attribute_index::create(f)
|
||||
| Not_global::bits(!f.global)
|
||||
| Base::Shareability::bits(
|
||||
Base::Shareability::OUTER_SHAREABLE)
|
||||
Base::Shareability::INNER_SHAREABLE)
|
||||
| Base::Output_address::masked(pa)
|
||||
| Base::Access_flag::bits(1)
|
||||
| Descriptor::Valid::bits(1)
|
||||
@ -270,7 +270,7 @@ class Hw::Long_translation_table
|
||||
addr_t const pa)
|
||||
{
|
||||
return Base::Shareability::bits(
|
||||
Base::Shareability::NON_SHAREABLE)
|
||||
Base::Shareability::INNER_SHAREABLE)
|
||||
| Base::Output_address::masked(pa)
|
||||
| Base::Access_flag::bits(1)
|
||||
| Descriptor::Valid::bits(1)
|
||||
|
@ -87,33 +87,56 @@ if { [have_spec arm] } {
|
||||
|
||||
if {![file exists bin/linux]} {
|
||||
puts "Download linux kernel ..."
|
||||
exec >& /dev/null wget -c -O bin/linux http://genode.org/files/release-15.02/arm_vt/linux
|
||||
exec >& /dev/null wget -c -O bin/linux http://genode.org/files/release-20.05/linux-arm32
|
||||
}
|
||||
|
||||
if {![file exists bin/dtb]} {
|
||||
puts "Download device tree blob ..."
|
||||
exec >& /dev/null wget -c -O bin/dtb http://genode.org/files/release-15.02/arm_vt/dtb
|
||||
exec >& /dev/null wget -c -O bin/dtb http://genode.org/files/release-20.05/dtb-arm32-virt
|
||||
}
|
||||
|
||||
if {![file exists bin/initrd]} {
|
||||
puts "Download initramfs ..."
|
||||
exec >& /dev/null wget -c -O bin/initrd http://genode.org/files/release-20.05/initrd-arm32
|
||||
}
|
||||
|
||||
#
|
||||
# This test uses a Linux kernel built from unmodified vanilla kernel sources
|
||||
# but using a slightly simplified kernel configuration, as well as device tree
|
||||
# for a minimal Versatile Express Cortex A15 like emulated board.
|
||||
# To obtain the linux kernel, do the following steps:
|
||||
#
|
||||
# wget https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.3.10.tar.xz
|
||||
#
|
||||
# tar -xJf linux-5.3.10.tar.xz
|
||||
# cd linux-5.3.10
|
||||
#
|
||||
# make O=../build-linux-aarch32 ARCH=arm CROSS_COMPILE=/usr/local/genode/tool/current/bin/genode-arm- defconfig
|
||||
# make O=../build-linux-aarch32 ARCH=arm CROSS_COMPILE=/usr/local/genode/tool/current/bin/genode-arm- -j32
|
||||
#
|
||||
# copy ../build-linux-aarch32/arch/arm/boot/zImage to your build directory in 'bin/linux'
|
||||
#
|
||||
# The used sources, including the modified device tree and configuration file
|
||||
# can be found in the following git repository/branch:
|
||||
#
|
||||
# https://github.com/skalk/linux/tree/vexpress-vt
|
||||
# To get the dtb (device-tree-binary), you have to compile the file:
|
||||
# repos/os/src/server/vmm/spec/arm_v7/virt.dts with the dtc compiler:
|
||||
# dtc repos/os/src/server/vmm/spec/arm_v7/virt.dts > bin/dtb
|
||||
#
|
||||
# To compile the kernel and device tree blob used in this script, do the
|
||||
# following steps:
|
||||
#
|
||||
# ! git checkout https://github.com/skalk/linux.git
|
||||
# ! cd linux
|
||||
# ! git checkout origin/vexpress-vt
|
||||
# ! make ARCH=arm CROSS_COMPILE=<cross_compiler_prefix> vexpress_config
|
||||
# ! make ARCH=arm CROSS_COMPILE=<cross_compiler_prefix> -j8 Image
|
||||
# ! make ARCH=arm CROSS_COMPILE=<cross_compiler_prefix> vexpress-v2p-ca15-tc1.dtb
|
||||
# To construct the initrd do the following:
|
||||
# * get and install gcc from bootlin:
|
||||
# (https://toolchains.bootlin.com/downloads/releases/toolchains/armv7-eabihf/tarballs/)
|
||||
# * build busybox
|
||||
# wget https://busybox.net/downloads/busybox-1.31.1.tar.bz2
|
||||
# tar xjf busybox-1.31.1.tar.bz2
|
||||
# mkdir build-busybox-aarch32
|
||||
# cd busybox-1.31.1
|
||||
# make O=../build-busybox-aarch32 defconfig
|
||||
# make O=../build-busybox-aarch32 menuconfig
|
||||
#
|
||||
# [*] Setting -> Build static binary (no shared libs)
|
||||
#
|
||||
# cd ../build-busybox-aarch32
|
||||
# make CROSS_COMPILE=/opt/armv7-eabihf--uclibc--stable-2020.02-1/bin/arm-buildroot-linux-uclibcgnueabihf- install -j6
|
||||
# * create ramdisk
|
||||
# cd _install
|
||||
# find . | cpio -H newc -o | gzip > ../initrd
|
||||
#
|
||||
}
|
||||
|
||||
@ -183,8 +206,8 @@ set boot_modules {
|
||||
vmm
|
||||
linux
|
||||
dtb
|
||||
initrd
|
||||
}
|
||||
append_if [have_spec arm_64] boot_modules initrd
|
||||
build_boot_image $boot_modules
|
||||
|
||||
#
|
||||
|
203
repos/os/src/server/vmm/cpu_base.cc
Normal file
203
repos/os/src/server/vmm/cpu_base.cc
Normal file
@ -0,0 +1,203 @@
|
||||
/*
|
||||
* \brief VMM cpu object
|
||||
* \author Stefan Kalkowski
|
||||
* \date 2019-07-18
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2019 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
#include <cpu.h>
|
||||
#include <vm.h>
|
||||
#include <psci.h>
|
||||
|
||||
using Vmm::Cpu_base;
|
||||
using Vmm::Cpu;
|
||||
using Vmm::Gic;
|
||||
|
||||
Genode::Lock & Vmm::lock() { static Genode::Lock l {}; return l; }
|
||||
|
||||
|
||||
Cpu_base::System_register::System_register(unsigned op0,
|
||||
unsigned crn,
|
||||
unsigned op1,
|
||||
unsigned crm,
|
||||
unsigned op2,
|
||||
const char * name,
|
||||
bool writeable,
|
||||
Genode::addr_t v,
|
||||
Genode::Avl_tree<System_register> & tree)
|
||||
: _encoding(Iss::value(op0, crn, op1, crm, op2)),
|
||||
_name(name),
|
||||
_writeable(writeable),
|
||||
_value(v)
|
||||
{
|
||||
tree.insert(this);
|
||||
}
|
||||
|
||||
|
||||
bool Cpu_base::_handle_sys_reg()
|
||||
{
|
||||
using Iss = System_register::Iss;
|
||||
|
||||
Iss::access_t v = _state.esr_el2;
|
||||
System_register * reg = _reg_tree.first();
|
||||
if (reg) reg = reg->find_by_encoding(Iss::mask_encoding(v));
|
||||
|
||||
if (!reg) {
|
||||
Genode::error("ignore unknown system register access @ ip=", (void*)_state.ip, ":");
|
||||
Genode::error(Iss::Direction::get(v) ? "read" : "write",
|
||||
": "
|
||||
"op0=", Iss::Opcode0::get(v), " "
|
||||
"op1=", Iss::Opcode1::get(v), " "
|
||||
"r", Iss::Register::get(v), " "
|
||||
"crn=", Iss::Crn::get(v), " "
|
||||
"crm=", Iss::Crm::get(v), " ",
|
||||
"op2=", Iss::Opcode2::get(v));
|
||||
if (Iss::Direction::get(v)) _state.reg(Iss::Register::get(v), 0);
|
||||
_state.ip += sizeof(Genode::uint32_t);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Iss::Direction::get(v)) { /* read access */
|
||||
_state.reg(Iss::Register::get(v), reg->read());
|
||||
} else { /* write access */
|
||||
if (!reg->writeable()) {
|
||||
Genode::error("writing to system register ",
|
||||
reg->name(), " not allowed!");
|
||||
return false;
|
||||
}
|
||||
reg->write(_state.reg(Iss::Register::get(v)));
|
||||
}
|
||||
_state.ip += sizeof(Genode::uint32_t);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void Cpu_base::_handle_wfi()
|
||||
{
|
||||
_state.ip += sizeof(Genode::uint32_t);
|
||||
|
||||
if (_state.esr_el2 & 1) return; /* WFE */
|
||||
|
||||
_active = false;
|
||||
_timer.schedule_timeout();
|
||||
}
|
||||
|
||||
|
||||
void Cpu_base::_handle_sync()
|
||||
{
|
||||
/* check device number*/
|
||||
switch (Esr::Ec::get(_state.esr_el2)) {
|
||||
case Esr::Ec::HVC:
|
||||
_handle_hyper_call();
|
||||
break;
|
||||
case Esr::Ec::MRC_MCR: [[fallthrough]];
|
||||
case Esr::Ec::MRS_MSR:
|
||||
_handle_sys_reg();
|
||||
break;
|
||||
case Esr::Ec::DA:
|
||||
_handle_data_abort();
|
||||
break;
|
||||
case Esr::Ec::WFI:
|
||||
_handle_wfi();
|
||||
return;
|
||||
case Esr::Ec::BRK:
|
||||
_handle_brk();
|
||||
return;
|
||||
default:
|
||||
throw Exception("Unknown trap: ",
|
||||
Esr::Ec::get(_state.esr_el2));
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
void Cpu_base::_handle_irq()
|
||||
{
|
||||
switch (_state.irqs.last_irq) {
|
||||
case VTIMER_IRQ:
|
||||
_timer.handle_irq();
|
||||
break;
|
||||
default:
|
||||
_gic.handle_irq();
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
void Cpu_base::_handle_hyper_call()
|
||||
{
|
||||
switch(_state.reg(0)) {
|
||||
case Psci::PSCI_VERSION:
|
||||
_state.reg(0, Psci::VERSION);
|
||||
return;
|
||||
case Psci::MIGRATE_INFO_TYPE:
|
||||
_state.reg(0, Psci::NOT_SUPPORTED);
|
||||
return;
|
||||
case Psci::PSCI_FEATURES:
|
||||
_state.reg(0, Psci::NOT_SUPPORTED);
|
||||
return;
|
||||
case Psci::CPU_ON:
|
||||
_vm.cpu((unsigned)_state.reg(1), [&] (Cpu & cpu) {
|
||||
cpu.state().ip = _state.reg(2);
|
||||
cpu.state().reg(0, _state.reg(3));
|
||||
cpu.run();
|
||||
});
|
||||
_state.reg(0, Psci::SUCCESS);
|
||||
return;
|
||||
default:
|
||||
Genode::warning("unknown hypercall! ", cpu_id());
|
||||
dump();
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
void Cpu_base::_handle_data_abort()
|
||||
{
|
||||
_vm.bus().handle_memory_access(*static_cast<Cpu*>(this));
|
||||
_state.ip += sizeof(Genode::uint32_t);
|
||||
}
|
||||
|
||||
|
||||
void Cpu_base::_update_state()
|
||||
{
|
||||
if (!_gic.pending_irq()) return;
|
||||
|
||||
_active = true;
|
||||
_timer.cancel_timeout();
|
||||
}
|
||||
|
||||
|
||||
unsigned Cpu_base::cpu_id() const { return _vcpu_id.id; }
|
||||
void Cpu_base::run() { _vm_session.run(_vcpu_id); }
|
||||
void Cpu_base::pause() { _vm_session.pause(_vcpu_id); }
|
||||
bool Cpu_base::active() const { return _active; }
|
||||
Cpu_base::State & Cpu_base::state() const { return _state; }
|
||||
Gic::Gicd_banked & Cpu_base::gic() { return _gic; }
|
||||
|
||||
|
||||
void Cpu_base::recall()
|
||||
{
|
||||
Genode::Signal_transmitter(_vm_handler).submit();
|
||||
};
|
||||
|
||||
|
||||
Cpu_base::Cpu_base(Vm & vm,
|
||||
Genode::Vm_connection & vm_session,
|
||||
Mmio_bus & bus,
|
||||
Gic & gic,
|
||||
Genode::Env & env,
|
||||
Genode::Heap & heap,
|
||||
Genode::Entrypoint & ep)
|
||||
: _vm(vm),
|
||||
_vm_session(vm_session),
|
||||
_heap(heap),
|
||||
_vm_handler(*this, ep, *this, &Cpu_base::_handle_nothing),
|
||||
_vcpu_id(_vm_session.with_upgrade([&]() {
|
||||
return _vm_session.create_vcpu(heap, env, _vm_handler);
|
||||
})),
|
||||
_state(*((State*)env.rm().attach(_vm_session.cpu_state(_vcpu_id)))),
|
||||
_gic(*this, gic, bus),
|
||||
_timer(env, ep, _gic.irq(VTIMER_IRQ), *this) { }
|
225
repos/os/src/server/vmm/cpu_base.h
Normal file
225
repos/os/src/server/vmm/cpu_base.h
Normal file
@ -0,0 +1,225 @@
|
||||
/*
|
||||
* \brief VMM cpu object
|
||||
* \author Stefan Kalkowski
|
||||
* \date 2019-07-18
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2019 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#ifndef _SRC__SERVER__VMM__CPU_BASE_H_
|
||||
#define _SRC__SERVER__VMM__CPU_BASE_H_
|
||||
|
||||
#include <exception.h>
|
||||
#include <generic_timer.h>
|
||||
|
||||
#include <base/env.h>
|
||||
#include <base/heap.h>
|
||||
#include <cpu/vm_state_virtualization.h>
|
||||
#include <util/mmio.h>
|
||||
#include <vm_session/connection.h>
|
||||
|
||||
namespace Vmm {
|
||||
class Vm;
|
||||
class Cpu_base;
|
||||
Genode::Lock & lock();
|
||||
}
|
||||
|
||||
class Vmm::Cpu_base
|
||||
{
|
||||
public:
|
||||
|
||||
struct State : Genode::Vm_state
|
||||
{
|
||||
Genode::uint64_t reg(unsigned idx) const;
|
||||
void reg(unsigned idx, Genode::uint64_t v);
|
||||
};
|
||||
|
||||
struct Esr : Genode::Register<32>
|
||||
{
|
||||
struct Ec : Bitfield<26, 6>
|
||||
{
|
||||
enum {
|
||||
WFI = 0x1,
|
||||
MRC_MCR = 0x3,
|
||||
HVC = 0x16,
|
||||
MRS_MSR = 0x18,
|
||||
DA = 0x24,
|
||||
BRK = 0x3c
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
Cpu_base(Vm & vm,
|
||||
Genode::Vm_connection & vm_session,
|
||||
Mmio_bus & bus,
|
||||
Gic & gic,
|
||||
Genode::Env & env,
|
||||
Genode::Heap & heap,
|
||||
Genode::Entrypoint & ep);
|
||||
|
||||
unsigned cpu_id() const;
|
||||
void run();
|
||||
void pause();
|
||||
bool active() const;
|
||||
State & state() const;
|
||||
Gic::Gicd_banked & gic();
|
||||
void dump();
|
||||
void handle_exception();
|
||||
void recall();
|
||||
void initialize_boot(Genode::addr_t ip,
|
||||
Genode::addr_t dtb);
|
||||
|
||||
template <typename FUNC>
|
||||
void handle_signal(FUNC handler)
|
||||
{
|
||||
if (active()) {
|
||||
pause();
|
||||
handle_exception();
|
||||
}
|
||||
|
||||
handler();
|
||||
_update_state();
|
||||
if (active()) run();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
struct Signal_handler : Genode::Vm_handler<Signal_handler<T>>
|
||||
{
|
||||
using Base = Genode::Vm_handler<Signal_handler<T>>;
|
||||
|
||||
Cpu_base & cpu;
|
||||
T & obj;
|
||||
void (T::*member)();
|
||||
|
||||
void handle()
|
||||
{
|
||||
try {
|
||||
cpu.handle_signal([this] () { (obj.*member)(); });
|
||||
} catch(Exception &e) {
|
||||
Genode::error(e);
|
||||
cpu.dump();
|
||||
}
|
||||
}
|
||||
|
||||
Signal_handler(Cpu_base & cpu,
|
||||
Genode::Entrypoint & ep,
|
||||
T & o,
|
||||
void (T::*f)())
|
||||
: Base(ep, *this, &Signal_handler::handle),
|
||||
cpu(cpu), obj(o), member(f) {}
|
||||
};
|
||||
|
||||
protected:
|
||||
|
||||
class System_register : public Genode::Avl_node<System_register>
|
||||
{
|
||||
private:
|
||||
|
||||
const Esr::access_t _encoding;
|
||||
const char *_name;
|
||||
const bool _writeable;
|
||||
Genode::uint64_t _value;
|
||||
|
||||
public:
|
||||
|
||||
struct Iss : Esr
|
||||
{
|
||||
struct Direction : Bitfield<0, 1> {};
|
||||
struct Crm : Bitfield<1, 4> {};
|
||||
struct Register : Bitfield<5, 5> {};
|
||||
struct Crn : Bitfield<10, 4> {};
|
||||
struct Opcode1 : Bitfield<14, 3> {};
|
||||
struct Opcode2 : Bitfield<17, 3> {};
|
||||
struct Opcode0 : Bitfield<20, 2> {};
|
||||
|
||||
static access_t value(unsigned op0,
|
||||
unsigned crn,
|
||||
unsigned op1,
|
||||
unsigned crm,
|
||||
unsigned op2);
|
||||
|
||||
static access_t mask_encoding(access_t v);
|
||||
};
|
||||
|
||||
System_register(unsigned op0,
|
||||
unsigned crn,
|
||||
unsigned op1,
|
||||
unsigned crm,
|
||||
unsigned op2,
|
||||
const char * name,
|
||||
bool writeable,
|
||||
Genode::addr_t v,
|
||||
Genode::Avl_tree<System_register> & tree);
|
||||
|
||||
System_register(unsigned crn,
|
||||
unsigned op1,
|
||||
unsigned crm,
|
||||
unsigned op2,
|
||||
const char * name,
|
||||
bool writeable,
|
||||
Genode::addr_t v,
|
||||
Genode::Avl_tree<System_register> & tree)
|
||||
: System_register(0, crn, op1, crm, op2,
|
||||
name, writeable, v, tree) {}
|
||||
|
||||
const char * name() const { return _name; }
|
||||
const bool writeable() const { return _writeable; }
|
||||
|
||||
System_register * find_by_encoding(Iss::access_t e)
|
||||
{
|
||||
if (e == _encoding) return this;
|
||||
|
||||
System_register * r =
|
||||
Avl_node<System_register>::child(e > _encoding);
|
||||
return r ? r->find_by_encoding(e) : nullptr;
|
||||
}
|
||||
|
||||
virtual void write(Genode::addr_t v) {
|
||||
_value = (Genode::addr_t)v; }
|
||||
|
||||
virtual Genode::addr_t read() const {
|
||||
return (Genode::addr_t)(_value); }
|
||||
|
||||
|
||||
/************************
|
||||
** Avl node interface **
|
||||
************************/
|
||||
|
||||
bool higher(System_register *r) {
|
||||
return (r->_encoding > _encoding); }
|
||||
};
|
||||
|
||||
bool _active { true };
|
||||
Vm & _vm;
|
||||
Genode::Vm_connection & _vm_session;
|
||||
Genode::Heap & _heap;
|
||||
Signal_handler<Cpu_base> _vm_handler;
|
||||
Genode::Vm_session::Vcpu_id _vcpu_id;
|
||||
State & _state;
|
||||
Genode::Avl_tree<System_register> _reg_tree;
|
||||
|
||||
|
||||
/***********************
|
||||
** Local peripherals **
|
||||
***********************/
|
||||
|
||||
Gic::Gicd_banked _gic;
|
||||
Generic_timer _timer;
|
||||
|
||||
void _handle_nothing() {}
|
||||
bool _handle_sys_reg();
|
||||
void _handle_brk();
|
||||
void _handle_wfi();
|
||||
void _handle_sync();
|
||||
void _handle_irq();
|
||||
void _handle_data_abort();
|
||||
void _handle_hyper_call();
|
||||
void _update_state();
|
||||
};
|
||||
|
||||
#endif /* _SRC__SERVER__VMM__CPU_BASE_H_ */
|
93
repos/os/src/server/vmm/generic_timer.cc
Normal file
93
repos/os/src/server/vmm/generic_timer.cc
Normal file
@ -0,0 +1,93 @@
|
||||
/*
|
||||
* \brief VMM ARM Generic timer device model
|
||||
* \author Stefan Kalkowski
|
||||
* \date 2019-08-20
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2019 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#include <cpu.h>
|
||||
#include <generic_timer.h>
|
||||
|
||||
using Vmm::Generic_timer;
|
||||
|
||||
bool Generic_timer::_enabled() {
|
||||
return Ctrl::Enabled::get(_cpu.state().timer.control); }
|
||||
|
||||
|
||||
bool Generic_timer::_masked() {
|
||||
return Ctrl::Imask::get(_cpu.state().timer.control); }
|
||||
|
||||
|
||||
bool Generic_timer::_pending() {
|
||||
return Ctrl::Istatus::get(_cpu.state().timer.control); }
|
||||
|
||||
|
||||
void Generic_timer::_handle_timeout(Genode::Duration)
|
||||
{
|
||||
_cpu.handle_signal([this] (void) {
|
||||
if (_enabled() && !_masked()) handle_irq();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Generic_timer::Generic_timer(Genode::Env & env,
|
||||
Genode::Entrypoint & ep,
|
||||
Gic::Irq & irq,
|
||||
Cpu_base & cpu)
|
||||
: _timer(env, ep),
|
||||
_timeout(_timer, *this, &Generic_timer::_handle_timeout),
|
||||
_irq(irq),
|
||||
_cpu(cpu)
|
||||
{
|
||||
_cpu.state().timer.irq = true;
|
||||
_irq.handler(*this);
|
||||
}
|
||||
|
||||
|
||||
void Generic_timer::schedule_timeout()
|
||||
{
|
||||
if (_pending()) {
|
||||
handle_irq();
|
||||
return;
|
||||
}
|
||||
|
||||
if (_enabled()) {
|
||||
if (_usecs_left()) {
|
||||
_timeout.schedule(Genode::Microseconds(_usecs_left()));
|
||||
} else _handle_timeout(Genode::Duration(Genode::Microseconds(0)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Generic_timer::cancel_timeout()
|
||||
{
|
||||
if (_timeout.scheduled()) _timeout.discard();
|
||||
}
|
||||
|
||||
|
||||
void Generic_timer::handle_irq()
|
||||
{
|
||||
_irq.assert();
|
||||
_cpu.state().timer.irq = false;
|
||||
}
|
||||
|
||||
|
||||
void Generic_timer::eoi()
|
||||
{
|
||||
_cpu.state().timer.irq = true;
|
||||
};
|
||||
|
||||
|
||||
void Generic_timer::dump()
|
||||
{
|
||||
using namespace Genode;
|
||||
|
||||
log(" timer.ctl = ", Hex(_cpu.state().timer.control, Hex::PREFIX, Hex::PAD));
|
||||
log(" timer.cmp = ", Hex(_cpu.state().timer.compare, Hex::PREFIX, Hex::PAD));
|
||||
}
|
@ -22,7 +22,7 @@
|
||||
#include <util/register.h>
|
||||
|
||||
namespace Vmm {
|
||||
class Cpu;
|
||||
class Cpu_base;
|
||||
class Generic_timer;
|
||||
}
|
||||
|
||||
@ -33,7 +33,7 @@ class Vmm::Generic_timer : Gic::Irq::Irq_handler
|
||||
Timer::Connection _timer;
|
||||
Timer::One_shot_timeout<Generic_timer> _timeout;
|
||||
Gic::Irq & _irq;
|
||||
Cpu & _cpu;
|
||||
Cpu_base & _cpu;
|
||||
|
||||
struct Ctrl : Genode::Register<32>
|
||||
{
|
||||
@ -56,7 +56,7 @@ class Vmm::Generic_timer : Gic::Irq::Irq_handler
|
||||
Generic_timer(Genode::Env & env,
|
||||
Genode::Entrypoint & ep,
|
||||
Gic::Irq & irq,
|
||||
Cpu & cpu);
|
||||
Cpu_base & cpu);
|
||||
|
||||
void schedule_timeout();
|
||||
void cancel_timeout();
|
@ -159,13 +159,13 @@ void Gic::Gicd_banked::handle_irq()
|
||||
|
||||
irq(i).deassert();
|
||||
|
||||
_cpu.state().irqs.virtual_irq = 1023;
|
||||
_cpu.state().irqs.virtual_irq = SPURIOUS;
|
||||
}
|
||||
|
||||
|
||||
bool Gic::Gicd_banked::pending_irq()
|
||||
{
|
||||
if (_cpu.state().irqs.virtual_irq != 1023) return true;
|
||||
if (_cpu.state().irqs.virtual_irq != SPURIOUS) return true;
|
||||
|
||||
Irq * i = _gic._pending_list.highest_enabled();
|
||||
Irq * j = _pending_list.highest_enabled();
|
||||
@ -178,7 +178,7 @@ bool Gic::Gicd_banked::pending_irq()
|
||||
}
|
||||
|
||||
|
||||
Gic::Gicd_banked::Gicd_banked(Cpu & cpu, Gic & gic, Mmio_bus & bus)
|
||||
Gic::Gicd_banked::Gicd_banked(Cpu_base & cpu, Gic & gic, Mmio_bus & bus)
|
||||
: _cpu(cpu), _gic(gic)
|
||||
{
|
||||
for (unsigned i = 0; i < MAX_SGI; i++)
|
||||
@ -187,11 +187,16 @@ Gic::Gicd_banked::Gicd_banked(Cpu & cpu, Gic & gic, Mmio_bus & bus)
|
||||
for (unsigned i = 0; i < MAX_PPI; i++)
|
||||
_ppi[i].construct(i+MAX_SGI, Irq::PPI, _pending_list);
|
||||
|
||||
_cpu.state().irqs.last_irq = 1023;
|
||||
_cpu.state().irqs.virtual_irq = 1023;
|
||||
_cpu.state().irqs.last_irq = SPURIOUS;
|
||||
_cpu.state().irqs.virtual_irq = SPURIOUS;
|
||||
|
||||
_rdist.construct(0x80a0000 + (cpu.cpu_id()*0x20000), 0x20000, cpu.cpu_id(), Vm::last_cpu() == cpu.cpu_id());
|
||||
bus.add(*_rdist);
|
||||
if (gic.version() >= 3) {
|
||||
_rdist.construct(GICR_MMIO_START +
|
||||
(cpu.cpu_id()*0x20000), 0x20000,
|
||||
cpu.cpu_id(),
|
||||
Vm::last_cpu() == cpu.cpu_id());
|
||||
bus.add(*_rdist);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -218,12 +223,18 @@ void Gic::Irq_reg::write(Address_range & access, Cpu & cpu, Register value)
|
||||
}
|
||||
|
||||
|
||||
Gic::Gic(const char * const name,
|
||||
const Genode::uint64_t addr,
|
||||
const Genode::uint64_t size,
|
||||
Mmio_bus & bus,
|
||||
Genode::Env & env)
|
||||
: Mmio_device(name, addr, size)
|
||||
unsigned Gic::version() { return _version; }
|
||||
|
||||
|
||||
Gic::Gic(const char * const name,
|
||||
const Genode::uint64_t addr,
|
||||
const Genode::uint64_t size,
|
||||
unsigned cpus,
|
||||
unsigned version,
|
||||
Genode::Vm_connection & vm,
|
||||
Mmio_bus & bus,
|
||||
Genode::Env & env)
|
||||
: Mmio_device(name, addr, size), _cpu_cnt(cpus), _version(version)
|
||||
{
|
||||
add(_ctrl);
|
||||
add(_typer);
|
||||
@ -248,4 +259,6 @@ Gic::Gic(const char * const name,
|
||||
_spi[i].construct(i+MAX_SGI+MAX_PPI, Irq::SPI, _pending_list);
|
||||
|
||||
bus.add(*this);
|
||||
|
||||
if (version < 3) vm.attach_pic(GICC_MMIO_START);
|
||||
}
|
@ -18,11 +18,15 @@
|
||||
|
||||
#include <base/env.h>
|
||||
#include <drivers/defs/arm_v7.h>
|
||||
#include <vm_session/connection.h>
|
||||
#include <util/list.h>
|
||||
#include <util/register.h>
|
||||
#include <util/reconstructible.h>
|
||||
|
||||
namespace Vmm { class Gic; }
|
||||
namespace Vmm {
|
||||
class Cpu_base;
|
||||
class Gic;
|
||||
}
|
||||
|
||||
class Vmm::Gic : public Vmm::Mmio_device
|
||||
{
|
||||
@ -120,11 +124,11 @@ class Vmm::Gic : public Vmm::Mmio_device
|
||||
void handle_irq();
|
||||
bool pending_irq();
|
||||
|
||||
Gicd_banked(Cpu & cpu, Gic & gic, Mmio_bus & bus);
|
||||
Gicd_banked(Cpu_base & cpu, Gic & gic, Mmio_bus & bus);
|
||||
|
||||
private:
|
||||
|
||||
Cpu & _cpu;
|
||||
Cpu_base & _cpu;
|
||||
Gic & _gic;
|
||||
Genode::Constructible<Irq> _sgi[MAX_SGI];
|
||||
Genode::Constructible<Irq> _ppi[MAX_PPI];
|
||||
@ -137,7 +141,9 @@ class Vmm::Gic : public Vmm::Mmio_device
|
||||
Mmio_register gicr_ctlr { "GICR_CTLR", Mmio_register::RO,
|
||||
0x0, 4, 0b10010 };
|
||||
Mmio_register gicr_typer { "GICR_TYPER", Mmio_register::RO,
|
||||
0x8, 8, (Genode::uint64_t)cpu_id<<32 | cpu_id<<8 | (last ? 1<<4 : 0) };
|
||||
0x8, 8,
|
||||
(Genode::uint64_t)cpu_id<<32 |
|
||||
cpu_id<<8 | (last ? 1<<4 : 0) };
|
||||
Mmio_register gicr_waker { "GICR_WAKER", Mmio_register::RO,
|
||||
0x14, 4, 0 };
|
||||
Mmio_register gicr_pidr2 { "GICR_PIDR2", Mmio_register::RO,
|
||||
@ -242,9 +248,14 @@ class Vmm::Gic : public Vmm::Mmio_device
|
||||
Genode::Constructible<Redistributor> _rdist;
|
||||
};
|
||||
|
||||
unsigned version();
|
||||
|
||||
Gic(const char * const name,
|
||||
const Genode::uint64_t addr,
|
||||
const Genode::uint64_t size,
|
||||
unsigned cpus,
|
||||
unsigned version,
|
||||
Genode::Vm_connection & vm,
|
||||
Mmio_bus & bus,
|
||||
Genode::Env & env);
|
||||
|
||||
@ -254,8 +265,8 @@ class Vmm::Gic : public Vmm::Mmio_device
|
||||
|
||||
Genode::Constructible<Irq> _spi[MAX_SPI];
|
||||
Irq::List _pending_list;
|
||||
unsigned _cpu_cnt { 2 }; /* FIXME: smp support */
|
||||
unsigned _version { 3 }; /* FIXME: version support */
|
||||
unsigned _cpu_cnt;
|
||||
unsigned _version;
|
||||
|
||||
struct Gicd_ctlr : Genode::Register<32>, Mmio_register
|
||||
{
|
||||
@ -282,7 +293,8 @@ class Vmm::Gic : public Vmm::Mmio_device
|
||||
|
||||
Gicd_typer(unsigned cpus)
|
||||
: Mmio_register("GICD_TYPER", Mmio_register::RO, 0x4, 4,
|
||||
It_lines_number::bits(31) | Cpu_number::bits(cpus-1) | Id_bits::bits(9)) {}
|
||||
It_lines_number::bits(31) |
|
||||
Cpu_number::bits(cpus-1) | Id_bits::bits(9)) {}
|
||||
} _typer { _cpu_cnt };
|
||||
|
||||
struct Gicd_iidr : Genode::Register<32>, Mmio_register
|
@ -112,11 +112,9 @@ void Vmm::Mmio_bus::handle_memory_access(Vmm::Cpu & cpu)
|
||||
Mmio_device & dev = get<Mmio_device>(bus_range);
|
||||
Address_range dev_range(ipa - dev.start,width);
|
||||
if (wr) {
|
||||
dev.write(dev_range, cpu, (idx == 31) ? 0 : state.r[idx]);
|
||||
dev.write(dev_range, cpu, state.reg(idx));
|
||||
} else {
|
||||
if (idx > 30)
|
||||
throw Exception("Wrong register index when reading ", bus_range);
|
||||
state.r[idx] = dev.read(dev_range, cpu);
|
||||
state.reg(idx, dev.read(dev_range, cpu));
|
||||
}
|
||||
} catch(Exception & e) {
|
||||
Genode::warning(e);
|
@ -1,77 +1,53 @@
|
||||
/*
|
||||
* \brief Driver for the Versatile Express A9X4 board
|
||||
* \author Martin stein
|
||||
* \date 2011-11-03
|
||||
* \brief VMM address space utility
|
||||
* \author Stefan Kalkowski
|
||||
* \date 2019-11-13
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2011-2017 Genode Labs GmbH
|
||||
* Copyright (C) 2019 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#ifndef _INCLUDE__PLATFORM__VEA9X4__DRIVERS__BOARD_BASE_H_
|
||||
#define _INCLUDE__PLATFORM__VEA9X4__DRIVERS__BOARD_BASE_H_
|
||||
#ifndef _SRC__SERVER__VMM__BOARD_H_
|
||||
#define _SRC__SERVER__VMM__BOARD_H_
|
||||
|
||||
namespace Vea9x4 { struct Board; }
|
||||
namespace Vmm {
|
||||
|
||||
enum {
|
||||
SIZE_1_MB = 1024 * 1024,
|
||||
KERNEL_OFFSET = 54 * SIZE_1_MB,
|
||||
DTB_OFFSET = 64 * SIZE_1_MB,
|
||||
INITRD_OFFSET = 96 * SIZE_1_MB,
|
||||
|
||||
/**
|
||||
* Driver for the Versatile Express A9X4 board
|
||||
*
|
||||
* Implies the uATX motherboard and the CoreTile Express A9X4 daughterboard
|
||||
*/
|
||||
struct Vea9x4::Board
|
||||
{
|
||||
enum
|
||||
{
|
||||
/* MMIO */
|
||||
MMIO_0_BASE = 0x10000000,
|
||||
MMIO_0_SIZE = 0x10000000,
|
||||
MMIO_1_BASE = 0x4C000000,
|
||||
MMIO_1_SIZE = 0x04000000,
|
||||
GIC_VERSION = 2,
|
||||
GICD_MMIO_START = 0x8000000,
|
||||
GICD_MMIO_SIZE = 0x10000,
|
||||
GICC_MMIO_START = 0x8010000,
|
||||
GICR_MMIO_START = 0x80a0000,
|
||||
GICR_MMIO_SIZE = 0xf60000,
|
||||
|
||||
/* RAM */
|
||||
RAM_0_BASE = 0x60000000,
|
||||
RAM_0_SIZE = 0x20000000,
|
||||
RAM_1_BASE = 0x84000000,
|
||||
RAM_1_SIZE = 0x1c000000,
|
||||
RAM_2_BASE = 0x48000000,
|
||||
RAM_2_SIZE = 0x02000000,
|
||||
PL011_MMIO_START = 0x9000000,
|
||||
PL011_MMIO_SIZE = 0x1000,
|
||||
PL011_IRQ = 33,
|
||||
|
||||
/* UART */
|
||||
PL011_0_MMIO_BASE = MMIO_0_BASE + 0x9000,
|
||||
PL011_0_MMIO_SIZE = 0x1000,
|
||||
PL011_0_CLOCK = 24*1000*1000,
|
||||
PL011_0_IRQ = 37,
|
||||
PL011_1_IRQ = 38,
|
||||
PL011_2_IRQ = 39,
|
||||
PL011_3_IRQ = 40,
|
||||
VIRTIO_CONSOLE_MMIO_START = 0xa000000,
|
||||
VIRTIO_CONSOLE_MMIO_SIZE = 0x200,
|
||||
VIRTIO_CONSOLE_IRQ = 48,
|
||||
|
||||
/* timer/counter */
|
||||
SP804_0_1_MMIO_BASE = MMIO_0_BASE + 0x11000,
|
||||
SP804_0_1_MMIO_SIZE = 0x1000,
|
||||
SP804_0_1_CLOCK = 1000*1000,
|
||||
SP804_0_1_IRQ = 34,
|
||||
VIRTIO_NET_MMIO_START = 0xa000200,
|
||||
VIRTIO_NET_MMIO_SIZE = 0x200,
|
||||
VIRTIO_NET_IRQ = 49,
|
||||
|
||||
/* PS2 */
|
||||
KMI_0_IRQ = 44,
|
||||
KMI_1_IRQ = 45,
|
||||
RAM_START = 0x40000000,
|
||||
RAM_SIZE = 128 * 1024 *1024,
|
||||
|
||||
/* LAN */
|
||||
LAN9118_IRQ = 47,
|
||||
VTIMER_IRQ = 27,
|
||||
|
||||
/* card reader */
|
||||
PL180_0_IRQ = 9,
|
||||
PL180_1_IRQ = 10,
|
||||
|
||||
/* CPU */
|
||||
CORTEX_A9_PRIVATE_MEM_BASE = 0x1e000000,
|
||||
CORTEX_A9_PRIVATE_MEM_SIZE = 0x2000,
|
||||
CORTEX_A9_PRIVATE_TIMER_CLK = 200010000,
|
||||
MAX_CPUS = 1,
|
||||
};
|
||||
};
|
||||
|
||||
#endif /* _INCLUDE__PLATFORM__VEA9X4__DRIVERS__BOARD_BASE_H_ */
|
||||
}
|
||||
|
||||
#endif /* _SRC__SERVER__VMM__BOARD_H_ */
|
||||
|
189
repos/os/src/server/vmm/spec/arm_v7/cpu.cc
Normal file
189
repos/os/src/server/vmm/spec/arm_v7/cpu.cc
Normal file
@ -0,0 +1,189 @@
|
||||
/*
|
||||
* \brief VMM cpu object
|
||||
* \author Stefan Kalkowski
|
||||
* \date 2019-07-18
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2019 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
#include <cpu.h>
|
||||
#include <vm.h>
|
||||
#include <psci.h>
|
||||
|
||||
using Vmm::Cpu_base;
|
||||
using Vmm::Cpu;
|
||||
using Vmm::Gic;
|
||||
|
||||
Genode::uint64_t Cpu_base::State::reg(unsigned idx) const
|
||||
{
|
||||
if (idx > 15) return 0;
|
||||
|
||||
Genode::uint32_t * r = (Genode::uint32_t*)this;
|
||||
r += idx;
|
||||
return *r;
|
||||
}
|
||||
|
||||
|
||||
void Cpu_base::State::reg(unsigned idx, Genode::uint64_t v)
|
||||
{
|
||||
if (idx > 15) return;
|
||||
|
||||
Genode::uint32_t * r = (Genode::uint32_t*)this;
|
||||
r += idx;
|
||||
*r = v;
|
||||
}
|
||||
|
||||
|
||||
Cpu_base::System_register::Iss::access_t
|
||||
Cpu_base::System_register::Iss::value(unsigned op0, unsigned crn, unsigned op1,
|
||||
unsigned crm, unsigned op2)
|
||||
{
|
||||
access_t v = 0;
|
||||
Crn::set(v, crn);
|
||||
Crm::set(v, crm);
|
||||
Opcode1::set(v, op1);
|
||||
Opcode2::set(v, op2);
|
||||
return v;
|
||||
};
|
||||
|
||||
|
||||
Cpu_base::System_register::Iss::access_t
|
||||
Cpu_base::System_register::Iss::mask_encoding(access_t v)
|
||||
{
|
||||
return Crm::masked(v) |
|
||||
Crn::masked(v) |
|
||||
Opcode1::masked(v) |
|
||||
Opcode2::masked(v);
|
||||
}
|
||||
|
||||
|
||||
void Cpu_base::_handle_brk()
|
||||
{
|
||||
Genode::error(__func__, " not implemented yet");
|
||||
}
|
||||
|
||||
|
||||
void Cpu_base::handle_exception()
|
||||
{
|
||||
/* check exception reason */
|
||||
switch (_state.cpu_exception) {
|
||||
case Cpu::NO_EXCEPTION: break;
|
||||
case Cpu::FIQ: [[fallthrough]];
|
||||
case Cpu::IRQ: _handle_irq(); break;
|
||||
case Cpu::TRAP: _handle_sync(); break;
|
||||
default:
|
||||
throw Exception("Curious exception ",
|
||||
_state.cpu_exception, " occured");
|
||||
}
|
||||
_state.cpu_exception = Cpu::NO_EXCEPTION;
|
||||
}
|
||||
|
||||
|
||||
void Cpu_base::dump()
|
||||
{
|
||||
using namespace Genode;
|
||||
|
||||
auto lambda = [] (unsigned i) {
|
||||
switch (i) {
|
||||
case 0: return "und";
|
||||
case 1: return "svc";
|
||||
case 2: return "abt";
|
||||
case 3: return "irq";
|
||||
case 4: return "fiq";
|
||||
default: return "unknown";
|
||||
};
|
||||
};
|
||||
|
||||
log("VM state (", _active ? "active" : "inactive", ") :");
|
||||
for (unsigned i = 0; i < 13; i++) {
|
||||
log(" r", i, " = ",
|
||||
Hex(_state.reg(i), Hex::PREFIX, Hex::PAD));
|
||||
}
|
||||
log(" sp = ", Hex(_state.sp, Hex::PREFIX, Hex::PAD));
|
||||
log(" lr = ", Hex(_state.lr, Hex::PREFIX, Hex::PAD));
|
||||
log(" ip = ", Hex(_state.ip, Hex::PREFIX, Hex::PAD));
|
||||
log(" cpsr = ", Hex(_state.cpsr, Hex::PREFIX, Hex::PAD));
|
||||
for (unsigned i = 0; i < State::Mode_state::MAX; i++) {
|
||||
log(" sp_", lambda(i), " = ",
|
||||
Hex(_state.mode[i].sp, Hex::PREFIX, Hex::PAD));
|
||||
log(" lr_", lambda(i), " = ",
|
||||
Hex(_state.mode[i].lr, Hex::PREFIX, Hex::PAD));
|
||||
log(" spsr_", lambda(i), " = ",
|
||||
Hex(_state.mode[i].spsr, Hex::PREFIX, Hex::PAD));
|
||||
}
|
||||
log(" exception = ", _state.cpu_exception);
|
||||
log(" esr_el2 = ", Hex(_state.esr_el2, Hex::PREFIX, Hex::PAD));
|
||||
log(" hpfar_el2 = ", Hex(_state.hpfar_el2, Hex::PREFIX, Hex::PAD));
|
||||
log(" far_el2 = ", Hex(_state.far_el2, Hex::PREFIX, Hex::PAD));
|
||||
log(" hifar = ", Hex(_state.hifar, Hex::PREFIX, Hex::PAD));
|
||||
log(" dfsr = ", Hex(_state.dfsr, Hex::PREFIX, Hex::PAD));
|
||||
log(" ifsr = ", Hex(_state.ifsr, Hex::PREFIX, Hex::PAD));
|
||||
log(" sctrl = ", Hex(_state.sctrl, Hex::PREFIX, Hex::PAD));
|
||||
_timer.dump();
|
||||
}
|
||||
|
||||
|
||||
void Cpu_base::initialize_boot(Genode::addr_t ip, Genode::addr_t dtb)
|
||||
{
|
||||
state().reg(1, 0xffffffff); /* invalid machine type */
|
||||
state().reg(2, dtb);
|
||||
state().ip = ip;
|
||||
}
|
||||
|
||||
|
||||
Genode::addr_t Cpu::Ccsidr::read() const
|
||||
{
|
||||
struct Csselr : Genode::Register<32>
|
||||
{
|
||||
struct Level : Bitfield<1, 4> {};
|
||||
};
|
||||
|
||||
enum { INVALID = 0xffffffff };
|
||||
|
||||
unsigned level = Csselr::Level::get(csselr.read());
|
||||
|
||||
if (level > 6) {
|
||||
Genode::warning("Invalid Csselr value!");
|
||||
return INVALID;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
Cpu::Cpu(Vm & vm,
|
||||
Genode::Vm_connection & vm_session,
|
||||
Mmio_bus & bus,
|
||||
Gic & gic,
|
||||
Genode::Env & env,
|
||||
Genode::Heap & heap,
|
||||
Genode::Entrypoint & ep)
|
||||
: Cpu_base(vm, vm_session, bus, gic, env, heap, ep),
|
||||
_sr_midr (0, 0, 0, 0, "MIDR", false, 0x412fc0f1, _reg_tree),
|
||||
_sr_mpidr (0, 0, 0, 5, "MPIDR", false, 1<<31|cpu_id(), _reg_tree),
|
||||
_sr_mmfr0 (0, 0, 1, 4, "MMFR0", false, 0x10201105, _reg_tree),
|
||||
_sr_mmfr1 (0, 0, 1, 5, "MMFR1", false, 0x20000000, _reg_tree),
|
||||
_sr_mmfr2 (0, 0, 1, 6, "MMFR2", false, 0x01240000, _reg_tree),
|
||||
_sr_mmfr3 (0, 0, 1, 7, "MMFR3", false, 0x02102211, _reg_tree),
|
||||
_sr_isar0 (0, 0, 2, 0, "ISAR0", false, 0x02101110, _reg_tree),
|
||||
_sr_isar1 (0, 0, 2, 1, "ISAR1", false, 0x13112111, _reg_tree),
|
||||
_sr_isar2 (0, 0, 2, 2, "ISAR2", false, 0x21232041, _reg_tree),
|
||||
_sr_isar3 (0, 0, 2, 3, "ISAR3", false, 0x11112131, _reg_tree),
|
||||
_sr_isar4 (0, 0, 2, 4, "ISAR4", false, 0x10011142, _reg_tree),
|
||||
_sr_isar5 (0, 0, 2, 5, "ISAR5", false, 0x0, _reg_tree),
|
||||
_sr_pfr0 (0, 0, 1, 0, "PFR0", false, 0x00001131, _reg_tree),
|
||||
_sr_pfr1 (0, 0, 1, 1, "PFR1", false, 0x00011011, _reg_tree),
|
||||
_sr_clidr (0, 1, 0, 1, "CLIDR", false, 0xa200023, _reg_tree),
|
||||
_sr_csselr (0, 2, 0, 0, "CSSELR", true, 0x0, _reg_tree),
|
||||
_sr_ctr (0, 0, 0, 1, "CTR", true, 0x8444c004, _reg_tree),
|
||||
_sr_revidr (0, 0, 0, 6, "REVIDR", true, 0x0, _reg_tree),
|
||||
_sr_ccsidr (_sr_csselr, _reg_tree),
|
||||
_sr_actlr (1, 0, 0, 1, "ACTLR", true, 0x0, _reg_tree)
|
||||
{
|
||||
_state.cpsr = 0x93; /* el1 mode and IRQs disabled */
|
||||
_state.sctrl = 0xc50078;
|
||||
}
|
91
repos/os/src/server/vmm/spec/arm_v7/cpu.h
Normal file
91
repos/os/src/server/vmm/spec/arm_v7/cpu.h
Normal file
@ -0,0 +1,91 @@
|
||||
/*
|
||||
* \brief VMM cpu object
|
||||
* \author Stefan Kalkowski
|
||||
* \date 2019-07-18
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2019 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#ifndef _SRC__SERVER__VMM__CPU_H_
|
||||
#define _SRC__SERVER__VMM__CPU_H_
|
||||
|
||||
#include <cpu_base.h>
|
||||
|
||||
namespace Vmm { class Cpu; }
|
||||
|
||||
class Vmm::Cpu : public Vmm::Cpu_base
|
||||
{
|
||||
public:
|
||||
|
||||
Cpu(Vm & vm,
|
||||
Genode::Vm_connection & vm_session,
|
||||
Mmio_bus & bus,
|
||||
Gic & gic,
|
||||
Genode::Env & env,
|
||||
Genode::Heap & heap,
|
||||
Genode::Entrypoint & ep);
|
||||
|
||||
enum Exception_type {
|
||||
NO_EXCEPTION,
|
||||
RESET,
|
||||
UNDEFINED,
|
||||
HVC,
|
||||
PF_ABORT,
|
||||
DATA_ABORT,
|
||||
IRQ,
|
||||
FIQ,
|
||||
TRAP
|
||||
};
|
||||
|
||||
private:
|
||||
|
||||
struct Ccsidr : System_register
|
||||
{
|
||||
System_register & csselr;
|
||||
|
||||
Ccsidr(System_register &csselr,
|
||||
Genode::Avl_tree<System_register> & tree)
|
||||
: System_register(0, 1, 0, 0, "CCSIDR", false, 0x0, tree),
|
||||
csselr(csselr) {}
|
||||
|
||||
virtual Genode::addr_t read() const override;
|
||||
};
|
||||
|
||||
/******************************
|
||||
** Identification registers **
|
||||
******************************/
|
||||
|
||||
System_register _sr_midr;
|
||||
System_register _sr_mpidr;
|
||||
System_register _sr_mmfr0;
|
||||
System_register _sr_mmfr1;
|
||||
System_register _sr_mmfr2;
|
||||
System_register _sr_mmfr3;
|
||||
System_register _sr_isar0;
|
||||
System_register _sr_isar1;
|
||||
System_register _sr_isar2;
|
||||
System_register _sr_isar3;
|
||||
System_register _sr_isar4;
|
||||
System_register _sr_isar5;
|
||||
System_register _sr_pfr0;
|
||||
System_register _sr_pfr1;
|
||||
System_register _sr_clidr;
|
||||
System_register _sr_csselr;
|
||||
System_register _sr_ctr;
|
||||
System_register _sr_revidr;
|
||||
Ccsidr _sr_ccsidr;
|
||||
|
||||
|
||||
/*********************
|
||||
** System register **
|
||||
*********************/
|
||||
|
||||
System_register _sr_actlr;
|
||||
};
|
||||
|
||||
#endif /* _SRC__SERVER__VMM__CPU_H_ */
|
41
repos/os/src/server/vmm/spec/arm_v7/generic_timer.cc
Normal file
41
repos/os/src/server/vmm/spec/arm_v7/generic_timer.cc
Normal file
@ -0,0 +1,41 @@
|
||||
/*
|
||||
* \brief VMM ARM Generic timer device model
|
||||
* \author Stefan Kalkowski
|
||||
* \date 2019-08-20
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2019 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#include <cpu.h>
|
||||
#include <generic_timer.h>
|
||||
|
||||
using Vmm::Generic_timer;
|
||||
|
||||
Genode::uint64_t Generic_timer::_ticks_per_ms()
|
||||
{
|
||||
static Genode::uint64_t ticks_per_ms = 0;
|
||||
if (!ticks_per_ms) {
|
||||
Genode::uint32_t freq = 0;
|
||||
asm volatile("mrc p15, 0, %0, c14, c0, 0" : "=r" (freq));
|
||||
ticks_per_ms = freq / 1000;
|
||||
}
|
||||
return ticks_per_ms;
|
||||
}
|
||||
|
||||
|
||||
Genode::uint64_t Generic_timer::_usecs_left()
|
||||
{
|
||||
Genode::uint64_t count;
|
||||
Genode::uint32_t low, high;
|
||||
asm volatile("mrrc p15, 0, %0, %1, c14" : "=r" (low), "=r" (high));
|
||||
count = (Genode::uint64_t)high << 32 | (Genode::uint64_t)low;
|
||||
count -= _cpu.state().timer.offset;
|
||||
if (count > _cpu.state().timer.compare) return 0;
|
||||
return Genode::timer_ticks_to_us(_cpu.state().timer.compare - count,
|
||||
_ticks_per_ms());
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,7 +1,19 @@
|
||||
TARGET = vmm
|
||||
REQUIRES = hw arm_v7
|
||||
LIBS = base
|
||||
SRC_CC = main.cc
|
||||
INC_DIR += $(PRG_DIR)
|
||||
SRC_CC += spec/arm_v7/generic_timer.cc
|
||||
SRC_CC += address_space.cc
|
||||
SRC_CC += cpu.cc
|
||||
SRC_CC += cpu_base.cc
|
||||
SRC_CC += generic_timer.cc
|
||||
SRC_CC += gic.cc
|
||||
SRC_CC += main.cc
|
||||
SRC_CC += mmio.cc
|
||||
SRC_CC += pl011.cc
|
||||
SRC_CC += vm.cc
|
||||
SRC_CC += virtio_device.cc
|
||||
INC_DIR += $(PRG_DIR)/../.. $(PRG_DIR)
|
||||
|
||||
vpath %.cc $(PRG_DIR)/../..
|
||||
|
||||
CC_CXX_WARN_STRICT :=
|
||||
|
@ -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
|
80
repos/os/src/server/vmm/spec/arm_v7/virt.dts
Executable file
80
repos/os/src/server/vmm/spec/arm_v7/virt.dts
Executable file
@ -0,0 +1,80 @@
|
||||
/dts-v1/;
|
||||
|
||||
/ {
|
||||
compatible = "linux,dummy-virt";
|
||||
#address-cells = <0x02>;
|
||||
#size-cells = <0x02>;
|
||||
interrupt-parent = <0x8001>;
|
||||
|
||||
cpus {
|
||||
#address-cells = <0x01>;
|
||||
#size-cells = <0x00>;
|
||||
|
||||
cpu@0 {
|
||||
compatible = "arm,cortex-a15";
|
||||
reg = <0x00>;
|
||||
device_type = "cpu";
|
||||
};
|
||||
};
|
||||
|
||||
timer {
|
||||
interrupts = <0x01 0x0d 0x04 0x01 0x0e 0x04 0x01 0x0b 0x04 0x01 0x0a 0x04>;
|
||||
compatible = "arm,armv7-timer";
|
||||
always-on;
|
||||
};
|
||||
|
||||
apb-pclk {
|
||||
compatible = "fixed-clock";
|
||||
phandle = <0x8000>;
|
||||
clock-output-names = "clk24mhz";
|
||||
clock-frequency = <0x16e3600>;
|
||||
#clock-cells = <0x00>;
|
||||
};
|
||||
|
||||
pl011@9000000 {
|
||||
interrupts = <0x00 0x01 0x04>;
|
||||
compatible = "arm,pl011\0arm,primecell";
|
||||
clock-names = "uartclk\0apb_pclk";
|
||||
reg = <0x00 0x9000000 0x00 0x1000>;
|
||||
clocks = <0x8000 0x8000>;
|
||||
};
|
||||
|
||||
memory@40000000 {
|
||||
reg = <0x00 0x40000000 0x00 0x8000000>;
|
||||
device_type = "memory";
|
||||
};
|
||||
|
||||
chosen {
|
||||
bootargs = "rdinit=/bin/sh ip=dhcp console=hvc0";
|
||||
linux,initrd-start = <0x46000000>;
|
||||
linux,initrd-end = <0x460b04b6>;
|
||||
stdout-path = "/pl011@9000000";
|
||||
};
|
||||
|
||||
intc@8000000 {
|
||||
compatible = "arm,cortex-a15-gic";
|
||||
phandle = <0x8001>;
|
||||
reg = <0x00 0x8000000 0x00 0x10000 0x00 0x8010000 0x00 0x10000>;
|
||||
ranges;
|
||||
#address-cells = <0x02>;
|
||||
#redistributor-regions = <0x01>;
|
||||
interrupt-controller;
|
||||
#interrupt-cells = <0x03>;
|
||||
#size-cells = <0x02>;
|
||||
};
|
||||
|
||||
|
||||
virtio_mmio@a000000 {
|
||||
interrupts = <0x00 0x10 0x01>;
|
||||
compatible = "virtio,mmio";
|
||||
dma-coherent;
|
||||
reg = <0x00 0xa000000 0x00 0x200>;
|
||||
};
|
||||
|
||||
virtio_mmio@a000200 {
|
||||
interrupts = <0x00 0x11 0x01>;
|
||||
compatible = "virtio,mmio";
|
||||
dma-coherent;
|
||||
reg = <0x00 0xa000200 0x00 0x200>;
|
||||
};
|
||||
};
|
53
repos/os/src/server/vmm/spec/arm_v8/board.h
Normal file
53
repos/os/src/server/vmm/spec/arm_v8/board.h
Normal file
@ -0,0 +1,53 @@
|
||||
/*
|
||||
* \brief VMM address space utility
|
||||
* \author Stefan Kalkowski
|
||||
* \date 2019-11-13
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2019 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#ifndef _SRC__SERVER__VMM__BOARD_H_
|
||||
#define _SRC__SERVER__VMM__BOARD_H_
|
||||
|
||||
namespace Vmm {
|
||||
|
||||
enum {
|
||||
SIZE_1_MB = 1024 * 1024,
|
||||
KERNEL_OFFSET = 0x80000,
|
||||
INITRD_OFFSET = 32 * SIZE_1_MB,
|
||||
DTB_OFFSET = 64 * SIZE_1_MB,
|
||||
|
||||
GIC_VERSION = 3,
|
||||
GICD_MMIO_START = 0x8000000,
|
||||
GICD_MMIO_SIZE = 0x10000,
|
||||
GICC_MMIO_START = 0x8010000,
|
||||
GICR_MMIO_START = 0x80a0000,
|
||||
GICR_MMIO_SIZE = 0xf60000,
|
||||
|
||||
PL011_MMIO_START = 0x9000000,
|
||||
PL011_MMIO_SIZE = 0x1000,
|
||||
PL011_IRQ = 33,
|
||||
|
||||
VIRTIO_CONSOLE_MMIO_START = 0xa000000,
|
||||
VIRTIO_CONSOLE_MMIO_SIZE = 0x200,
|
||||
VIRTIO_CONSOLE_IRQ = 48,
|
||||
|
||||
VIRTIO_NET_MMIO_START = 0xa000200,
|
||||
VIRTIO_NET_MMIO_SIZE = 0x200,
|
||||
VIRTIO_NET_IRQ = 49,
|
||||
|
||||
RAM_START = 0x40000000,
|
||||
RAM_SIZE = 128 * 1024 *1024,
|
||||
|
||||
VTIMER_IRQ = 27,
|
||||
|
||||
MAX_CPUS = 1,
|
||||
};
|
||||
}
|
||||
|
||||
#endif /* _SRC__SERVER__VMM__BOARD_H_ */
|
@ -14,15 +14,27 @@
|
||||
#include <vm.h>
|
||||
#include <psci.h>
|
||||
|
||||
using Vmm::Cpu_base;
|
||||
using Vmm::Cpu;
|
||||
using Vmm::Gic;
|
||||
|
||||
Genode::Lock & Vmm::lock() { static Genode::Lock l {}; return l; }
|
||||
Genode::uint64_t Cpu_base::State::reg(unsigned idx) const
|
||||
{
|
||||
if (idx > 30) return 0;
|
||||
return r[idx];
|
||||
}
|
||||
|
||||
|
||||
Cpu::System_register::Iss::access_t
|
||||
Cpu::System_register::Iss::value(unsigned op0, unsigned crn, unsigned op1,
|
||||
unsigned crm, unsigned op2)
|
||||
void Cpu_base::State::reg(unsigned idx, Genode::uint64_t v)
|
||||
{
|
||||
if (idx > 30) return;
|
||||
r[idx] = v;
|
||||
}
|
||||
|
||||
|
||||
Cpu_base::System_register::Iss::access_t
|
||||
Cpu_base::System_register::Iss::value(unsigned op0, unsigned crn, unsigned op1,
|
||||
unsigned crm, unsigned op2)
|
||||
{
|
||||
access_t v = 0;
|
||||
Crn::set(v, crn);
|
||||
@ -34,8 +46,8 @@ Cpu::System_register::Iss::value(unsigned op0, unsigned crn, unsigned op1,
|
||||
};
|
||||
|
||||
|
||||
Cpu::System_register::Iss::access_t
|
||||
Cpu::System_register::Iss::mask_encoding(access_t v)
|
||||
Cpu_base::System_register::Iss::access_t
|
||||
Cpu_base::System_register::Iss::mask_encoding(access_t v)
|
||||
{
|
||||
return Crm::masked(v) |
|
||||
Crn::masked(v) |
|
||||
@ -45,21 +57,69 @@ Cpu::System_register::Iss::mask_encoding(access_t v)
|
||||
}
|
||||
|
||||
|
||||
Cpu::System_register::System_register(unsigned op0,
|
||||
unsigned crn,
|
||||
unsigned op1,
|
||||
unsigned crm,
|
||||
unsigned op2,
|
||||
const char * name,
|
||||
bool writeable,
|
||||
Genode::addr_t v,
|
||||
Genode::Avl_tree<System_register> & tree)
|
||||
: _encoding(Iss::value(op0, crn, op1, crm, op2)),
|
||||
_name(name),
|
||||
_writeable(writeable),
|
||||
_value(v)
|
||||
void Cpu_base::_handle_brk()
|
||||
{
|
||||
tree.insert(this);
|
||||
Genode::uint64_t offset = 0x0;
|
||||
if (!(_state.pstate & 0b100)) {
|
||||
offset = 0x400;
|
||||
} else if (_state.pstate & 0b1) {
|
||||
offset = 0x200;
|
||||
}
|
||||
_state.esr_el1 = _state.esr_el2;
|
||||
_state.spsr_el1 = _state.pstate;
|
||||
_state.elr_el1 = _state.ip;
|
||||
_state.ip = _state.vbar_el1 + offset;
|
||||
_state.pstate = 0b1111000101;
|
||||
}
|
||||
|
||||
|
||||
void Cpu_base::handle_exception()
|
||||
{
|
||||
/* check exception reason */
|
||||
switch (_state.exception_type) {
|
||||
case Cpu::NO_EXCEPTION: break;
|
||||
case Cpu::AARCH64_IRQ: _handle_irq(); break;
|
||||
case Cpu::AARCH64_SYNC: _handle_sync(); break;
|
||||
default:
|
||||
throw Exception("Curious exception ",
|
||||
_state.exception_type, " occured");
|
||||
}
|
||||
_state.exception_type = Cpu::NO_EXCEPTION;
|
||||
}
|
||||
|
||||
|
||||
void Cpu_base::dump()
|
||||
{
|
||||
using namespace Genode;
|
||||
|
||||
auto lambda = [] (addr_t exc) {
|
||||
switch (exc) {
|
||||
case Cpu::AARCH64_SYNC: return "aarch64 sync";
|
||||
case Cpu::AARCH64_IRQ: return "aarch64 irq";
|
||||
case Cpu::AARCH64_FIQ: return "aarch64 fiq";
|
||||
case Cpu::AARCH64_SERROR: return "aarch64 serr";
|
||||
case Cpu::AARCH32_SYNC: return "aarch32 sync";
|
||||
case Cpu::AARCH32_IRQ: return "aarch32 irq";
|
||||
case Cpu::AARCH32_FIQ: return "aarch32 fiq";
|
||||
case Cpu::AARCH32_SERROR: return "aarch32 serr";
|
||||
default: return "unknown";
|
||||
};
|
||||
};
|
||||
|
||||
log("VM state (", _active ? "active" : "inactive", ") :");
|
||||
for (unsigned i = 0; i < 31; i++) {
|
||||
log(" r", i, " = ",
|
||||
Hex(_state.r[i], Hex::PREFIX, Hex::PAD));
|
||||
}
|
||||
log(" sp = ", Hex(_state.sp, Hex::PREFIX, Hex::PAD));
|
||||
log(" ip = ", Hex(_state.ip, Hex::PREFIX, Hex::PAD));
|
||||
log(" sp_el1 = ", Hex(_state.sp_el1, Hex::PREFIX, Hex::PAD));
|
||||
log(" elr_el1 = ", Hex(_state.elr_el1, Hex::PREFIX, Hex::PAD));
|
||||
log(" pstate = ", Hex(_state.pstate, Hex::PREFIX, Hex::PAD));
|
||||
log(" exception = ", _state.exception_type, " (",
|
||||
lambda(_state.exception_type), ")");
|
||||
log(" esr_el2 = ", Hex(_state.esr_el2, Hex::PREFIX, Hex::PAD));
|
||||
_timer.dump();
|
||||
}
|
||||
|
||||
|
||||
@ -139,216 +199,13 @@ void Cpu::Icc_sgi1r_el1::write(Genode::addr_t v)
|
||||
};
|
||||
|
||||
|
||||
bool Cpu::_handle_sys_reg()
|
||||
void Cpu_base::initialize_boot(Genode::addr_t ip, Genode::addr_t dtb)
|
||||
{
|
||||
using Iss = System_register::Iss;
|
||||
|
||||
Iss::access_t v = _state.esr_el2;
|
||||
System_register * reg = _reg_tree.first();
|
||||
if (reg) reg = reg->find_by_encoding(Iss::mask_encoding(v));
|
||||
|
||||
if (!reg) {
|
||||
Genode::error("ignore unknown system register access @ ip=", (void*)_state.ip, ":");
|
||||
Genode::error(Iss::Direction::get(v) ? "read" : "write",
|
||||
": "
|
||||
"op0=", Iss::Opcode0::get(v), " "
|
||||
"op1=", Iss::Opcode1::get(v), " "
|
||||
"r", Iss::Register::get(v), " "
|
||||
"crn=", Iss::Crn::get(v), " "
|
||||
"crm=", Iss::Crm::get(v), " ",
|
||||
"op2=", Iss::Opcode2::get(v));
|
||||
if (Iss::Direction::get(v)) _state.r[Iss::Register::get(v)] = 0;
|
||||
_state.ip += sizeof(Genode::uint32_t);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Iss::Direction::get(v)) { /* read access */
|
||||
_state.r[Iss::Register::get(v)] = reg->read();
|
||||
} else { /* write access */
|
||||
if (!reg->writeable()) {
|
||||
Genode::error("writing to system register ",
|
||||
reg->name(), " not allowed!");
|
||||
return false;
|
||||
}
|
||||
reg->write(_state.r[Iss::Register::get(v)]);
|
||||
}
|
||||
_state.ip += sizeof(Genode::uint32_t);
|
||||
return true;
|
||||
state().reg(0, dtb);
|
||||
state().ip = ip;
|
||||
}
|
||||
|
||||
|
||||
void Cpu::_handle_wfi()
|
||||
{
|
||||
_state.ip += sizeof(Genode::uint32_t);
|
||||
|
||||
if (_state.esr_el2 & 1) return; /* WFE */
|
||||
|
||||
_active = false;
|
||||
_timer.schedule_timeout();
|
||||
}
|
||||
|
||||
|
||||
void Cpu::_handle_brk()
|
||||
{
|
||||
Genode::uint64_t offset = 0x0;
|
||||
if (!(_state.pstate & 0b100)) {
|
||||
offset = 0x400;
|
||||
} else if (_state.pstate & 0b1) {
|
||||
offset = 0x200;
|
||||
}
|
||||
_state.esr_el1 = _state.esr_el2;
|
||||
_state.spsr_el1 = _state.pstate;
|
||||
_state.elr_el1 = _state.ip;
|
||||
_state.ip = _state.vbar_el1 + offset;
|
||||
_state.pstate = 0b1111000101;
|
||||
}
|
||||
|
||||
|
||||
void Cpu::_handle_sync()
|
||||
{
|
||||
/* check device number*/
|
||||
switch (Esr::Ec::get(_state.esr_el2)) {
|
||||
case Esr::Ec::HVC:
|
||||
_handle_hyper_call();
|
||||
break;
|
||||
case Esr::Ec::MRS_MSR:
|
||||
_handle_sys_reg();
|
||||
break;
|
||||
case Esr::Ec::DA:
|
||||
_handle_data_abort();
|
||||
break;
|
||||
case Esr::Ec::WFI:
|
||||
_handle_wfi();
|
||||
return;
|
||||
case Esr::Ec::BRK:
|
||||
_handle_brk();
|
||||
return;
|
||||
default:
|
||||
throw Exception("Unknown trap: %x",
|
||||
Esr::Ec::get(_state.esr_el2));
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
void Cpu::_handle_irq()
|
||||
{
|
||||
enum { /* FIXME */ VT_TIMER_IRQ = 27 };
|
||||
switch (_state.irqs.last_irq) {
|
||||
case VT_TIMER_IRQ:
|
||||
_timer.handle_irq();
|
||||
break;
|
||||
default:
|
||||
_gic.handle_irq();
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
void Cpu::_handle_hyper_call()
|
||||
{
|
||||
switch(_state.r[0]) {
|
||||
case Psci::PSCI_VERSION:
|
||||
_state.r[0] = Psci::VERSION;
|
||||
return;
|
||||
case Psci::MIGRATE_INFO_TYPE:
|
||||
_state.r[0] = Psci::NOT_SUPPORTED;
|
||||
return;
|
||||
case Psci::PSCI_FEATURES:
|
||||
_state.r[0] = Psci::NOT_SUPPORTED;
|
||||
return;
|
||||
case Psci::CPU_ON:
|
||||
_vm.cpu((unsigned)_state.r[1], [&] (Cpu & cpu) {
|
||||
cpu.state().ip = _state.r[2];
|
||||
cpu.state().r[0] = _state.r[3];
|
||||
cpu.run();
|
||||
});
|
||||
_state.r[0] = Psci::SUCCESS;
|
||||
return;
|
||||
default:
|
||||
Genode::warning("unknown hypercall! ", cpu_id());
|
||||
dump();
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
void Cpu::_handle_data_abort()
|
||||
{
|
||||
_vm.bus().handle_memory_access(*this);
|
||||
_state.ip += sizeof(Genode::uint32_t);
|
||||
}
|
||||
|
||||
|
||||
void Cpu::_update_state()
|
||||
{
|
||||
if (!_gic.pending_irq()) return;
|
||||
|
||||
_active = true;
|
||||
_timer.cancel_timeout();
|
||||
}
|
||||
|
||||
unsigned Cpu::cpu_id() const { return _vcpu_id.id; }
|
||||
void Cpu::run() { _vm_session.run(_vcpu_id); }
|
||||
void Cpu::pause() { _vm_session.pause(_vcpu_id); }
|
||||
bool Cpu::active() const { return _active; }
|
||||
Cpu::State & Cpu::state() const { return _state; }
|
||||
Gic::Gicd_banked & Cpu::gic() { return _gic; }
|
||||
|
||||
|
||||
void Cpu::handle_exception()
|
||||
{
|
||||
/* check exception reason */
|
||||
switch (_state.exception_type) {
|
||||
case NO_EXCEPTION: break;
|
||||
case AARCH64_IRQ: _handle_irq(); break;
|
||||
case AARCH64_SYNC: _handle_sync(); break;
|
||||
default:
|
||||
throw Exception("Curious exception ",
|
||||
_state.exception_type, " occured");
|
||||
}
|
||||
_state.exception_type = NO_EXCEPTION;
|
||||
}
|
||||
|
||||
|
||||
void Cpu::dump()
|
||||
{
|
||||
using namespace Genode;
|
||||
|
||||
auto lambda = [] (addr_t exc) {
|
||||
switch (exc) {
|
||||
case AARCH64_SYNC: return "aarch64 sync";
|
||||
case AARCH64_IRQ: return "aarch64 irq";
|
||||
case AARCH64_FIQ: return "aarch64 fiq";
|
||||
case AARCH64_SERROR: return "aarch64 serr";
|
||||
case AARCH32_SYNC: return "aarch32 sync";
|
||||
case AARCH32_IRQ: return "aarch32 irq";
|
||||
case AARCH32_FIQ: return "aarch32 fiq";
|
||||
case AARCH32_SERROR: return "aarch32 serr";
|
||||
default: return "unknown";
|
||||
};
|
||||
};
|
||||
|
||||
log("VM state (", _active ? "active" : "inactive", ") :");
|
||||
for (unsigned i = 0; i < 31; i++) {
|
||||
log(" r", i, " = ",
|
||||
Hex(_state.r[i], Hex::PREFIX, Hex::PAD));
|
||||
}
|
||||
log(" sp = ", Hex(_state.sp, Hex::PREFIX, Hex::PAD));
|
||||
log(" ip = ", Hex(_state.ip, Hex::PREFIX, Hex::PAD));
|
||||
log(" sp_el1 = ", Hex(_state.sp_el1, Hex::PREFIX, Hex::PAD));
|
||||
log(" elr_el1 = ", Hex(_state.elr_el1, Hex::PREFIX, Hex::PAD));
|
||||
log(" pstate = ", Hex(_state.pstate, Hex::PREFIX, Hex::PAD));
|
||||
log(" exception = ", _state.exception_type, " (",
|
||||
lambda(_state.exception_type), ")");
|
||||
log(" esr_el2 = ", Hex(_state.esr_el2, Hex::PREFIX, Hex::PAD));
|
||||
_timer.dump();
|
||||
}
|
||||
|
||||
|
||||
void Cpu::recall()
|
||||
{
|
||||
Genode::Signal_transmitter(_vm_handler).submit();
|
||||
};
|
||||
|
||||
|
||||
Cpu::Cpu(Vm & vm,
|
||||
Genode::Vm_connection & vm_session,
|
||||
Mmio_bus & bus,
|
||||
@ -356,15 +213,7 @@ Cpu::Cpu(Vm & vm,
|
||||
Genode::Env & env,
|
||||
Genode::Heap & heap,
|
||||
Genode::Entrypoint & ep)
|
||||
: _vm(vm),
|
||||
_vm_session(vm_session),
|
||||
_heap(heap),
|
||||
_vm_handler(*this, ep, *this, &Cpu::_handle_nothing),
|
||||
_vcpu_id(_vm_session.with_upgrade([&]() {
|
||||
return _vm_session.create_vcpu(heap, env, _vm_handler);
|
||||
})),
|
||||
_state(*((State*)env.rm().attach(_vm_session.cpu_state(_vcpu_id)))),
|
||||
// op0, crn, op1, crm, op2, writeable, reset value
|
||||
: Cpu_base(vm, vm_session, bus, gic, env, heap, ep),
|
||||
_sr_id_aa64afr0_el1 (3, 0, 0, 5, 4, "ID_AA64AFR0_EL1", false, 0x0, _reg_tree),
|
||||
_sr_id_aa64afr1_el1 (3, 0, 0, 5, 5, "ID_AA64AFR1_EL1", false, 0x0, _reg_tree),
|
||||
_sr_id_aa64dfr0_el1 (3, 0, 0, 5, 0, "ID_AA64DFR0_EL1", false, 0x6, _reg_tree),
|
||||
@ -379,13 +228,10 @@ Cpu::Cpu(Vm & vm,
|
||||
_sr_id_aa64zfr0_el1 (3, 0, 0, 4, 4, "ID_AA64ZFR0_EL1", false, 0x0, _reg_tree),
|
||||
_sr_aidr_el1 (3, 0, 1, 0, 7, "AIDR_EL1", false, 0x0, _reg_tree),
|
||||
_sr_revidr_el1 (3, 0, 0, 0, 6, "REVIDR_EL1", false, 0x0, _reg_tree),
|
||||
|
||||
_sr_clidr_el1 (3, 0, 1, 0, 1, "CLIDR_EL1", false, _state.clidr_el1, _reg_tree),
|
||||
_sr_csselr_el1 (3, 0, 2, 0, 0, "CSSELR_EL1", true, 0x0, _reg_tree),
|
||||
_sr_ctr_el0 (_reg_tree),
|
||||
_sr_ccsidr_el1 (_sr_csselr_el1, _state, _reg_tree),
|
||||
|
||||
//_sr_pmccfiltr_el0 (3, 14, 3, 15, 7, "PMCCFILTR_EL0", true, 0x0, _reg_tree),
|
||||
_sr_pmuserenr_el0 (3, 9, 3, 14, 0, "PMUSEREN_EL0", true, 0x0, _reg_tree),
|
||||
_sr_dbgbcr0 (2, 0, 0, 0, 5, "DBGBCR_EL1", true, 0x0, _reg_tree),
|
||||
_sr_dbgbvr0 (2, 0, 0, 0, 4, "DBGBVR_EL1", true, 0x0, _reg_tree),
|
||||
@ -394,9 +240,7 @@ Cpu::Cpu(Vm & vm,
|
||||
_sr_mdscr (2, 0, 0, 2, 2, "MDSCR_EL1", true, 0x0, _reg_tree),
|
||||
_sr_osdlr (2, 1, 0, 3, 4, "OSDLR_EL1", true, 0x0, _reg_tree),
|
||||
_sr_oslar (2, 1, 0, 0, 4, "OSLAR_EL1", true, 0x0, _reg_tree),
|
||||
_sr_sgi1r_el1 (_reg_tree, vm),
|
||||
_gic(*this, gic, bus),
|
||||
_timer(env, ep, _gic.irq(27), *this)
|
||||
_sr_sgi1r_el1 (_reg_tree, vm)
|
||||
{
|
||||
_state.pstate = 0b1111000101; /* el1 mode and IRQs disabled */
|
||||
_state.vmpidr_el2 = cpu_id();
|
||||
|
@ -14,41 +14,14 @@
|
||||
#ifndef _SRC__SERVER__VMM__CPU_H_
|
||||
#define _SRC__SERVER__VMM__CPU_H_
|
||||
|
||||
#include <exception.h>
|
||||
#include <generic_timer.h>
|
||||
#include <cpu_base.h>
|
||||
|
||||
#include <base/env.h>
|
||||
#include <base/heap.h>
|
||||
#include <cpu/vm_state_virtualization.h>
|
||||
#include <util/mmio.h>
|
||||
#include <vm_session/connection.h>
|
||||
namespace Vmm { class Cpu; }
|
||||
|
||||
namespace Vmm {
|
||||
class Vm;
|
||||
class Cpu;
|
||||
Genode::Lock & lock();
|
||||
}
|
||||
|
||||
class Vmm::Cpu
|
||||
class Vmm::Cpu : public Vmm::Cpu_base
|
||||
{
|
||||
public:
|
||||
|
||||
using State = Genode::Vm_state;
|
||||
|
||||
struct Esr : Genode::Register<32>
|
||||
{
|
||||
struct Ec : Bitfield<26, 6>
|
||||
{
|
||||
enum {
|
||||
WFI = 0x1,
|
||||
HVC = 0x16,
|
||||
MRS_MSR = 0x18,
|
||||
DA = 0x24,
|
||||
BRK = 0x3c
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
Cpu(Vm & vm,
|
||||
Genode::Vm_connection & vm_session,
|
||||
Mmio_bus & bus,
|
||||
@ -57,58 +30,6 @@ class Vmm::Cpu
|
||||
Genode::Heap & heap,
|
||||
Genode::Entrypoint & ep);
|
||||
|
||||
unsigned cpu_id() const;
|
||||
void run();
|
||||
void pause();
|
||||
bool active() const;
|
||||
State & state() const;
|
||||
Gic::Gicd_banked & gic();
|
||||
void dump();
|
||||
void handle_exception();
|
||||
void recall();
|
||||
|
||||
template <typename FUNC>
|
||||
void handle_signal(FUNC handler)
|
||||
{
|
||||
if (active()) {
|
||||
pause();
|
||||
handle_exception();
|
||||
}
|
||||
|
||||
handler();
|
||||
_update_state();
|
||||
if (active()) run();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
struct Signal_handler : Genode::Vm_handler<Signal_handler<T>>
|
||||
{
|
||||
using Base = Genode::Vm_handler<Signal_handler<T>>;
|
||||
|
||||
Cpu & cpu;
|
||||
T & obj;
|
||||
void (T::*member)();
|
||||
|
||||
void handle()
|
||||
{
|
||||
try {
|
||||
cpu.handle_signal([this] () { (obj.*member)(); });
|
||||
} catch(Exception &e) {
|
||||
Genode::error(e);
|
||||
cpu.dump();
|
||||
}
|
||||
}
|
||||
|
||||
Signal_handler(Cpu & cpu,
|
||||
Genode::Entrypoint & ep,
|
||||
T & o,
|
||||
void (T::*f)())
|
||||
: Base(ep, *this, &Signal_handler::handle),
|
||||
cpu(cpu), obj(o), member(f) {}
|
||||
};
|
||||
|
||||
private:
|
||||
|
||||
enum Exception_type {
|
||||
AARCH64_SYNC = 0x400,
|
||||
AARCH64_IRQ = 0x480,
|
||||
@ -121,72 +42,7 @@ class Vmm::Cpu
|
||||
NO_EXCEPTION = 0xffff
|
||||
};
|
||||
|
||||
class System_register : public Genode::Avl_node<System_register>
|
||||
{
|
||||
private:
|
||||
|
||||
const Esr::access_t _encoding;
|
||||
const char *_name;
|
||||
const bool _writeable;
|
||||
Genode::uint64_t _value;
|
||||
|
||||
public:
|
||||
|
||||
struct Iss : Esr
|
||||
{
|
||||
struct Direction : Bitfield<0, 1> {};
|
||||
struct Crm : Bitfield<1, 4> {};
|
||||
struct Register : Bitfield<5, 5> {};
|
||||
struct Crn : Bitfield<10, 4> {};
|
||||
struct Opcode1 : Bitfield<14, 3> {};
|
||||
struct Opcode2 : Bitfield<17, 3> {};
|
||||
struct Opcode0 : Bitfield<20, 2> {};
|
||||
|
||||
static access_t value(unsigned op0,
|
||||
unsigned crn,
|
||||
unsigned op1,
|
||||
unsigned crm,
|
||||
unsigned op2);
|
||||
|
||||
static access_t mask_encoding(access_t v);
|
||||
};
|
||||
|
||||
System_register(unsigned op0,
|
||||
unsigned crn,
|
||||
unsigned op1,
|
||||
unsigned crm,
|
||||
unsigned op2,
|
||||
const char * name,
|
||||
bool writeable,
|
||||
Genode::addr_t v,
|
||||
Genode::Avl_tree<System_register> & tree);
|
||||
|
||||
const char * name() const { return _name; }
|
||||
const bool writeable() const { return _writeable; }
|
||||
|
||||
System_register * find_by_encoding(Iss::access_t e)
|
||||
{
|
||||
if (e == _encoding) return this;
|
||||
|
||||
System_register * r =
|
||||
Avl_node<System_register>::child(e > _encoding);
|
||||
return r ? r->find_by_encoding(e) : nullptr;
|
||||
}
|
||||
|
||||
virtual void write(Genode::addr_t v) {
|
||||
_value = (Genode::addr_t)v; }
|
||||
|
||||
virtual Genode::addr_t read() const {
|
||||
return (Genode::addr_t)(_value); }
|
||||
|
||||
|
||||
/************************
|
||||
** Avl node interface **
|
||||
************************/
|
||||
|
||||
bool higher(System_register *r) {
|
||||
return (r->_encoding > _encoding); }
|
||||
};
|
||||
private:
|
||||
|
||||
class Id_aa64pfr0 : public System_register,
|
||||
public Genode::Register<64>
|
||||
@ -252,15 +108,6 @@ class Vmm::Cpu
|
||||
virtual void write(Genode::addr_t v) override;
|
||||
};
|
||||
|
||||
bool _active { true };
|
||||
Vm & _vm;
|
||||
Genode::Vm_connection & _vm_session;
|
||||
Genode::Heap & _heap;
|
||||
Signal_handler<Cpu> _vm_handler;
|
||||
Genode::Vm_session::Vcpu_id _vcpu_id;
|
||||
State & _state;
|
||||
Genode::Avl_tree<System_register> _reg_tree;
|
||||
|
||||
/******************************
|
||||
** Identification registers **
|
||||
******************************/
|
||||
@ -308,22 +155,10 @@ class Vmm::Cpu
|
||||
System_register _sr_oslar;
|
||||
|
||||
/***********************
|
||||
** Local peripherals **
|
||||
** GIC cpu interface **
|
||||
***********************/
|
||||
|
||||
Icc_sgi1r_el1 _sr_sgi1r_el1;
|
||||
Gic::Gicd_banked _gic;
|
||||
Generic_timer _timer;
|
||||
|
||||
void _handle_nothing() {}
|
||||
bool _handle_sys_reg();
|
||||
void _handle_brk();
|
||||
void _handle_wfi();
|
||||
void _handle_sync();
|
||||
void _handle_irq();
|
||||
void _handle_data_abort();
|
||||
void _handle_hyper_call();
|
||||
void _update_state();
|
||||
};
|
||||
|
||||
#endif /* _SRC__SERVER__VMM__CPU_H_ */
|
||||
|
@ -28,26 +28,6 @@ Genode::uint64_t Generic_timer::_ticks_per_ms()
|
||||
}
|
||||
|
||||
|
||||
bool Generic_timer::_enabled() {
|
||||
return Ctrl::Enabled::get(_cpu.state().timer.control); }
|
||||
|
||||
|
||||
bool Generic_timer::_masked() {
|
||||
return Ctrl::Imask::get(_cpu.state().timer.control); }
|
||||
|
||||
|
||||
bool Generic_timer::_pending() {
|
||||
return Ctrl::Istatus::get(_cpu.state().timer.control); }
|
||||
|
||||
|
||||
void Generic_timer::_handle_timeout(Genode::Duration)
|
||||
{
|
||||
_cpu.handle_signal([this] (void) {
|
||||
if (_enabled() && !_masked()) handle_irq();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Genode::uint64_t Generic_timer::_usecs_left()
|
||||
{
|
||||
Genode::uint64_t count;
|
||||
@ -57,60 +37,3 @@ Genode::uint64_t Generic_timer::_usecs_left()
|
||||
return Genode::timer_ticks_to_us(_cpu.state().timer.compare - count,
|
||||
_ticks_per_ms());
|
||||
}
|
||||
|
||||
|
||||
Generic_timer::Generic_timer(Genode::Env & env,
|
||||
Genode::Entrypoint & ep,
|
||||
Gic::Irq & irq,
|
||||
Cpu & cpu)
|
||||
: _timer(env, ep),
|
||||
_timeout(_timer, *this, &Generic_timer::_handle_timeout),
|
||||
_irq(irq),
|
||||
_cpu(cpu)
|
||||
{
|
||||
_cpu.state().timer.irq = true;
|
||||
_irq.handler(*this);
|
||||
}
|
||||
|
||||
|
||||
void Generic_timer::schedule_timeout()
|
||||
{
|
||||
if (_pending()) {
|
||||
handle_irq();
|
||||
return;
|
||||
}
|
||||
|
||||
if (_enabled()) {
|
||||
if (_usecs_left()) {
|
||||
_timeout.schedule(Genode::Microseconds(_usecs_left()));
|
||||
} else _handle_timeout(Genode::Duration(Genode::Microseconds(0)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Generic_timer::cancel_timeout()
|
||||
{
|
||||
if (_timeout.scheduled()) _timeout.discard();
|
||||
}
|
||||
|
||||
|
||||
void Generic_timer::handle_irq()
|
||||
{
|
||||
_irq.assert();
|
||||
_cpu.state().timer.irq = false;
|
||||
}
|
||||
|
||||
|
||||
void Generic_timer::eoi()
|
||||
{
|
||||
_cpu.state().timer.irq = true;
|
||||
};
|
||||
|
||||
|
||||
void Generic_timer::dump()
|
||||
{
|
||||
using namespace Genode;
|
||||
|
||||
log(" timer.ctl = ", Hex(_cpu.state().timer.control, Hex::PREFIX, Hex::PAD));
|
||||
log(" timer.cmp = ", Hex(_cpu.state().timer.compare, Hex::PREFIX, Hex::PAD));
|
||||
}
|
||||
|
@ -1,15 +1,19 @@
|
||||
TARGET = vmm
|
||||
REQUIRES = hw arm_v8
|
||||
LIBS = base
|
||||
SRC_CC += spec/arm_v8/generic_timer.cc
|
||||
SRC_CC += address_space.cc
|
||||
SRC_CC += cpu.cc
|
||||
SRC_CC += cpu_base.cc
|
||||
SRC_CC += generic_timer.cc
|
||||
SRC_CC += gicv2.cc
|
||||
SRC_CC += gic.cc
|
||||
SRC_CC += main.cc
|
||||
SRC_CC += mmio.cc
|
||||
SRC_CC += pl011.cc
|
||||
SRC_CC += virtio_device.cc
|
||||
SRC_CC += vm.cc
|
||||
INC_DIR += $(PRG_DIR)
|
||||
INC_DIR += $(PRG_DIR)/../.. $(PRG_DIR)
|
||||
|
||||
vpath %.cc $(PRG_DIR)/../..
|
||||
|
||||
CC_CXX_WARN_STRICT :=
|
||||
|
@ -48,9 +48,12 @@ struct Vmm::Virtio_queue_data
|
||||
uint32_t ready { 0 };
|
||||
bool tx { false };
|
||||
|
||||
addr_t descr() const { return ((addr_t)descr_high << 32) | descr_low; }
|
||||
addr_t driver() const { return ((addr_t)driver_high << 32) | driver_low; }
|
||||
addr_t device() const { return ((addr_t)device_high << 32) | device_low; }
|
||||
uint64_t descr() const {
|
||||
return ((uint64_t)descr_high << 32) | descr_low; }
|
||||
uint64_t driver() const {
|
||||
return ((uint64_t)driver_high << 32) | driver_low; }
|
||||
uint64_t device() const {
|
||||
return ((uint64_t)device_high << 32) | device_low; }
|
||||
|
||||
enum { MAX_QUEUE_SIZE = 1 << 15 };
|
||||
};
|
||||
@ -83,10 +86,10 @@ class Vmm::Virtio_descriptor : Genode::Mmio
|
||||
return Virtio_descriptor(base() + (size() * idx));
|
||||
}
|
||||
|
||||
addr_t address() const { return read<Address>(); }
|
||||
size_t length () const { return read<Length>(); }
|
||||
uint16_t flags() const { return read<Flags>(); }
|
||||
uint16_t next() const { return read<Next>(); }
|
||||
uint64_t address() const { return read<Address>(); }
|
||||
size_t length () const { return read<Length>(); }
|
||||
uint16_t flags() const { return read<Flags>(); }
|
||||
uint16_t next() const { return read<Next>(); }
|
||||
};
|
||||
|
||||
|
||||
@ -173,7 +176,7 @@ class Vmm::Virtio_queue
|
||||
id %= _length;
|
||||
|
||||
Virtio_descriptor descr = _descr.index(id);
|
||||
addr_t address = descr.address();
|
||||
uint64_t address = descr.address();
|
||||
size_t length = descr.length();
|
||||
if (!address || !length) break;
|
||||
|
@ -48,14 +48,16 @@ Vmm::Cpu & Vm::boot_cpu()
|
||||
|
||||
Vm::Vm(Genode::Env & env)
|
||||
: _env(env),
|
||||
_gic("Gicv3", 0x8000000, 0x10000, _bus, env),
|
||||
_uart("Pl011", 0x9000000, 0x1000, 33, boot_cpu(), _bus, env),
|
||||
_virtio_console("HVC", 0xa000000, 0x200, 48, boot_cpu(), _bus, _ram, env),
|
||||
_virtio_net("Net", 0xa000200, 0x200, 49, boot_cpu(), _bus, _ram, env)
|
||||
_gic("Gicv3", GICD_MMIO_START, GICD_MMIO_SIZE,
|
||||
MAX_CPUS, GIC_VERSION, _vm, _bus, env),
|
||||
_uart("Pl011", PL011_MMIO_START, PL011_MMIO_SIZE,
|
||||
PL011_IRQ, boot_cpu(), _bus, env),
|
||||
_virtio_console("HVC", VIRTIO_CONSOLE_MMIO_START, VIRTIO_CONSOLE_MMIO_SIZE,
|
||||
VIRTIO_CONSOLE_IRQ, boot_cpu(), _bus, _ram, env),
|
||||
_virtio_net("Net", VIRTIO_NET_MMIO_START, VIRTIO_NET_MMIO_SIZE,
|
||||
VIRTIO_NET_IRQ, boot_cpu(), _bus, _ram, env)
|
||||
{
|
||||
_vm.attach(_vm_ram.cap(), RAM_ADDRESS);
|
||||
|
||||
/* FIXME extend for gicv2 by: _vm.attach_pic(0x8010000); */
|
||||
_vm.attach(_vm_ram.cap(), RAM_START);
|
||||
|
||||
_load_kernel();
|
||||
_load_dtb();
|
||||
@ -71,7 +73,7 @@ Vm::Vm(Genode::Env & env)
|
||||
Genode::log("Start virtual machine ...");
|
||||
|
||||
Cpu & cpu = boot_cpu();
|
||||
cpu.state().ip = _ram.base() + KERNEL_OFFSET;
|
||||
cpu.state().r[0] = _ram.base() + DTB_OFFSET;
|
||||
cpu.initialize_boot(_ram.base() + KERNEL_OFFSET,
|
||||
_ram.base() + DTB_OFFSET);
|
||||
cpu.run();
|
||||
};
|
@ -14,6 +14,7 @@
|
||||
#ifndef _SRC__SERVER__VMM__VM_H_
|
||||
#define _SRC__SERVER__VMM__VM_H_
|
||||
|
||||
#include <board.h>
|
||||
#include <ram.h>
|
||||
#include <exception.h>
|
||||
#include <cpu.h>
|
||||
@ -34,15 +35,7 @@ class Vmm::Vm
|
||||
|
||||
using Ep = Genode::Entrypoint;
|
||||
|
||||
enum {
|
||||
RAM_ADDRESS = 0x40000000,
|
||||
RAM_SIZE = 128 * 1024 *1024,
|
||||
KERNEL_OFFSET = 0x80000,
|
||||
INITRD_OFFSET = 32 * 1024 * 1024,
|
||||
DTB_OFFSET = 64 * 1024 * 1024,
|
||||
MAX_CPUS = 1,
|
||||
STACK_SIZE = sizeof(unsigned long) * 2048,
|
||||
};
|
||||
enum { STACK_SIZE = sizeof(unsigned long) * 2048, };
|
||||
|
||||
Genode::Env & _env;
|
||||
Genode::Vm_connection _vm { _env };
|
||||
@ -51,7 +44,7 @@ class Vmm::Vm
|
||||
Genode::Attached_rom_dataspace _initrd_rom { _env, "initrd" };
|
||||
Genode::Attached_ram_dataspace _vm_ram { _env.ram(), _env.rm(),
|
||||
RAM_SIZE, Genode::CACHED };
|
||||
Ram _ram { RAM_ADDRESS, RAM_SIZE,
|
||||
Ram _ram { RAM_START, RAM_SIZE,
|
||||
(Genode::addr_t)_vm_ram.local_addr<void>()};
|
||||
Genode::Heap _heap { _env.ram(), _env.rm() };
|
||||
Mmio_bus _bus;
|
Loading…
Reference in New Issue
Block a user