Cherry referring to my last name kirschju.re Forward and Reverse Engineering

Exposing Model Specific Registers on the Qemu Monitor

Model Specific Registers (MSRs) are pretty helpful when trying to understand a x86-64 virtual machine’s state. For example, the fs and gs segment bases are no longer specified via an entry in one of the discriptor table, but are read from MSR_GSBASE and MSR_FSBASE instead. Operating systems make use of this mechanism to access data structures like the Thread Local Storage (TLS) on Linux or the Thread Environment Block (TEB) on Windows. Similarly, there is a MSR_KERNELGSBASE whose contents are swapped with MSR_GSBASE upon encountering a swapgs instruction.

The following patch (against qemu-devel commit 55a19ad8b2d0797e3a8fe90ab99a9bb713824059) adds two commands msr_get and msr_set to the qemu monitor that allow reading and writing MSR values. To access them, either type them directly into the monitor console, or use the monitor command of an attached remote gdb.

diff --git a/hmp-commands.hx b/hmp-commands.hx
index 88192817b2..07a09120aa 100644
--- a/hmp-commands.hx
+++ b/hmp-commands.hx
@@ -1568,6 +1568,44 @@ STEXI
 Inject an MCE on the given CPU (x86 only).
 ETEXI
 
+
+#if defined(TARGET_I386)
+
+    {
+        .name         = "msr_get",
+        .args_type    = "msr_index:i",
+        .params       = "msrindex",
+        .help         = "get value of model specific register (MSR) on x86",
+        .cmd          =  hmp_msr_get,
+    },
+
+#endif
+STEXI
+@item msr-get @var{msridx}
+@findex msr-get (x86)
+Print value of model specific register @var{msr_index} on current CPU (x86
+only).
+ETEXI
+
+
+#if defined(TARGET_I386)
+
+    {
+        .name         = "msr_set",
+        .args_type    = "msr_index:i,value:l",
+        .params       = "msrindex value",
+        .help         = "set value of model specific register (MSR) on x86",
+        .cmd          =  hmp_msr_set,
+    },
+
+#endif
+STEXI
+@item msr-set @var{msr_index} @var{value}
+@findex msr-set
+Set value of model specific register @var{msr_index} on current CPU to
+@var{value} (x86 only).
+ETEXI
+
     {
         .name       = "getfd",
         .args_type  = "fdname:s",
diff --git a/include/monitor/hmp-target.h b/include/monitor/hmp-target.h
index 454e8ed155..a0da1eb05b 100644
--- a/include/monitor/hmp-target.h
+++ b/include/monitor/hmp-target.h
@@ -46,5 +46,7 @@ void hmp_info_tlb(Monitor *mon, const QDict *qdict);
 void hmp_mce(Monitor *mon, const QDict *qdict);
 void hmp_info_local_apic(Monitor *mon, const QDict *qdict);
 void hmp_info_io_apic(Monitor *mon, const QDict *qdict);
+void hmp_msr_get(Monitor *mon, const QDict *qdict);
+void hmp_msr_set(Monitor *mon, const QDict *qdict);
 
 #endif /* MONITOR_HMP_TARGET_H */
diff --git a/linux-user/syscall.c b/linux-user/syscall.c
index cec8428589..1348c5d02e 100644
--- a/linux-user/syscall.c
+++ b/linux-user/syscall.c
@@ -7426,7 +7426,7 @@ static int open_self_maps(void *cpu_env, int fd)
         char flag_r, flag_w, flag_x, flag_p;
         char path[512] = "";
         fields = sscanf(line, "%"PRIx64"-%"PRIx64" %c%c%c%c %"PRIx64" %x:%x %d"
-                        " %512s", &min, &max, &flag_r, &flag_w, &flag_x,
+                        " %511s", &min, &max, &flag_r, &flag_w, &flag_x,
                         &flag_p, &offset, &dev_maj, &dev_min, &inode, path);
 
         if ((fields < 10) || (fields > 11)) {
diff --git a/target/i386/cpu.h b/target/i386/cpu.h
index 07401ad9fe..84b7080ade 100644
--- a/target/i386/cpu.h
+++ b/target/i386/cpu.h
@@ -1310,6 +1310,9 @@ int x86_cpu_write_elf32_qemunote(WriteCoreDumpFunction f, CPUState *cpu,
 void x86_cpu_get_memory_mapping(CPUState *cpu, MemoryMappingList *list,
                                 Error **errp);
 
+uint64_t x86_cpu_rdmsr(CPUX86State *env, uint32_t idx, bool *valid);
+void x86_cpu_wrmsr(CPUX86State *env, uint32_t idx, uint64_t val, bool *valid);
+
 void x86_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function cpu_fprintf,
                         int flags);
 
diff --git a/target/i386/helper.c b/target/i386/helper.c
index e2af3404f2..5b2dee3f52 100644
--- a/target/i386/helper.c
+++ b/target/i386/helper.c
@@ -26,6 +26,7 @@
 #include "sysemu/sysemu.h"
 #include "sysemu/hw_accel.h"
 #include "monitor/monitor.h"
+#include "hw/i386/apic.h"
 #include "hw/i386/apic_internal.h"
 #endif
 
@@ -364,11 +365,529 @@ void x86_cpu_dump_local_apic_state(CPUState *cs, FILE *f,
     }
     cpu_fprintf(f, " PPR 0x%02x\n", apic_get_ppr(s));
 }
+
+void x86_cpu_wrmsr(CPUX86State *env, uint32_t idx, uint64_t val, bool *valid)
+{
+    *valid = true;
+    /* FIXME: With KVM nabled, only report success if we are sure the new value
+     * will actually be written back by the KVM subsystem later. */
+
+    switch (idx) {
+    case MSR_IA32_SYSENTER_CS:
+        env->sysenter_cs = val & 0xffff;
+        break;
+    case MSR_IA32_SYSENTER_ESP:
+        env->sysenter_esp = val;
+        break;
+    case MSR_IA32_SYSENTER_EIP:
+        env->sysenter_eip = val;
+        break;
+    case MSR_PAT:
+        env->pat = val;
+        break;
+    case MSR_STAR:
+        env->star = val;
+        break;
+    case MSR_VM_HSAVE_PA:
+        env->vm_hsave = val;
+        break;
+    case MSR_TSC_AUX:
+        env->tsc_aux = val;
+        break;
+    case MSR_TSC_ADJUST:
+        env->tsc_adjust = val;
+        break;
+    case MSR_IA32_MISC_ENABLE:
+        env->msr_ia32_misc_enable = val;
+        break;
+    case MSR_IA32_SMBASE:
+        env->smbase = val;
+        break;
+    case MSR_IA32_BNDCFGS:
+        /* FIXME: #GP if reserved bits are set.  */
+        /* FIXME: Extend highest implemented bit of linear address.  */
+        env->msr_bndcfgs = val;
+        cpu_sync_bndcs_hflags(env);
+        break;
+    case MSR_IA32_XSS:
+        env->xss = val;
+        break;
+#ifdef TARGET_X86_64
+    case MSR_CSTAR:
+        env->cstar = val;
+        break;
+    case MSR_KERNELGSBASE:
+        env->kernelgsbase = val;
+        break;
+    case MSR_GSBASE:
+        env->segs[R_GS].base = val;
+        break;
+    case MSR_FSBASE:
+        env->segs[R_FS].base = val;
+        break;
+    case MSR_FMASK:
+        env->fmask = val;
+        break;
+    case MSR_LSTAR:
+        env->lstar = val;
+        break;
+#endif
+    case MSR_EFER:
+        {
+            uint64_t update_mask;
+
+            update_mask = 0;
+            if (env->features[FEAT_8000_0001_EDX] & CPUID_EXT2_SYSCALL) {
+                update_mask |= MSR_EFER_SCE;
+            }
+            if (env->features[FEAT_8000_0001_EDX] & CPUID_EXT2_LM) {
+                update_mask |= MSR_EFER_LME;
+            }
+            if (env->features[FEAT_8000_0001_EDX] & CPUID_EXT2_FFXSR) {
+                update_mask |= MSR_EFER_FFXSR;
+            }
+            if (env->features[FEAT_8000_0001_EDX] & CPUID_EXT2_NX) {
+                update_mask |= MSR_EFER_NXE;
+            }
+            if (env->features[FEAT_8000_0001_ECX] & CPUID_EXT3_SVM) {
+                update_mask |= MSR_EFER_SVME;
+            }
+            if (env->features[FEAT_8000_0001_EDX] & CPUID_EXT2_FFXSR) {
+                update_mask |= MSR_EFER_FFXSR;
+            }
+            cpu_load_efer(env, (env->efer & ~update_mask) |
+                          (val & update_mask));
+        }
+        break;
+    case MSR_IA32_APICBASE:
+        cpu_set_apic_base(x86_env_get_cpu(env)->apic_state, val);
+        break;
+    case MSR_IA32_TSC:
+        env->tsc = val;
+        break;
+#ifdef CONFIG_KVM
+    case MSR_KVM_SYSTEM_TIME:
+        env->system_time_msr = val;
+        break;
+    case MSR_KVM_WALL_CLOCK:
+        env->wall_clock_msr = val;
+        break;
+    case MSR_KVM_ASYNC_PF_EN:
+        if (env->features[FEAT_KVM] & (1 << KVM_FEATURE_ASYNC_PF)) {
+            env->async_pf_en_msr = val;
+        } else {
+            *valid = false;
+        }
+    case MSR_KVM_PV_EOI_EN:
+        if (env->features[FEAT_KVM] & (1 << KVM_FEATURE_PV_EOI)) {
+            env->pv_eoi_en_msr = val;
+        } else {
+            *valid = false;
+        }
+
+#endif
+    case MSR_CORE_PERF_FIXED_CTR_CTRL:
+        env->msr_fixed_ctr_ctrl = val;
+        break;
+    case MSR_CORE_PERF_GLOBAL_CTRL:
+        env->msr_global_ctrl = val;
+        break;
+    case MSR_CORE_PERF_GLOBAL_STATUS:
+        env->msr_global_status = val;
+        break;
+    case MSR_CORE_PERF_GLOBAL_OVF_CTRL:
+        env->msr_global_ovf_ctrl = val;
+        break;
+    case MSR_CORE_PERF_FIXED_CTR0 ... MSR_CORE_PERF_FIXED_CTR0 + MAX_FIXED_COUNTERS - 1:
+        env->msr_fixed_counters[idx - MSR_CORE_PERF_FIXED_CTR0] = val;
+        break;
+    case MSR_P6_PERFCTR0 ... MSR_P6_PERFCTR0 + MAX_GP_COUNTERS - 1:
+        env->msr_gp_counters[idx - MSR_P6_PERFCTR0] = val;
+        break;
+    case MSR_P6_EVNTSEL0 ... MSR_P6_EVNTSEL0 + MAX_GP_COUNTERS - 1:
+        env->msr_gp_evtsel[idx - MSR_P6_EVNTSEL0] = val;
+        break;
+#if defined CONFIG_KVM
+    case HV_X64_MSR_HYPERCALL:
+        env->msr_hv_hypercall = val;
+        break;
+    case HV_X64_MSR_GUEST_OS_ID:
+        env->msr_hv_guest_os_id = val;
+        break;
+    case HV_X64_MSR_APIC_ASSIST_PAGE:
+        env->msr_hv_vapic = val;
+        break;
+    case HV_X64_MSR_REFERENCE_TSC:
+        env->msr_hv_tsc = val;
+        break;
+    case HV_X64_MSR_CRASH_P0 ... HV_X64_MSR_CRASH_P4:
+        env->msr_hv_crash_params[idx - HV_X64_MSR_CRASH_P0] = val;
+        break;
+    case HV_X64_MSR_VP_RUNTIME:
+        env->msr_hv_runtime = val;
+        break;
+    case HV_X64_MSR_SCONTROL:
+        env->msr_hv_synic_control = val;
+        break;
+    case HV_X64_MSR_SVERSION:
+        env->msr_hv_synic_version = val;
+        break;
+    case HV_X64_MSR_SIEFP:
+        env->msr_hv_synic_evt_page = val;
+        break;
+    case HV_X64_MSR_SIMP:
+        env->msr_hv_synic_msg_page = val;
+        break;
+    case HV_X64_MSR_SINT0 ... HV_X64_MSR_SINT15:
+        env->msr_hv_synic_sint[idx - HV_X64_MSR_SINT0] = val;
+        break;
+    case HV_X64_MSR_STIMER0_CONFIG:
+    case HV_X64_MSR_STIMER1_CONFIG:
+    case HV_X64_MSR_STIMER2_CONFIG:
+    case HV_X64_MSR_STIMER3_CONFIG:
+        env->msr_hv_stimer_config[(idx - HV_X64_MSR_STIMER0_CONFIG) / 2] = val;
+        break;
+    case HV_X64_MSR_STIMER0_COUNT:
+    case HV_X64_MSR_STIMER1_COUNT:
+    case HV_X64_MSR_STIMER2_COUNT:
+    case HV_X64_MSR_STIMER3_COUNT:
+        env->msr_hv_stimer_count[(idx - HV_X64_MSR_STIMER0_COUNT) / 2] = val;
+        break;
+#endif
+    case MSR_MTRRdefType:
+        env->mtrr_deftype = val;
+        break;
+    case MSR_MTRRfix64K_00000:
+        env->mtrr_fixed[idx - MSR_MTRRfix64K_00000] = val;
+        break;
+    case MSR_MTRRfix16K_80000:
+    case MSR_MTRRfix16K_A0000:
+        env->mtrr_fixed[idx - MSR_MTRRfix16K_80000 + 1] = val;
+        break;
+    case MSR_MTRRfix4K_C0000:
+    case MSR_MTRRfix4K_C8000:
+    case MSR_MTRRfix4K_D0000:
+    case MSR_MTRRfix4K_D8000:
+    case MSR_MTRRfix4K_E0000:
+    case MSR_MTRRfix4K_E8000:
+    case MSR_MTRRfix4K_F0000:
+    case MSR_MTRRfix4K_F8000:
+        env->mtrr_fixed[idx - MSR_MTRRfix4K_C0000 + 3] = val;
+        break;
+    case MSR_MTRRphysBase(0):
+    case MSR_MTRRphysBase(1):
+    case MSR_MTRRphysBase(2):
+    case MSR_MTRRphysBase(3):
+    case MSR_MTRRphysBase(4):
+    case MSR_MTRRphysBase(5):
+    case MSR_MTRRphysBase(6):
+    case MSR_MTRRphysBase(7):
+        env->mtrr_var[(idx - MSR_MTRRphysBase(0)) / 2].base = val;
+        break;
+    case MSR_MTRRphysMask(0):
+    case MSR_MTRRphysMask(1):
+    case MSR_MTRRphysMask(2):
+    case MSR_MTRRphysMask(3):
+    case MSR_MTRRphysMask(4):
+    case MSR_MTRRphysMask(5):
+    case MSR_MTRRphysMask(6):
+    case MSR_MTRRphysMask(7):
+        env->mtrr_var[(idx - MSR_MTRRphysMask(0)) / 2].mask = val;
+        break;
+    case MSR_MCG_STATUS:
+        env->mcg_status = val;
+        break;
+    case MSR_MCG_CTL:
+        if ((env->mcg_cap & MCG_CTL_P)
+            && (val == 0 || val == ~(uint64_t)0)) {
+            env->mcg_ctl = val;
+        }
+        break;
+    default:
+        *valid = false;
+        if (idx >= MSR_MC0_CTL && idx < MSR_MC0_CTL +
+            (4 * env->mcg_cap & 0xff)) {
+            uint32_t offset = idx - MSR_MC0_CTL;
+            if ((offset & 0x3) != 0
+                || (val == 0 || val == ~(uint64_t)0)) {
+                env->mce_banks[offset] = val;
+                *valid = true;
+            }
+            break;
+        }
+        /* XXX: Pass request to underlying KVM? */
+        val = 0;
+        *valid = false;
+        break;
+    }
+}
+
+uint64_t x86_cpu_rdmsr(CPUX86State *env, uint32_t idx, bool *valid)
+{
+    uint64_t val;
+    *valid = true;
+
+    /* MSR list loosely following the order in kvm.c */
+    switch (idx) {
+    case MSR_IA32_SYSENTER_CS:
+        val = env->sysenter_cs;
+        break;
+    case MSR_IA32_SYSENTER_ESP:
+        val = env->sysenter_esp;
+        break;
+    case MSR_IA32_SYSENTER_EIP:
+        val = env->sysenter_eip;
+        break;
+    case MSR_PAT:
+        val = env->pat;
+        break;
+    case MSR_STAR:
+        val = env->star;
+        break;
+#ifdef TARGET_X86_64
+    case MSR_CSTAR:
+        val = env->cstar;
+        break;
+    case MSR_KERNELGSBASE:
+        val = env->kernelgsbase;
+        break;
+    case MSR_GSBASE:
+        val = env->segs[R_GS].base;
+        break;
+    case MSR_FSBASE:
+        val = env->segs[R_FS].base;
+        break;
+    case MSR_FMASK:
+        val = env->fmask;
+        break;
+    case MSR_LSTAR:
+        val = env->lstar;
+        break;
+#endif
+    case MSR_EFER:
+        val = env->efer;
+        break;
+    case MSR_IA32_APICBASE:
+        val = cpu_get_apic_base(x86_env_get_cpu(env)->apic_state);
+        break;
+    case MSR_IA32_PERF_STATUS:
+        /* tsc_increment_by_tick */
+        val = 1000ULL;
+        /* CPU multiplier */
+        val |= (((uint64_t)4ULL) << 40);
+        break;
+    case MSR_IA32_TSC:
+        val = env->tsc;
+        break;
+    case MSR_TSC_AUX:
+        val = env->tsc_aux;
+        break;
+    case MSR_TSC_ADJUST:
+        val = env->tsc_adjust;
+        break;
+    case MSR_IA32_TSCDEADLINE:
+        val = env->tsc_deadline;
+        break;
+    case MSR_VM_HSAVE_PA:
+        val = env->vm_hsave;
+        break;
+#ifdef CONFIG_KVM
+    /* Export kvm specific pseudo MSRs using their new ordinals only */
+    case MSR_KVM_SYSTEM_TIME_NEW:
+        val = env->system_time_msr;
+        break;
+    case MSR_KVM_WALL_CLOCK_NEW:
+        val = env->wall_clock_msr;
+        break;
+    case MSR_KVM_ASYNC_PF_EN:
+        val = env->async_pf_en_msr;
+        break;
+    case MSR_KVM_PV_EOI_EN:
+        val = env->pv_eoi_en_msr;
+        break;
+    case MSR_KVM_STEAL_TIME:
+        val = env->steal_time_msr;
+        break;
+#endif
+    case MSR_MCG_STATUS:
+        val = env->mcg_status;
+        break;
+    case MSR_MCG_CAP:
+        val = env->mcg_cap;
+        break;
+    case MSR_MCG_CTL:
+        if (env->mcg_cap & MCG_CTL_P) {
+            val = env->mcg_ctl;
+        } else {
+            /* XXX: exception */
+            *valid = false;
+            val = 0;
+        }
+        break;
+    case MSR_MCG_EXT_CTL:
+        if (env->mcg_cap & MCG_CTL_P) {
+            val = env->mcg_ext_ctl;
+        } else {
+            /* XXX: exception */
+            *valid = false;
+            val = 0;
+        }
+        break;
+    case MSR_IA32_MISC_ENABLE:
+        val = env->msr_ia32_misc_enable;
+        break;
+    case MSR_IA32_SMBASE:
+        val = env->smbase;
+        break;
+    case MSR_IA32_FEATURE_CONTROL:
+        val = env->msr_ia32_feature_control;
+        break;
+    case MSR_IA32_BNDCFGS:
+        val = env->msr_bndcfgs;
+        break;
+    case MSR_IA32_XSS:
+        val = env->xss;
+        break;
+    case MSR_CORE_PERF_FIXED_CTR_CTRL:
+        val = env->msr_fixed_ctr_ctrl;
+        break;
+    case MSR_CORE_PERF_GLOBAL_CTRL:
+        val = env->msr_global_ctrl;
+        break;
+    case MSR_CORE_PERF_GLOBAL_STATUS:
+        val = env->msr_global_status;
+        break;
+    case MSR_CORE_PERF_GLOBAL_OVF_CTRL:
+        val = env->msr_global_ovf_ctrl;
+        break;
+    case MSR_CORE_PERF_FIXED_CTR0 ... MSR_CORE_PERF_FIXED_CTR0 + MAX_FIXED_COUNTERS - 1:
+        val = env->msr_fixed_counters[idx - MSR_CORE_PERF_FIXED_CTR0];
+        break;
+    case MSR_P6_PERFCTR0 ... MSR_P6_PERFCTR0 + MAX_GP_COUNTERS - 1:
+        val = env->msr_gp_counters[idx - MSR_P6_PERFCTR0];
+        break;
+    case MSR_P6_EVNTSEL0 ... MSR_P6_EVNTSEL0 + MAX_GP_COUNTERS - 1:
+        val = env->msr_gp_evtsel[idx - MSR_P6_EVNTSEL0];
+        break;
+#if defined CONFIG_KVM
+    case HV_X64_MSR_HYPERCALL:
+        val = env->msr_hv_hypercall;
+        break;
+    case HV_X64_MSR_GUEST_OS_ID:
+        val = env->msr_hv_guest_os_id;
+        break;
+    case HV_X64_MSR_APIC_ASSIST_PAGE:
+        val = env->msr_hv_vapic;
+        break;
+    case HV_X64_MSR_REFERENCE_TSC:
+        val = env->msr_hv_tsc;
+        break;
+    case HV_X64_MSR_CRASH_P0 ... HV_X64_MSR_CRASH_P4:
+        val = env->msr_hv_crash_params[idx - HV_X64_MSR_CRASH_P0];
+        break;
+    case HV_X64_MSR_VP_RUNTIME:
+        val = env->msr_hv_runtime;
+        break;
+    case HV_X64_MSR_SCONTROL:
+        val = env->msr_hv_synic_control;
+        break;
+    case HV_X64_MSR_SVERSION:
+        val = env->msr_hv_synic_version;
+        break;
+    case HV_X64_MSR_SIEFP:
+        val = env->msr_hv_synic_evt_page;
+        break;
+    case HV_X64_MSR_SIMP:
+        val = env->msr_hv_synic_msg_page;
+        break;
+    case HV_X64_MSR_SINT0 ... HV_X64_MSR_SINT15:
+        val = env->msr_hv_synic_sint[idx - HV_X64_MSR_SINT0];
+        break;
+    case HV_X64_MSR_STIMER0_CONFIG:
+    case HV_X64_MSR_STIMER1_CONFIG:
+    case HV_X64_MSR_STIMER2_CONFIG:
+    case HV_X64_MSR_STIMER3_CONFIG:
+        val = env->msr_hv_stimer_config[(idx - HV_X64_MSR_STIMER0_CONFIG) / 2];
+        break;
+    case HV_X64_MSR_STIMER0_COUNT:
+    case HV_X64_MSR_STIMER1_COUNT:
+    case HV_X64_MSR_STIMER2_COUNT:
+    case HV_X64_MSR_STIMER3_COUNT:
+        val = env->msr_hv_stimer_count[(idx - HV_X64_MSR_STIMER0_COUNT) / 2];
+        break;
+#endif
+    case MSR_MTRRdefType:
+        val = env->mtrr_deftype;
+        break;
+    case MSR_MTRRfix64K_00000:
+        val = env->mtrr_fixed[0];
+        break;
+    case MSR_MTRRfix16K_80000:
+    case MSR_MTRRfix16K_A0000:
+        val = env->mtrr_fixed[idx - MSR_MTRRfix16K_80000 + 1];
+        break;
+    case MSR_MTRRfix4K_C0000:
+    case MSR_MTRRfix4K_C8000:
+    case MSR_MTRRfix4K_D0000:
+    case MSR_MTRRfix4K_D8000:
+    case MSR_MTRRfix4K_E0000:
+    case MSR_MTRRfix4K_E8000:
+    case MSR_MTRRfix4K_F0000:
+    case MSR_MTRRfix4K_F8000:
+        val = env->mtrr_fixed[idx - MSR_MTRRfix4K_C0000 + 3];
+        break;
+    case MSR_MTRRphysBase(0):
+    case MSR_MTRRphysBase(1):
+    case MSR_MTRRphysBase(2):
+    case MSR_MTRRphysBase(3):
+    case MSR_MTRRphysBase(4):
+    case MSR_MTRRphysBase(5):
+    case MSR_MTRRphysBase(6):
+    case MSR_MTRRphysBase(7):
+        val = env->mtrr_var[(idx - MSR_MTRRphysBase(0)) / 2].base;
+        break;
+    case MSR_MTRRphysMask(0):
+    case MSR_MTRRphysMask(1):
+    case MSR_MTRRphysMask(2):
+    case MSR_MTRRphysMask(3):
+    case MSR_MTRRphysMask(4):
+    case MSR_MTRRphysMask(5):
+    case MSR_MTRRphysMask(6):
+    case MSR_MTRRphysMask(7):
+        val = env->mtrr_var[(idx - MSR_MTRRphysMask(0)) / 2].mask;
+        break;
+            /* XXX: exception? */
+            *valid = false;
+            val = 0;
+        }
+        break;
+    default:
+        if (idx >= MSR_MC0_CTL && idx < MSR_MC0_CTL +
+            (4 * env->mcg_cap & 0xff)) {
+            val = env->mce_banks[idx - MSR_MC0_CTL];
+            break;
+        }
+        *valid = false;
+        /* XXX: Pass request to underlying KVM, if present? */
+        val = 0;
+        break;
+    }
+    return val;
+}
 #else
 void x86_cpu_dump_local_apic_state(CPUState *cs, FILE *f,
                                    fprintf_function cpu_fprintf, int flags)
 {
 }
