Skip to content

Commit

Permalink
fix handling of debug registers. closes #5
Browse files Browse the repository at this point in the history
  • Loading branch information
wbenny committed Aug 26, 2018
1 parent 6e7cee8 commit ca561fa
Show file tree
Hide file tree
Showing 3 changed files with 74 additions and 58 deletions.
83 changes: 37 additions & 46 deletions src/hvpp/hvpp/vmexit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -465,8 +465,8 @@ void vmexit_handler::handle_mov_dr(vcpu_t& vp) noexcept

if (vp.guest_cs().access.descriptor_privilege_level != 0)
{
__debugbreak();
vp.inject(interrupt_info_t(vmx::interrupt_type::hardware_exception, exception_vector::general_protection));
vp.inject(interrupt_info_t(vmx::interrupt_type::hardware_exception, exception_vector::general_protection, exception_error_code_t{ 0 }));
vp.suppress_rip_adjust();
return;
}

Expand All @@ -478,17 +478,21 @@ void vmexit_handler::handle_mov_dr(vcpu_t& vp) noexcept
// (ref: Vol3B[17.2.2(Debug Registers DR4 and DR5)])
//

if (vp.guest_cr4().debugging_extensions && (
exit_qualification.dr_number == 4 ||
exit_qualification.dr_number == 5))
if (exit_qualification.dr_number == 4 ||
exit_qualification.dr_number == 5)
{
__debugbreak();
vp.inject(interrupt_info_t(vmx::interrupt_type::hardware_exception, exception_vector::invalid_opcode));
return;
if (vp.guest_cr4().debugging_extensions)
{
vp.inject(interrupt_info_t(vmx::interrupt_type::hardware_exception, exception_vector::invalid_opcode));
vp.suppress_rip_adjust();
return;
}
else
{
exit_qualification.dr_number += 2;
}
}

//
// Blatantly copied from ksm. This is what Intel Manual says:
//
// Enables (when set) debug-register protection, which causes a
// debug exception to be generated prior to any MOV instruction that accesses a debug register.When such a
Expand All @@ -503,16 +507,34 @@ void vmexit_handler::handle_mov_dr(vcpu_t& vp) noexcept

if (vp.guest_dr7().general_detect)
{
__debugbreak();

auto dr6 = read<dr6_t>();
dr6.breakpoint_condition = 0;
dr6.restricted_transactional_memory = true;
dr6.debug_register_access_detected = true;

write<dr6_t>(dr6);

vp.inject(interrupt_info_t(vmx::interrupt_type::hardware_exception, exception_vector::debug));

auto dr7 = vp.guest_dr7();
dr7.general_detect = false;
vp.guest_dr7(dr7);

vp.suppress_rip_adjust();
return;
}

//
// In 64-bit mode, the upper 32 bits of DR6 and DR7 are reserved and must be written with zeros. Writing 1 to any of
// the upper 32 bits results in a #GP(0) exception.
// (ref: Vol3B[17.2.6(Debug Registers and Intel® 64 Processors)])
//
if (exit_qualification.access_type == vmx::exit_qualification_mov_dr_t::access_to_dr && (
exit_qualification.dr_number == 6 ||
exit_qualification.dr_number == 7) &&
(gp_register >> 32) != 0)
{
vp.inject(interrupt_info_t(vmx::interrupt_type::hardware_exception, exception_vector::general_protection, exception_error_code_t{ 0 }));
vp.suppress_rip_adjust();
return;
}

Expand All @@ -525,37 +547,8 @@ void vmexit_handler::handle_mov_dr(vcpu_t& vp) noexcept
case 1: write<dr1_t>(dr1_t{ gp_register }); break;
case 2: write<dr2_t>(dr2_t{ gp_register }); break;
case 3: write<dr3_t>(dr3_t{ gp_register }); break;
case 4: write<dr4_t>(dr4_t{ gp_register }); break;
case 5: write<dr5_t>(dr5_t{ gp_register }); break;

//
// In 64-bit mode, the upper 32 bits of DR6 and DR7 are reserved and must be written with zeros. Writing 1 to any of
// the upper 32 bits results in a #GP(0) exception.
// (ref: Vol3B[17.2.6(Debug Registers and Intel® 64 Processors)])
//

case 6:
if ((gp_register >> 32) == 0)
{
write<dr6_t>(dr6_t{ gp_register });
}
else
{
vp.inject(interrupt_info_t(vmx::interrupt_type::hardware_exception, exception_vector::general_protection));
}
break;

case 7:
if ((gp_register >> 32) == 0)
{
vp.guest_dr7(dr7_t{ gp_register });
}
else
{
vp.inject(interrupt_info_t(vmx::interrupt_type::hardware_exception, exception_vector::general_protection));
}
break;

