diff --git a/lib/x86/msr.h b/lib/x86/msr.h
index 0e3fd03..9748436 100644
--- a/lib/x86/msr.h
+++ b/lib/x86/msr.h
@@ -430,6 +430,9 @@
 #define MSR_CORE_PERF_GLOBAL_CTRL	0x0000038f
 #define MSR_CORE_PERF_GLOBAL_OVF_CTRL	0x00000390
 
+/* PERF_GLOBAL_OVF_CTRL bits */
+#define MSR_CORE_PERF_GLOBAL_OVF_CTRL_LBR_FREEZE  (1ULL << 58)
+
 /* AMD Performance Counter Global Status and Control MSRs */
 #define MSR_AMD64_PERF_CNTR_GLOBAL_STATUS	0xc0000300
 #define MSR_AMD64_PERF_CNTR_GLOBAL_CTL		0xc0000301
diff --git a/x86/pmu_lbr.c b/x86/pmu_lbr.c
index 40b63fa..24220f0 100644
--- a/x86/pmu_lbr.c
+++ b/x86/pmu_lbr.c
@@ -2,11 +2,17 @@
 #include "x86/processor.h"
 #include "x86/pmu.h"
 #include "x86/desc.h"
+#include "x86/apic-defs.h"
+#include "x86/apic.h"
+#include "x86/isr.h"
 
 #define N 1000000
+#define MAX_LBR 64
 
 volatile int count;
 u32 lbr_from, lbr_to;
+int max;
+bool pmi_received = false;
 
 static noinline int compute_flag(int i)
 {
@@ -41,9 +47,102 @@ static bool test_init_lbr_from_exception(u64 index)
 	return test_for_exception(GP_VECTOR, init_lbr, &index);
 }
 
+static void pmi_handler(isr_regs_t *regs)
+{
+	uint64_t lbr_tos, from[MAX_LBR], to[MAX_LBR];
+	uint64_t gbl_status, debugctl = 0, lbr_cur;
+	int i;
+
+	pmi_received = true;
+
+	if (pmu.version < 4) {
+		debugctl = rdmsr(MSR_IA32_DEBUGCTLMSR);
+		report((debugctl & DEBUGCTLMSR_LBR) == 0,
+			"The guest LBR_EN is cleared in guest PMI");
+		gbl_status = rdmsr(pmu.msr_global_status);
+		report((gbl_status & BIT_ULL(0)) == BIT_ULL(0),
+			"GP counter 0 overflow.");
+	} else {
+		gbl_status = rdmsr(pmu.msr_global_status);
+		report((gbl_status & (MSR_CORE_PERF_GLOBAL_OVF_CTRL_LBR_FREEZE | BIT_ULL(0)))
+			== (MSR_CORE_PERF_GLOBAL_OVF_CTRL_LBR_FREEZE | BIT_ULL(0)),
+			"GP counter 0 overflow and LBR freeze.");
+	}
+
+	lbr_tos = rdmsr(MSR_LBR_TOS);
+	for (i = 0; i < max; ++i) {
+		from[i] = rdmsr(lbr_from + i);
+		to[i] = rdmsr(lbr_to + i);
+	}
+
+	lbr_test();
+
+	lbr_cur = rdmsr(MSR_LBR_TOS);
+	report(lbr_cur == lbr_tos,
+	       "LBR TOS freezed in PMI, %lx -> %lx", lbr_tos, lbr_cur);
+	for (i = 0; i < max; ++i) {
+		lbr_cur = rdmsr(lbr_from + i);
+		report(lbr_cur == from[i],
+		       "LBR from %d freezed in PMI, %lx -> %lx", i, from[i], lbr_cur);
+		lbr_cur = rdmsr(lbr_to + i);
+		report(lbr_cur == to[i],
+		       "LBR to %d freezed in PMI, %lx -> %lx", i, to[i], lbr_cur);
+	}
+
+	if (pmu.version < 4) {
+		debugctl |= DEBUGCTLMSR_LBR;
+		wrmsr(MSR_IA32_DEBUGCTLMSR, debugctl);
+	}
+
+	wrmsr(pmu.msr_global_status_clr, gbl_status);
+
+	apic_write(APIC_EOI, 0);
+}
+
+/* GP counter 0 overflow after 2 instructions. */
+static void setup_gp_counter_0_overflow(void)
+{
+	uint64_t count, ctrl;
+	int i;
+
+	count = (1ull << pmu.gp_counter_width) - 1 - 2;
+	wrmsr(pmu.msr_gp_counter_base, count);
+
+	ctrl = EVNTSEL_EN | EVNTSEL_USR | EVNTSEL_OS | EVNTSEL_INT | 0xc0;
+	wrmsr(pmu.msr_gp_event_select_base, ctrl);
+
+	wrmsr(pmu.msr_global_ctl, 0x1);
+
+	apic_write(APIC_LVTPC, PMI_VECTOR);
+
+	irq_enable();
+	asm volatile("nop; nop; nop");
+	for (i=0; i < 100000 && !pmi_received; i++)
+		asm volatile("pause");
+	irq_disable();
+}
+
+static void stop_gp_counter_0(void)
+{
+	wrmsr(pmu.msr_global_ctl, 0);
+	wrmsr(pmu.msr_gp_event_select_base, 0);
+}
+
+static void test_freeze_lbr_on_pmi(void)
+{
+	wrmsr(MSR_IA32_DEBUGCTLMSR,
+	      DEBUGCTLMSR_LBR | DEBUGCTLMSR_FREEZE_LBRS_ON_PMI);
+
+	setup_gp_counter_0_overflow();
+
+	stop_gp_counter_0();
+
+	wrmsr(MSR_IA32_DEBUGCTLMSR, 0);
+}
+
 int main(int ac, char **av)
 {
-	int max, i;
+	int i;
 
 	setup_vm();
 
@@ -70,6 +169,12 @@ int main(int ac, char **av)
 	printf("PMU version:		 %d\n", pmu.version);
 	printf("LBR version:		 %ld\n", pmu_lbr_version());
 
+	handle_irq(PMI_VECTOR, pmi_handler);
+	apic_write(APIC_LVTPC, PMI_VECTOR);
+
+	if (pmu_has_full_writes())
+		pmu.msr_gp_counter_base = MSR_IA32_PMC0;
+
 	/* Look for LBR from and to MSRs */
 	lbr_from = MSR_LBR_CORE_FROM;
 	lbr_to = MSR_LBR_CORE_TO;
@@ -104,5 +209,7 @@ int main(int ac, char **av)
 	}
 	report(i == max, "The guest LBR FROM_IP/TO_IP values are good.");
 
+	test_freeze_lbr_on_pmi();
+
 	return report_summary();
 }