+void x86_cpu_wrmsr(CPUX86State *env, uint32_t idx, uint64_t val, bool *valid)
+{
+    *valid = false;
+}
+uint64_t x86_cpu_rdmsr(CPUX86State *env, uint32_t idx, bool *valid)
+{
+    *valid = false;
+    return 0ULL;
+}
 #endif /* !CONFIG_USER_ONLY */
 
 #define DUMP_CODE_BYTES_TOTAL    50
diff --git a/target/i386/misc_helper.c b/target/i386/misc_helper.c
index ca2ea09f54..fbbf9d146e 100644
--- a/target/i386/misc_helper.c
+++ b/target/i386/misc_helper.c
@@ -227,307 +227,32 @@ void helper_rdmsr(CPUX86State *env)
 void helper_wrmsr(CPUX86State *env)
 {
     uint64_t val;
+    bool res_valid;
 
     cpu_svm_check_intercept_param(env, SVM_EXIT_MSR, 1, GETPC());
 
     val = ((uint32_t)env->regs[R_EAX]) |
         ((uint64_t)((uint32_t)env->regs[R_EDX]) << 32);
 
-    switch ((uint32_t)env->regs[R_ECX]) {
-    case MSR_IA32_SYSENTER_CS:
-        env->sysenter_cs = val & 0xffff;
-        break;
-    case MSR_IA32_SYSENTER_ESP:
-        env->sysenter_esp = val;
-        break;
-    case MSR_IA32_SYSENTER_EIP:
-        env->sysenter_eip = val;
-        break;
-    case MSR_IA32_APICBASE:
-        cpu_set_apic_base(x86_env_get_cpu(env)->apic_state, val);
-        break;
-    case MSR_EFER:
-        {
-            uint64_t update_mask;
+    x86_cpu_wrmsr(env, (uint32_t)env->regs[R_ECX], val, &res_valid);
+
+    /* XXX: exception? */
+    if (!res_valid) { }
 
-            update_mask = 0;
-            if (env->features[FEAT_8000_0001_EDX] & CPUID_EXT2_SYSCALL) {
-                update_mask |= MSR_EFER_SCE;
-            }
-            if (env->features[FEAT_8000_0001_EDX] & CPUID_EXT2_LM) {
-                update_mask |= MSR_EFER_LME;
-            }
-            if (env->features[FEAT_8000_0001_EDX] & CPUID_EXT2_FFXSR) {
-                update_mask |= MSR_EFER_FFXSR;
-            }
-            if (env->features[FEAT_8000_0001_EDX] & CPUID_EXT2_NX) {
-                update_mask |= MSR_EFER_NXE;
-            }
-            if (env->features[FEAT_8000_0001_ECX] & CPUID_EXT3_SVM) {
-                update_mask |= MSR_EFER_SVME;
-            }
-            if (env->features[FEAT_8000_0001_EDX] & CPUID_EXT2_FFXSR) {
-                update_mask |= MSR_EFER_FFXSR;
-            }
-            cpu_load_efer(env, (env->efer & ~update_mask) |
-                          (val & update_mask));
-        }
-        break;
-    case MSR_STAR:
-        env->star = val;
-        break;
-    case MSR_PAT:
-        env->pat = val;
-        break;
-    case MSR_VM_HSAVE_PA:
-        env->vm_hsave = val;
-        break;
-#ifdef TARGET_X86_64
-    case MSR_LSTAR:
-        env->lstar = val;
-        break;
-    case MSR_CSTAR:
-        env->cstar = val;
-        break;
-    case MSR_FMASK:
-        env->fmask = val;
-        break;
-    case MSR_FSBASE:
-        env->segs[R_FS].base = val;
-        break;
-    case MSR_GSBASE:
-        env->segs[R_GS].base = val;
-        break;
-    case MSR_KERNELGSBASE:
-        env->kernelgsbase = val;
-        break;
-#endif
-    case MSR_MTRRphysBase(0):
-    case MSR_MTRRphysBase(1):
-    case MSR_MTRRphysBase(2):
-    case MSR_MTRRphysBase(3):
-    case MSR_MTRRphysBase(4):
-    case MSR_MTRRphysBase(5):
-    case MSR_MTRRphysBase(6):
-    case MSR_MTRRphysBase(7):
-        env->mtrr_var[((uint32_t)env->regs[R_ECX] -
-                       MSR_MTRRphysBase(0)) / 2].base = val;
-        break;
-    case MSR_MTRRphysMask(0):
-    case MSR_MTRRphysMask(1):
-    case MSR_MTRRphysMask(2):
-    case MSR_MTRRphysMask(3):
-    case MSR_MTRRphysMask(4):
-    case MSR_MTRRphysMask(5):
-    case MSR_MTRRphysMask(6):
-    case MSR_MTRRphysMask(7):
-        env->mtrr_var[((uint32_t)env->regs[R_ECX] -
-                       MSR_MTRRphysMask(0)) / 2].mask = val;
-        break;
-    case MSR_MTRRfix64K_00000:
-        env->mtrr_fixed[(uint32_t)env->regs[R_ECX] -
-                        MSR_MTRRfix64K_00000] = val;
-        break;
-    case MSR_MTRRfix16K_80000:
-    case MSR_MTRRfix16K_A0000:
-        env->mtrr_fixed[(uint32_t)env->regs[R_ECX] -
-                        MSR_MTRRfix16K_80000 + 1] = val;
-        break;
-    case MSR_MTRRfix4K_C0000:
-    case MSR_MTRRfix4K_C8000:
-    case MSR_MTRRfix4K_D0000:
-    case MSR_MTRRfix4K_D8000:
-    case MSR_MTRRfix4K_E0000:
-    case MSR_MTRRfix4K_E8000:
-    case MSR_MTRRfix4K_F0000:
-    case MSR_MTRRfix4K_F8000:
-        env->mtrr_fixed[(uint32_t)env->regs[R_ECX] -
-                        MSR_MTRRfix4K_C0000 + 3] = val;
-        break;
-    case MSR_MTRRdefType:
-        env->mtrr_deftype = val;
-        break;
-    case MSR_MCG_STATUS:
-        env->mcg_status = val;
-        break;
-    case MSR_MCG_CTL:
-        if ((env->mcg_cap & MCG_CTL_P)
-            && (val == 0 || val == ~(uint64_t)0)) {
-            env->mcg_ctl = val;
-        }
-        break;
-    case MSR_TSC_AUX:
-        env->tsc_aux = val;
-        break;
-    case MSR_IA32_MISC_ENABLE:
-        env->msr_ia32_misc_enable = val;
-        break;
-    case MSR_IA32_BNDCFGS:
-        /* FIXME: #GP if reserved bits are set.  */
-        /* FIXME: Extend highest implemented bit of linear address.  */
-        env->msr_bndcfgs = val;
-        cpu_sync_bndcs_hflags(env);
-        break;
-    default:
-        if ((uint32_t)env->regs[R_ECX] >= MSR_MC0_CTL
-            && (uint32_t)env->regs[R_ECX] < MSR_MC0_CTL +
-            (4 * env->mcg_cap & 0xff)) {
-            uint32_t offset = (uint32_t)env->regs[R_ECX] - MSR_MC0_CTL;
-            if ((offset & 0x3) != 0
-                || (val == 0 || val == ~(uint64_t)0)) {
-                env->mce_banks[offset] = val;
-            }
-            break;
-        }
-        /* XXX: exception? */
-        break;
-    }
 }
 
 void helper_rdmsr(CPUX86State *env)
 {
     uint64_t val;
+    bool res_valid;
 
     cpu_svm_check_intercept_param(env, SVM_EXIT_MSR, 0, GETPC());
 
-    switch ((uint32_t)env->regs[R_ECX]) {
-    case MSR_IA32_SYSENTER_CS:
-        val = env->sysenter_cs;
-        break;
-    case MSR_IA32_SYSENTER_ESP:
-        val = env->sysenter_esp;
-        break;
-    case MSR_IA32_SYSENTER_EIP:
-        val = env->sysenter_eip;
-        break;
-    case MSR_IA32_APICBASE:
-        val = cpu_get_apic_base(x86_env_get_cpu(env)->apic_state);
-        break;
-    case MSR_EFER:
-        val = env->efer;
-        break;
-    case MSR_STAR:
-        val = env->star;
-        break;
-    case MSR_PAT:
-        val = env->pat;
-        break;
-    case MSR_VM_HSAVE_PA:
-        val = env->vm_hsave;
-        break;
-    case MSR_IA32_PERF_STATUS:
-        /* tsc_increment_by_tick */
-        val = 1000ULL;
-        /* CPU multiplier */
-        val |= (((uint64_t)4ULL) << 40);
-        break;
-#ifdef TARGET_X86_64
-    case MSR_LSTAR:
-        val = env->lstar;
-        break;
-    case MSR_CSTAR:
-        val = env->cstar;
-        break;
-    case MSR_FMASK:
-        val = env->fmask;
-        break;
-    case MSR_FSBASE:
-        val = env->segs[R_FS].base;
-        break;
-    case MSR_GSBASE:
-        val = env->segs[R_GS].base;
-        break;
-    case MSR_KERNELGSBASE:
-        val = env->kernelgsbase;
-        break;
-    case MSR_TSC_AUX:
-        val = env->tsc_aux;
-        break;
-#endif
-    case MSR_MTRRphysBase(0):
-    case MSR_MTRRphysBase(1):
-    case MSR_MTRRphysBase(2):
-    case MSR_MTRRphysBase(3):
-    case MSR_MTRRphysBase(4):
-    case MSR_MTRRphysBase(5):
-    case MSR_MTRRphysBase(6):
-    case MSR_MTRRphysBase(7):
-        val = env->mtrr_var[((uint32_t)env->regs[R_ECX] -
-                             MSR_MTRRphysBase(0)) / 2].base;
-        break;
-    case MSR_MTRRphysMask(0):
-    case MSR_MTRRphysMask(1):
-    case MSR_MTRRphysMask(2):
-    case MSR_MTRRphysMask(3):
-    case MSR_MTRRphysMask(4):
-    case MSR_MTRRphysMask(5):
-    case MSR_MTRRphysMask(6):
-    case MSR_MTRRphysMask(7):
-        val = env->mtrr_var[((uint32_t)env->regs[R_ECX] -
-                             MSR_MTRRphysMask(0)) / 2].mask;
-        break;
-    case MSR_MTRRfix64K_00000:
-        val = env->mtrr_fixed[0];
-        break;
-    case MSR_MTRRfix16K_80000:
-    case MSR_MTRRfix16K_A0000:
-        val = env->mtrr_fixed[(uint32_t)env->regs[R_ECX] -
-                              MSR_MTRRfix16K_80000 + 1];
-        break;
-    case MSR_MTRRfix4K_C0000:
-    case MSR_MTRRfix4K_C8000:
-    case MSR_MTRRfix4K_D0000:
-    case MSR_MTRRfix4K_D8000:
-    case MSR_MTRRfix4K_E0000:
-    case MSR_MTRRfix4K_E8000:
-    case MSR_MTRRfix4K_F0000:
-    case MSR_MTRRfix4K_F8000:
-        val = env->mtrr_fixed[(uint32_t)env->regs[R_ECX] -
-                              MSR_MTRRfix4K_C0000 + 3];
-        break;
-    case MSR_MTRRdefType:
-        val = env->mtrr_deftype;
-        break;
-    case MSR_MTRRcap:
-        if (env->features[FEAT_1_EDX] & CPUID_MTRR) {
-            val = MSR_MTRRcap_VCNT | MSR_MTRRcap_FIXRANGE_SUPPORT |
-                MSR_MTRRcap_WC_SUPPORTED;
-        } else {
-            /* XXX: exception? */
-            val = 0;
-        }
-        break;
-    case MSR_MCG_CAP:
-        val = env->mcg_cap;
-        break;
-    case MSR_MCG_CTL:
-        if (env->mcg_cap & MCG_CTL_P) {
-            val = env->mcg_ctl;
-        } else {
-            val = 0;
-        }
-        break;
-    case MSR_MCG_STATUS:
-        val = env->mcg_status;
-        break;
-    case MSR_IA32_MISC_ENABLE:
-        val = env->msr_ia32_misc_enable;
-        break;
-    case MSR_IA32_BNDCFGS:
-        val = env->msr_bndcfgs;
-        break;
-    default:
-        if ((uint32_t)env->regs[R_ECX] >= MSR_MC0_CTL
-            && (uint32_t)env->regs[R_ECX] < MSR_MC0_CTL +
-            (4 * env->mcg_cap & 0xff)) {
-            uint32_t offset = (uint32_t)env->regs[R_ECX] - MSR_MC0_CTL;
-            val = env->mce_banks[offset];
-            break;
-        }
-        /* XXX: exception? */
-        val = 0;
-        break;
-    }
+    val = x86_cpu_rdmsr(env, (uint32_t)env->regs[R_ECX], &res_valid);
+
+    /* XXX: exception? */
+    if (!res_valid) { }
+
     env->regs[R_EAX] = (uint32_t)(val);
     env->regs[R_EDX] = (uint32_t)(val >> 32);
 }
