Skip to content

Commit 885df2d

Browse files
xinli-intelsean-jc
authored andcommitted
KVM: x86: Add support for RDMSR/WRMSRNS w/ immediate on Intel
Add support for the immediate forms of RDMSR and WRMSRNS (currently Intel-only). The immediate variants are only valid in 64-bit mode, and use a single general purpose register for the data (the register is also encoded in the instruction, i.e. not implicit like regular RDMSR/WRMSR). The immediate variants are primarily motivated by performance, not code size: by having the MSR index in an immediate, it is available *much* earlier in the CPU pipeline, which allows hardware much more leeway about how a particular MSR is handled. Intel VMX support for the immediate forms of MSR accesses communicates exit information to the host as follows: 1) The immediate form of RDMSR uses VM-Exit Reason 84. 2) The immediate form of WRMSRNS uses VM-Exit Reason 85. 3) For both VM-Exit reasons 84 and 85, the Exit Qualification field is set to the MSR index that triggered the VM-Exit. 4) Bits 3 ~ 6 of the VM-Exit Instruction Information field are set to the register encoding used by the immediate form of the instruction, i.e. the destination register for RDMSR, and the source for WRMSRNS. 5) The VM-Exit Instruction Length field records the size of the immediate form of the MSR instruction. To deal with userspace RDMSR exits, stash the destination register in a new kvm_vcpu_arch field, similar to cui_linear_rip, pio, etc. Alternatively, the register could be saved in kvm_run.msr or re-retrieved from the VMCS, but the former would require sanitizing the value to ensure userspace doesn't clobber the value to an out-of-bounds index, and the latter would require a new one-off kvm_x86_ops hook. Don't bother adding support for the instructions in KVM's emulator, as the only way for RDMSR/WRMSR to be encountered is if KVM is emulating large swaths of code due to invalid guest state, and a vCPU cannot have invalid guest state while in 64-bit mode. Signed-off-by: Xin Li (Intel) <xin@zytor.com> [sean: minor tweaks, massage and expand changelog] Link: https://lore.kernel.org/r/20250805202224.1475590-5-seanjc@google.com Signed-off-by: Sean Christopherson <seanjc@google.com>
1 parent 87a877d commit 885df2d

File tree

6 files changed

+90
-13
lines changed

6 files changed

+90
-13
lines changed

