@@ -402,6 +402,11 @@ static inline void safe_halt(void)
asm volatile("sti; hlt");
}
+static inline void cpu_relax(void)
+{
+ asm volatile ("rep; nop");
+}
+
static inline u32 read_pkru(void)
{
unsigned int eax, edx;
@@ -45,6 +45,7 @@ tests-common = $(TEST_DIR)/vmexit.flat $(TEST_DIR)/tsc.flat \
$(TEST_DIR)/tsc_adjust.flat $(TEST_DIR)/asyncpf.flat \
$(TEST_DIR)/init.flat $(TEST_DIR)/smap.flat \
$(TEST_DIR)/hyperv_synic.flat $(TEST_DIR)/hyperv_stimer.flat \
+ $(TEST_DIR)/pv_unhalt.flat \
ifdef API
tests-common += api/api-sample
new file mode 100644
@@ -0,0 +1,73 @@
+#include "libcflat.h"
+#include "vm.h"
+#include "desc.h"
+#include "smp.h"
+
+/* Intel and AMD hypercalls should be interchangeable. */
+#define KVM_HYPERCALL ".byte 0x0f,0x01,0xc1"
+
+#define KVM_HC_KICK_CPU 5
+
+#define KVM_CPUID_FEATURES 0x40000001
+#define KVM_FEATURE_PV_UNHALT (1 << 7)
+
+static volatile bool pv_unhalt_works;
+
+static inline long kvm_hypercall2(unsigned int nr, unsigned long p1,
+ unsigned long p2)
+{
+ long ret;
+ asm volatile(KVM_HYPERCALL
+ : "=a"(ret)
+ : "a"(nr), "b"(p1), "c"(p2)
+ : "memory");
+ return ret;
+}
+
+static void halt(void * nothing)
+{
+ asm volatile("cli; hlt; sti" ::: "memory");
+
+ pv_unhalt_works = true;
+}
+
+static void test_pv_unhalt(void)
+{
+ unsigned long delay;
+
+ if (cpu_count() < 2) {
+ report_skip("pv unhalt");
+ return;
+ }
+
+ /* XXX: KVM_CPUID_FEATURES can have a different base offset */
+ if (!(cpuid(KVM_CPUID_FEATURES).a & KVM_FEATURE_PV_UNHALT)) {
+ /* TODO: use argv to decide whether the presence of pv_unhalt
+ * is a skip or pass? */
+ report("pv unhalt", 1);
+ return;
+ }
+
+ on_cpu_async(1, halt, 0);
+ /* we need a small delay because CPU1 can't notify that it halted */
+ for (delay = 1<<28; delay; delay--)
+ cpu_relax();
+
+ kvm_hypercall2(KVM_HC_KICK_CPU, 0, 1);
+
+ /* TODO: sane delay loops (ignoring pv_unhalt_works is fine) */
+ for (delay = 1<<28; delay && !pv_unhalt_works; delay--)
+ cpu_relax();
+
+ report("pv unhalt", pv_unhalt_works);
+}
+
+int main(int ac, char **av)
+{
+ setup_vm();
+ smp_init();
+
+ test_pv_unhalt();
+
+ return report_summary();
+}
@@ -197,3 +197,13 @@ extra_params = -cpu kvm64,hv_synic -device hyperv-testdev
file = hyperv_stimer.flat
smp = 2
extra_params = -cpu kvm64,hv_time,hv_synic,hv_stimer -device hyperv-testdev
+
+[pv_unhalt]
+file = pv_unhalt.flat
+smp = 2
+extra_params = -cpu kvm64,+kvm_pv_unhalt
+
+[pv_unhalt_irqchip_off]
+file = pv_unhalt.flat
+smp = 2
+extra_params = -cpu kvm64,+kvm_pv_unhalt -machine kernel_irqchip=off