introduce vcpu_t::guest_resume() method

Calling this method will perform "long-jump" at the end of the VM-exit handler.  You can imagine it as some kind of "throw"-statement, that VM-exit handler "catch".

Note that calling this method IS DANGEROUS, AS IT WON'T FREE ANY OBJECTS ON STACK.
This commit is contained in:
Petr Benes 2019-08-01 19:34:00 +02:00
parent b5a99457b1
commit 1cdbec6159
6 changed files with 128 additions and 16 deletions

View file

@ -66,12 +66,13 @@ vcpu_t::vcpu_t(vmexit_handler& handler) noexcept
memset(stack_.data, 0xcc, sizeof(stack_));
//
// Reset CPU context.
// Reset CPU contexts.
// This is not really needed, as they are overwritten anyway (in
// entry_guest_()/entry_host_()), but since initialization is done
// just once, it also doesn't hurt.
//
context_.clear();
resume_context_.clear();
//
// Assertions.
@ -421,6 +422,12 @@ void vcpu_t::suppress_rip_adjust() noexcept
suppress_rip_adjust_ = true;
}
void vcpu_t::guest_resume() noexcept
{
resume_context_.rax = 1;
resume_context_.restore();
}
auto vcpu_t::guest_memory_mapper() noexcept -> mm::memory_mapper&
{
return mapper_;
@ -820,26 +827,40 @@ void vcpu_t::entry_host() noexcept
stack_.machine_frame.rip = context_.rip + exit_instruction_length();
stack_.machine_frame.rsp = context_.rsp;
if (!resume_context_.capture())
{
handler_.handle(*this);
if (state_ == state::terminated)
}
else
{
//
// Some spinlocks on the stack still might be locked.
// Unlock them.
//
while (spinlock_queue_.size())
{
//
// At this point we're not in the VMX-root mode (vmxoff has been
// executed) and we want to return control back to whomever caused
// this VM-exit.
//
// Note that at this point, we can't call any VMX instructions,
// as they would raise #UD (invalid opcode exception).
//
goto exit;
stacked_lock_guard_pop();
}
if (!suppress_rip_adjust_)
{
context_.rip += exit_instruction_length();
}
handler_.handle_guest_resume(*this);
}
if (state_ == state::terminated)
{
//
// At this point we're not in the VMX-root mode (vmxoff has been
// executed) and we want to return control back to whomever caused
// this VM-exit.
//
// Note that at this point, we can't call any VMX instructions,
// as they would raise #UD (invalid opcode exception).
//
goto exit;
}
if (!suppress_rip_adjust_)
{
context_.rip += exit_instruction_length();
}
guest_rsp(context_.rsp);

View file

@ -6,6 +6,7 @@
#include "lib/deque.h"
#include "lib/error.h"
#include "lib/spinlock.h"
#include "lib/mm/memory_mapper.h"
#include "lib/mm/memory_translator.h"
@ -45,6 +46,9 @@ class vcpu_t final
// Guest helper methods.
//
[[noreturn]]
void guest_resume() noexcept;
auto guest_memory_mapper() noexcept -> mm::memory_mapper&;
auto guest_memory_translator() noexcept -> mm::memory_translator&;
@ -56,6 +60,28 @@ class vcpu_t final
auto tsc_delta_previous() const noexcept -> uint64_t;
auto tsc_delta_sum() const noexcept -> uint64_t;
//
// Stacked lock guard.
//
struct stacked_lock_guard_t
{
stacked_lock_guard_t(vcpu_t& vp, spinlock& lock) noexcept;
~stacked_lock_guard_t() noexcept;
stacked_lock_guard_t(const stacked_lock_guard_t& other) noexcept = delete;
stacked_lock_guard_t(stacked_lock_guard_t&& other) noexcept = delete;
stacked_lock_guard_t& operator=(const stacked_lock_guard_t& other) noexcept = delete;
stacked_lock_guard_t& operator=(stacked_lock_guard_t&& other) noexcept = delete;
private:
vcpu_t& vp_;
};
auto stacked_lock_guard(spinlock& lock) noexcept -> stacked_lock_guard_t;
void stacked_lock_guard_push(spinlock& lock) noexcept;
void stacked_lock_guard_pop() noexcept;
//
// VMCS manipulation. Implementation is in vcpu.inl.
//
@ -367,6 +393,8 @@ class vcpu_t final
};
};
using spinlock_queue_t = fixed_dequeue<spinlock*, 32>;
static_assert(sizeof(stack_t) == stack_t::size);
static_assert(sizeof(stack_t::shadow_space_t) == 32);
@ -408,11 +436,20 @@ class vcpu_t final
uint16_t ept_index_;
//
// Guest-resume support.
//
context_t resume_context_;
spinlock_queue_t spinlock_queue_;
//
// Memory translation support.
//
mm::memory_mapper mapper_;
mm::memory_translator translator_;
//
// Timestamp-counter.
//
uint64_t tsc_entry_;
uint64_t tsc_delta_previous_;
uint64_t tsc_delta_sum_;

