base-hw: make ARMv7/v8 hypervisor smp ready

* Introduce hypervisor-stack per CPU
* Introduce host world context per CPU
* Mark EL2 translation table memory as inner shareable
* The VMID is not bound to a single VCPU, but to the Vm_session as a whole
* Set affinity of the VCPU accordingly
* Add VMPIDR to VM state

Ref #3926
This commit is contained in:
Stefan Kalkowski 2020-09-11 14:43:29 +02:00 committed by Christian Helmuth
parent 1d826a2c48
commit 7298b00013
22 changed files with 350 additions and 321 deletions

View File

@ -50,6 +50,7 @@ struct Genode::Vm_state : Genode::Cpu_state_modes
Genode::uint32_t tls2 { 0 };
Genode::uint32_t tls3 { 0 };
Genode::uint32_t cpacr { 0 };
Genode::uint32_t vmpidr { 0 };
/**
* Fpu registers

View File

@ -58,7 +58,8 @@ static inline void prepare_non_secure_world()
}
static inline void prepare_hypervisor(Cpu::Ttbr::access_t const ttbr)
static inline void prepare_hypervisor(Cpu::Ttbr::access_t const ttbr,
unsigned cpu_id)
{
using namespace Hw::Mm;
@ -80,7 +81,7 @@ static inline void prepare_hypervisor(Cpu::Ttbr::access_t const ttbr)
/* set hypervisor exception vector */
Cpu::Vbar_el2::write(el2_addr(hypervisor_exception_vector().base));
Genode::addr_t const stack_el2 = el2_addr(hypervisor_stack().base +
hypervisor_stack().size);
(cpu_id+1) * 0x1000);
/* set hypervisor's translation table */
Cpu::Ttbr0_el2::write(ttbr);
@ -89,7 +90,7 @@ static inline void prepare_hypervisor(Cpu::Ttbr::access_t const ttbr)
Cpu::Tcr_el2::T0sz::set(tcr_el2, 25);
Cpu::Tcr_el2::Irgn0::set(tcr_el2, 1);
Cpu::Tcr_el2::Orgn0::set(tcr_el2, 1);
Cpu::Tcr_el2::Sh0::set(tcr_el2, 0b10);
Cpu::Tcr_el2::Sh0::set(tcr_el2, 0b11);
/* prepare MMU usage by hypervisor code */
Cpu::Tcr_el2::write(tcr_el2);
@ -141,6 +142,8 @@ unsigned Bootstrap::Platform::enable_mmu()
bool primary = primary_cpu;
if (primary) primary_cpu = false;
unsigned cpu_id = (Cpu::Mpidr::read() & 0xff);
Cpu::Ttbr::access_t ttbr =
Cpu::Ttbr::Baddr::masked((Genode::addr_t)core_pd->table_base);
@ -152,7 +155,7 @@ unsigned Bootstrap::Platform::enable_mmu()
prepare_non_secure_world();
} else {
::Board::Pic pic __attribute__((unused)) {};
prepare_hypervisor(ttbr);
prepare_hypervisor(ttbr, cpu_id);
}
}
@ -201,5 +204,5 @@ unsigned Bootstrap::Platform::enable_mmu()
Cpu::Sctlr::Uct::set(sctlr, 1);
Cpu::Sctlr_el1::write(sctlr);
return (Cpu::Mpidr::read() & 0xff);
return cpu_id;
}

View File

@ -12,11 +12,11 @@
*/
#include <platform.h>
#include <hw/memory_map.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 */
extern "C" void * _start_setup_stack; /* entrypoint for non-boot CPUs */
using namespace Board;
@ -161,7 +161,7 @@ Bootstrap::Platform::Board::Board()
}
static inline void switch_to_supervisor_mode()
static inline void switch_to_supervisor_mode(unsigned cpu_id)
{
using Cpsr = Hw::Arm_cpu::Psr;
@ -170,6 +170,9 @@ static inline void switch_to_supervisor_mode()
Cpsr::F::set(cpsr, 1);
Cpsr::I::set(cpsr, 1);
Genode::addr_t const stack = Hw::Mm::hypervisor_stack().base +
(cpu_id+1) * 0x1000;
asm volatile (
"msr sp_svc, sp \n" /* copy current mode's sp */
"msr lr_svc, lr \n" /* copy current mode's lr */
@ -178,7 +181,7 @@ static inline void switch_to_supervisor_mode()
"msr spsr_cxfs, %[cpsr] \n" /* set psr for supervisor mode */
"adr lr, 1f \n" /* load exception return address */
"eret \n" /* exception return */
"1:":: [cpsr] "r" (cpsr), [stack] "r" (&hyp_mode_stack));
"1:":: [cpsr] "r" (cpsr), [stack] "r" (stack));
}
@ -186,13 +189,14 @@ unsigned Bootstrap::Platform::enable_mmu()
{
static volatile bool primary_cpu = true;
static unsigned long timer_freq = Cpu::Cntfrq::read();
unsigned cpu = Cpu::Mpidr::Aff_0::get(Cpu::Mpidr::read());
/* locally initialize interrupt controller */
::Board::Pic pic { };
prepare_nonsecure_world(timer_freq);
prepare_hypervisor((addr_t)core_pd->table_base);
switch_to_supervisor_mode();
switch_to_supervisor_mode(cpu);
Cpu::Sctlr::init();
Cpu::Cpsr::init();
@ -206,7 +210,7 @@ unsigned Bootstrap::Platform::enable_mmu()
Cpu::enable_mmu_and_caches((Genode::addr_t)core_pd->table_base);
return Cpu::Mpidr::Aff_0::get(Cpu::Mpidr::read());
return cpu;
}

View File

