mirror of
https://github.com/wbenny/hvpp
synced 2026-08-17 04:26:04 -04:00
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:
parent
b5a99457b1
commit
1cdbec6159
6 changed files with 128 additions and 16 deletions
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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_;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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, ...)
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue