Verify that CPU interruptibility due to POP SS is set correctly (and #DB is suppressed). Signed-off-by: Michal Luczaj <mhal@xxxxxxx> --- Initially I wanted to test #DB suppression with EFLAGS.TF=1, but it turned out that making the emulator set ctxt->interruptibility like this asm volatile("pushf\n\t" "push %[flags]\n\t" "popf\n\t" KVM_FEP "mov %[ss], %%ss\n\t" "popf" : : [ss] "r" (read_ss()), [flags] "r" (read_rflags() | X86_EFLAGS_TF) : "memory"); results in "KVM: entry failed, hardware error 0x80000021". kvm_intel.dump_invalid_vmcs=1 tells me at that moment Interruptibility = 00000002 DebugExceptions = 0x0000000000000000 so perhaps it's related to the problem described in https://lkml.kernel.org/kvm/20220120000624.655815-1-seanjc@xxxxxxxxxx/ ? That said, I don't know if combining FEP+blocking+TF+DB is a misuse on my side, a bug, or a detail that happens to be unimplemented. Anyway, the POP-SS blocking test avoids touching EFLAGS.TF by using DR0/7. Note that doing this the ASM_TRY() way would require extending setup_idt() (to handle #DB) and introducing another ASM_TRY() variant (one without the initial `movl $0, %%gs:4`). x86/Makefile.i386 | 3 ++- x86/popss.c | 59 +++++++++++++++++++++++++++++++++++++++++++++++ x86/unittests.cfg | 4 ++++ 3 files changed, 65 insertions(+), 1 deletion(-) create mode 100644 x86/popss.c diff --git a/x86/Makefile.i386 b/x86/Makefile.i386 index 0a845e6..b570ae2 100644 --- a/x86/Makefile.i386 +++ b/x86/Makefile.i386 @@ -9,6 +9,7 @@ arch_LDFLAGS = -m elf_i386 cflatobjs += lib/x86/setjmp32.o lib/ldiv32.o tests = $(TEST_DIR)/taskswitch.$(exe) $(TEST_DIR)/taskswitch2.$(exe) \ - $(TEST_DIR)/cmpxchg8b.$(exe) $(TEST_DIR)/la57.$(exe) + $(TEST_DIR)/cmpxchg8b.$(exe) $(TEST_DIR)/la57.$(exe) \ + $(TEST_DIR)/popss.$(exe) include $(SRCDIR)/$(TEST_DIR)/Makefile.common diff --git a/x86/popss.c b/x86/popss.c new file mode 100644 index 0000000..7201f1e --- /dev/null +++ b/x86/popss.c @@ -0,0 +1,59 @@ +#include <asm/debugreg.h> + +#include "processor.h" +#include "libcflat.h" +#include "vmalloc.h" +#include "desc.h" + +static void test_pop_ss_blocking_handler(struct ex_regs *regs) +{ + extern char test_pop_ss_blocking_cont; + + regs->rip = (ulong)&test_pop_ss_blocking_cont; +} + +static void test_pop_ss_blocking(void) +{ + extern char db_blocked; + int success = 0; + handler old; + + old = handle_exception(DB_VECTOR, test_pop_ss_blocking_handler); + + write_dr0(&db_blocked); + write_dr7(DR7_FIXED_1 | + DR7_GLOBAL_ENABLE_DRx(0) | + DR7_EXECUTE_DRx(0) | + DR7_LEN_1_DRx(0)); + + /* + * The idea is that #DB on the instruction following POP SS should be + * suppressed. If the exception is actually suppressed, `success` gets + * set to 1, otherwise exception handler advances RIP away. + */ + asm volatile("push %[ss]\n\t" + KVM_FEP "pop %%ss\n\t" + "db_blocked: mov $1, %[success]\n\t" + "test_pop_ss_blocking_cont:" + : [success] "+g" (success) + : [ss] "r" (read_ss()) + : "memory"); + + write_dr7(DR7_FIXED_1); + handle_exception(DB_VECTOR, old); + + report(success, "#DB suppressed after POP SS"); +} + +int main(void) +{ + setup_vm(); + + if (is_fep_available()) + test_pop_ss_blocking(); + else + report_skip("skipping POP-SS blocking test, " + "use kvm.force_emulation_prefix=1 to enable"); + + return report_summary(); +} diff --git a/x86/unittests.cfg b/x86/unittests.cfg index ed65185..8d4e917 100644 --- a/x86/unittests.cfg +++ b/x86/unittests.cfg @@ -305,6 +305,10 @@ extra_params = -cpu qemu64,+umip file = la57.flat arch = i386 +[popss] +file = popss.flat +arch = i386 + [vmx] file = vmx.flat extra_params = -cpu max,+vmx -append "-exit_monitor_from_l2_test -ept_access* -vmx_smp* -vmx_vmcs_shadow_test -atomic_switch_overflow_msrs_test -vmx_init_signal_test -vmx_apic_passthrough_tpr_threshold_test -apic_reg_virt_test -virt_x2apic_mode_test -vmx_pf_exception_test -vmx_pf_no_vpid_test -vmx_pf_invvpid_test -vmx_pf_vpid_test" -- 2.37.2