diff --git a/lib/x86/pmu.c b/lib/x86/pmu.c
index 35b7efb..c0d100d 100644
--- a/lib/x86/pmu.c
+++ b/lib/x86/pmu.c
@@ -8,4 +8,6 @@ void pmu_init(void)
     cpuid_10 = cpuid(10);
     if (this_cpu_has(X86_FEATURE_PDCM))
         pmu.perf_cap = rdmsr(MSR_IA32_PERF_CAPABILITIES);
+    pmu.msr_gp_counter_base = MSR_IA32_PERFCTR0;
+    pmu.msr_gp_event_select_base = MSR_P6_EVNTSEL0;
 }
\ No newline at end of file
diff --git a/lib/x86/pmu.h b/lib/x86/pmu.h
index 95b17da..7487a30 100644
--- a/lib/x86/pmu.h
+++ b/lib/x86/pmu.h
@@ -35,6 +35,8 @@
 
 struct pmu_caps {
     u64 perf_cap;
+    u32 msr_gp_counter_base;
+    u32 msr_gp_event_select_base;
 };
 
 extern struct cpuid cpuid_10;
@@ -42,6 +44,46 @@ extern struct pmu_caps pmu;
 
 void pmu_init(void);
 
+static inline u32 gp_counter_base(void)
+{
+	return pmu.msr_gp_counter_base;
+}
+
+static inline void set_gp_counter_base(u32 new_base)
+{
+	pmu.msr_gp_counter_base = new_base;
+}
+
+static inline u32 gp_event_select_base(void)
+{
+	return pmu.msr_gp_event_select_base;
+}
+
+static inline void set_gp_event_select_base(u32 new_base)
+{
+	pmu.msr_gp_event_select_base = new_base;
+}
+
+static inline u32 gp_counter_msr(unsigned int i)
+{
+	return gp_counter_base() + i;
+}
+
+static inline u32 gp_event_select_msr(unsigned int i)
+{
+	return gp_event_select_base() + i;
+}
+
+static inline void write_gp_counter_value(unsigned int i, u64 value)
+{
+	wrmsr(gp_counter_msr(i), value);
+}
+
+static inline void write_gp_event_select(unsigned int i, u64 value)
+{
+	wrmsr(gp_event_select_msr(i), value);
+}
+
 static inline u8 pmu_version(void)
 {
 	return cpuid_10.a & 0xff;
@@ -109,4 +151,9 @@ static inline bool pmu_has_full_writes(void)
 	return this_cpu_perf_capabilities() & PMU_CAP_FW_WRITES;
 }
 
+static inline bool pmu_use_full_writes(void)
+{
+	return gp_counter_base() == MSR_IA32_PMC0;
+}
+
 #endif /* _X86_PMU_H_ */
diff --git a/x86/pmu.c b/x86/pmu.c
index a6329cd..589c7cb 100644
--- a/x86/pmu.c
+++ b/x86/pmu.c
@@ -44,8 +44,6 @@ struct pmu_event {
 	{"fixed 3", MSR_CORE_PERF_FIXED_CTR0 + 2, 0.1*N, 30*N}
 };
 
-static u64 gp_counter_base = MSR_IA32_PERFCTR0;
-
 char *buf;
 
 static inline void loop(void)