arch/x86/include/asm/kvm_host.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -929,6 +929,7 @@ struct kvm_vcpu_arch {
929929
bool emulate_regs_need_sync_from_vcpu;
930930
int (*complete_userspace_io)(struct kvm_vcpu *vcpu);
931931
unsigned long cui_linear_rip;
932+
int cui_rdmsr_imm_reg;
932933

933934
gpa_t time;
934935
s8 pvclock_tsc_shift;
@@ -2158,7 +2159,9 @@ int __kvm_get_msr(struct kvm_vcpu *vcpu, u32 index, u64 *data, bool host_initiat
21582159
int kvm_get_msr(struct kvm_vcpu *vcpu, u32 index, u64 *data);
21592160
int kvm_set_msr(struct kvm_vcpu *vcpu, u32 index, u64 data);
21602161
int kvm_emulate_rdmsr(struct kvm_vcpu *vcpu);
2162+
int kvm_emulate_rdmsr_imm(struct kvm_vcpu *vcpu, u32 msr, int reg);
21612163
int kvm_emulate_wrmsr(struct kvm_vcpu *vcpu);
2164+
int kvm_emulate_wrmsr_imm(struct kvm_vcpu *vcpu, u32 msr, int reg);
21622165
int kvm_emulate_as_nop(struct kvm_vcpu *vcpu);
21632166
int kvm_emulate_invd(struct kvm_vcpu *vcpu);
21642167
int kvm_emulate_mwait(struct kvm_vcpu *vcpu);

arch/x86/include/uapi/asm/vmx.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,8 @@
9494
#define EXIT_REASON_BUS_LOCK 74
9595
#define EXIT_REASON_NOTIFY 75
9696
#define EXIT_REASON_TDCALL 77
97+
#define EXIT_REASON_MSR_READ_IMM 84
98+
#define EXIT_REASON_MSR_WRITE_IMM 85
9799

98100
#define VMX_EXIT_REASONS \
99101
{ EXIT_REASON_EXCEPTION_NMI, "EXCEPTION_NMI" }, \
@@ -158,7 +160,9 @@
158160
{ EXIT_REASON_TPAUSE, "TPAUSE" }, \
159161
{ EXIT_REASON_BUS_LOCK, "BUS_LOCK" }, \
160162
{ EXIT_REASON_NOTIFY, "NOTIFY" }, \
161-
{ EXIT_REASON_TDCALL, "TDCALL" }
163+
{ EXIT_REASON_TDCALL, "TDCALL" }, \
164+
{ EXIT_REASON_MSR_READ_IMM, "MSR_READ_IMM" }, \
165+
{ EXIT_REASON_MSR_WRITE_IMM, "MSR_WRITE_IMM" }
162166

163167
#define VMX_EXIT_REASON_FLAGS \
164168
{ VMX_EXIT_REASONS_FAILED_VMENTRY, "FAILED_VMENTRY" }

arch/x86/kvm/vmx/nested.c

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6216,19 +6216,26 @@ static bool nested_vmx_exit_handled_msr(struct kvm_vcpu *vcpu,
62166216
struct vmcs12 *vmcs12,
62176217
union vmx_exit_reason exit_reason)
62186218
{
6219-
u32 msr_index = kvm_rcx_read(vcpu);
6219+
u32 msr_index;
62206220
gpa_t bitmap;
62216221

62226222
if (!nested_cpu_has(vmcs12, CPU_BASED_USE_MSR_BITMAPS))
62236223
return true;
62246224

6225+
if (exit_reason.basic == EXIT_REASON_MSR_READ_IMM ||
6226+
exit_reason.basic == EXIT_REASON_MSR_WRITE_IMM)
6227+
msr_index = vmx_get_exit_qual(vcpu);
6228+
else
6229+
msr_index = kvm_rcx_read(vcpu);
6230+
62256231
/*
62266232
* The MSR_BITMAP page is divided into four 1024-byte bitmaps,
62276233
* for the four combinations of read/write and low/high MSR numbers.
62286234
* First we need to figure out which of the four to use:
62296235
*/
62306236
bitmap = vmcs12->msr_bitmap;
6231-
if (exit_reason.basic == EXIT_REASON_MSR_WRITE)
6237+
if (exit_reason.basic == EXIT_REASON_MSR_WRITE ||
6238+
exit_reason.basic == EXIT_REASON_MSR_WRITE_IMM)
62326239
bitmap += 2048;
62336240
if (msr_index >= 0xc0000000) {
62346241
msr_index -= 0xc0000000;
@@ -6527,6 +6534,8 @@ static bool nested_vmx_l1_wants_exit(struct kvm_vcpu *vcpu,
65276534
return nested_cpu_has2(vmcs12, SECONDARY_EXEC_DESC);
65286535
case EXIT_REASON_MSR_READ:
65296536
case EXIT_REASON_MSR_WRITE:
6537+
case EXIT_REASON_MSR_READ_IMM:
6538+
case EXIT_REASON_MSR_WRITE_IMM:
65306539
return nested_vmx_exit_handled_msr(vcpu, vmcs12, exit_reason);
65316540
case EXIT_REASON_INVALID_STATE:
65326541
return true;

arch/x86/kvm/vmx/vmx.c

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6003,6 +6003,23 @@ static int handle_notify(struct kvm_vcpu *vcpu)
60036003
return 1;
60046004
}
60056005

6006+
static int vmx_get_msr_imm_reg(struct kvm_vcpu *vcpu)
6007+
{
6008+
return vmx_get_instr_info_reg(vmcs_read32(VMX_INSTRUCTION_INFO));
6009+
}
6010+
6011+
static int handle_rdmsr_imm(struct kvm_vcpu *vcpu)
6012+
{
6013+
return kvm_emulate_rdmsr_imm(vcpu, vmx_get_exit_qual(vcpu),
6014+
vmx_get_msr_imm_reg(vcpu));
6015+
}
6016+
6017+
static int handle_wrmsr_imm(struct kvm_vcpu *vcpu)
6018+
{
6019+
return kvm_emulate_wrmsr_imm(vcpu, vmx_get_exit_qual(vcpu),
6020+
vmx_get_msr_imm_reg(vcpu));
6021+
}
6022+
60066023
/*
60076024
* The exit handlers return 1 if the exit was handled fully and guest execution
60086025
* may resume. Otherwise they set the kvm_run parameter to indicate what needs
@@ -6061,6 +6078,8 @@ static int (*kvm_vmx_exit_handlers[])(struct kvm_vcpu *vcpu) = {
60616078
[EXIT_REASON_ENCLS] = handle_encls,
60626079
[EXIT_REASON_BUS_LOCK] = handle_bus_lock_vmexit,
60636080
[EXIT_REASON_NOTIFY] = handle_notify,
6081+
[EXIT_REASON_MSR_READ_IMM] = handle_rdmsr_imm,
6082+
[EXIT_REASON_MSR_WRITE_IMM] = handle_wrmsr_imm,
60646083
};
60656084

60666085
static const int kvm_vmx_max_exit_handlers =
@@ -6495,6 +6514,8 @@ static int __vmx_handle_exit(struct kvm_vcpu *vcpu, fastpath_t exit_fastpath)
64956514
#ifdef CONFIG_MITIGATION_RETPOLINE
64966515
if (exit_reason.basic == EXIT_REASON_MSR_WRITE)
64976516
return kvm_emulate_wrmsr(vcpu);
6517+
else if (exit_reason.basic == EXIT_REASON_MSR_WRITE_IMM)
6518+
return handle_wrmsr_imm(vcpu);
64986519
else if (exit_reason.basic == EXIT_REASON_PREEMPTION_TIMER)
64996520
return handle_preemption_timer(vcpu);
65006521
else if (exit_reason.basic == EXIT_REASON_INTERRUPT_WINDOW)

arch/x86/kvm/vmx/vmx.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -706,6 +706,11 @@ static inline bool vmx_guest_state_valid(struct kvm_vcpu *vcpu)
706706

707707
void dump_vmcs(struct kvm_vcpu *vcpu);
708708

709+
static inline int vmx_get_instr_info_reg(u32 vmx_instr_info)
710+
{
711+
return (vmx_instr_info >> 3) & 0xf;
712+
}
713+
709714
static inline int vmx_get_instr_info_reg2(u32 vmx_instr_info)
710715
{
711716
return (vmx_instr_info >> 28) & 0xf;

arch/x86/kvm/x86.c

Lines changed: 45 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1991,6 +1991,15 @@ static int complete_fast_rdmsr(struct kvm_vcpu *vcpu)
19911991
return complete_fast_msr_access(vcpu);
19921992
}
19931993

1994+
static int complete_fast_rdmsr_imm(struct kvm_vcpu *vcpu)
1995+
{
1996+
if (!vcpu->run->msr.error)
1997+
kvm_register_write(vcpu, vcpu->arch.cui_rdmsr_imm_reg,
1998+
vcpu->run->msr.data);
1999+
2000+
return complete_fast_msr_access(vcpu);
2001+
}
2002+
19942003
static u64 kvm_msr_reason(int r)
19952004
{
19962005
switch (r) {
@@ -2025,39 +2034,53 @@ static int kvm_msr_user_space(struct kvm_vcpu *vcpu, u32 index,
20252034
return 1;
20262035
}
20272036

2028-
int kvm_emulate_rdmsr(struct kvm_vcpu *vcpu)
2037+
static int __kvm_emulate_rdmsr(struct kvm_vcpu *vcpu, u32 msr, int reg,
2038+
int (*complete_rdmsr)(struct kvm_vcpu *))
20292039
{
2030-
u32 msr = kvm_rcx_read(vcpu);
20312040
u64 data;
20322041
int r;
20332042

20342043
r = kvm_get_msr_with_filter(vcpu, msr, &data);
2035-
20362044
if (!r) {
20372045
trace_kvm_msr_read(msr, data);
20382046

2039-
kvm_rax_write(vcpu, data & -1u);
2040-
kvm_rdx_write(vcpu, (data >> 32) & -1u);
2047+
if (reg < 0) {
2048+
kvm_rax_write(vcpu, data & -1u);
2049+
kvm_rdx_write(vcpu, (data >> 32) & -1u);
2050+
} else {
2051+
kvm_register_write(vcpu, reg, data);
2052+
}
20412053
} else {
20422054
/* MSR read failed? See if we should ask user space */
20432055
if (kvm_msr_user_space(vcpu, msr, KVM_EXIT_X86_RDMSR, 0,
2044-
complete_fast_rdmsr, r))
2056+
complete_rdmsr, r))
20452057
return 0;
20462058
trace_kvm_msr_read_ex(msr);
20472059
}
20482060

20492061
return kvm_x86_call(complete_emulated_msr)(vcpu, r);
20502062
}
2063+
2064+
int kvm_emulate_rdmsr(struct kvm_vcpu *vcpu)
2065+
{
2066+
return __kvm_emulate_rdmsr(vcpu, kvm_rcx_read(vcpu), -1,
2067+
complete_fast_rdmsr);
2068+
}
20512069
EXPORT_SYMBOL_GPL(kvm_emulate_rdmsr);
20522070