View file

@ -38,6 +38,36 @@
namespace hvpp {
auto vcpu_t::stacked_lock_guard(spinlock& lock) noexcept -> stacked_lock_guard_t
{
return stacked_lock_guard_t{ *this, lock };
}
void vcpu_t::stacked_lock_guard_push(spinlock& lock) noexcept
{
lock.lock();
spinlock_queue_.push_back(&lock);
}
void vcpu_t::stacked_lock_guard_pop() noexcept
{
auto& lock = *spinlock_queue_.back();
spinlock_queue_.pop_back();
lock.unlock();
}
vcpu_t::stacked_lock_guard_t::stacked_lock_guard_t(vcpu_t& vp, spinlock& lock) noexcept
: vp_{ vp }
{
vp_.stacked_lock_guard_push(lock);
}
vcpu_t::stacked_lock_guard_t::~stacked_lock_guard_t() noexcept
{
vp_.stacked_lock_guard_pop();
}
auto vcpu_t::interrupt_info() const noexcept -> interrupt_t
{
interrupt_t result;

View file

@ -100,6 +100,11 @@ void vmexit_handler::handle(vcpu_t& vp) noexcept
(this->*handlers_[handler_index])(vp);
}
void vmexit_handler::handle_guest_resume(vcpu_t& vp) noexcept
{
(void)(vp);
}
//
// "Do-nothing" handlers for all VM-exits.
// VMX-instruction related VM-exits (VMREAD, VMWRITE, INVEPT, ...)

View file

@ -171,6 +171,17 @@ class vmexit_handler
//
virtual void handle(vcpu_t& vp) noexcept;
//
// This method is called in reaction to `vp.guest_resume()' call.
// It should be responsible for cleaning up allocated resources,
// that haven't been freed by object desctructors.
//
// Note:
// Use `auto _ = vp.stacked_lock_guard(lock)' for acquiring spinlocks.
// Spinlocks that are locked this way are by always unlocked.
//
virtual void handle_guest_resume(vcpu_t& vp) noexcept;
protected:
//
// Separate handlers for each VM-exit reason.

View file

@ -1114,6 +1114,10 @@ void vmexit_passthrough_handler::handle_interrupt(vcpu_t& vp) noexcept
vp.interrupt_inject(interrupt::page_fault);
vp.suppress_rip_adjust();
//
// `vp.guest_resume()' can be also used instead of `return'.
//
return;
}
@ -1157,6 +1161,10 @@ void vmexit_passthrough_handler::handle_interrupt(vcpu_t& vp) noexcept
vp.interrupt_inject(interrupt::page_fault);
vp.suppress_rip_adjust();
//
// `vp.guest_resume()' can be also used instead of `return'.
//
return;
}