@@ -84,7 +82,7 @@ static bool is_gp(pmu_counter_t *evt)
 
 static int event_to_global_idx(pmu_counter_t *cnt)
 {
-	return cnt->ctr - (is_gp(cnt) ? gp_counter_base :
+	return cnt->ctr - (is_gp(cnt) ? gp_counter_base() :
 		(MSR_CORE_PERF_FIXED_CTR0 - FIXED_CNT_INDEX));
 }
 
@@ -121,8 +119,7 @@ static void __start_event(pmu_counter_t *evt, uint64_t count)
     evt->count = count;
     wrmsr(evt->ctr, evt->count);
     if (is_gp(evt))
-	    wrmsr(MSR_P6_EVNTSEL0 + event_to_global_idx(evt),
-			    evt->config | EVNTSEL_EN);
+	    write_gp_event_select(event_to_global_idx(evt), evt->config | EVNTSEL_EN);
     else {
 	    uint32_t ctrl = rdmsr(MSR_CORE_PERF_FIXED_CTR_CTRL);
 	    int shift = (evt->ctr - MSR_CORE_PERF_FIXED_CTR0) * 4;
@@ -150,8 +147,7 @@ static void stop_event(pmu_counter_t *evt)
 {
 	global_disable(evt);
 	if (is_gp(evt))
-		wrmsr(MSR_P6_EVNTSEL0 + event_to_global_idx(evt),
-				evt->config & ~EVNTSEL_EN);
+		write_gp_event_select(event_to_global_idx(evt), evt->config & ~EVNTSEL_EN);
 	else {
 		uint32_t ctrl = rdmsr(MSR_CORE_PERF_FIXED_CTR_CTRL);
 		int shift = (evt->ctr - MSR_CORE_PERF_FIXED_CTR0) * 4;
@@ -198,12 +194,12 @@ static void check_gp_counter(struct pmu_event *evt)
 {
 	int nr_gp_counters = pmu_nr_gp_counters();
 	pmu_counter_t cnt = {
-		.ctr = gp_counter_base,
 		.config = EVNTSEL_OS | EVNTSEL_USR | evt->unit_sel,
 	};
 	int i;
 
-	for (i = 0; i < nr_gp_counters; i++, cnt.ctr++) {
+	for (i = 0; i < nr_gp_counters; i++) {
+		cnt.ctr = gp_counter_msr(i);
 		measure_one(&cnt);
 		report(verify_event(cnt.count, evt), "%s-%d", evt->name, i);
 	}
@@ -247,7 +243,7 @@ static void check_counters_many(void)
 		if (!pmu_gp_counter_is_available(i))
 			continue;
 
-		cnt[n].ctr = gp_counter_base + n;
+		cnt[n].ctr = gp_counter_msr(n);
 		cnt[n].config = EVNTSEL_OS | EVNTSEL_USR |
 			gp_events[i % ARRAY_SIZE(gp_events)].unit_sel;
 		n++;
@@ -287,7 +283,7 @@ static void check_counter_overflow(void)
 	uint64_t overflow_preset;
 	int i;
 	pmu_counter_t cnt = {
-		.ctr = gp_counter_base,
+		.ctr = gp_counter_msr(0),
 		.config = EVNTSEL_OS | EVNTSEL_USR | gp_events[1].unit_sel /* instructions */,
 	};
 	overflow_preset = measure_for_overflow(&cnt);
@@ -297,18 +293,20 @@ static void check_counter_overflow(void)
 
 	report_prefix_push("overflow");
 
-	for (i = 0; i < nr_gp_counters + 1; i++, cnt.ctr++) {
+	for (i = 0; i < nr_gp_counters + 1; i++) {
 		uint64_t status;
 		int idx;
 
 		cnt.count = overflow_preset;
-		if (gp_counter_base == MSR_IA32_PMC0)
+		if (pmu_use_full_writes())
 			cnt.count &= (1ull << pmu_gp_counter_width()) - 1;
 
 		if (i == nr_gp_counters) {
 			cnt.ctr = fixed_events[0].unit_sel;
 			cnt.count = measure_for_overflow(&cnt);
 			cnt.count &= (1ull << pmu_fixed_counter_width()) - 1;
+		} else {
+			cnt.ctr = gp_counter_msr(i);
 		}
 
 		if (i % 2)
@@ -332,7 +330,7 @@ static void check_counter_overflow(void)
 static void check_gp_counter_cmask(void)
 {
 	pmu_counter_t cnt = {
-		.ctr = gp_counter_base,
+		.ctr = gp_counter_msr(0),
 		.config = EVNTSEL_OS | EVNTSEL_USR | gp_events[1].unit_sel /* instructions */,
 	};
 	cnt.config |= (0x2 << EVNTSEL_CMASK_SHIFT);
@@ -367,7 +365,7 @@ static void check_rdpmc(void)
 	for (i = 0; i < nr_gp_counters; i++) {
 		uint64_t x;
 		pmu_counter_t cnt = {
-			.ctr = gp_counter_base + i,
+			.ctr = gp_counter_msr(i),
 			.idx = i
 		};
 
@@ -375,7 +373,7 @@ static void check_rdpmc(void)
 	         * Without full-width writes, only the low 32 bits are writable,
 	         * and the value is sign-extended.
 	         */
-		if (gp_counter_base == MSR_IA32_PERFCTR0)
+		if (gp_counter_base() == MSR_IA32_PERFCTR0)
 			x = (uint64_t)(int64_t)(int32_t)val;
 		else
 			x = (uint64_t)(int64_t)val;
@@ -383,7 +381,7 @@ static void check_rdpmc(void)
 		/* Mask according to the number of supported bits */
 		x &= (1ull << gp_counter_width) - 1;
 
-		wrmsr(gp_counter_base + i, val);
+		write_gp_counter_value(i, val);
 		report(rdpmc(i) == x, "cntr-%d", i);
 
 		exc = test_for_exception(GP_VECTOR, do_rdpmc_fast, &cnt);
@@ -417,7 +415,7 @@ static void check_running_counter_wrmsr(void)
 	uint64_t status;
 	uint64_t count;
 	pmu_counter_t evt = {
-		.ctr = gp_counter_base,
+		.ctr = gp_counter_msr(0),
 		.config = EVNTSEL_OS | EVNTSEL_USR | gp_events[1].unit_sel,
 	};
 
@@ -425,7 +423,7 @@ static void check_running_counter_wrmsr(void)
 
 	start_event(&evt);
 	loop();
-	wrmsr(gp_counter_base, 0);
+	write_gp_counter_value(0, 0);
 	stop_event(&evt);
 	report(evt.count < gp_events[1].min, "cntr");
 
@@ -436,10 +434,10 @@ static void check_running_counter_wrmsr(void)
 	start_event(&evt);
 
 	count = -1;
-	if (gp_counter_base == MSR_IA32_PMC0)
+	if (pmu_use_full_writes())
 		count &= (1ull << pmu_gp_counter_width()) - 1;
 
-	wrmsr(gp_counter_base, count);
+	write_gp_counter_value(0, count);
 
 	loop();
 	stop_event(&evt);
@@ -453,12 +451,12 @@ static void check_emulated_instr(void)
 {
 	uint64_t status, instr_start, brnch_start;
 	pmu_counter_t brnch_cnt = {
-		.ctr = MSR_IA32_PERFCTR0,
+		.ctr = gp_counter_msr(0),
 		/* branch instructions */
 		.config = EVNTSEL_OS | EVNTSEL_USR | gp_events[5].unit_sel,
 	};
 	pmu_counter_t instr_cnt = {
-		.ctr = MSR_IA32_PERFCTR0 + 1,
+		.ctr = gp_counter_msr(1),
 		/* instructions */
 		.config = EVNTSEL_OS | EVNTSEL_USR | gp_events[1].unit_sel,
 	};
@@ -472,8 +470,8 @@ static void check_emulated_instr(void)
 
 	brnch_start = -EXPECTED_BRNCH;
 	instr_start = -EXPECTED_INSTR;
-	wrmsr(MSR_IA32_PERFCTR0, brnch_start);
-	wrmsr(MSR_IA32_PERFCTR0 + 1, instr_start);
+	write_gp_counter_value(0, brnch_start);
+	write_gp_counter_value(1, instr_start);
 	// KVM_FEP is a magic prefix that forces emulation so
 	// 'KVM_FEP "jne label\n"' just counts as a single instruction.
 	asm volatile(
@@ -670,7 +668,7 @@ int main(int ac, char **av)
 	check_counters();
 
 	if (pmu_has_full_writes()) {
-		gp_counter_base = MSR_IA32_PMC0;
+		set_gp_counter_base(MSR_IA32_PMC0);
 		report_prefix_push("full-width writes");
 		check_counters();
 		check_gp_counters_write_width();