diff --git a/target/i386/monitor.c b/target/i386/monitor.c
index 77ead60437..d33228416e 100644
--- a/target/i386/monitor.c
+++ b/target/i386/monitor.c
@@ -27,7 +27,9 @@
 #include "monitor/hmp-target.h"
 #include "hw/i386/pc.h"
 #include "sysemu/kvm.h"
+#include "sysemu/hw_accel.h"
 #include "hmp.h"
+#include "qapi/error.h"
 
 
 static void print_pte(Monitor *mon, CPUArchState *env, hwaddr addr,
@@ -651,3 +653,77 @@ void hmp_info_io_apic(Monitor *mon, const QDict *qdict)
         ioapic_dump_state(mon, qdict);
     }
 }
+
+void hmp_msr_get(Monitor *mon, const QDict *qdict)
+{
+    bool valid = false;
+    X86CPU *cpu;
+    CPUState *cs;
+    Error *err = NULL;
+    uint64_t value;
+
+    uint32_t index = qdict_get_int(qdict, "msr_index");
+
+    /* N.B.: mon_get_cpu already synchronizes the CPU state */
+    cs = mon_get_cpu();
+    if (cs != NULL) {
+        cpu = X86_CPU(cs);
+
+        value = x86_cpu_rdmsr(&cpu->env, index, &valid);
+        if (valid) {
+            monitor_printf(mon, "0x%016" PRIx64 "\n", value);
+        } else {
+            error_setg(&err, "Failed to read MSR 0x%08x", index);
+        }
+    } else {
+        monitor_printf(mon, "No CPU available\n");
+        return;
+    }
+
+    if (err) {
+        error_report_err(err);
+    }
+}
+
+void hmp_msr_set(Monitor *mon, const QDict *qdict)
+{
+    bool valid = false;
+    X86CPU *cpu;
+    CPUState *cs;
+    Error *err = NULL;
+    uint64_t new_value;
+
+    uint32_t index = qdict_get_int(qdict, "msr_index");
+    uint64_t value = qdict_get_int(qdict, "value");
+
+    /* N.B.: mon_get_cpu already synchronizes the CPU state */
+    cs = mon_get_cpu();
+    if (cs != NULL) {
+        cpu = X86_CPU(cs);
+
+        x86_cpu_wrmsr(&cpu->env, index, value, &valid);
+        if (!valid) {
+            error_setg(&err, "Failed to write MSR 0x%08x", index);
+        }
+#ifdef CONFIG_KVM
+        else if (kvm_enabled()) {
+            /* Force KVM to flush registers at KVM_PUT_FULL_STATE level. */
+            cpu_synchronize_post_init(cs);
+
+            /* Read back the value from KVM to check if it flushed them. */
+            cpu_synchronize_state(cs);
+            new_value = x86_cpu_rdmsr(&cpu->env, index, &valid);
+            if (new_value != value) {
+                error_setg(&err, "Failed to flush MSR 0x%08x to KVM", index);
+            }
+        }
+#endif
+    } else {
+        monitor_printf(mon, "No CPU available\n");
+        return;
+    }
+
+    if (err) {
+        error_report_err(err);
+    }
+}