Skip to content

Commit afcb7f6

Browse files
xinli-intelgregkh
authored andcommitted
KVM: x86: Add support for RDMSR/WRMSRNS w/ immediate on Intel
[ Upstream commit 885df2d2109a60f84d84639ce6d95a91045f6c45 ] 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) <[email protected]> [sean: minor tweaks, massage and expand changelog] Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Sean Christopherson <[email protected]> Stable-dep-of: 9d7dfb95da2c ("KVM: VMX: Inject #UD if guest tries to execute SEAMCALL or TDCALL") Signed-off-by: Sasha Levin <[email protected]> Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent e5779f2 commit afcb7f6

6 files changed

Lines changed: 90 additions & 13 deletions

File tree

arch/x86/include/asm/kvm_host.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -926,6 +926,7 @@ struct kvm_vcpu_arch {
926926
bool emulate_regs_need_sync_from_vcpu;
927927
int (*complete_userspace_io)(struct kvm_vcpu *vcpu);
928928
unsigned long cui_linear_rip;
929+
int cui_rdmsr_imm_reg;
929930

930931
gpa_t time;
931932
s8 pvclock_tsc_shift;
@@ -2155,7 +2156,9 @@ int __kvm_get_msr(struct kvm_vcpu *vcpu, u32 index, u64 *data, bool host_initiat
21552156
int kvm_get_msr(struct kvm_vcpu *vcpu, u32 index, u64 *data);
21562157
int kvm_set_msr(struct kvm_vcpu *vcpu, u32 index, u64 data);
21572158
int kvm_emulate_rdmsr(struct kvm_vcpu *vcpu);
2159+
int kvm_emulate_rdmsr_imm(struct kvm_vcpu *vcpu, u32 msr, int reg);
21582160
int kvm_emulate_wrmsr(struct kvm_vcpu *vcpu);
2161+
int kvm_emulate_wrmsr_imm(struct kvm_vcpu *vcpu, u32 msr, int reg);
21592162
int kvm_emulate_as_nop(struct kvm_vcpu *vcpu);
21602163
int kvm_emulate_invd(struct kvm_vcpu *vcpu);
21612164
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
@@ -1997,6 +1997,15 @@ static int complete_fast_rdmsr(struct kvm_vcpu *vcpu)
19971997
return complete_fast_msr_access(vcpu);
19981998
}
19991999

2000+
static int complete_fast_rdmsr_imm(struct kvm_vcpu *vcpu)
2001+
{
2002+
if (!vcpu->run->msr.error)
2003+
kvm_register_write(vcpu, vcpu->arch.cui_rdmsr_imm_reg,
2004+
vcpu->run->msr.data);
2005+
2006+
return complete_fast_msr_access(vcpu);
2007+
}
2008+
20002009
static u64 kvm_msr_reason(int r)
20012010
{
20022011
switch (r) {
@@ -2031,39 +2040,53 @@ static int kvm_msr_user_space(struct kvm_vcpu *vcpu, u32 index,
20312040
return 1;
20322041
}
20332042

2034-
int kvm_emulate_rdmsr(struct kvm_vcpu *vcpu)
2043+
static int __kvm_emulate_rdmsr(struct kvm_vcpu *vcpu, u32 msr, int reg,
2044+
int (*complete_rdmsr)(struct kvm_vcpu *))
20352045
{
2036-
u32 msr = kvm_rcx_read(vcpu);
20372046
u64 data;
20382047
int r;
20392048

20402049
r = kvm_get_msr_with_filter(vcpu, msr, &data);
2041-
20422050
if (!r) {
20432051
trace_kvm_msr_read(msr, data);
20442052

2045-
kvm_rax_write(vcpu, data & -1u);
2046-
kvm_rdx_write(vcpu, (data >> 32) & -1u);
2053+
if (reg < 0) {
2054+
kvm_rax_write(vcpu, data & -1u);
2055+
kvm_rdx_write(vcpu, (data >> 32) & -1u);
2056+
} else {
2057+
kvm_register_write(vcpu, reg, data);
2058+
}
20472059
} else {
20482060
/* MSR read failed? See if we should ask user space */
20492061
if (kvm_msr_user_space(vcpu, msr, KVM_EXIT_X86_RDMSR, 0,
2050-
complete_fast_rdmsr, r))
2062+
complete_rdmsr, r))
20512063
return 0;
20522064
trace_kvm_msr_read_ex(msr);
20532065
}
20542066

20552067
return kvm_x86_call(complete_emulated_msr)(vcpu, r);
20562068
}
2069+
2070+
int kvm_emulate_rdmsr(struct kvm_vcpu *vcpu)
2071+
{
2072+
return __kvm_emulate_rdmsr(vcpu, kvm_rcx_read(vcpu), -1,
2073+
complete_fast_rdmsr);
2074+
}
20572075
EXPORT_SYMBOL_GPL(kvm_emulate_rdmsr);
20582076

2059-
int kvm_emulate_wrmsr(struct kvm_vcpu *vcpu)
2077+
int kvm_emulate_rdmsr_imm(struct kvm_vcpu *vcpu, u32 msr, int reg)
2078+
{
2079+
vcpu->arch.cui_rdmsr_imm_reg = reg;
2080+
2081+
return __kvm_emulate_rdmsr(vcpu, msr, reg, complete_fast_rdmsr_imm);
2082+
}
2083+
EXPORT_SYMBOL_GPL(kvm_emulate_rdmsr_imm);
2084+
2085+
static int __kvm_emulate_wrmsr(struct kvm_vcpu *vcpu, u32 msr, u64 data)
20602086
{
2061-
u32 msr = kvm_rcx_read(vcpu);
2062-
u64 data = kvm_read_edx_eax(vcpu);
20632087
int r;
20642088

20652089
r = kvm_set_msr_with_filter(vcpu, msr, data);
2066-
20672090
if (!r) {
20682091
trace_kvm_msr_write(msr, data);
20692092
} else {
@@ -2079,8 +2102,20 @@ int kvm_emulate_wrmsr(struct kvm_vcpu *vcpu)
20792102

20802103
return kvm_x86_call(complete_emulated_msr)(vcpu, r);
20812104
}
2105+
2106+
int kvm_emulate_wrmsr(struct kvm_vcpu *vcpu)
2107+
{
2108+
return __kvm_emulate_wrmsr(vcpu, kvm_rcx_read(vcpu),
2109+
kvm_read_edx_eax(vcpu));
2110+
}
20822111
EXPORT_SYMBOL_GPL(kvm_emulate_wrmsr);
20832112

2113+
int kvm_emulate_wrmsr_imm(struct kvm_vcpu *vcpu, u32 msr, int reg)
2114+
{
2115+
return __kvm_emulate_wrmsr(vcpu, msr, kvm_register_read(vcpu, reg));
2116+
}
2117+
EXPORT_SYMBOL_GPL(kvm_emulate_wrmsr_imm);
2118+
20842119
int kvm_emulate_as_nop(struct kvm_vcpu *vcpu)
20852120
{
20862121
return kvm_skip_emulated_instruction(vcpu);

0 commit comments

Comments
 (0)