diff --git a/repos/base-hw/include/spec/arm/cpu/vm_state_trustzone.h b/repos/base-hw/include/spec/arm/cpu/vm_state_trustzone.h index b29f387c44..de28e7a174 100644 --- a/repos/base-hw/include/spec/arm/cpu/vm_state_trustzone.h +++ b/repos/base-hw/include/spec/arm/cpu/vm_state_trustzone.h @@ -1,12 +1,13 @@ /* - * \brief CPU context of a virtual machine for TrustZone - * \author Stefan Kalkowski - * \author Martin Stein - * \date 2013-10-30 + * \brief CPU context of a virtual machine for TrustZone + * \author Stefan Kalkowski + * \author Martin Stein + * \author Benjamin Lamowski + * \date 2013-10-30 */ /* - * Copyright (C) 2013-2017 Genode Labs GmbH + * Copyright (C) 2013-2023 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. @@ -20,6 +21,8 @@ namespace Genode { + enum { VCPU_EXCEPTION_STARTUP = 0xfe }; + /** * CPU context of a virtual machine */ diff --git a/repos/base-hw/include/spec/arm/cpu/vm_state_virtualization.h b/repos/base-hw/include/spec/arm/cpu/vm_state_virtualization.h index fa5d4cccc2..7f2e4294b5 100644 --- a/repos/base-hw/include/spec/arm/cpu/vm_state_virtualization.h +++ b/repos/base-hw/include/spec/arm/cpu/vm_state_virtualization.h @@ -1,11 +1,12 @@ /* - * \brief CPU, PIC, and timer context of a virtual machine - * \author Stefan Kalkowski - * \date 2015-02-10 + * \brief CPU, PIC, and timer context of a virtual machine + * \author Stefan Kalkowski + * \author Benjamin Lamowski + * \date 2015-02-10 */ /* - * Copyright (C) 2015-2017 Genode Labs GmbH + * Copyright (C) 2015-2023 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 +20,8 @@ namespace Genode { + enum { VCPU_EXCEPTION_STARTUP = 0xfe }; + /** * CPU context of a virtual machine */ diff --git a/repos/base-hw/include/spec/arm_64/cpu/vm_state_virtualization.h b/repos/base-hw/include/spec/arm_64/cpu/vm_state_virtualization.h index 933e0feb89..49993fe970 100644 --- a/repos/base-hw/include/spec/arm_64/cpu/vm_state_virtualization.h +++ b/repos/base-hw/include/spec/arm_64/cpu/vm_state_virtualization.h @@ -1,11 +1,12 @@ /* - * \brief CPU, PIC, and timer context of a virtual machine - * \author Stefan Kalkowski - * \date 2015-02-10 + * \brief CPU, PIC, and timer context of a virtual machine + * \author Stefan Kalkowski + * \author Benjamin Lamowski + * \date 2015-02-10 */ /* - * Copyright (C) 2015-2017 Genode Labs GmbH + * Copyright (C) 2015-2023 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 +20,8 @@ namespace Genode { + enum { VCPU_EXCEPTION_STARTUP = 0xfe }; + /** * CPU context of a virtual machine */ diff --git a/repos/base-hw/include/spec/x86_64/cpu/vm_state_virtualization.h b/repos/base-hw/include/spec/x86_64/cpu/vm_state_virtualization.h index 1b72ff5833..227f06f02b 100644 --- a/repos/base-hw/include/spec/x86_64/cpu/vm_state_virtualization.h +++ b/repos/base-hw/include/spec/x86_64/cpu/vm_state_virtualization.h @@ -15,7 +15,7 @@ #define _INCLUDE__SPEC__PC__VM_STATE_H_ /* x86 CPU state */ -#include +#include #include namespace Genode { @@ -24,8 +24,13 @@ namespace Genode { * CPU context of a virtual machine */ struct Vm_data; + + struct Vm_state; } +struct Genode::Vm_state : Genode::Vcpu_state +{ +}; struct Genode::Vm_data { diff --git a/repos/base-hw/include/spec/x86_64/virtualization/extended_vcpu_state.h b/repos/base-hw/include/spec/x86_64/virtualization/extended_vcpu_state.h deleted file mode 100644 index db4acf13e5..0000000000 --- a/repos/base-hw/include/spec/x86_64/virtualization/extended_vcpu_state.h +++ /dev/null @@ -1,139 +0,0 @@ -/* - * \brief Extended vCPU state - * \author Benjamin Lamowski - * \date 2023-05-25 - */ - -/* - * Copyright (C) 2023 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__X86_64_VIRTUALIZATION__EXTENDED_VCPU_STATE_H -#define _INCLUDE__SPEC__X86_64_VIRTUALIZATION__EXTENDED_VCPU_STATE_H - -/* x86 CPU state */ -#include -#include - -namespace Genode { - struct Vm_state; - struct Vcpu_run_state; -} - -/* - * Run state of the Vcpu to sync between the VMM library and the - * kernel. - */ -class Genode::Vcpu_run_state : Noncopyable -{ - public: - - enum Value : int { - /* - * vCPU isn't initialized yet. Needed for initialization in - * Vm::exception() and to block premature pause requests. - */ - STARTUP = 0, - - /* - * The vCPU is runable but not yet running. Used in pause() to - * make the vCPU run once (RUN_ONCE) - */ - RUNNABLE = 1, - - /* - * The vCPU hasn't run yet, but a pause has been requested. - * Run the vCPU once, dispatch the result and then issue a pause - * request. - */ - RUN_ONCE = 2, - - /* - * The vCPU is running. Used in pause() to force an exit only when the - * vCPU is actually running. - */ - RUNNING = 3, - - /* - * vCPU has exited because of an external interrupt and could run - * without state syncing. Needed to skip state syncing in Vm::proceed - * and to request updating the state from the vCPU in case of a - * Vcpu::pause() (SYNC_FROM_VCPU) - */ - INTERRUPTIBLE = 4, - - /* - * vCPU is running and is being forced out by a thread on a remote core - * by signaling the vCPU's handler. Causes a state writeback and - * Vm::pause() after an external interrupt VM exit. - */ - EXITING = 5, - - /* - * A Vcpu::pause() request was issued while the vCPU was INTERRUPTIBLE. - * Skips the next run in Vm::proceed() and causes a full pause exit in - * the subsequent Vm::exception(). - */ - SYNC_FROM_VCPU = 6, - - /* - * The vCPU is dispatching a signal to the handler in the VMM. Needed to - * distinguish between a dispatch from the vCPU and a dispatch from an - * assynchronous pause request. - */ - DISPATCHING = 7, - - /* - * The vCPU needs to first dispatch an exit in the VMM, and a pause - * request needs to be injected right after. - */ - DISPATCHING_PAUSED = 8, - - /* - * An exit has been dispatched to the VMM. Needed to let - * an assynchrous pause request dispatch a new signal. - */ - DISPATCHED = 9, - - /* - * The vCPU was RUNNABLE, or DISPATCHED but a pause has been requested. - * Used to create a pause exit in the wrapper. - */ - PAUSING = 10, - - /* - * The vCPU handler in the VMM is dispatching and a pause - * signal has been issued. Needed to skip more pause requests. - * FIXME - */ - PAUSED = 11 - }; - - private: - - int _value { }; /* base type of Vcpu_run_state */ - - public: - - Value value() const { return Value(_value); } - - void set(Value const &value) - { - _value = value; - } - - bool cas(Value cmp_val, Value new_val) - { - return cmpxchg(&_value, cmp_val, new_val); - } -}; - -struct Genode::Vm_state : Genode::Vcpu_state -{ - Vcpu_run_state run_state; -}; - -#endif /* _INCLUDE__SPEC__X86_64_VIRTUALIZATION__EXTENDED_VCPU_STATE_H */ diff --git a/repos/base-hw/src/core/kernel/vm.h b/repos/base-hw/src/core/kernel/vm.h index 056de2920e..628769986b 100644 --- a/repos/base-hw/src/core/kernel/vm.h +++ b/repos/base-hw/src/core/kernel/vm.h @@ -1,11 +1,12 @@ /* * \brief Kernel backend for virtual machines * \author Martin Stein + * \author Benjamin Lamowski * \date 2013-10-30 */ /* - * Copyright (C) 2013-2017 Genode Labs GmbH + * Copyright (C) 2013-2023 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. @@ -62,6 +63,16 @@ class Kernel::Vm : private Kernel::Object, public Cpu_job Scheduler_state _scheduled = INACTIVE; Board::Vcpu_context _vcpu_context; + void _sync_to_vmm(); + void _sync_from_vmm(); + void _pause_vcpu() + { + if (_scheduled != INACTIVE) + Cpu_job::_deactivate_own_share(); + + _scheduled = INACTIVE; + } + public: /** @@ -125,16 +136,15 @@ class Kernel::Vm : private Kernel::Object, public Cpu_job void run() { + _sync_from_vmm(); if (_scheduled != ACTIVE) Cpu_job::_activate_own_share(); _scheduled = ACTIVE; } void pause() { - if (_scheduled != INACTIVE) - Cpu_job::_deactivate_own_share(); - - _scheduled = INACTIVE; + _pause_vcpu(); + _sync_to_vmm(); } diff --git a/repos/base-hw/src/core/spec/arm_v7/trustzone/kernel/vm.cc b/repos/base-hw/src/core/spec/arm_v7/trustzone/kernel/vm.cc index 91f6e316a3..e4bca8ee8c 100644 --- a/repos/base-hw/src/core/spec/arm_v7/trustzone/kernel/vm.cc +++ b/repos/base-hw/src/core/spec/arm_v7/trustzone/kernel/vm.cc @@ -1,12 +1,13 @@ /* - * \brief Kernel backend for virtual machines - * \author Martin Stein - * \author Stefan Kalkowski - * \date 2013-10-30 + * \brief Kernel backend for virtual machines + * \author Martin Stein + * \author Stefan Kalkowski + * \author Benjamin Lamowski + * \date 2013-10-30 */ /* - * Copyright (C) 2013-2017 Genode Labs GmbH + * Copyright (C) 2013-2023 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. @@ -35,6 +36,10 @@ Vm::Vm(Irq::Pool & user_irq_pool, _vcpu_context(cpu) { affinity(cpu); + /* once constructed, exit with a startup exception */ + pause(); + _state.cpu_exception = Genode::VCPU_EXCEPTION_STARTUP; + _context.submit(1); } @@ -79,3 +84,11 @@ void Vm::proceed(Cpu & cpu) monitor_mode_enter_normal_world(_state, (void*) cpu.stack_start()); } + + +void Vm::_sync_to_vmm() +{} + + +void Vm::_sync_from_vmm() +{} diff --git a/repos/base-hw/src/core/spec/arm_v7/virtualization/kernel/vm.cc b/repos/base-hw/src/core/spec/arm_v7/virtualization/kernel/vm.cc index 64ba52e1a2..56c31cee7b 100644 --- a/repos/base-hw/src/core/spec/arm_v7/virtualization/kernel/vm.cc +++ b/repos/base-hw/src/core/spec/arm_v7/virtualization/kernel/vm.cc @@ -1,11 +1,12 @@ /* - * \brief Kernel backend for virtual machines - * \author Stefan Kalkowski - * \date 2015-02-10 + * \brief Kernel backend for virtual machines + * \author Stefan Kalkowski + * \author Benjamin Lamowski + * \date 2015-02-10 */ /* - * Copyright (C) 2015-2017 Genode Labs GmbH + * Copyright (C) 2015-2023 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. @@ -147,6 +148,10 @@ Kernel::Vm::Vm(Irq::Pool & user_irq_pool, _vcpu_context(cpu) { affinity(cpu); + /* once constructed, exit with a startup exception */ + pause(); + _state.cpu_exception = Genode::VCPU_EXCEPTION_STARTUP; + _context.submit(1); } @@ -201,6 +206,14 @@ void Kernel::Vm::proceed(Cpu & cpu) } +void Vm::_sync_to_vmm() +{} + + +void Vm::_sync_from_vmm() +{} + + void Vm::inject_irq(unsigned irq) { _state.irqs.last_irq = irq; diff --git a/repos/base-hw/src/core/spec/arm_v8/virtualization/kernel/vm.cc b/repos/base-hw/src/core/spec/arm_v8/virtualization/kernel/vm.cc index 5925fb1585..da41235d83 100644 --- a/repos/base-hw/src/core/spec/arm_v8/virtualization/kernel/vm.cc +++ b/repos/base-hw/src/core/spec/arm_v8/virtualization/kernel/vm.cc @@ -1,11 +1,12 @@ /* - * \brief Kernel backend for virtual machines - * \author Stefan Kalkowski - * \date 2015-02-10 + * \brief Kernel backend for virtual machines + * \author Stefan Kalkowski + * \author Benjamin Lamowski + * \date 2015-02-10 */ /* - * Copyright (C) 2015-2017 Genode Labs GmbH + * Copyright (C) 2015-2023 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. @@ -149,6 +150,11 @@ Vm::Vm(Irq::Pool & user_irq_pool, _state.ccsidr_data_el1[level] = Cpu::Ccsidr_el1::read(); } } + + /* once constructed, exit with a startup exception */ + pause(); + _state.exception_type = Genode::VCPU_EXCEPTION_STARTUP; + _context.submit(1); } @@ -208,6 +214,14 @@ void Vm::proceed(Cpu & cpu) } +void Vm::_sync_to_vmm() +{} + + +void Vm::_sync_from_vmm() +{} + + void Vm::inject_irq(unsigned irq) { _state.irqs.last_irq = irq; 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 8e0a47418c..fe74909891 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 Genode Labs GmbH + * Copyright (C) 2022-2023 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. @@ -41,6 +41,7 @@ namespace Board { /* FIXME move into Vcpu_context as 'enum class' when we have C++20 */ enum Platform_exitcodes : Genode::uint64_t { EXIT_NPF = 0xfc, + EXIT_INIT = 0xfd, EXIT_STARTUP = 0xfe, EXIT_PAUSED = 0xff, }; @@ -64,12 +65,13 @@ struct Board::Vcpu_context Genode::addr_t context_phys_addr); void initialize_svm(Kernel::Cpu &cpu, void *table); void read_vcpu_state(Genode::Vcpu_state &state); - void write_vcpu_state(Genode::Vcpu_state &state, unsigned exit_reason); + void write_vcpu_state(Genode::Vcpu_state &state); Vmcb &vmcb; Genode::Align_at regs; Genode::uint64_t tsc_aux_host = 0U; Genode::uint64_t tsc_aux_guest = 0U; + Genode::uint64_t exitcode = EXIT_INIT; }; #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 38ca81fb76..2a75043e7b 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 @@ -1,11 +1,11 @@ /* - * \brief SVM specific implementations - * \author Benjamin Lamowski - * \date 2022-10-14 + * \brief SVM specific implementations + * \author Benjamin Lamowski + * \date 2022-10-14 */ /* - * Copyright (C) 2022 Genode Labs GmbH + * Copyright (C) 2022-2023 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. @@ -156,12 +156,12 @@ void Board::Vcpu_context::initialize_svm(Kernel::Cpu & cpu, void * table) } -void Board::Vcpu_context::write_vcpu_state(Genode::Vcpu_state &state, unsigned exit_reason) +void Board::Vcpu_context::write_vcpu_state(Genode::Vcpu_state &state) { typedef Genode::Vcpu_state::Range Range; state.discharge(); - state.exit_reason = exit_reason; + state.exit_reason = (unsigned) exitcode; state.fpu.charge([&] (Genode::Vcpu_state::Fpu::State &fpu) { memcpy(&fpu, (void *) regs->fpu_context(), sizeof(fpu)); 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 4969c30eb7..2f55384348 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 @@ -1,11 +1,11 @@ /* - * \brief Kernel backend for x86 virtual machines - * \author Benjamin Lamowski - * \date 2022-10-14 + * \brief Kernel backend for x86 virtual machines + * \author Benjamin Lamowski + * \date 2022-10-14 */ /* - * Copyright (C) 2022 Genode Labs GmbH + * Copyright (C) 2022-2023 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. @@ -33,7 +33,6 @@ using Genode::addr_t; using Kernel::Cpu; using Kernel::Vm; using Board::Vmcb; -using Vcpu_run_state = Genode::Vcpu_run_state; Vm::Vm(Irq::Pool & user_irq_pool, @@ -51,7 +50,6 @@ Vm::Vm(Irq::Pool & user_irq_pool, _vcpu_context(id.id, &data.vmcb, data.vmcb_phys_addr) { affinity(cpu); - _state.run_state.set(Vcpu_run_state::STARTUP); } @@ -65,53 +63,31 @@ void Vm::proceed(Cpu & cpu) using namespace Board; cpu.switch_to(*_vcpu_context.regs); - bool do_world_switch = false; - - switch (_state.run_state.value()) { - case Vcpu_run_state::STARTUP: break; - case Vcpu_run_state::SYNC_FROM_VCPU: break; - case Vcpu_run_state::PAUSING: break; - case Vcpu_run_state::INTERRUPTIBLE: - if (_state.run_state.cas(Vcpu_run_state::INTERRUPTIBLE, - Vcpu_run_state::RUNNING)) - do_world_switch = true; - break; - case Vcpu_run_state::RUNNABLE: - _state.run_state.cas(Vcpu_run_state::RUNNABLE, - Vcpu_run_state::RUNNING); - [[fallthrough]]; - case Vcpu_run_state::RUN_ONCE: - _vcpu_context.read_vcpu_state(_state); - do_world_switch = true; - break; - default: - Genode::error("proceed: illegal state ", - Genode::Hex(_state.run_state.value())); - } - - if (do_world_switch) { - Cpu::Ia32_tsc_aux::write((Cpu::Ia32_tsc_aux::access_t) _vcpu_context.tsc_aux_guest); - - /* - * We push the host context's physical address to trapno so that - * we can pop it later - * */ - _vcpu_context.regs->trapno = _vcpu_context.vmcb.root_vmcb_phys; - Hypervisor::switch_world(_vcpu_context.vmcb.phys_addr, - (addr_t)&_vcpu_context.regs->r8, - _vcpu_context.regs->fpu_context()); - /* - * This will fall into an interrupt or otherwise jump into - * _kernel_entry - * */ - } else { + if (_vcpu_context.exitcode == EXIT_INIT) { _vcpu_context.regs->trapno = TRAP_VMSKIP; Hypervisor::restore_state_for_entry((addr_t)&_vcpu_context.regs->r8, _vcpu_context.regs->fpu_context()); /* jumps to _kernel_entry */ } + + Cpu::Ia32_tsc_aux::write( + (Cpu::Ia32_tsc_aux::access_t)_vcpu_context.tsc_aux_guest); + + /* + * We push the host context's physical address to trapno so that + * we can pop it later + */ + _vcpu_context.regs->trapno = _vcpu_context.vmcb.root_vmcb_phys; + Hypervisor::switch_world(_vcpu_context.vmcb.phys_addr, + (addr_t) &_vcpu_context.regs->r8, + _vcpu_context.regs->fpu_context()); + /* + * This will fall into an interrupt or otherwise jump into + * _kernel_entry + */ } + void Vm::exception(Cpu & cpu) { using namespace Board; @@ -134,7 +110,7 @@ void Vm::exception(Cpu & cpu) " at ip=", (void *)_vcpu_context.regs->ip, " sp=", (void *)_vcpu_context.regs->sp); - pause(); + _pause_vcpu(); return; }; @@ -144,73 +120,58 @@ void Vm::exception(Cpu & cpu) VMEXIT_NPF = 0x400, }; - switch (_state.run_state.value()) { - case Vcpu_run_state::STARTUP: + if (_vcpu_context.exitcode == EXIT_INIT) { _vcpu_context.initialize_svm(cpu, _id.table); _vcpu_context.tsc_aux_host = cpu.id(); - _vcpu_context.write_vcpu_state(_state, EXIT_STARTUP); - _state.run_state.set(Vcpu_run_state::DISPATCHING); - pause(); + _vcpu_context.exitcode = EXIT_STARTUP; + _pause_vcpu(); _context.submit(1); return; - case Vcpu_run_state::SYNC_FROM_VCPU: - _vcpu_context.write_vcpu_state(_state, EXIT_PAUSED); - _state.run_state.set(Vcpu_run_state::PAUSED); - pause(); - _context.submit(1); - return; - case Vcpu_run_state::EXITING: break; - case Vcpu_run_state::RUNNING: break; - case Vcpu_run_state::RUN_ONCE: break; - case Vcpu_run_state::PAUSING: return; - default: - Genode::error("exception: illegal state ", - Genode::Hex(_state.run_state.value())); } - Genode::uint64_t exitcode = _vcpu_context.vmcb.read(); + _vcpu_context.exitcode = _vcpu_context.vmcb.read(); - switch (exitcode) { + switch (_vcpu_context.exitcode) { case VMEXIT_INVALID: Genode::error("Vm::exception: invalid SVM state!"); return; case 0x40 ... 0x5f: Genode::error("Vm::exception: unhandled SVM exception ", - Genode::Hex(exitcode)); + Genode::Hex(_vcpu_context.exitcode)); return; case VMEXIT_INTR: - if (!_state.run_state.cas(Vcpu_run_state::RUNNING, - Vcpu_run_state::INTERRUPTIBLE)) - { - _vcpu_context.write_vcpu_state(_state, EXIT_PAUSED); - - /* - * If the interruptible state couldn't be set, the state might - * be EXITING and a pause() signal might have already been send - * (to cause the vCPU exit in the first place). - */ - bool submit = false; - /* In the RUN_ONCE case, first we will need to send a signal. */ - if (_state.run_state.value() == Vcpu_run_state::RUN_ONCE) - submit = true; - - _state.run_state.set(Vcpu_run_state::PAUSED); - pause(); - - if (submit) - _context.submit(1); - } + _vcpu_context.exitcode = EXIT_PAUSED; return; case VMEXIT_NPF: - exitcode = EXIT_NPF; + _vcpu_context.exitcode = EXIT_NPF; [[fallthrough]]; default: - _vcpu_context.write_vcpu_state(_state, (unsigned) exitcode); - _state.run_state.set(Vcpu_run_state::DISPATCHING); - pause(); + _pause_vcpu(); _context.submit(1); return; - }; + } +} + + +void Vm::_sync_to_vmm() +{ + _vcpu_context.write_vcpu_state(_state); + + /* + * Set exit code so that if _run() was not called after an exit, the + * next exit due to a signal will be interpreted as PAUSE request. + */ + _vcpu_context.exitcode = Board::EXIT_PAUSED; +} + + +void Vm::_sync_from_vmm() +{ + /* first run() will skip through to issue startup exit */ + if (_vcpu_context.exitcode == Board::EXIT_INIT) + return; + + _vcpu_context.read_vcpu_state(_state); } diff --git a/repos/base-hw/src/lib/base/vm.cc b/repos/base-hw/src/lib/base/vm.cc index 009efdf158..d5c7aecd22 100644 --- a/repos/base-hw/src/lib/base/vm.cc +++ b/repos/base-hw/src/lib/base/vm.cc @@ -1,5 +1,5 @@ /* - * \brief Client-side VM session interface + * \brief Client-side VM session interface (base-hw generic) * \author Alexander Boettcher * \date 2018-08-27 */ @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -26,6 +27,7 @@ using namespace Genode; using Exit_config = Vm_connection::Exit_config; +using Call_with_state = Vm_connection::Call_with_state; /**************************** @@ -38,20 +40,24 @@ struct Hw_vcpu : Rpc_client, Noncopyable Attached_dataspace _state; Native_capability _kernel_vcpu { }; + void *_ep_handler { nullptr }; Capability _create_vcpu(Vm_connection &, Vcpu_handler_base &); + Vcpu_state & _local_state() + { + return *_state.local_addr(); + } + public: + const Hw_vcpu& operator=(const Hw_vcpu &) = delete; + Hw_vcpu(const Hw_vcpu&) = delete; + Hw_vcpu(Env &, Vm_connection &, Vcpu_handler_base &); - void run() { - Kernel::run_vm(Capability_space::capid(_kernel_vcpu)); } - void pause() { - Kernel::pause_vm(Capability_space::capid(_kernel_vcpu)); } - - Vcpu_state & state() { return *_state.local_addr(); } + void with_state(Call_with_state &); }; @@ -60,11 +66,25 @@ Hw_vcpu::Hw_vcpu(Env &env, Vm_connection &vm, Vcpu_handler_base &handler) Rpc_client(_create_vcpu(vm, handler)), _state(env.rm(), vm.with_upgrade([&] () { return call(); })) { + _ep_handler = reinterpret_cast(&handler.rpc_ep()); call(handler.signal_cap()); _kernel_vcpu = call(); } +void Hw_vcpu::with_state(Call_with_state &cw) +{ + if (Thread::myself() != _ep_handler) { + error("vCPU state requested outside of vcpu_handler EP"); + sleep_forever(); + } + Kernel::pause_vm(Capability_space::capid(_kernel_vcpu)); + + if (cw.call_with_state(_local_state())) + Kernel::run_vm(Capability_space::capid(_kernel_vcpu)); +} + + Capability Hw_vcpu::_create_vcpu(Vm_connection &vm, Vcpu_handler_base &handler) { @@ -79,9 +99,7 @@ Capability Hw_vcpu::_create_vcpu(Vm_connection &vm, ** vCPU API ** **************/ -void Vm_connection::Vcpu::run() { static_cast(_native_vcpu).run(); } -void Vm_connection::Vcpu::pause() { static_cast(_native_vcpu).pause(); } -Vcpu_state & Vm_connection::Vcpu::state() { return static_cast(_native_vcpu).state(); } +void Vm_connection::Vcpu::_with_state(Call_with_state &cw) { static_cast(_native_vcpu).with_state(cw); } Vm_connection::Vcpu::Vcpu(Vm_connection &vm, Allocator &alloc, diff --git a/repos/base-hw/src/lib/base/x86_64/vm.cc b/repos/base-hw/src/lib/base/x86_64/vm.cc index 441360017f..0687dd4357 100644 --- a/repos/base-hw/src/lib/base/x86_64/vm.cc +++ b/repos/base-hw/src/lib/base/x86_64/vm.cc @@ -1,5 +1,5 @@ /* - * \brief Client-side VM session interface + * \brief Client-side VM session interface (x86_64 specific) * \author Alexander Boettcher * \author Benjamin Lamowski * \date 2018-08-27 @@ -17,8 +17,8 @@ #include #include #include +#include #include -#include #include #include @@ -29,6 +29,7 @@ using namespace Genode; using Exit_config = Vm_connection::Exit_config; +using Call_with_state = Vm_connection::Call_with_state; /**************************** @@ -42,17 +43,13 @@ struct Hw_vcpu : Rpc_client, Noncopyable Attached_dataspace _state; Native_capability _kernel_vcpu { }; Vcpu_handler_base & _vcpu_handler; - Thread * _ep_handler { nullptr }; unsigned _id { 0 }; - Vcpu_state _stashed_state { }; - bool _need_state_update { false }; - bool _extra_pause { false }; - Vcpu_handler _wrapper; + void *_ep_handler { nullptr }; - void _wrapper_dispatch(); - void _prepare_pause_exit(); - void _update_charged_state(Vcpu_state & old_state, Vcpu_state & new_state); - Capability _create_vcpu(Vm_connection &, Vcpu_handler_base &); + void _run(); + Vcpu_state & _local_state() { return *_state.local_addr(); } + Capability _create_vcpu(Vm_connection &, + Vcpu_handler_base &); public: @@ -61,10 +58,8 @@ struct Hw_vcpu : Rpc_client, Noncopyable Hw_vcpu(Env &, Vm_connection &, Vcpu_handler_base &); - void run(); - void pause(); + void with_state(Call_with_state &); - Vm_state & state() { return *_state.local_addr(); } }; @@ -72,383 +67,33 @@ Hw_vcpu::Hw_vcpu(Env &env, Vm_connection &vm, Vcpu_handler_base &handler) : Rpc_client(_create_vcpu(vm, handler)), _state(env.rm(), vm.with_upgrade([&] () { return call(); })), - _vcpu_handler(handler), - _wrapper(handler.ep(), *this, &Hw_vcpu::_wrapper_dispatch) + _vcpu_handler(handler) { static unsigned counter = 0; - call(_wrapper.signal_cap()); + call(handler.signal_cap()); _kernel_vcpu = call(); _id = counter++; + _ep_handler = reinterpret_cast(&handler.rpc_ep()); + _run(); } -void Hw_vcpu::_wrapper_dispatch() +void Hw_vcpu::_run() { - /* - * If this is running, the VM is not. Either it hasn't, or it has been - * forced out and has written any state back. - */ - - /* - * We run from the same EP as the orignal dispatch handler that - * will call run() from its dispatch loop, set _ep_handler. - */ - if (!_ep_handler) - _ep_handler = Thread::myself(); - - int run_state = state().run_state.value(); - - /* - * In case the VMM dispatch method waits for a pause signal, - * we need a different state to make the pause() method - * send another signal. - */ - if (run_state == Vcpu_run_state::DISPATCHING) - state().run_state.set(Vcpu_run_state::DISPATCHED); - - if (run_state == Vcpu_run_state::DISPATCHING_PAUSED) - state().run_state.set(Vcpu_run_state::PAUSING); - - /* - * Dispatch the exit originating from the vCPU - */ - if (run_state == Vcpu_run_state::DISPATCHING || - run_state == Vcpu_run_state::DISPATCHING_PAUSED || - run_state == Vcpu_run_state::PAUSED) { - /* Call the VMM's dispatch method. */ - _vcpu_handler.dispatch(1); - /* - * Dispatch will probably have called run(), but if the state is set - * to PAUSING it won't. - */ - } - - /* - * Dispatch a possible folded in pause signal. - * Note that we only the local run_state against pausing. - * If the DISPATCHED state was changed to PAUSING in between, pause() - * has sent a new signal. - */ - if (run_state == Vcpu_run_state::PAUSING || - run_state == Vcpu_run_state::DISPATCHING_PAUSED || - _extra_pause) { - Kernel::pause_vm(Capability_space::capid(_kernel_vcpu)); - _update_charged_state(_stashed_state, state()); - /* - * Tell run() to get any stashed state from the original dispatch. - * Necessary because that state is discharged when the VMM - * dispatches and would be lost otherwise. - */ - _need_state_update = true; - _extra_pause = false; - _prepare_pause_exit(); - state().run_state.set(Vcpu_run_state::PAUSED); - _vcpu_handler.dispatch(1); - } -} - - -void Hw_vcpu::run() -{ - if (_need_state_update) { - _update_charged_state(state(), _stashed_state); - _stashed_state.discharge(); - _need_state_update = false; - } - - switch (state().run_state.value()) { - case Vcpu_run_state::STARTUP: - break; - case Vcpu_run_state::DISPATCHED: - if (_ep_handler != Thread::myself()) { - Genode::error("Vcpu (", _id, ") run: setting run from remote CPU unsupported"); - return; - } - - if (!state().run_state.cas(Vcpu_run_state::DISPATCHED, - Vcpu_run_state::RUNNABLE)) - return; /* state changed to PAUSING */ - break; - case Vcpu_run_state::PAUSED: - state().run_state.set(Vcpu_run_state::RUNNABLE); - /* - * It is the VMM's responsibility to be reasonable here. - * If Vcpu::run() is called assynchronously while the vCPU handler - * is still dispatching a request before pause this breaks. - */ - if (_ep_handler != Thread::myself()) - Genode::warning("Vcpu (", _id, ") run: asynchronous call of run()"); - break; - case Vcpu_run_state::PAUSING: - return; - default: - Genode::error("Vcpu (", _id, ") run: ignoring state ", - Genode::Hex(state().run_state.value())); - return; - } - Kernel::run_vm(Capability_space::capid(_kernel_vcpu)); } -void Hw_vcpu::pause() +void Hw_vcpu::with_state(Call_with_state &cw) { - switch (state().run_state.value()) { - /* - * Ignore pause requests before the vCPU has started up. - */ - case Vcpu_run_state::STARTUP: - return; + if (Thread::myself() != _ep_handler) { + error("vCPU state requested outside of vcpu_handler EP"); + sleep_forever(); + } + Kernel::pause_vm(Capability_space::capid(_kernel_vcpu)); - /* - * When a pause is requested while starting or dispatching, the original - * exit needs to be handled before a pause exit can be injected. - * In these two cases it may happen be that the pause signal would be - * folded in with the signal from the kernel, therefore we need to make - * sure that the wrapper will prepare the pause exit anyway. - */ - case Vcpu_run_state::DISPATCHING: - if (!state().run_state.cas(Vcpu_run_state::DISPATCHING, - Vcpu_run_state::DISPATCHING_PAUSED)) - pause(); /* moved on to DISPATCHED, retry */ - return; - - /* - * The vCPU could run anytime. Switch to RUN_ONCE to make the kernel - * exit and send a signal after running. - * If the state has changed, it must be to running, in that case retry - * the pause. - */ - case Vcpu_run_state::RUNNABLE: - if (!state().run_state.cas(Vcpu_run_state::RUNNABLE, - Vcpu_run_state::RUN_ONCE)) - { - pause(); - return; - } - - _extra_pause = true; - return; - - /* - * The vCPU may be running, signal that any interrupt exit is because it - * is forced out. - * - * If the CPU is already at the beginning of the exception handling, - * the handler will get two signals: whatever the normal exit would have - * been and the pause exit straight after, which is ok. - * - * If the state is written after it was already switched to - * INTERRUPTIBLE in the exit handler, we simply retry. - */ - case Vcpu_run_state::RUNNING: - if (_ep_handler == Thread::myself()) { - Genode::error("Vcpu (", _id, " ) pause: illegal state in line ", __LINE__ ); - return; - }; - - if (!state().run_state.cas(Vcpu_run_state::RUNNING, - Vcpu_run_state::EXITING)) { - pause(); - return; - } - break; - - /* - * A pause request is received when the CPU was already forced out. - * In this case we need to write the state back first and send the - * signal later. If this comes from another thread then it may be - * interrupted after reading the state, while the vCPU thread starts - * RUNNING. Therefore if the swap fails, retry the pause(). - */ - case Vcpu_run_state::INTERRUPTIBLE: - if (!state().run_state.cas(Vcpu_run_state::INTERRUPTIBLE, - Vcpu_run_state::SYNC_FROM_VCPU)) - pause(); - return; - - /* - * A pause is requested while the VM has been dispatched. - * Send a new signal in case the VMM waits for a pause() exit - * before doing another run. - */ - case Vcpu_run_state::DISPATCHED: - if (!state().run_state.cas(Vcpu_run_state::DISPATCHED, - Vcpu_run_state::PAUSING)) { - pause(); - return; - } - break; - - /* - * We're already pausing or paused, ignore it. - */ - default: - return; - } - - _wrapper.local_submit(); -} - - -/* - * Prepare a pause exit to dispatch to the VMM. - * Because we don't do a round trip to the kernel we charge some state to keep - * seoul happy. - */ -void Hw_vcpu::_prepare_pause_exit() -{ - state().exit_reason = 0xFF; - state().ax.set_charged(); - state().bx.set_charged(); - state().cx.set_charged(); - state().dx.set_charged(); - - state().bp.set_charged(); - state().di.set_charged(); - state().si.set_charged(); - - state().flags.set_charged(); - - state().sp.set_charged(); - - state().ip.set_charged(); - state().ip_len.set_charged(); - - state().qual_primary.set_charged(); - state().qual_secondary.set_charged(); - - state().intr_state.set_charged(); - state().actv_state.set_charged(); - - state().inj_info.set_charged(); - state().inj_error.set_charged(); -} - - -/* - * Update fields not already charged from one Vcpu_state to the other. - */ -void Hw_vcpu::_update_charged_state(Vcpu_state & old_state, Vcpu_state & new_state) -{ - if (new_state.ax.charged() || new_state.cx.charged() || - new_state.dx.charged() || new_state.bx.charged()) { - old_state.ax.update(new_state.ax.value()); - old_state.cx.update(new_state.cx.value()); - old_state.dx.update(new_state.dx.value()); - old_state.bx.update(new_state.bx.value()); - } - if (new_state.bp.charged() || new_state.di.charged() || - new_state.si.charged()) { - old_state.bp.update(new_state.bp.value()); - old_state.si.update(new_state.si.value()); - old_state.di.update(new_state.di.value()); - } - if (new_state.sp.charged()) { - old_state.sp.update(new_state.sp.value()); - } - if (new_state.ip.charged()) { - old_state.ip.update(new_state.ip.value()); - old_state.ip_len.update(new_state.ip_len.value()); - } - if (new_state.flags.charged()) { - old_state.flags.update(new_state.flags.value()); - } - if (new_state.es.charged() || new_state.ds.charged()) { - old_state.es.update(new_state.es.value()); - old_state.ds.update(new_state.ds.value()); - } - if (new_state.fs.charged() || new_state.gs.charged()) { - old_state.fs.update(new_state.fs.value()); - old_state.gs.update(new_state.gs.value()); - } - if (new_state.cs.charged() || new_state.ss.charged()) { - old_state.cs.update(new_state.cs.value()); - old_state.ss.update(new_state.ss.value()); - } - if (new_state.tr.charged()) { - old_state.tr.update(new_state.tr.value()); - } - if (new_state.ldtr.charged()) { - old_state.ldtr.update(new_state.ldtr.value()); - } - if (new_state.gdtr.charged()) { - old_state.gdtr.update(new_state.gdtr.value()); - } - if (new_state.idtr.charged()) { - old_state.idtr.update(new_state.idtr.value()); - } - if (new_state.cr0.charged() || new_state.cr2.charged() || - new_state.cr3.charged() || new_state.cr4.charged()) { - old_state.cr0.update(new_state.cr0.value()); - old_state.cr2.update(new_state.cr2.value()); - old_state.cr3.update(new_state.cr3.value()); - old_state.cr4.update(new_state.cr4.value()); - } - if (new_state.dr7.charged()) { - old_state.dr7.update(new_state.dr7.value()); - } - if (new_state.sysenter_cs.charged() || new_state.sysenter_sp.charged() || - new_state.sysenter_ip.charged()) { - old_state.sysenter_ip.update(new_state.sysenter_ip.value()); - old_state.sysenter_sp.update(new_state.sysenter_sp.value()); - old_state.sysenter_cs.update(new_state.sysenter_cs.value()); - } - if (new_state.ctrl_primary.charged() || - new_state.ctrl_secondary.charged()) { - old_state.ctrl_primary.update(new_state.ctrl_primary.value()); - old_state.ctrl_secondary.update(new_state.ctrl_secondary.value()); - } - if (new_state.inj_info.charged() || new_state.inj_error.charged()) { - old_state.inj_info.update(new_state.inj_info.value()); - old_state.inj_error.update(new_state.inj_error.value()); - } - if (new_state.intr_state.charged() || new_state.actv_state.charged()) { - old_state.intr_state.update(new_state.intr_state.value()); - old_state.actv_state.update(new_state.actv_state.value()); - } - if (new_state.tsc_offset.charged()) { - old_state.tsc.update(new_state.tsc.value()); - old_state.tsc_offset.update(new_state.tsc_offset.value()); - old_state.tsc_aux.update(new_state.tsc_aux.value()); - } - if (new_state.efer.charged()) { - old_state.efer.update(new_state.efer.value()); - } - if (new_state.pdpte_0.charged() || new_state.pdpte_1.charged() || - new_state.pdpte_2.charged() || new_state.pdpte_3.charged()) { - old_state.pdpte_0.update(new_state.pdpte_0.value()); - old_state.pdpte_1.update(new_state.pdpte_1.value()); - old_state.pdpte_2.update(new_state.pdpte_2.value()); - old_state.pdpte_3.update(new_state.pdpte_3.value()); - } - - if (new_state.r8 .charged() || new_state.r9 .charged() || - new_state.r10.charged() || new_state.r11.charged() || - new_state.r12.charged() || new_state.r13.charged() || - new_state.r14.charged() || new_state.r15.charged()) { - old_state.r8.update(new_state.r8.value()); - old_state.r9.update(new_state.r9.value()); - old_state.r10.update(new_state.r10.value()); - old_state.r11.update(new_state.r11.value()); - old_state.r12.update(new_state.r12.value()); - old_state.r13.update(new_state.r13.value()); - old_state.r14.update(new_state.r14.value()); - old_state.r15.update(new_state.r15.value()); - } - if (new_state.star .charged() || new_state.lstar.charged() || - new_state.cstar.charged() || new_state.fmask.charged() || - new_state.kernel_gs_base.charged()) { - old_state.star.update(new_state.star.value()); - old_state.lstar.update(new_state.lstar.value()); - old_state.cstar.update(new_state.cstar.value()); - old_state.fmask.update(new_state.fmask.value()); - old_state.kernel_gs_base.update(new_state.kernel_gs_base.value()); - } - if (new_state.tpr.charged() || new_state.tpr_threshold.charged()) { - old_state.tpr.update(new_state.tpr.value()); - old_state.tpr_threshold.update(new_state.tpr_threshold.value()); - } + if(cw.call_with_state(_local_state())) + _run(); } @@ -466,9 +111,7 @@ Capability Hw_vcpu::_create_vcpu(Vm_connection &vm, ** vCPU API ** **************/ -void Vm_connection::Vcpu::run() { static_cast(_native_vcpu).run(); } -void Vm_connection::Vcpu::pause() { static_cast(_native_vcpu).pause(); } -Vcpu_state & Vm_connection::Vcpu::state() { return static_cast(_native_vcpu).state(); } +void Vm_connection::Vcpu::_with_state(Call_with_state &cw) { static_cast(_native_vcpu).with_state(cw); } Vm_connection::Vcpu::Vcpu(Vm_connection &vm, Allocator &alloc,