case 6: write<dr6_t>(vmx::adjust(dr6_t{ gp_register })); break;
case 7: vp.guest_dr7(vmx::adjust(dr7_t{ gp_register })); break;
default:
break;
}
Expand All @@ -568,8 +561,6 @@ void vmexit_handler::handle_mov_dr(vcpu_t& vp) noexcept
case 1: gp_register = read<dr1_t>().flags; break;
case 2: gp_register = read<dr2_t>().flags; break;
case 3: gp_register = read<dr3_t>().flags; break;
case 4: gp_register = read<dr4_t>().flags; break;
case 5: gp_register = read<dr5_t>().flags; break;
case 6: gp_register = read<dr6_t>().flags; break;
case 7: gp_register = vp.guest_dr7().flags; break;
default:
Expand Down
10 changes: 6 additions & 4 deletions src/hvpp/ia32/arch/dr.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,13 @@ struct dr6_t
struct
{
uint64_t breakpoint_condition : 4;
uint64_t reserved_1 : 9;
uint64_t reserved_1 : 8; // always 1
uint64_t reserved_2 : 1; // always 0
uint64_t debug_register_access_detected : 1;
uint64_t single_instruction : 1;
uint64_t task_switch : 1;
uint64_t restricted_transactional_memory : 1;
uint64_t reserved_3 : 15; // always 1
};
};
};
Expand All @@ -46,11 +48,11 @@ struct dr7_t
uint64_t global_breakpoint_3 : 1;
uint64_t local_exact_breakpoint : 1;
uint64_t global_exact_breakpoint : 1;
uint64_t reserved_1 : 1;
uint64_t reserved_1 : 1; // always 1
uint64_t restricted_transactional_memory : 1;
uint64_t reserved_2 : 1;
uint64_t reserved_2 : 1; // always 0
uint64_t general_detect : 1;
uint64_t reserved_3 : 2;
uint64_t reserved_3 : 2; // always 0
uint64_t read_write_0 : 2;
uint64_t length_0 : 2;
uint64_t read_write_1 : 2;
Expand Down
39 changes: 31 additions & 8 deletions src/hvpp/ia32/vmx.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,12 @@ auto adjust(T desired) noexcept
std::is_same_v<T, cr0_t> ||
std::is_same_v<T, cr4_t>;

constexpr bool is_dr6 =
std::is_same_v<T, dr6_t>;

constexpr bool is_dr7 =
std::is_same_v<T, dr7_t>;

constexpr bool is_vmx_ctl_msr =
std::is_same_v<T, msr::vmx_pinbased_ctls_t> ||
std::is_same_v<T, msr::vmx_procbased_ctls_t> ||
Expand All @@ -54,7 +60,8 @@ auto adjust(T desired) noexcept
constexpr bool is_vmx_procbased_ctls2_msr =
std::is_same_v<T, msr::vmx_procbased_ctls2_t>;

static_assert(is_control_register || is_vmx_ctl_msr || is_vmx_procbased_ctls2_msr,
static_assert(is_control_register || is_dr6|| is_dr7 || is_vmx_ctl_msr ||
is_vmx_procbased_ctls2_msr,
"type is not adjustable");

if constexpr (is_control_register)
Expand All @@ -67,8 +74,26 @@ auto adjust(T desired) noexcept

desired.flags |= cr_fixed0.flags;
desired.flags &= cr_fixed1.flags;

return desired;
}
else if constexpr (is_dr6)
{
//
// x |= ~x will set all bits of the bitfield to 1's. It would be prettier to
// just use ~0, but compilers would complain about value not fitting into
// bitfield.
//
// Vol3B[17.2(Debug Registers)] describes which reserved fields should be
// set to 0 and 1 respectively.
//
desired.reserved_1 |= ~desired.reserved_1;
desired.reserved_2 = 0;
desired.reserved_3 |= ~desired.reserved_3;
}
else if constexpr (is_dr7)
{
desired.reserved_1 |= ~desired.reserved_1;
desired.reserved_2 = 0;
desired.reserved_3 = 0;
}
else if constexpr (is_vmx_ctl_msr)
{
Expand All @@ -77,18 +102,16 @@ auto adjust(T desired) noexcept

desired.flags |= true_ctls.allowed_0_settings;
desired.flags &= true_ctls.allowed_1_settings;

return desired;
}
else if constexpr(is_vmx_procbased_ctls2_msr)
else if constexpr (is_vmx_procbased_ctls2_msr)
{
auto true_ctls = msr::read<msr::vmx_true_ctls_t>(T::msr_id);

desired.flags |= true_ctls.allowed_0_settings;
desired.flags &= true_ctls.allowed_1_settings;

return desired;
}

return desired;
}

inline error_code on(pa_t pa) noexcept
Expand Down

0 comments on commit ca561fa

Please sign in to comment.