From 5049f03f5be9127bcb3a2347341cb747cd7486e1 Mon Sep 17 00:00:00 2001 From: Benjamin Lamowski Date: Thu, 15 Feb 2024 13:27:48 +0100 Subject: [PATCH] hw: generic interface for x86 virtualization Create a generic interface for x86 virtualization. Split the VMCB data structure in `Vmcb_buf` to represent the physical VMCB page and `VMCB` as a control interface. Issue #5113 --- .../core/spec/x86_64/virtualization/board.h | 18 +- .../spec/x86_64/virtualization/kernel/svm.cc | 321 +++++++++--------- .../spec/x86_64/virtualization/kernel/vm.cc | 20 +- .../src/core/spec/x86_64/virtualization/svm.h | 60 +++- .../x86_64/virtualization/virt_interface.h | 55 +++ 5 files changed, 282 insertions(+), 192 deletions(-) create mode 100644 repos/base-hw/src/core/spec/x86_64/virtualization/virt_interface.h diff --git a/repos/base-hw/src/core/spec/x86_64/virtualization/board.h b/repos/base-hw/src/core/spec/x86_64/virtualization/board.h index a20c3fd09c..265abae512 100644 --- a/repos/base-hw/src/core/spec/x86_64/virtualization/board.h +++ b/repos/base-hw/src/core/spec/x86_64/virtualization/board.h @@ -5,7 +5,7 @@ */ /* - * Copyright (C) 2022-2023 Genode Labs GmbH + * Copyright (C) 2022-2024 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. @@ -39,7 +39,6 @@ namespace Board { VCPU_MAX = 16 }; - /* FIXME move into Vcpu_context as 'enum class' when we have C++20 */ enum Platform_exitcodes : uint64_t { EXIT_NPF = 0xfc, EXIT_INIT = 0xfd, @@ -67,16 +66,21 @@ struct Board::Vcpu_context void read_vcpu_state(Vcpu_state &state); void write_vcpu_state(Vcpu_state &state); - Vmcb *vmcb { nullptr }; - Genode::Align_at regs; - Vcpu_data &vcpu_data; + + Virt_interface &virt; + uint64_t tsc_aux_host = 0U; uint64_t tsc_aux_guest = 0U; uint64_t exitcode = EXIT_INIT; - Vcpu_context(const Vcpu_context &) = delete; - const Vcpu_context &operator=(Vcpu_context &) = delete; + static Virt_interface &detect_virtualization(Vcpu_data &vcpu_data, + unsigned id) + { + return *Genode::construct_at( + vcpu_data.virt_area, + vcpu_data, id); + } }; #endif /* _CORE__SPEC__PC__VIRTUALIZATION__BOARD_H_ */ diff --git a/repos/base-hw/src/core/spec/x86_64/virtualization/kernel/svm.cc b/repos/base-hw/src/core/spec/x86_64/virtualization/kernel/svm.cc index 213cc8dcda..2b5099b415 100644 --- a/repos/base-hw/src/core/spec/x86_64/virtualization/kernel/svm.cc +++ b/repos/base-hw/src/core/spec/x86_64/virtualization/kernel/svm.cc @@ -5,7 +5,7 @@ */ /* - * Copyright (C) 2022-2023 Genode Labs GmbH + * Copyright (C) 2022-2024 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. @@ -24,9 +24,10 @@ using namespace Genode; using Kernel::Cpu; using Kernel::Vm; using Board::Vmcb; +using Board::Vmcb_buf; -Vmcb::Vmcb(addr_t vmcb_page_addr, uint32_t id) +Vmcb_buf::Vmcb_buf(addr_t vmcb_page_addr, uint32_t id) : Mmio({(char *)vmcb_page_addr, Mmio::SIZE}) { @@ -44,10 +45,18 @@ Vmcb::Vmcb(addr_t vmcb_page_addr, uint32_t id) } -Vmcb & Vmcb::host_vmcb(size_t cpu_id) +Vmcb::Vmcb(Vcpu_data &vcpu_data, uint32_t id) +: + Board::Virt_interface(vcpu_data), + v((addr_t) vcpu_data.virt_area + get_page_size(), id) +{ +} + + +Vmcb_buf &Vmcb::host_vmcb(size_t cpu_id) { static uint8_t host_vmcb_pages[get_page_size() * NR_OF_CPUS]; - static Constructible host_vmcb[NR_OF_CPUS]; + static Constructible host_vmcb[NR_OF_CPUS]; if (!host_vmcb[cpu_id].constructed()) { host_vmcb[cpu_id].construct( @@ -58,7 +67,8 @@ Vmcb & Vmcb::host_vmcb(size_t cpu_id) return *host_vmcb[cpu_id]; } -void Vmcb::initialize(Kernel::Cpu &cpu, addr_t page_table_phys_addr) +void Vmcb::initialize(Kernel::Cpu &cpu, addr_t page_table_phys_addr, + Core::Cpu::Context &) { using Cpu = Hw::X86_64_cpu; @@ -79,11 +89,11 @@ void Vmcb::initialize(Kernel::Cpu &cpu, addr_t page_table_phys_addr) /* * enable nested paging */ - write(1); - write(page_table_phys_addr); + v.write(1); + v.write(page_table_phys_addr); - write(1); /* See 15.2 */ - write(17); /* AC */ + v.write(1); /* See 15.2 */ + v.write(17); /* AC */ enforce_intercepts(); } @@ -94,23 +104,23 @@ void Vmcb::initialize(Kernel::Cpu &cpu, addr_t page_table_phys_addr) */ void Vmcb::enforce_intercepts(uint32_t desired_primary, uint32_t desired_secondary) { - write( + v.write( desired_primary | - Vmcb::Intercept_misc1::Intr::bits(1) | - Vmcb::Intercept_misc1::Nmi::bits(1) | - Vmcb::Intercept_misc1::Init::bits(1) | - Vmcb::Intercept_misc1::Invd::bits(1) | - Vmcb::Intercept_misc1::Hlt::bits(1) | - Vmcb::Intercept_misc1::Ioio_prot::bits(1) | - Vmcb::Intercept_misc1::Msr_prot::bits(1) | - Vmcb::Intercept_misc1::Shutdown::bits(1) + Vmcb_buf::Intercept_misc1::Intr::bits(1) | + Vmcb_buf::Intercept_misc1::Nmi::bits(1) | + Vmcb_buf::Intercept_misc1::Init::bits(1) | + Vmcb_buf::Intercept_misc1::Invd::bits(1) | + Vmcb_buf::Intercept_misc1::Hlt::bits(1) | + Vmcb_buf::Intercept_misc1::Ioio_prot::bits(1) | + Vmcb_buf::Intercept_misc1::Msr_prot::bits(1) | + Vmcb_buf::Intercept_misc1::Shutdown::bits(1) ); - write( + v.write( desired_secondary | - Vmcb::Intercept_misc2::Vmload::bits(1) | - Vmcb::Intercept_misc2::Vmsave::bits(1) | - Vmcb::Intercept_misc2::Clgi::bits(1) | - Vmcb::Intercept_misc2::Skinit::bits(1) + Vmcb_buf::Intercept_misc2::Vmload::bits(1) | + Vmcb_buf::Intercept_misc2::Vmsave::bits(1) | + Vmcb_buf::Intercept_misc2::Clgi::bits(1) | + Vmcb_buf::Intercept_misc2::Skinit::bits(1) ); } @@ -119,7 +129,7 @@ void Vmcb::enforce_intercepts(uint32_t desired_primary, uint32_t desired_seconda * AMD Vol.2 15.11: MSR Permissions Map * All set to 1 since we want all MSRs to be intercepted. */ -addr_t Vmcb::dummy_msrpm() +addr_t Vmcb_buf::dummy_msrpm() { static Board::Msrpm msrpm; @@ -131,7 +141,7 @@ addr_t Vmcb::dummy_msrpm() * AMD Vol.2 15.10.1 I/O Permissions Map * All set to 1 since we want all IO port accesses to be intercepted. */ -addr_t Vmcb::dummy_iopm() +addr_t Vmcb_buf::dummy_iopm() { static Board::Iopm iopm; @@ -153,217 +163,217 @@ Board::Iopm::Iopm() void Vmcb::write_vcpu_state(Vcpu_state &state) { - state.ax.charge(read()); - state.ip.charge(read()); + state.ax.charge(v.read()); + state.ip.charge(v.read()); /* * SVM doesn't use ip_len, so just leave the old value. * We still have to charge it when charging ip. */ state.ip_len.set_charged(); - state.flags.charge(read()); - state.sp.charge(read()); + state.flags.charge(v.read()); + state.sp.charge(v.read()); - state.dr7.charge(read()); + state.dr7.charge(v.read()); - state.cr0.charge(read()); - state.cr2.charge(read()); - state.cr3.charge(read()); - state.cr4.charge(read()); + state.cr0.charge(v.read()); + state.cr2.charge(v.read()); + state.cr3.charge(v.read()); + state.cr4.charge(v.read()); state.cs.charge(Vcpu_state::Segment { - .sel = Vmcb::cs.read(), - .ar = Vmcb::cs.read(), - .limit = Vmcb::cs.read(), - .base = Vmcb::cs.read(), + .sel = v.cs.read(), + .ar = v.cs.read(), + .limit = v.cs.read(), + .base = v.cs.read(), }); state.ss.charge(Vcpu_state::Segment { - .sel = Vmcb::ss.read(), - .ar = Vmcb::ss.read(), - .limit = Vmcb::ss.read(), - .base = Vmcb::ss.read(), + .sel = v.ss.read(), + .ar = v.ss.read(), + .limit = v.ss.read(), + .base = v.ss.read(), }); state.es.charge(Vcpu_state::Segment { - .sel = Vmcb::es.read(), - .ar = Vmcb::es.read(), - .limit = Vmcb::es.read(), - .base = Vmcb::es.read(), + .sel = v.es.read(), + .ar = v.es.read(), + .limit = v.es.read(), + .base = v.es.read(), }); state.ds.charge(Vcpu_state::Segment { - .sel = Vmcb::ds.read(), - .ar = Vmcb::ds.read(), - .limit = Vmcb::ds.read(), - .base = Vmcb::ds.read(), + .sel = v.ds.read(), + .ar = v.ds.read(), + .limit = v.ds.read(), + .base = v.ds.read(), }); state.fs.charge(Vcpu_state::Segment { - .sel = Vmcb::fs.read(), - .ar = Vmcb::fs.read(), - .limit = Vmcb::fs.read(), - .base = Vmcb::fs.read(), + .sel = v.fs.read(), + .ar = v.fs.read(), + .limit = v.fs.read(), + .base = v.fs.read(), }); state.gs.charge(Vcpu_state::Segment { - .sel = Vmcb::gs.read(), - .ar = Vmcb::gs.read(), - .limit = Vmcb::gs.read(), - .base = Vmcb::gs.read(), + .sel = v.gs.read(), + .ar = v.gs.read(), + .limit = v.gs.read(), + .base = v.gs.read(), }); state.tr.charge(Vcpu_state::Segment { - .sel = Vmcb::tr.read(), - .ar = Vmcb::tr.read(), - .limit = Vmcb::tr.read(), - .base = Vmcb::tr.read(), + .sel = v.tr.read(), + .ar = v.tr.read(), + .limit = v.tr.read(), + .base = v.tr.read(), }); state.ldtr.charge(Vcpu_state::Segment { - .sel = Vmcb::ldtr.read(), - .ar = Vmcb::ldtr.read(), - .limit = Vmcb::ldtr.read(), - .base = Vmcb::ldtr.read(), + .sel = v.ldtr.read(), + .ar = v.ldtr.read(), + .limit = v.ldtr.read(), + .base = v.ldtr.read(), }); state.gdtr.charge(Vcpu_state::Range { - .limit = Vmcb::gdtr.read(), - .base = Vmcb::gdtr.read(), + .limit = v.gdtr.read(), + .base = v.gdtr.read(), }); state.idtr.charge(Vcpu_state::Range { - .limit = Vmcb::idtr.read(), - .base = Vmcb::idtr.read(), + .limit = v.idtr.read(), + .base = v.idtr.read(), }); - state.sysenter_cs.charge(read()); - state.sysenter_sp.charge(read()); - state.sysenter_ip.charge(read()); + state.sysenter_cs.charge(v.read()); + state.sysenter_sp.charge(v.read()); + state.sysenter_ip.charge(v.read()); - state.qual_primary.charge(read()); - state.qual_secondary.charge(read()); + state.qual_primary.charge(v.read()); + state.qual_secondary.charge(v.read()); /* Charging ctrl_primary and ctrl_secondary breaks Virtualbox 6 */ - state.inj_info.charge(read() & 0xFFFFFFFF); + state.inj_info.charge(v.read() & 0xFFFFFFFF); state.inj_error.charge( - (uint32_t)(read() >> 32)); + (uint32_t)(v.read() >> 32)); /* Guest is in an interrupt shadow, see 15.21.5 */ state.intr_state.charge( - (unsigned)read()); + (unsigned)v.read()); /* Guest activity state (actv) not used by SVM */ state.actv_state.set_charged(); state.tsc.charge(Hw::Lapic::rdtsc()); - state.tsc_offset.charge(read()); + state.tsc_offset.charge(v.read()); - state.efer.charge(read()); + state.efer.charge(v.read()); /* pdpte not used by SVM */ - state.star.charge(read()); - state.lstar.charge(read()); - state.cstar.charge(read()); - state.fmask.charge(read()); - state.kernel_gs_base.charge(read()); + state.star.charge(v.read()); + state.lstar.charge(v.read()); + state.cstar.charge(v.read()); + state.fmask.charge(v.read()); + state.kernel_gs_base.charge(v.read()); /* Task Priority Register, see 15.24 */ - state.tpr.charge((unsigned)read()); + state.tpr.charge((unsigned)v.read()); /* TPR threshold not used by SVM */ } void Vmcb::read_vcpu_state(Vcpu_state &state) { - if (state.ax.charged()) write(state.ax.value()); - if (state.flags.charged()) write(state.flags.value()); - if (state.sp.charged()) write(state.sp.value()); - if (state.ip.charged()) write(state.ip.value()); + if (state.ax.charged()) v.write(state.ax.value()); + if (state.flags.charged()) v.write(state.flags.value()); + if (state.sp.charged()) v.write(state.sp.value()); + if (state.ip.charged()) v.write(state.ip.value()); /* ip_len not used by SVM */ - if (state.dr7.charged()) write(state.dr7.value()); + if (state.dr7.charged()) v.write(state.dr7.value()); - if (state.cr0.charged()) write(state.cr0.value()); - if (state.cr2.charged()) write(state.cr2.value()); - if (state.cr3.charged()) write(state.cr3.value()); - if (state.cr4.charged()) write(state.cr4.value()); + if (state.cr0.charged()) v.write(state.cr0.value()); + if (state.cr2.charged()) v.write(state.cr2.value()); + if (state.cr3.charged()) v.write(state.cr3.value()); + if (state.cr4.charged()) v.write(state.cr4.value()); if (state.cs.charged()) { - Vmcb::cs.write(state.cs.value().sel); - Vmcb::cs.write(state.cs.value().ar); - Vmcb::cs.write(state.cs.value().limit); - Vmcb::cs.write(state.cs.value().base); + v.cs.write(state.cs.value().sel); + v.cs.write(state.cs.value().ar); + v.cs.write(state.cs.value().limit); + v.cs.write(state.cs.value().base); } if (state.ss.charged()) { - Vmcb::ss.write(state.ss.value().sel); - Vmcb::ss.write(state.ss.value().ar); - Vmcb::ss.write(state.ss.value().limit); - Vmcb::ss.write(state.ss.value().base); + v.ss.write(state.ss.value().sel); + v.ss.write(state.ss.value().ar); + v.ss.write(state.ss.value().limit); + v.ss.write(state.ss.value().base); } if (state.es.charged()) { - Vmcb::es.write(state.es.value().sel); - Vmcb::es.write(state.es.value().ar); - Vmcb::es.write(state.es.value().limit); - Vmcb::es.write(state.es.value().base); + v.es.write(state.es.value().sel); + v.es.write(state.es.value().ar); + v.es.write(state.es.value().limit); + v.es.write(state.es.value().base); } if (state.ds.charged()) { - Vmcb::ds.write(state.ds.value().sel); - Vmcb::ds.write(state.ds.value().ar); - Vmcb::ds.write(state.ds.value().limit); - Vmcb::ds.write(state.ds.value().base); + v.ds.write(state.ds.value().sel); + v.ds.write(state.ds.value().ar); + v.ds.write(state.ds.value().limit); + v.ds.write(state.ds.value().base); } if (state.fs.charged()) { - Vmcb::gs.write(state.gs.value().sel); - Vmcb::gs.write(state.gs.value().ar); - Vmcb::gs.write(state.gs.value().limit); - Vmcb::gs.write(state.gs.value().base); + v.gs.write(state.gs.value().sel); + v.gs.write(state.gs.value().ar); + v.gs.write(state.gs.value().limit); + v.gs.write(state.gs.value().base); } if (state.gs.charged()) { - Vmcb::fs.write(state.fs.value().sel); - Vmcb::fs.write(state.fs.value().ar); - Vmcb::fs.write(state.fs.value().limit); - Vmcb::fs.write(state.fs.value().base); + v.fs.write(state.fs.value().sel); + v.fs.write(state.fs.value().ar); + v.fs.write(state.fs.value().limit); + v.fs.write(state.fs.value().base); } if (state.tr.charged()) { - Vmcb::tr.write(state.tr.value().sel); - Vmcb::tr.write(state.tr.value().ar); - Vmcb::tr.write(state.tr.value().limit); - Vmcb::tr.write(state.tr.value().base); + v.tr.write(state.tr.value().sel); + v.tr.write(state.tr.value().ar); + v.tr.write(state.tr.value().limit); + v.tr.write(state.tr.value().base); } if (state.ldtr.charged()) { - Vmcb::ldtr.write(state.ldtr.value().sel); - Vmcb::ldtr.write(state.ldtr.value().ar); - Vmcb::ldtr.write(state.ldtr.value().limit); - Vmcb::ldtr.write(state.ldtr.value().base); + v.ldtr.write(state.ldtr.value().sel); + v.ldtr.write(state.ldtr.value().ar); + v.ldtr.write(state.ldtr.value().limit); + v.ldtr.write(state.ldtr.value().base); } if (state.gdtr.charged()) { - Vmcb::gdtr.write(state.gdtr.value().limit); - Vmcb::gdtr.write(state.gdtr.value().base); + v.gdtr.write(state.gdtr.value().limit); + v.gdtr.write(state.gdtr.value().base); } if (state.idtr.charged()) { - Vmcb::idtr.write(state.idtr.value().limit); - Vmcb::idtr.write(state.idtr.value().base); + v.idtr.write(state.idtr.value().limit); + v.idtr.write(state.idtr.value().base); } if (state.sysenter_cs.charged()) - write(state.sysenter_cs.value()); + v.write(state.sysenter_cs.value()); if (state.sysenter_sp.charged()) - write(state.sysenter_sp.value()); + v.write(state.sysenter_sp.value()); if (state.sysenter_ip.charged()) - write(state.sysenter_ip.value()); + v.write(state.sysenter_ip.value()); if (state.ctrl_primary.charged() || state.ctrl_secondary.charged()) { enforce_intercepts(state.ctrl_primary.value(), @@ -373,16 +383,16 @@ void Vmcb::read_vcpu_state(Vcpu_state &state) if (state.inj_info.charged() || state.inj_error.charged()) { /* Honor special signaling bit */ if (state.inj_info.value() & 0x1000) { - write(1); - write(1); - write(1); + v.write(1); + v.write(1); + v.write(1); } else { - write(0); - write(0); - write(0); + v.write(0); + v.write(0); + v.write(0); } - write( + v.write( /* Filter out special signaling bits */ (state.inj_info.value() & (uint32_t) ~0x3000) | @@ -391,36 +401,37 @@ void Vmcb::read_vcpu_state(Vcpu_state &state) } if (state.intr_state.charged()) { - write( + v.write( state.intr_state.value()); } /* Guest activity state (actv) not used by SVM */ if (state.tsc_offset.charged()) { /* state.tsc not used by SVM */ - write(read() + - state.tsc_offset.value()); + v.write(v.read() + + state.tsc_offset.value()); } if (state.efer.charged()) { - write(state.efer.value()); + v.write(state.efer.value()); } /* pdpte not used by SVM */ - if (state.star.charged()) write(state.star.value()); - if (state.cstar.charged()) write(state.cstar.value()); - if (state.lstar.charged()) write(state.lstar.value()); - if (state.fmask.charged()) write(state.lstar.value()); + if (state.star.charged()) v.write(state.star.value()); + if (state.cstar.charged()) v.write(state.cstar.value()); + if (state.lstar.charged()) v.write(state.lstar.value()); + if (state.fmask.charged()) v.write(state.lstar.value()); if (state.kernel_gs_base.charged()) - write(state.kernel_gs_base.value()); + v.write(state.kernel_gs_base.value()); if (state.tpr.charged()) - write(state.tpr.value()); + v.write(state.tpr.value()); /* TPR threshold not used on AMD */ } -uint64_t Vmcb::get_exitcode() + +uint64_t Vmcb::handle_vm_exit() { enum Svm_exitcodes : uint64_t { @@ -429,7 +440,7 @@ uint64_t Vmcb::get_exitcode() SVM_VMEXIT_NPF = 0x400, }; - uint64_t exitcode = read(); + uint64_t exitcode = v.read(); switch (exitcode) { case SVM_EXIT_INVALID: error("VM: invalid SVM state!"); @@ -451,7 +462,7 @@ uint64_t Vmcb::get_exitcode() return exitcode; } -void Vmcb::switch_world(addr_t vmcb_phys_addr, Core::Cpu::Context ®s) +void Vmcb::switch_world(Core::Cpu::Context ®s) { /* * We push the host context's physical address to trapno so that @@ -497,7 +508,7 @@ void Vmcb::switch_world(addr_t vmcb_phys_addr, Core::Cpu::Context ®s) GPRs without breaking any */ : : [regs] "r"(®s.r8), [fpu_context] "r"(regs.fpu_context()), - [guest_state] "r"(vmcb_phys_addr), + [guest_state] "r"(vcpu_data.phys_addr + get_page_size()), [trap_vmexit] "i"(TRAP_VMEXIT) : "rax", "memory"); } diff --git a/repos/base-hw/src/core/spec/x86_64/virtualization/kernel/vm.cc b/repos/base-hw/src/core/spec/x86_64/virtualization/kernel/vm.cc index 4e094e2676..30a0a1446d 100644 --- a/repos/base-hw/src/core/spec/x86_64/virtualization/kernel/vm.cc +++ b/repos/base-hw/src/core/spec/x86_64/virtualization/kernel/vm.cc @@ -10,8 +10,6 @@ * This file is part of the Genode OS framework, which is distributed * under the terms of the GNU Affero General Public License version 3. */ - -#include "base/internal/page_size.h" #include #include #include @@ -34,7 +32,6 @@ using namespace Genode; using Kernel::Cpu; using Kernel::Vm; -using Board::Vmcb; Vm::Vm(Irq::Pool & user_irq_pool, @@ -75,8 +72,7 @@ void Vm::proceed(Cpu & cpu) Cpu::Ia32_tsc_aux::write( (Cpu::Ia32_tsc_aux::access_t)_vcpu_context.tsc_aux_guest); - _vcpu_context.vmcb->switch_world(_vcpu_context.vcpu_data.phys_addr + get_page_size(), - *_vcpu_context.regs); + _vcpu_context.virt.switch_world(*_vcpu_context.regs); /* * This will fall into an interrupt or otherwise jump into * _kernel_entry @@ -120,7 +116,7 @@ void Vm::exception(Cpu & cpu) return; } - _vcpu_context.exitcode = _vcpu_context.vmcb->get_exitcode(); + _vcpu_context.exitcode = _vcpu_context.virt.handle_vm_exit(); if (_vcpu_context.exitcode != EXIT_PAUSED) { _pause_vcpu(); @@ -150,21 +146,17 @@ void Vm::_sync_from_vmm() _vcpu_context.read_vcpu_state(_state); } - Board::Vcpu_context::Vcpu_context(unsigned id, Vcpu_data &vcpu_data) : regs(1), - vcpu_data(vcpu_data) + virt(detect_virtualization(vcpu_data, id)) { - vmcb = construct_at(vcpu_data.virt_area, - ((addr_t) vcpu_data.virt_area) + - get_page_size(), id); regs->trapno = TRAP_VMEXIT; } void Board::Vcpu_context::read_vcpu_state(Vcpu_state &state) { - vmcb->read_vcpu_state(state); + virt.read_vcpu_state(state); if (state.cx.charged() || state.dx.charged() || state.bx.charged()) { regs->rax = state.ax.value(); @@ -236,11 +228,11 @@ void Board::Vcpu_context::write_vcpu_state(Vcpu_state &state) state.tsc_aux.charge(tsc_aux_guest); Cpu::Ia32_tsc_aux::write((Cpu::Ia32_tsc_aux::access_t) tsc_aux_host); - vmcb->write_vcpu_state(state); + virt.write_vcpu_state(state); } void Board::Vcpu_context::initialize(Kernel::Cpu &cpu, addr_t table_phys_addr) { - vmcb->initialize(cpu, table_phys_addr); + virt.initialize(cpu, table_phys_addr, *regs); } diff --git a/repos/base-hw/src/core/spec/x86_64/virtualization/svm.h b/repos/base-hw/src/core/spec/x86_64/virtualization/svm.h index e2f3c5b801..565d558f52 100644 --- a/repos/base-hw/src/core/spec/x86_64/virtualization/svm.h +++ b/repos/base-hw/src/core/spec/x86_64/virtualization/svm.h @@ -5,7 +5,7 @@ */ /* - * Copyright (C) 2022 Genode Labs GmbH + * Copyright (C) 2022-2024 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. @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -33,16 +34,14 @@ using Genode::Vcpu_state; using Genode::get_page_size; using Genode::memset; -namespace Kernel -{ -class Cpu; -} +namespace Kernel { class Cpu; } namespace Board { struct Msrpm; struct Iopm; struct Vmcb; + struct Vmcb_buf; } @@ -67,7 +66,7 @@ Board::Iopm * VMCB data structure * See: AMD Manual Vol. 2, Appendix B Layout of VMCB */ -struct Board::Vmcb +struct Board::Vmcb_buf : public Mmio { @@ -77,20 +76,11 @@ struct Board::Vmcb }; - addr_t root_vmcb_phys = { 0 }; + Vmcb_buf(addr_t vmcb_addr, uint32_t id); - Vmcb(addr_t vmcb_page_addr, uint32_t id); - static Vmcb & host_vmcb(size_t cpu_id); static addr_t dummy_msrpm(); - void enforce_intercepts(uint32_t desired_primary = 0U, uint32_t desired_secondary = 0U); static addr_t dummy_iopm(); - void initialize(Kernel::Cpu &cpu, addr_t page_table_phys_addr); - void write_vcpu_state(Vcpu_state &state); - void read_vcpu_state(Vcpu_state &state); - void switch_world(addr_t vmcb_phys_addr, Core::Cpu::Context ®s); - uint64_t get_exitcode(); - /* * AMD Manual Vol. 2, Table B-1: VMCB Layout, Control Area */ @@ -318,4 +308,42 @@ struct Board::Vmcb struct G_pat : Register { }; }; + + +/* + * VMCB data structure + * See: AMD Manual Vol. 2, Appendix B Layout of VMCB + */ +struct Board::Vmcb +: + public Board::Virt_interface +{ + enum + { + Asid_host = 0, + State_off = 1024, + }; + + Vmcb_buf v; + addr_t root_vmcb_phys = { 0 }; + + Vmcb(Vcpu_data &vcpu_data, uint32_t id); + static Vmcb_buf &host_vmcb(size_t cpu_id); + void enforce_intercepts(uint32_t desired_primary = 0U, + uint32_t desired_secondary = 0U); + void initialize(Kernel::Cpu &cpu, + addr_t page_table_phys_addr, + Core::Cpu::Context &) override; + void write_vcpu_state(Vcpu_state &state) override; + void read_vcpu_state(Vcpu_state &state) override; + void switch_world(Core::Cpu::Context ®s) override; + Genode::uint64_t handle_vm_exit() override; + + Virt_type virt_type() override + { + return Virt_type::SVM; + } +}; +static_assert(sizeof(Board::Vmcb) <= get_page_size()); + #endif /* _INCLUDE__SPEC__PC__SVM_H_ */ diff --git a/repos/base-hw/src/core/spec/x86_64/virtualization/virt_interface.h b/repos/base-hw/src/core/spec/x86_64/virtualization/virt_interface.h new file mode 100644 index 0000000000..d7c36d4acd --- /dev/null +++ b/repos/base-hw/src/core/spec/x86_64/virtualization/virt_interface.h @@ -0,0 +1,55 @@ +/* + * \brief Virtualization interface + * \author Benjamin Lamowski + * \date 2024-02-15 + */ + +/* + * Copyright (C) 2024 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU Affero General Public License version 3. + */ + +#ifndef _INCLUDE__SPEC__PC__VIRT_INTERFACE_H_ +#define _INCLUDE__SPEC__PC__VIRT_INTERFACE_H_ + +#include +#include +#include + +using Genode::addr_t; +using Genode::Vcpu_state; + +namespace Kernel { + class Cpu; + class Vm; +}; + +namespace Board +{ +enum Virt_type { + SVM, +}; + +struct Virt_interface +{ + Genode::Vcpu_data &vcpu_data; + + virtual void initialize(Kernel::Cpu &cpu, + addr_t page_table_phys_addr, + Core::Cpu::Context ®s) = 0; + virtual void write_vcpu_state(Vcpu_state &state) = 0; + virtual void read_vcpu_state(Vcpu_state &state) = 0; + virtual void switch_world(Core::Cpu::Context ®s) = 0; + virtual Virt_type virt_type() = 0; + virtual Genode::uint64_t handle_vm_exit() = 0; + + Virt_interface(Genode::Vcpu_data &vcpu_data) : vcpu_data(vcpu_data) + { } + + virtual ~Virt_interface() = default; +}; +} /* namespace Board */ + +#endif /* _INCLUDE__SPEC__PC__VIRT_INTERFACE_H_ */