For both VT-x and SVM, the RDTSCP intercept will trigger if the pipeline
supports the instruction, but the guest may not have RDTSCP in its featureset.
Bring the vmexit handlers in line with the main emulator behaviour by
optionally handing back #UD.
Next on the AMD side, if RDTSCP actually ends up being intercepted on a debug
build or first-gen SVM hardware which lacks NRIP, we first update regs->rcx,
then call __get_instruction_length() asking for RDTSC. As the two
instructions are different (and indeed, different lengths!),
__get_instruction_length_from_list() fails and hands back a #GP fault.
This can demonstrated by putting a guest into tsc_mode="always emulate" and
executing an RDTSCP instruction:
(d1) --- Xen Test Framework ---
(d1) Environment: HVM 64bit (Long mode 4 levels)
(d1) Test rdtscp
(d1) TSC mode 1
(XEN) emulate.c:147:d1v0 __get_instruction_length: Mismatch between expected and actual instruction:
(XEN) emulate.c:152:d1v0 insn_index 8, opcode 0xf0031 modrm 0
(XEN) emulate.c:154:d1v0 rip 0x10475f, nextrip 0x104762, len 3
(XEN) SVM insn len emulation failed (1): d1v0 64bit @ 0008:
0010475f -> 0f 01 f9 0f 31 5b 31 ff 31 c0 e9 c2 db ff ff 00
(d1) ******************************
(d1) PANIC: Unhandled exception at 0008:
000000000010475f
(d1) Vec 13 #GP[0000]
(d1) ******************************
First, teach __get_instruction_length() to cope with RDTSCP, and improve
svm_vmexit_do_rdtsc() to ask for the correct instruction. Move the regs->rcx
adjustment into this function to ensure it gets done after we are done
potentially raising faults.
Reported-by: Paul Durrant <paul.durrant@citrix.com>
Signed-off-by: Andrew Cooper <andrew.cooper3@citrix.com>
Reviewed-by: Brian Woods <brian.woods@amd.com>
Reviewed-by: Jan Beulich <jbeulich@suse.com>
Reviewed-by: Kevin Tian <kevin.tian@intel.com>
Reviewed-by: Roger Pau Monné <roger.pau@citrix.com>
[INSTR_STGI] = { X86EMUL_OPC(0x0f, 0x01), MODRM(3, 3, 4) },
[INSTR_CLGI] = { X86EMUL_OPC(0x0f, 0x01), MODRM(3, 3, 5) },
[INSTR_INVLPGA] = { X86EMUL_OPC(0x0f, 0x01), MODRM(3, 3, 7) },
+ [INSTR_RDTSCP] = { X86EMUL_OPC(0x0f, 0x01), MODRM(3, 7, 1) },
[INSTR_INVD] = { X86EMUL_OPC(0x0f, 0x08) },
[INSTR_WBINVD] = { X86EMUL_OPC(0x0f, 0x09) },
[INSTR_WRMSR] = { X86EMUL_OPC(0x0f, 0x30) },
hvm_hlt(regs->eflags);
}
-static void svm_vmexit_do_rdtsc(struct cpu_user_regs *regs)
+static void svm_vmexit_do_rdtsc(struct cpu_user_regs *regs, bool rdtscp)
{
+ struct vcpu *curr = current;
+ const struct domain *currd = curr->domain;
+ enum instruction_index insn = rdtscp ? INSTR_RDTSCP : INSTR_RDTSC;
unsigned int inst_len;
- if ( (inst_len = __get_instruction_length(current, INSTR_RDTSC)) == 0 )
+ if ( rdtscp && !currd->arch.cpuid->extd.rdtscp &&
+ currd->arch.tsc_mode != TSC_MODE_PVRDTSCP )
+ {
+ hvm_inject_hw_exception(TRAP_invalid_op, X86_EVENT_NO_EC);
return;
+ }
+
+ if ( (inst_len = __get_instruction_length(curr, insn)) == 0 )
+ return;
+
__update_guest_eip(regs, inst_len);
+ if ( rdtscp )
+ regs->rcx = hvm_msr_tsc_aux(curr);
+
hvm_rdtsc_intercept(regs);
}
break;
case VMEXIT_RDTSCP:
- regs->rcx = hvm_msr_tsc_aux(v);
- /* fall through */
case VMEXIT_RDTSC:
- svm_vmexit_do_rdtsc(regs);
+ svm_vmexit_do_rdtsc(regs, exit_reason == VMEXIT_RDTSCP);
break;
case VMEXIT_MONITOR:
unsigned long exit_qualification, exit_reason, idtv_info, intr_info = 0;
unsigned int vector = 0, mode;
struct vcpu *v = current;
+ struct domain *currd = v->domain;
__vmread(GUEST_RIP, ®s->rip);
__vmread(GUEST_RSP, ®s->rsp);
vmx_invlpg_intercept(exit_qualification);
break;
case EXIT_REASON_RDTSCP:
+ if ( !currd->arch.cpuid->extd.rdtscp &&
+ currd->arch.tsc_mode != TSC_MODE_PVRDTSCP )
+ {
+ hvm_inject_hw_exception(TRAP_invalid_op, X86_EVENT_NO_EC);
+ break;
+ }
+
regs->rcx = hvm_msr_tsc_aux(v);
/* fall through */
case EXIT_REASON_RDTSC:
INSTR_HLT,
INSTR_INT3,
INSTR_RDTSC,
+ INSTR_RDTSCP,
INSTR_PAUSE,
INSTR_XSETBV,
INSTR_VMRUN,