@ -15,7 +15,6 @@
#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 */
using namespace Board;
@ -31,7 +30,7 @@ Bootstrap::Platform::Board::Board()
Cpu_mmio::IRQ_CONTROLLER_VT_CTRL_SIZE }) {}
static inline void switch_to_supervisor_mode()
static inline void switch_to_supervisor_mode(unsigned cpu_id)
{
using Cpsr = Hw::Arm_cpu::Psr;
@ -40,6 +39,9 @@ static inline void switch_to_supervisor_mode()
Cpsr::F::set(cpsr, 1);
Cpsr::I::set(cpsr, 1);
Genode::addr_t const stack = Hw::Mm::hypervisor_stack().base +
(cpu_id+1) * 0x1000;
asm volatile (
"msr sp_svc, sp \n" /* copy current mode's sp */
"msr lr_svc, lr \n" /* copy current mode's lr */
@ -48,13 +50,14 @@ static inline void switch_to_supervisor_mode()
"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));
"1:":: [cpsr] "r" (cpsr), [stack] "r" (stack));
}
unsigned Bootstrap::Platform::enable_mmu()
{
static volatile bool primary_cpu = true;
unsigned cpu = Cpu::Mpidr::Aff_0::get(Cpu::Mpidr::read());
/* locally initialize interrupt controller */
::Board::Pic pic { };
@ -67,14 +70,14 @@ unsigned Bootstrap::Platform::enable_mmu()
}
prepare_hypervisor((addr_t)core_pd->table_base);
switch_to_supervisor_mode();
switch_to_supervisor_mode(cpu);
Cpu::Sctlr::init();
Cpu::Cpsr::init();
Cpu::enable_mmu_and_caches((Genode::addr_t)core_pd->table_base);
return Cpu::Mpidr::Aff_0::get(Cpu::Mpidr::read());
return cpu;
}

View File

