Skip to content

Commit

Permalink
introduce vcpu_t::guest_resume() method
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
wbenny committed Aug 1, 2019
1 parent b5a9945 commit 1cdbec6
Show file tree
Hide file tree
Showing 6 changed files with 128 additions and 16 deletions.
53 changes: 37 additions & 16 deletions src/hvpp/hvpp/vcpu.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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_;
Expand Down Expand Up @@ -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);
Expand Down
37 changes: 37 additions & 0 deletions src/hvpp/hvpp/vcpu.h
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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&;

Expand All @@ -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.
//
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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_;
Expand Down
30 changes: 30 additions & 0 deletions src/hvpp/hvpp/vcpu.inl
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
5 changes: 5 additions & 0 deletions src/hvpp/hvpp/vmexit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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, ...)
Expand Down
11 changes: 11 additions & 0 deletions src/hvpp/hvpp/vmexit.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
8 changes: 8 additions & 0 deletions src/hvpp/hvpp/vmexit/vmexit_passthrough.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down Expand Up @@ -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;
}

Expand Down

0 comments on commit 1cdbec6

Please sign in to comment.