// MOV to / from DRx _Use_decl_annotations_ static void VmmpHandleDrAccess( GuestContext *guest_context) { HYPERPLATFORM_PERFORMANCE_MEASURE_THIS_SCOPE(); const MovDrQualification exit_qualification = { UtilVmRead(VmcsField::kExitQualification)}; const auto register_used = VmmpSelectRegister(exit_qualification.fields.gp_register, guest_context); // Emulate the instruction switch (static_cast<MovDrDirection>(exit_qualification.fields.direction)) { case MovDrDirection::kMoveToDr: // clang-format off switch (exit_qualification.fields.debugl_register) { case 0: __writedr(0, *register_used); break; case 1: __writedr(1, *register_used); break; case 2: __writedr(2, *register_used); break; case 3: __writedr(3, *register_used); break; case 4: __writedr(4, *register_used); break; case 5: __writedr(5, *register_used); break; case 6: __writedr(6, *register_used); break; case 7: __writedr(7, *register_used); break; default: break; } // clang-format on break; case MovDrDirection::kMoveFromDr: // clang-format off switch (exit_qualification.fields.debugl_register) { case 0: *register_used = __readdr(0); break; case 1: *register_used = __readdr(1); break; case 2: *register_used = __readdr(2); break; case 3: *register_used = __readdr(3); break; case 4: *register_used = __readdr(4); break; case 5: *register_used = __readdr(5); break; case 6: *register_used = __readdr(6); break; case 7: *register_used = __readdr(7); break; default: break; } // clang-format on break; default: HYPERPLATFORM_COMMON_BUG_CHECK(HyperPlatformBugCheck::kUnspecified, 0, 0, 0); break; } VmmpAdjustGuestInstructionPointer(guest_context->ip); }
// MOV to / from CRx _Use_decl_annotations_ static void VmmpHandleCrAccess( GuestContext *guest_context) { HYPERPLATFORM_PERFORMANCE_MEASURE_THIS_SCOPE(); const MovCrQualification exit_qualification = { UtilVmRead(VmcsField::kExitQualification)}; const auto register_used = VmmpSelectRegister(exit_qualification.fields.gp_register, guest_context); switch (static_cast<MovCrAccessType>(exit_qualification.fields.access_type)) { case MovCrAccessType::kMoveToCr: { switch (exit_qualification.fields.control_register) { // CR0 <- Reg case 0: if (UtilIsX86Pae()) { UtilLoadPdptes(UtilVmRead(VmcsField::kGuestCr3)); } UtilVmWrite(VmcsField::kGuestCr0, *register_used); UtilVmWrite(VmcsField::kCr0ReadShadow, *register_used); break; // CR3 <- Reg case 3: if (UtilIsX86Pae()) { UtilLoadPdptes(*register_used); } UtilVmWrite(VmcsField::kGuestCr3, *register_used); break; // CR4 <- Reg case 4: if (UtilIsX86Pae()) { UtilLoadPdptes(UtilVmRead(VmcsField::kGuestCr3)); } UtilVmWrite(VmcsField::kGuestCr4, *register_used); UtilVmWrite(VmcsField::kCr4ReadShadow, *register_used); break; // CR8 <- Reg case 8: guest_context->cr8 = *register_used; break; default: HYPERPLATFORM_COMMON_BUG_CHECK(HyperPlatformBugCheck::kUnknown, 0, 0, 0); break; } } break; // Note that MOV from CRx should never cause VM-exit with the current // settings. This is just for case when you enable it. case MovCrAccessType::kMoveFromCr: { switch (exit_qualification.fields.control_register) { // Reg <- CR3 case 3: *register_used = UtilVmRead(VmcsField::kGuestCr3); break; // Reg <- CR8 case 8: *register_used = guest_context->cr8; break; default: HYPERPLATFORM_COMMON_BUG_CHECK(HyperPlatformBugCheck::kUnknown, 0, 0, 0); break; } } break; // Unimplemented case MovCrAccessType::kClts: case MovCrAccessType::kLmsw: default: HYPERPLATFORM_COMMON_DBG_BREAK(); break; } VmmpAdjustGuestInstructionPointer(guest_context->ip); }
// LLDT, LTR, SLDT, and STR _Use_decl_annotations_ static void VmmpHandleLdtrOrTrAccess( GuestContext *guest_context) { HYPERPLATFORM_PERFORMANCE_MEASURE_THIS_SCOPE(); const LdtrOrTrAccessQualification exit_qualification = { static_cast<ULONG32>(UtilVmRead(VmcsField::kVmxInstructionInfo))}; // Calculate an address or a register to be used for the instruction const auto displacement = UtilVmRead(VmcsField::kExitQualification); ULONG_PTR operation_address = 0; if (exit_qualification.fields.register_access) { // Register const auto register_used = VmmpSelectRegister(exit_qualification.fields.register1, guest_context); operation_address = reinterpret_cast<ULONG_PTR>(register_used); } else { // Base ULONG_PTR base_value = 0; if (!exit_qualification.fields.base_register_invalid) { const auto register_used = VmmpSelectRegister( exit_qualification.fields.base_register, guest_context); base_value = *register_used; } // Index ULONG_PTR index_value = 0; if (!exit_qualification.fields.index_register_invalid) { const auto register_used = VmmpSelectRegister( exit_qualification.fields.index_register, guest_context); index_value = *register_used; switch ( static_cast<GdtrOrIdtrScaling>(exit_qualification.fields.scalling)) { case GdtrOrIdtrScaling::kNoScaling: index_value = index_value; break; case GdtrOrIdtrScaling::kScaleBy2: index_value = index_value * 2; break; case GdtrOrIdtrScaling::kScaleBy4: index_value = index_value * 4; break; case GdtrOrIdtrScaling::kScaleBy8: index_value = index_value * 8; break; default: break; } } operation_address = base_value + index_value + displacement; if (static_cast<GdtrOrIdtrAaddressSize>( exit_qualification.fields.address_size) == GdtrOrIdtrAaddressSize::k32bit) { operation_address &= MAXULONG; } } // Update CR3 with that of the guest since below code is going to access // memory. const auto guest_cr3 = UtilVmRead(VmcsField::kGuestCr3); const auto vmm_cr3 = __readcr3(); __writecr3(guest_cr3); // Emulate the instruction auto selector = reinterpret_cast<USHORT *>(operation_address); switch (static_cast<LdtrOrTrInstructionIdentity>( exit_qualification.fields.instruction_identity)) { case LdtrOrTrInstructionIdentity::kSldt: *selector = static_cast<USHORT>(UtilVmRead(VmcsField::kGuestLdtrSelector)); break; case LdtrOrTrInstructionIdentity::kStr: *selector = static_cast<USHORT>(UtilVmRead(VmcsField::kGuestTrSelector)); break; case LdtrOrTrInstructionIdentity::kLldt: UtilVmWrite(VmcsField::kGuestLdtrSelector, *selector); break; case LdtrOrTrInstructionIdentity::kLtr: UtilVmWrite(VmcsField::kGuestTrSelector, *selector); break; } __writecr3(vmm_cr3); VmmpAdjustGuestInstructionPointer(guest_context->ip); }
// LIDT, SIDT, LGDT and SGDT _Use_decl_annotations_ static void VmmpHandleGdtrOrIdtrAccess( GuestContext *guest_context) { HYPERPLATFORM_PERFORMANCE_MEASURE_THIS_SCOPE(); const GdtrOrIdtrAccessQualification exit_qualification = { static_cast<ULONG32>(UtilVmRead(VmcsField::kVmxInstructionInfo))}; // Calculate an address to be used for the instruction const auto displacement = UtilVmRead(VmcsField::kExitQualification); // Base ULONG_PTR base_value = 0; if (!exit_qualification.fields.base_register_invalid) { const auto register_used = VmmpSelectRegister( exit_qualification.fields.base_register, guest_context); base_value = *register_used; } // Index ULONG_PTR index_value = 0; if (!exit_qualification.fields.index_register_invalid) { const auto register_used = VmmpSelectRegister( exit_qualification.fields.index_register, guest_context); index_value = *register_used; switch ( static_cast<GdtrOrIdtrScaling>(exit_qualification.fields.scalling)) { case GdtrOrIdtrScaling::kNoScaling: index_value = index_value; break; case GdtrOrIdtrScaling::kScaleBy2: index_value = index_value * 2; break; case GdtrOrIdtrScaling::kScaleBy4: index_value = index_value * 4; break; case GdtrOrIdtrScaling::kScaleBy8: index_value = index_value * 8; break; default: break; } } auto operation_address = base_value + index_value + displacement; if (static_cast<GdtrOrIdtrAaddressSize>( exit_qualification.fields.address_size) == GdtrOrIdtrAaddressSize::k32bit) { operation_address &= MAXULONG; } // Update CR3 with that of the guest since below code is going to access // memory. const auto guest_cr3 = UtilVmRead(VmcsField::kGuestCr3); const auto vmm_cr3 = __readcr3(); __writecr3(guest_cr3); // Emulate the instruction auto descriptor_table_reg = reinterpret_cast<Idtr *>(operation_address); switch (static_cast<GdtrOrIdtrInstructionIdentity>( exit_qualification.fields.instruction_identity)) { case GdtrOrIdtrInstructionIdentity::kSgdt: descriptor_table_reg->base = UtilVmRead(VmcsField::kGuestGdtrBase); descriptor_table_reg->limit = static_cast<unsigned short>(UtilVmRead(VmcsField::kGuestGdtrLimit)); break; case GdtrOrIdtrInstructionIdentity::kSidt: descriptor_table_reg->base = UtilVmRead(VmcsField::kGuestIdtrBase); descriptor_table_reg->limit = static_cast<unsigned short>(UtilVmRead(VmcsField::kGuestIdtrLimit)); break; case GdtrOrIdtrInstructionIdentity::kLgdt: UtilVmWrite(VmcsField::kGuestGdtrBase, descriptor_table_reg->base); UtilVmWrite(VmcsField::kGuestGdtrLimit, descriptor_table_reg->limit); break; case GdtrOrIdtrInstructionIdentity::kLidt: UtilVmWrite(VmcsField::kGuestIdtrBase, descriptor_table_reg->base); UtilVmWrite(VmcsField::kGuestIdtrLimit, descriptor_table_reg->limit); break; } __writecr3(vmm_cr3); VmmpAdjustGuestInstructionPointer(guest_context->ip); }
// MOV to / from CRx _Use_decl_annotations_ static void VmmpHandleCrAccess( GuestContext *guest_context) { HYPERPLATFORM_PERFORMANCE_MEASURE_THIS_SCOPE(); const MovCrQualification exit_qualification = { UtilVmRead(VmcsField::kExitQualification)}; const auto register_used = VmmpSelectRegister(exit_qualification.fields.gp_register, guest_context); switch (static_cast<MovCrAccessType>(exit_qualification.fields.access_type)) { case MovCrAccessType::kMoveToCr: switch (exit_qualification.fields.control_register) { // CR0 <- Reg case 0: { HYPERPLATFORM_PERFORMANCE_MEASURE_THIS_SCOPE(); if (UtilIsX86Pae()) { UtilLoadPdptes(UtilVmRead(VmcsField::kGuestCr3)); } UtilVmWrite(VmcsField::kGuestCr0, *register_used); UtilVmWrite(VmcsField::kCr0ReadShadow, *register_used); break; } // CR3 <- Reg case 3: { HYPERPLATFORM_PERFORMANCE_MEASURE_THIS_SCOPE(); if (UtilIsX86Pae()) { UtilLoadPdptes(*register_used); } UtilVmWrite(VmcsField::kGuestCr3, *register_used); break; } // CR4 <- Reg case 4: { HYPERPLATFORM_PERFORMANCE_MEASURE_THIS_SCOPE(); if (UtilIsX86Pae()) { UtilLoadPdptes(UtilVmRead(VmcsField::kGuestCr3)); } UtilVmWrite(VmcsField::kGuestCr4, *register_used); UtilVmWrite(VmcsField::kCr4ReadShadow, *register_used); break; } // CR8 <- Reg case 8: { HYPERPLATFORM_PERFORMANCE_MEASURE_THIS_SCOPE(); guest_context->cr8 = *register_used; break; } default: HYPERPLATFORM_COMMON_BUG_CHECK(HyperPlatformBugCheck::kUnspecified, 0, 0, 0); break; } break; case MovCrAccessType::kMoveFromCr: switch (exit_qualification.fields.control_register) { // Reg <- CR3 case 3: { HYPERPLATFORM_PERFORMANCE_MEASURE_THIS_SCOPE(); *register_used = UtilVmRead(VmcsField::kGuestCr3); break; } // Reg <- CR8 case 8: { HYPERPLATFORM_PERFORMANCE_MEASURE_THIS_SCOPE(); *register_used = guest_context->cr8; break; } default: HYPERPLATFORM_COMMON_BUG_CHECK(HyperPlatformBugCheck::kUnspecified, 0, 0, 0); break; } break; // Unimplemented case MovCrAccessType::kClts: case MovCrAccessType::kLmsw: default: HYPERPLATFORM_COMMON_DBG_BREAK(); break; } VmmpAdjustGuestInstructionPointer(guest_context->ip); }