@ -35,6 +35,14 @@ namespace Kernel
class Kernel::Vm : private Kernel::Object, public Cpu_job
{
public:
struct Identity
{
unsigned const id;
void * const table;
};
private:
using State = Board::Vm_state;
@ -48,10 +56,9 @@ class Kernel::Vm : private Kernel::Object, public Cpu_job
enum Scheduler_state { ACTIVE, INACTIVE };
Object _kernel_object { *this };
unsigned _id = 0;
State & _state;
Signal_context & _context;
void * const _table;
Identity const _id;
Scheduler_state _scheduled = INACTIVE;
Board::Vcpu_context _vcpu_context;
@ -60,16 +67,14 @@ class Kernel::Vm : private Kernel::Object, public Cpu_job
/**
* Constructor
*
* \param cpu cpu affinity
* \param state initial CPU state
* \param context signal for VM exceptions other than interrupts
* \param table translation table for guest to host physical memory
*/
Vm(unsigned cpu,
State & state,
Signal_context & context,
void * const table);
~Vm();
Identity & id);
/**
* Inject an interrupt to this VM
@ -85,8 +90,7 @@ class Kernel::Vm : private Kernel::Object, public Cpu_job
* \param dst memory donation for the VM object
* \param state location of the CPU state of the VM
* \param signal_context_id kernel name of the signal context for VM events
* \param table guest-physical to host-physical translation
* table pointer
* \param id VM identity
*
* \retval cap id when successful, otherwise invalid cap id
*/
@ -94,10 +98,10 @@ class Kernel::Vm : private Kernel::Object, public Cpu_job
unsigned cpu,
void * const state,
capid_t const signal_context_id,
void * const table)
Identity & id)
{
return call(call_id_new_vm(), (Call_arg)&vm, (Call_arg)cpu,
(Call_arg)state, (Call_arg)table, signal_context_id);
(Call_arg)state, (Call_arg)&id, signal_context_id);
}
/**

View File

@ -25,7 +25,7 @@ void Kernel::Thread::_call_new_vm()
}
_call_new<Vm>((unsigned)user_arg_2(), *(Board::Vm_state*)user_arg_3(),
*context, (void*)user_arg_4());
*context, *(Vm::Identity*)user_arg_4());
}

View File

@ -80,6 +80,22 @@ void * Vm_session_component::_alloc_table()
}
using Vmid_allocator = Genode::Bit_allocator<256>;
static Vmid_allocator &alloc()
{
static Vmid_allocator * allocator = nullptr;
if (!allocator) {
allocator = unmanaged_singleton<Vmid_allocator>();
/* reserve VM ID 0 for the hypervisor */
unsigned id = allocator->alloc();
assert (id == 0);
}
return *allocator;
}
Vm_session_component::Vm_session_component(Rpc_entrypoint &ds_ep,
Resources resources,
Label const &,
@ -96,7 +112,8 @@ Vm_session_component::Vm_session_component(Rpc_entrypoint &ds_ep,
_region_map(region_map),
_table(*construct_at<Board::Vm_page_table>(_alloc_table())),
_table_array(*(new (cma()) Board::Vm_page_table_array([this] (void * virt) {
return (addr_t)cma().phys_addr(virt);})))
return (addr_t)cma().phys_addr(virt);}))),
_id({(unsigned)alloc().alloc(), cma().phys_addr(&_table)})
{
/* configure managed VM area */
_map.add_range(0, 0UL - 0x1000);
@ -117,7 +134,7 @@ Vm_session_component::~Vm_session_component()
}
/* free region in allocator */
for (unsigned i = 0; i < _id_alloc; i++) {
for (unsigned i = 0; i < _vcpu_id_alloc; i++) {
Vcpu & vcpu = _vcpus[i];
if (vcpu.ds_cap.valid()) {
_region_map.detach(vcpu.ds_addr);
@ -128,4 +145,5 @@ Vm_session_component::~Vm_session_component()
/* free guest-to-host page tables */
destroy(platform().core_mem_alloc(), &_table);
destroy(platform().core_mem_alloc(), &_table_array);
alloc().free(_id.id);
}

View File

@ -23,21 +23,18 @@ using namespace Kernel;
Kernel::Vm::Vm(unsigned,
Genode::Vm_state & state,
Kernel::Signal_context & context,
void * const)
Identity & id)
: Kernel::Object { *this },
Cpu_job(Cpu_priority::MIN, 0),
_state(state),
_context(context),
_table(0),
_vcpu_context(cpu_pool().primary_cpu())
Cpu_job(Cpu_priority::MIN, 0),
_state(state),
_context(context),
_id(id),
_vcpu_context(cpu_pool().primary_cpu())
{
affinity(cpu_pool().primary_cpu());
}
Kernel::Vm::~Vm() {}
void Vm::exception(Cpu & cpu)
{
switch(_state.cpu_exception) {

View File

@ -48,6 +48,9 @@ void * Vm_session_component::_alloc_table()
}
static unsigned id_alloc = 0;
Vm_session_component::Vm_session_component(Rpc_entrypoint &ep,
Resources resources,
Label const &,
@ -55,15 +58,21 @@ Vm_session_component::Vm_session_component(Rpc_entrypoint &ep,
Ram_allocator &ram_alloc,
Region_map &region_map,
unsigned, Trace::Source_registry &)
:
Ram_quota_guard(resources.ram_quota),
Cap_quota_guard(resources.cap_quota),
_ep(ep),
_constrained_md_ram_alloc(ram_alloc, _ram_quota_guard(), _cap_quota_guard()),
_sliced_heap(_constrained_md_ram_alloc, region_map),
_region_map(region_map),
: Ram_quota_guard(resources.ram_quota),
Cap_quota_guard(resources.cap_quota),
_ep(ep),
_constrained_md_ram_alloc(ram_alloc, _ram_quota_guard(), _cap_quota_guard()),
_sliced_heap(_constrained_md_ram_alloc, region_map),
_region_map(region_map),
_table(*construct_at<Board::Vm_page_table>(_alloc_table())),
_table_array(dummy_array()) { }
_table_array(dummy_array()),
_id({id_alloc++, nullptr})
{
if (_id.id) {
error("Only one TrustZone VM available!");
throw Service_denied();
}
}
Vm_session_component::~Vm_session_component()
@ -79,11 +88,13 @@ Vm_session_component::~Vm_session_component()
}
/* free region in allocator */
for (unsigned i = 0; i < _id_alloc; i++) {
for (unsigned i = 0; i < _vcpu_id_alloc; i++) {
Vcpu & vcpu = _vcpus[i];
if (vcpu.ds_cap.valid()) {
_region_map.detach(vcpu.ds_addr);
_constrained_md_ram_alloc.free(vcpu.ds_cap);
}
}
id_alloc--;
}

View File

@ -11,34 +11,12 @@
* under the terms of the GNU Affero General Public License version 3.
*/
.set USR_MODE, 16
.set FIQ_MODE, 17
.set IRQ_MODE, 18
.set SVC_MODE, 19
.set ABT_MODE, 23
.set UND_MODE, 27
.set SYS_MODE, 31
.macro _save_bank mode
cps #\mode /* switch to given mode */
mrs r1, spsr /* store mode-specific spsr */
stmia r0!, {r1,sp,lr} /* store mode-specific sp and lr */
.endm /* _save_bank mode */
.macro _restore_bank mode
cps #\mode /* switch to given mode */
ldmia r0!, {r1,sp,lr} /* load mode-specific sp, lr, and spsr into r1 */
msr spsr_cxfs, r1 /* load mode-specific spsr */
.endm
.macro _vm_exit exception_type
str r0, [sp]
push { r0 }
mrc p15, 4, r0, c1, c1, 0 /* read HCR register */
tst r0, #1 /* check VM bit */
ldreq r0, [sp]
beq _host_to_vm
mov r0, #\exception_type
str r0, [sp, #17*4]
b _vm_to_host
.endm /* _vm_exit */
@ -70,146 +48,176 @@ _vt_irq_entry: _vm_exit 6
_vt_trp_entry: _vm_exit 8
_host_to_vm:
msr elr_hyp, r2
msr spsr_cxfs, r3 /* load cpsr */
mcrr p15, 6, r5, r6, c2 /* write VTTBR */
mcr p15, 0, r7, c1, c0, 0 /* write SCTRL */
mcr p15, 4, r8, c1, c1, 3 /* write HSTR */
mcr p15, 4, r9, c1, c1, 0 /* write HCR register */
mcr p15, 0, r12, c2, c0, 2 /* write TTBRC */
sub sp, r0, #46*4
ldm r0!, {r1-r12}
mcr p15, 0, r1, c2, c0, 0 /* write TTBR0 */
mcr p15, 0, r2, c2, c0, 1 /* write TTBR1 */
mcr p15, 0, r3, c10, c2, 0 /* write PRRR */
mcr p15, 0, r4, c10, c2, 1 /* write NMRR */
mcr p15, 0, r5, c3, c0, 0 /* write DACR */
mcr p15, 0, r6, c5, c0, 0 /* write DFSR */
mcr p15, 0, r7, c5, c0, 1 /* write IFSR */
mcr p15, 0, r8, c5, c1, 0 /* write ADFSR */
mcr p15, 0, r9, c5, c1, 1 /* write AIFSR */
mcr p15, 0, r10, c6, c0, 0 /* write DFAR */
mcr p15, 0, r11, c6, c0, 2 /* write IFAR */
mcr p15, 0, r12, c13, c0, 1 /* write CIDR */
ldm r0!, {r1-r4}
mcr p15, 0, r1, c13, c0, 2 /* write TLS1 */
mcr p15, 0, r2, c13, c0, 3 /* write TLS2 */
mcr p15, 0, r3, c13, c0, 4 /* write TLS3 */
mcr p15, 0, r4, c1, c0, 2 /* write CPACR */
ldr r1, [r0]
vmsr fpscr, r1
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 */
push { r1 }
ldr r0, [sp, #1*4]
add r0, r0, #13*4
ldmia r0!, { r1-r5 }
msr sp_usr, r1
mov lr, r2
msr elr_hyp, r3
msr spsr_cxfs, r4
ldmia r0!, { r1-r12 }
msr spsr_und, r1
msr sp_und, r2
msr lr_und, r3
msr spsr_svc, r4
msr sp_svc, r5
msr lr_svc, r6
msr spsr_abt, r7
msr sp_abt, r8
msr lr_abt, r9
msr spsr_irq, r10
msr sp_irq, r11
msr lr_irq, r12
ldmia r0!, {r5 - r12}
msr spsr_fiq, r5
msr sp_fiq, r6
msr lr_fiq, r7
msr r8_fiq, r8
msr r9_fiq, r9
msr r10_fiq, r10
msr r11_fiq, r11
msr r12_fiq, r12
ldmia r0!, {r1 - r12}
mcrr p15, 6, r1, r2, c2 /* write VTTBR */
mcr p15, 0, r3, c1, c0, 0 /* write SCTRL */
mcr p15, 4, r4, c1, c1, 3 /* write HSTR */
mcr p15, 4, r5, c1, c1, 0 /* write HCR register */
mcr p15, 0, r8, c2, c0, 2 /* write TTBRC */
mcr p15, 0, r9, c2, c0, 0 /* write TTBR0 */
mcr p15, 0, r10, c2, c0, 1 /* write TTBR1 */
mcr p15, 0, r11, c10, c2, 0 /* write PRRR */
mcr p15, 0, r12, c10, c2, 1 /* write NMRR */
ldmia r0!, {r1-r12}
mcr p15, 0, r1, c3, c0, 0 /* write DACR */
mcr p15, 0, r2, c5, c0, 0 /* write DFSR */
mcr p15, 0, r3, c5, c0, 1 /* write IFSR */
mcr p15, 0, r4, c5, c1, 0 /* write ADFSR */
mcr p15, 0, r5, c5, c1, 1 /* write AIFSR */
mcr p15, 0, r6, c6, c0, 0 /* write DFAR */
mcr p15, 0, r7, c6, c0, 2 /* write IFAR */
mcr p15, 0, r8, c13, c0, 1 /* write CIDR */
mcr p15, 0, r9, c13, c0, 2 /* write TLS1 */
mcr p15, 0, r10, c13, c0, 3 /* write TLS2 */
mcr p15, 0, r11, c13, c0, 4 /* write TLS3 */
mcr p15, 0, r12, c1, c0, 2 /* write CPACR */
ldmia r0!, {r1-r2}
mcr p15, 4, r1, c0, c0, 5 /* write VMPIDR */
vmsr fpscr, r2
vldm r0!, {d0-d15}
vldm r0!, {d16-d31}
ldmia r0!, {r1-r6}
mcrr p15, 4, r1, r2, c14 /* write cntvoff */
mcrr p15, 3, r3, r4, c14 /* write cntv_cval */
mcr p15, 0, r5, c14, c3, 1 /* write cntv_ctl */
mcr p15, 0, r6, c14, c1, 0 /* write cntkctl */
ldr r0, [sp, #1*4]
ldmia r0, {r0-r12} /* load vm's r0-r12 */
eret
_vm_to_host:
add r0, sp, #1*4
stmia r0, {r1-r12} /* save regs r1-r12 */
mov r1, #0
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 */
push { r0 } /* push cpu excep. */
ldr r0, [sp, #3*4] /* load vm state ptr */
add r0, r0, #1*4 /* skip r0 */
stmia r0!, {r1-r12} /* save regs r1-r12 */
pop { r5 } /* pop cpu excep. */
pop { r1 } /* pop r0 */
str r1, [r0, #-13*4] /* save r0 */
mrs r1, sp_usr /* read USR sp */
mov r2, lr /* read USR lr */
mrs r3, elr_hyp /* read ip */
mrs r4, spsr /* read cpsr */
mrs r6, spsr_und
mrs r7, sp_und
mrs r8, lr_und
mrs r9, spsr_svc
mrs r10, sp_svc
mrs r11, lr_svc
mrs r12, spsr_abt
stmia r0!, {r1-r12}
mrs r1, sp_abt
mrs r2, lr_abt
mrs r3, spsr_irq
mrs r4, sp_irq
mrs r5, lr_irq
mrs r6, spsr_fiq
mrs r7, sp_fiq
mrs r8, lr_fiq
mrs r9, r8_fiq
mrs r10, r9_fiq
mrs r11, r10_fiq
mrs r12, r11_fiq
stmia r0!, {r1-r12}
mrs r1, r12_fiq
str r1, [r0]
add r0, r0, #3*4 /* skip VTTBR */
mrc p15, 0, r1, c1, c0, 0 /* read SCTRL */
mrc p15, 4, r2, c5, c2, 0 /* read HSR */
mrc p15, 4, r3, c6, c0, 4 /* read HPFAR */
mrc p15, 4, r4, c6, c0, 0 /* read HDFAR */
mrc p15, 4, r5, c6, c0, 2 /* read HIFAR */
mrc p15, 0, r6, c2, c0, 2 /* read TTBRC */
mrc p15, 0, r7, c2, c0, 0 /* read TTBR0 */
mrc p15, 0, r8, c2, c0, 1 /* read TTBR1 */
mrc p15, 0, r9, c10, c2, 0 /* read PRRR */
mrc p15, 0, r10, c10, c2, 1 /* read NMRR */
mrc p15, 0, r11, c3, c0, 0 /* read DACR */
mrc p15, 0, r12, c5, c0, 0 /* read DFSR */
stmia r0!, {r1-r12}
mrc p15, 0, r1, c5, c0, 1 /* read IFSR */
mrc p15, 0, r2, c5, c1, 0 /* read ADFSR */
mrc p15, 0, r3, c5, c1, 1 /* read AIFSR */
mrc p15, 0, r4, c6, c0, 0 /* read DFAR */
mrc p15, 0, r5, c6, c0, 2 /* read IFAR */
mrc p15, 0, r6, c13, c0, 1 /* read CIDR */
mrc p15, 0, r7, c13, c0, 2 /* read TLS1 */
mrc p15, 0, r8, c13, c0, 3 /* read TLS2 */
mrc p15, 0, r9, c13, c0, 4 /* read TLS3 */
mrc p15, 0, r10, c1, c0, 2 /* read CPACR */
mrc p15, 4, r11, c0, c0, 5 /* read VMPIDR */
stmia r0!, {r1-r11}
mov r1, #1 /* clear fpu excep. state */
lsl r1, #30
vmsr fpexc, r1
vmrs r12, fpscr
stmia r0!, {r12}
vstm r0!, {d0-d15}
vstm r0!, {d16-d31}
mrrc p15, 4, r1, r2, c14 /* read cntvoff */
mrrc p15, 3, r3, r4, c14 /* read cntv_cval */
mrc p15, 0, r5, c14, c3, 1 /* read cntv_ctl */
mrc p15, 0, r6, c14, c1, 0 /* read cntkctl */
stmia r0!, {r1-r6}
mrs r1, ELR_hyp /* read ip */
mrs r2, spsr /* read cpsr */
mrc p15, 0, r3, c1, c0, 0 /* read SCTRL */
mrc p15, 4, r4, c5, c2, 0 /* read HSR */
mrc p15, 4, r5, c6, c0, 4 /* read HPFAR */
mrc p15, 4, r6, c6, c0, 0 /* read HDFAR */
mrc p15, 4, r7, c6, c0, 2 /* read HIFAR */
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-r12}
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}
clrex
dsb sy
isb sy
mov r3, #0xf
lsl r3, #20
mcr p15, 0, r3, c1, c0, 2 /* write CPACR */
/**************************
** Restore host context **
**************************/
mov r3, #1 /* clear fpu exception state */
lsl r3, #30
vmsr fpexc, r3
vmrs r4, fpscr
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]
add r3, r3, #2*4
ldm r3, {r4-r11}
mcrr p15, 0, r6, r7, c2
mcrr p15, 1, r6, r7, c2
mcr p15, 0, r8, c1, c0, 0 /* write SCTRL */
mcr p15, 0, r9, c2, c0, 2 /* write TTBRC */
mcr p15, 0, r10, c10, c2, 0 /* write MAIR0 */
mcr p15, 0, r11, c3, c0, 0 /* write DACR */
mov r10, #7
lsl r10, #6
add r10, r10, #SVC_MODE
msr spsr_cxsf, r10
adr r10, _svc_mode_ret
msr ELR_hyp, r10
pop { r0, r1 }
ldmia r0!, {r1-r12}
mcrr p15, 6, r1, r2, c2 /* write VTTBR */
mcr p15, 4, r3, c1, c1, 0 /* write HCR register */
mcr p15, 4, r4, c1, c1, 3 /* write HSTR register */
mcr p15, 0, r5, c1, c0, 2 /* write CPACR */
msr sp_svc, r6
msr elr_hyp, r7
msr spsr_cxfs, r8
mcrr p15, 0, r9, r10, c2 /* write TTBR0 */
mcrr p15, 1, r11, r12, c2 /* write TTBR1 */
ldmia r0, {r1-r5}
mcr p15, 0, r1, c1, c0, 0 /* write SCTRL */
mcr p15, 0, r2, c2, c0, 2 /* write TTBRC */
mcr p15, 0, r3, c10, c2, 0 /* write MAIR0 */
mcr p15, 0, r4, c3, c0, 0 /* write DACR */
mcr p15, 4, r5, c0, c0, 5 /* write VMPIDR */
eret
_svc_mode_ret:
stmia r0, {r13-r14}^ /* save user regs sp,lr */
add r0, r0, #2*4
stmia r0!, {r1-r2} /* save ip, cpsr */
add r0, r0, #1*4
_save_bank UND_MODE /* save undefined banks */
_save_bank SVC_MODE /* save supervisor banks */
_save_bank ABT_MODE /* save abort banks */
_save_bank IRQ_MODE /* save irq banks */
_save_bank FIQ_MODE /* save fiq banks */
stmia r0!, {r8-r12} /* save fiq r8-r12 */
cps #SVC_MODE
ldr r0, _vt_host_context_ptr
ldm r0, {sp,pc}
/* host kernel must jump to this point to switch to a vm */
.global hypervisor_enter_vm
hypervisor_enter_vm:
add r0, r0, #13*4
ldm r0, {r13 - r14}^
add r0, r0, #2*4
ldmia r0!, {r2 - r4}
_restore_bank UND_MODE
_restore_bank SVC_MODE
_restore_bank ABT_MODE
_restore_bank IRQ_MODE
_restore_bank FIQ_MODE
ldmia r0!, {r8 - r12}
cps #SVC_MODE
ldm r0!, {r5 - r12}
hvc #0
_vt_host_context_ptr: .long vt_host_context

View File

@ -39,22 +39,53 @@ namespace Kernel
using namespace Kernel;
extern "C" void kernel();
extern void * kernel_stack;
extern "C" void hypervisor_enter_vm(Genode::Vm_state&);
struct Host_context {
Cpu::Ttbr_64bit::access_t vttbr;
Cpu::Hcr::access_t hcr;
Cpu::Hstr::access_t hstr;
Cpu::Cpacr::access_t cpacr;
addr_t sp;
addr_t ip;
addr_t spsr;
Cpu::Ttbr_64bit::access_t ttbr0;
Cpu::Ttbr_64bit::access_t ttbr1;
Cpu::Sctlr::access_t sctlr;
Cpu::Ttbcr::access_t ttbcr;
Cpu::Mair0::access_t mair0;
Cpu::Dacr::access_t dacr;
Cpu::Vmpidr::access_t vmpidr;
} vt_host_context;
extern "C" void kernel();
extern "C" void hypervisor_enter_vm(Genode::Vm_state&, Host_context&);
static Host_context & host_context(Cpu & cpu)
{
static Genode::Constructible<Host_context> host_context[NR_OF_CPUS];
if (!host_context[cpu.id()].constructed()) {
host_context[cpu.id()].construct();
Host_context & c = *host_context[cpu.id()];
c.sp = cpu.stack_start();
c.ttbr0 = Cpu::Ttbr0_64bit::read();
c.ttbr1 = Cpu::Ttbr1_64bit::read();
c.sctlr = Cpu::Sctlr::read();
c.ttbcr = Cpu::Ttbcr::read();
c.mair0 = Cpu::Mair0::read();
c.dacr = Cpu::Dacr::read();
c.vmpidr = Cpu::Mpidr::read();
c.ip = (addr_t) &kernel;
c.vttbr = 0;
c.hcr = 0;
c.hstr = 0;
c.cpacr = 0xf00000;
c.spsr = 0x1d3;
}
return *host_context[cpu.id()];
}
Board::Vcpu_context::Vm_irq::Vm_irq(unsigned const irq, Cpu & cpu)
: Kernel::Irq(irq, cpu.irq_pool())
{ }
@ -92,50 +123,22 @@ void Board::Vcpu_context::Virtual_timer_irq::disable()
asm volatile("mcr p15, 0, %0, c14, c1, 0" :: "r" (0b11));
}
using Vmid_allocator = Genode::Bit_allocator<256>;
static Vmid_allocator &alloc()
{
static Vmid_allocator * allocator = nullptr;
if (!allocator) {
allocator = unmanaged_singleton<Vmid_allocator>();
/* reserve VM ID 0 for the hypervisor */
unsigned id = allocator->alloc();
assert (id == 0);
}
return *allocator;
}
Kernel::Vm::Vm(unsigned, /* FIXME: smp support */
Kernel::Vm::Vm(unsigned cpu,
Genode::Vm_state & state,
Kernel::Signal_context & context,
void * const table)
Identity & id)
: Kernel::Object { *this },
Cpu_job(Cpu_priority::MIN, 0),
_id(alloc().alloc()),
_state(state),
_context(context),
_table(table),
_vcpu_context(cpu_pool().primary_cpu())
_id(id),
_vcpu_context(cpu_pool().cpu(cpu))
{
affinity(cpu_pool().primary_cpu());
vt_host_context.sp = _cpu->stack_start();
vt_host_context.ttbr0 = Cpu::Ttbr0_64bit::read();
vt_host_context.ttbr1 = Cpu::Ttbr1_64bit::read();
vt_host_context.sctlr = Cpu::Sctlr::read();
vt_host_context.ttbcr = Cpu::Ttbcr::read();
vt_host_context.mair0 = Cpu::Mair0::read();
vt_host_context.dacr = Cpu::Dacr::read();
vt_host_context.ip = (addr_t) &kernel;
affinity(cpu_pool().cpu(cpu));
}
Kernel::Vm::~Vm() { alloc().free(_id); }
void Kernel::Vm::exception(Cpu & cpu)
{
switch(_state.cpu_exception) {
@ -163,8 +166,8 @@ void Kernel::Vm::proceed(Cpu & cpu)
/*
* the following values have to be enforced by the hypervisor
*/
_state.vttbr = Cpu::Ttbr_64bit::Ba::masked((Cpu::Ttbr_64bit::access_t)_table);
Cpu::Ttbr_64bit::Asid::set(_state.vttbr, _id);
_state.vttbr = Cpu::Ttbr_64bit::Ba::masked((Cpu::Ttbr_64bit::access_t)_id.table);
Cpu::Ttbr_64bit::Asid::set(_state.vttbr, _id.id);
/*
* use the following report fields not needed for loading the context
@ -174,7 +177,7 @@ void Kernel::Vm::proceed(Cpu & cpu)
_state.esr_el2 = Cpu::Hstr::init();
_state.hpfar_el2 = Cpu::Hcr::init();
hypervisor_enter_vm(_state);
hypervisor_enter_vm(_state, host_context(cpu));
}

View File

@ -32,32 +32,34 @@ extern "C" void hypervisor_enter_vm(addr_t vm, addr_t host,
addr_t pic, addr_t guest_table);
static Genode::Vm_state & host_context()
static Genode::Vm_state & host_context(Cpu & cpu)
{
static Genode::Constructible<Genode::Vm_state> host_context;
if (!host_context.constructed()) {
host_context.construct();
host_context->ip = (addr_t) &kernel;
host_context->pstate = 0;
Cpu::Spsr::Sp::set(host_context->pstate, 1); /* select non-el0 stack pointer */
Cpu::Spsr::El::set(host_context->pstate, Cpu::Current_el::EL1);
Cpu::Spsr::F::set(host_context->pstate, 1);
Cpu::Spsr::I::set(host_context->pstate, 1);
Cpu::Spsr::A::set(host_context->pstate, 1);
Cpu::Spsr::D::set(host_context->pstate, 1);
host_context->fpcr = Cpu::Fpcr::read();
host_context->fpsr = 0;
host_context->sctlr_el1 = Cpu::Sctlr_el1::read();
host_context->actlr_el1 = Cpu::Actlr_el1::read();
host_context->vbar_el1 = Cpu::Vbar_el1::read();
host_context->cpacr_el1 = Cpu::Cpacr_el1::read();
host_context->ttbr0_el1 = Cpu::Ttbr0_el1::read();
host_context->ttbr1_el1 = Cpu::Ttbr1_el1::read();
host_context->tcr_el1 = Cpu::Tcr_el1::read();
host_context->mair_el1 = Cpu::Mair_el1::read();
host_context->amair_el1 = Cpu::Amair_el1::read();
static Genode::Constructible<Genode::Vm_state> host_context[NR_OF_CPUS];
if (!host_context[cpu.id()].constructed()) {
host_context[cpu.id()].construct();
Genode::Vm_state & c = *host_context[cpu.id()];
c.sp_el1 = cpu.stack_start();
c.ip = (addr_t) &kernel;
c.pstate = 0;
Cpu::Spsr::Sp::set(c.pstate, 1); /* select non-el0 stack pointer */
Cpu::Spsr::El::set(c.pstate, Cpu::Current_el::EL1);
Cpu::Spsr::F::set(c.pstate, 1);
Cpu::Spsr::I::set(c.pstate, 1);
Cpu::Spsr::A::set(c.pstate, 1);
Cpu::Spsr::D::set(c.pstate, 1);
c.fpcr = Cpu::Fpcr::read();
c.fpsr = 0;
c.sctlr_el1 = Cpu::Sctlr_el1::read();
c.actlr_el1 = Cpu::Actlr_el1::read();
c.vbar_el1 = Cpu::Vbar_el1::read();
c.cpacr_el1 = Cpu::Cpacr_el1::read();
c.ttbr0_el1 = Cpu::Ttbr0_el1::read();
c.ttbr1_el1 = Cpu::Ttbr1_el1::read();
c.tcr_el1 = Cpu::Tcr_el1::read();
c.mair_el1 = Cpu::Mair_el1::read();
c.amair_el1 = Cpu::Amair_el1::read();
}
return *host_context;
return *host_context[cpu.id()];
}
@ -99,32 +101,15 @@ void Board::Vcpu_context::Virtual_timer_irq::disable()
}
using Vmid_allocator = Genode::Bit_allocator<256>;
static Vmid_allocator &alloc()
{
static Vmid_allocator * allocator = nullptr;
if (!allocator) {
allocator = unmanaged_singleton<Vmid_allocator>();
/* reserve VM ID 0 for the hypervisor */
unsigned id = allocator->alloc();
assert (id == 0);
}
return *allocator;
}
Vm::Vm(unsigned cpu,
Genode::Vm_state & state,
Kernel::Signal_context & context,
void * const table)
Identity & id)
: Kernel::Object { *this },
Cpu_job(Cpu_priority::MIN, 0),
_id(alloc().alloc()),
_state(state),
_context(context),
_table(table),
_id(id),
_vcpu_context(cpu_pool().cpu(cpu))
{
affinity(cpu_pool().cpu(cpu));
@ -158,9 +143,6 @@ Vm::Vm(unsigned cpu,
}
Vm::~Vm() { alloc().free(_id); }
void Vm::exception(Cpu & cpu)
{
switch (_state.exception_type) {
@ -198,12 +180,11 @@ void Vm::proceed(Cpu & cpu)
* the following values have to be enforced by the hypervisor
*/
Cpu::Vttbr_el2::access_t vttbr_el2 =
Cpu::Vttbr_el2::Ba::masked((Cpu::Vttbr_el2::access_t)_table);
Cpu::Vttbr_el2::Asid::set(vttbr_el2, _id);
Cpu::Vttbr_el2::Ba::masked((Cpu::Vttbr_el2::access_t)_id.table);
Cpu::Vttbr_el2::Asid::set(vttbr_el2, _id.id);
addr_t guest = Hw::Mm::el2_addr(&_state);
addr_t pic = Hw::Mm::el2_addr(&_vcpu_context.pic);
addr_t host = Hw::Mm::el2_addr(&host_context());
host_context().sp_el1 = cpu.stack_start();
addr_t host = Hw::Mm::el2_addr(&host_context(cpu));
hypervisor_enter_vm(guest, host, pic, vttbr_el2);
}

View File

@ -25,7 +25,7 @@ namespace Board {
struct Virtual_local_pic {};
enum { TIMER_IRQ = 30, VCPU_MAX = 1 };
enum { TIMER_IRQ = 30, VCPU_MAX = 16 };
}
#endif /* _CORE__SPEC__IMX7_SABRELITE__BOARD_H_ */

View File

@ -27,7 +27,7 @@ namespace Board {
TIMER_IRQ = 14 + 16,
VT_TIMER_IRQ = 11 + 16,
VT_MAINTAINANCE_IRQ = 9 + 16,
VCPU_MAX = 4
VCPU_MAX = 16
};
};

View File

@ -31,7 +31,7 @@ namespace Board {
TIMER_IRQ = 30 /* PPI IRQ 14 */,
VT_TIMER_IRQ = 27,
VT_MAINTAINANCE_IRQ = 25,
VCPU_MAX = 1
VCPU_MAX = 16
};
};

View File

@ -19,23 +19,20 @@
#include <kernel/vm.h>
Kernel::Vm::Vm(unsigned,
Board::Vm_state & state,
Board::Vm_state & state,
Kernel::Signal_context & context,
void * const)
Identity & id)
: Kernel::Object { *this },
Cpu_job(Cpu_priority::MIN, 0),
_state(state),
_context(context),
_table(nullptr),
_id(id),
_vcpu_context(cpu_pool().primary_cpu())
{
affinity(cpu_pool().primary_cpu());
}
Kernel::Vm::~Vm() { }
void Kernel::Vm::exception(Cpu & cpu)
{
pause();

View File

@ -55,15 +55,15 @@ Vm_session_component::Vm_session_component(Rpc_entrypoint &ep,
Ram_allocator &ram_alloc,
Region_map &region_map,
unsigned, Trace::Source_registry &)
:
Ram_quota_guard(resources.ram_quota),
Cap_quota_guard(resources.cap_quota),
_ep(ep),
_constrained_md_ram_alloc(ram_alloc, _ram_quota_guard(), _cap_quota_guard()),
_sliced_heap(_constrained_md_ram_alloc, region_map),
_region_map(region_map),
: Ram_quota_guard(resources.ram_quota),
Cap_quota_guard(resources.cap_quota),
_ep(ep),
_constrained_md_ram_alloc(ram_alloc, _ram_quota_guard(), _cap_quota_guard()),
_sliced_heap(_constrained_md_ram_alloc, region_map),
_region_map(region_map),
_table(*construct_at<Board::Vm_page_table>(_alloc_table())),
_table_array(dummy_array()) { }
_table_array(dummy_array()),
_id({0, nullptr}) { }
Vm_session_component::~Vm_session_component()
@ -79,7 +79,7 @@ Vm_session_component::~Vm_session_component()
}
/* free region in allocator */
for (unsigned i = 0; i < _id_alloc; i++) {
for (unsigned i = 0; i < _vcpu_id_alloc; i++) {
Vcpu & vcpu = _vcpus[i];
if (vcpu.ds_cap.valid()) {
_region_map.detach(vcpu.ds_addr);

View File

@ -23,10 +23,6 @@
using namespace Genode;
static Core_mem_allocator & cma() {
return static_cast<Core_mem_allocator&>(platform().core_mem_alloc()); }
size_t Vm_session_component::_ds_size() {
return align_addr(sizeof(Board::Vm_state), get_page_size_log2()); }
@ -70,8 +66,7 @@ void Vm_session_component::_exception_handler(Signal_context_capability handler,
unsigned const cpu = vcpu.location.valid() ? vcpu.location.xpos() : 0;
if (!vcpu.kobj.create(cpu, vcpu.ds_addr, Capability_space::capid(handler),
cma().phys_addr(&_table)))
if (!vcpu.kobj.create(cpu, vcpu.ds_addr, Capability_space::capid(handler), _id))
Genode::warning("Cannot instantiate vm kernel object, ",
"invalid signal context?");
}
@ -81,7 +76,7 @@ Vm_session::Vcpu_id Vm_session_component::_create_vcpu(Thread_capability tcap)
{
using namespace Genode;
if (_id_alloc == Board::VCPU_MAX) return Vcpu_id{Vcpu_id::INVALID};
if (_vcpu_id_alloc == Board::VCPU_MAX) return Vcpu_id{Vcpu_id::INVALID};
Affinity::Location vcpu_location;
auto lambda = [&] (Cpu_thread_component *ptr) {
@ -90,7 +85,7 @@ Vm_session::Vcpu_id Vm_session_component::_create_vcpu(Thread_capability tcap)
};
_ep.apply(tcap, lambda);
Vcpu & vcpu = _vcpus[_id_alloc];
Vcpu & vcpu = _vcpus[_vcpu_id_alloc];
vcpu.ds_cap = _constrained_md_ram_alloc.alloc(_ds_size(),
Cache_attribute::UNCACHED);
try {
@ -101,7 +96,7 @@ Vm_session::Vcpu_id Vm_session_component::_create_vcpu(Thread_capability tcap)
}
vcpu.location = vcpu_location;
return Vcpu_id { _id_alloc++ };
return Vcpu_id { _vcpu_id_alloc++ };
}

View File

@ -65,7 +65,8 @@ class Genode::Vm_session_component
Region_map &_region_map;
Board::Vm_page_table &_table;
Board::Vm_page_table_array &_table_array;
unsigned _id_alloc { 0 };
Kernel::Vm::Identity _id;
unsigned _vcpu_id_alloc { 0 };
static size_t _ds_size();
bool _valid_id(Vcpu_id id) { return id.id < Board::VCPU_MAX; }

View File

@ -36,6 +36,9 @@ struct Hw::Arm_cpu
struct Me : Bitfield<31, 1> { }; /* multiprocessing extension */
);
/* Virtual Multiprocessor Affinity Register */
ARM_CP15_REGISTER_32BIT(Vmpidr, c0, c0, 4, 5);
/* System Control Register */
ARM_CP15_REGISTER_32BIT(Sctlr, c1, c0, 0, 0,
struct M : Bitfield<0,1> { }; /* enable MMU */

View File

@ -46,7 +46,7 @@ Memory_region const Hw::Mm::hypervisor_exception_vector() {
return Memory_region(0xfff10000UL, 0x1000UL); }
Memory_region const Hw::Mm::hypervisor_stack() {
return Memory_region(0xfff12000UL, 0x1000UL); }
return Memory_region(0xfff20000UL, 0x10000UL); }
Memory_region const Hw::Mm::boot_info() {
return Memory_region(0xfffe0000UL, 0x1000UL); }

View File

@ -51,7 +51,7 @@ Memory_region const Hw::Mm::hypervisor_exception_vector() {
return Memory_region(0xffffffe050000000UL, 0x2000UL); }
Memory_region const Hw::Mm::hypervisor_stack() {
return Memory_region(0xffffffe050003000UL, 0x1000UL); }
return Memory_region(0xffffffe060000000UL, 0x10000UL); }
Memory_region const Hw::Mm::supervisor_exception_vector() {
return Memory_region(KERNEL_START, 0x1000UL); }