@@ -882,4 +882,13 @@ void virt_map_level(struct kvm_vm *vm, uint64_t vaddr, uint64_t paddr,
#define XSTATE_XTILE_DATA_MASK (1ULL << XSTATE_XTILE_DATA_BIT)
#define XFEATURE_XTILE_MASK (XSTATE_XTILE_CFG_MASK | \
XSTATE_XTILE_DATA_MASK)
+
+/*
+ * Accessors to get R/M, REG, and Mod bits described in the SDM vol 2,
+ * figure 2-2 "Table Interpretation of ModR/M Byte (C8H)".
+ */
+#define GET_RM(insn_byte) ((insn_byte) & 0x7)
+#define GET_REG(insn_byte) (((insn_byte) & 0x38) >> 3)
+#define GET_MOD(insn_byte) (((insn_byte) & 0xc) >> 6)
+
#endif /* SELFTEST_KVM_PROCESSOR_H */
new file mode 100644
@@ -0,0 +1,67 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+#ifndef SELFTEST_KVM_FLDS_EMULATION_H
+#define SELFTEST_KVM_FLDS_EMULATION_H
+
+#include "processor.h"
+#include "kvm_util.h"
+
+/*
+ * flds is an instruction that the KVM instruction emulator is known not to
+ * support. This can be used in guest code along with a mechanism to force
+ * KVM to emulate the instruction (e.g. by providing an MMIO address) to
+ * exercise emulation failures.
+ */
+static inline void flds(uint64_t address)
+{
+ __asm__ __volatile__("flds (%0)" :: "r"(address));
+}
+
+static inline void assert_exit_for_flds_emulation_failure(struct kvm_vcpu *vcpu)
+{
+ struct kvm_run *run = vcpu->run;
+ uint8_t *insn_bytes;
+ uint8_t insn_size;
+ uint64_t flags;
+
+ TEST_ASSERT(run->exit_reason == KVM_EXIT_INTERNAL_ERROR,
+ "Unexpected exit reason: %u (%s)",
+ run->exit_reason,
+ exit_reason_str(run->exit_reason));
+
+ TEST_ASSERT(run->emulation_failure.suberror == KVM_INTERNAL_ERROR_EMULATION,
+ "Unexpected suberror: %u",
+ run->emulation_failure.suberror);
+
+ flags = run->emulation_failure.flags;
+ TEST_ASSERT(run->emulation_failure.ndata >= 3 &&
+ flags & KVM_INTERNAL_ERROR_EMULATION_FLAG_INSTRUCTION_BYTES,
+ "run->emulation_failure is missing instruction bytes");
+
+ insn_size = run->emulation_failure.insn_size;
+ insn_bytes = run->emulation_failure.insn_bytes;
+
+ TEST_ASSERT(insn_size <= 15 && insn_size > 0,
+ "Unexpected instruction size: %u",
+ insn_size);
+
+ TEST_ASSERT(insn_size >= 2 &&
+ insn_bytes[0] == 0xd9 &&
+ GET_REG(insn_bytes[1]) == 0x0 &&
+ GET_MOD(insn_bytes[1]) == 0x0 &&
+ /* Ensure there is no SIB byte. */
+ GET_RM(insn_bytes[1]) != 0x4 &&
+ /* Ensure there is no displacement byte. */
+ GET_RM(insn_bytes[1]) != 0x5,
+ "Unexpected instruction. Expected 'flds' (0xd9 /0)");
+}
+
+static inline void skip_flds_instruction(struct kvm_vcpu *vcpu)
+{
+ struct kvm_regs regs;
+
+ vcpu_regs_get(vcpu, ®s);
+ regs.rip += 2;
+ vcpu_regs_set(vcpu, ®s);
+}
+
+#endif /* !SELFTEST_KVM_FLDS_EMULATION_H */
@@ -8,6 +8,8 @@
#define _GNU_SOURCE /* for program_invocation_short_name */
+#include "flds_emulation.h"
+
#include "test_util.h"
#include "kvm_util.h"
#include "vmx.h"
@@ -21,75 +23,10 @@
static void guest_code(void)
{
- __asm__ __volatile__("flds (%[addr])"
- :: [addr]"r"(MEM_REGION_GVA));
-
+ flds(MEM_REGION_GVA);
GUEST_DONE();
}
-/*
- * Accessors to get R/M, REG, and Mod bits described in the SDM vol 2,
- * figure 2-2 "Table Interpretation of ModR/M Byte (C8H)".
- */
-#define GET_RM(insn_byte) (insn_byte & 0x7)
-#define GET_REG(insn_byte) ((insn_byte & 0x38) >> 3)
-#define GET_MOD(insn_byte) ((insn_byte & 0xc) >> 6)
-
-/* Ensure we are dealing with a simple 2-byte flds instruction. */
-static bool is_flds(uint8_t *insn_bytes, uint8_t insn_size)
-{
- return insn_size >= 2 &&
- insn_bytes[0] == 0xd9 &&
- GET_REG(insn_bytes[1]) == 0x0 &&
- GET_MOD(insn_bytes[1]) == 0x0 &&
- /* Ensure there is no SIB byte. */
- GET_RM(insn_bytes[1]) != 0x4 &&
- /* Ensure there is no displacement byte. */
- GET_RM(insn_bytes[1]) != 0x5;
-}
-
-static void process_exit_on_emulation_error(struct kvm_vcpu *vcpu)
-{
- struct kvm_run *run = vcpu->run;
- struct kvm_regs regs;
- uint8_t *insn_bytes;
- uint8_t insn_size;
- uint64_t flags;
-
- TEST_ASSERT(run->exit_reason == KVM_EXIT_INTERNAL_ERROR,
- "Unexpected exit reason: %u (%s)",
- run->exit_reason,
- exit_reason_str(run->exit_reason));
-
- TEST_ASSERT(run->emulation_failure.suberror == KVM_INTERNAL_ERROR_EMULATION,
- "Unexpected suberror: %u",
- run->emulation_failure.suberror);
-
- flags = run->emulation_failure.flags;
- TEST_ASSERT(run->emulation_failure.ndata >= 3 &&
- flags & KVM_INTERNAL_ERROR_EMULATION_FLAG_INSTRUCTION_BYTES,
- "run->emulation_failure is missing instruction bytes");
-
- insn_size = run->emulation_failure.insn_size;
- insn_bytes = run->emulation_failure.insn_bytes;
-
- TEST_ASSERT(insn_size <= 15 && insn_size > 0,
- "Unexpected instruction size: %u",
- insn_size);
-
- TEST_ASSERT(is_flds(insn_bytes, insn_size),
- "Unexpected instruction. Expected 'flds' (0xd9 /0)");
-
- /*
- * If is_flds() succeeded then the instruction bytes contained an flds
- * instruction that is 2-bytes in length (ie: no prefix, no SIB, no
- * displacement).
- */
- vcpu_regs_get(vcpu, ®s);
- regs.rip += 2;
- vcpu_regs_set(vcpu, ®s);
-}
-
static void assert_ucall_done(struct kvm_vcpu *vcpu)
{
struct ucall uc;
@@ -133,7 +70,8 @@ int main(int argc, char *argv[])
vm_set_page_table_entry(vm, vcpu, MEM_REGION_GVA, pte | (1ull << 36));
vcpu_run(vcpu);
- process_exit_on_emulation_error(vcpu);
+ assert_exit_for_flds_emulation_failure(vcpu);
+ skip_flds_instruction(vcpu);
vcpu_run(vcpu);
assert_ucall_done(vcpu);
Move the flds instruction emulation failure handling code to a header so it can be re-used in an upcoming test. No functional change intended. Signed-off-by: David Matlack <dmatlack@google.com> --- .../selftests/kvm/include/x86_64/processor.h | 9 +++ .../selftests/kvm/x86_64/flds_emulation.h | 67 +++++++++++++++++ .../smaller_maxphyaddr_emulation_test.c | 72 ++----------------- 3 files changed, 81 insertions(+), 67 deletions(-) create mode 100644 tools/testing/selftests/kvm/x86_64/flds_emulation.h