2053-
int kvm_emulate_wrmsr(struct kvm_vcpu *vcpu)
2071+
int kvm_emulate_rdmsr_imm(struct kvm_vcpu *vcpu, u32 msr, int reg)
2072+
{
2073+
vcpu->arch.cui_rdmsr_imm_reg = reg;
2074+
2075+
return __kvm_emulate_rdmsr(vcpu, msr, reg, complete_fast_rdmsr_imm);
2076+
}
2077+
EXPORT_SYMBOL_GPL(kvm_emulate_rdmsr_imm);
2078+
2079+
static int __kvm_emulate_wrmsr(struct kvm_vcpu *vcpu, u32 msr, u64 data)
20542080
{
2055-
u32 msr = kvm_rcx_read(vcpu);
2056-
u64 data = kvm_read_edx_eax(vcpu);
20572081
int r;
20582082

20592083
r = kvm_set_msr_with_filter(vcpu, msr, data);
2060-
20612084
if (!r) {
20622085
trace_kvm_msr_write(msr, data);
20632086
} else {
@@ -2073,8 +2096,20 @@ int kvm_emulate_wrmsr(struct kvm_vcpu *vcpu)
20732096

20742097
return kvm_x86_call(complete_emulated_msr)(vcpu, r);
20752098
}
2099+
2100+
int kvm_emulate_wrmsr(struct kvm_vcpu *vcpu)
2101+
{
2102+
return __kvm_emulate_wrmsr(vcpu, kvm_rcx_read(vcpu),
2103+
kvm_read_edx_eax(vcpu));
2104+
}
20762105
EXPORT_SYMBOL_GPL(kvm_emulate_wrmsr);
20772106

2107+
int kvm_emulate_wrmsr_imm(struct kvm_vcpu *vcpu, u32 msr, int reg)
2108+
{
2109+
return __kvm_emulate_wrmsr(vcpu, msr, kvm_register_read(vcpu, reg));
2110+
}
2111+
EXPORT_SYMBOL_GPL(kvm_emulate_wrmsr_imm);
2112+
20782113
int kvm_emulate_as_nop(struct kvm_vcpu *vcpu)
20792114
{
20802115
return kvm_skip_emulated_instruction(vcpu);

0 commit comments

Comments
 (0)