Imagine a simple CPU timing check that is used for VM detection.
static inline unsigned long long rdtsc_diff_vmexit() {
unsigned long long ret, ret2;
unsigned eax, edx;
__asm__ volatile("rdtsc" : "=a" (eax), "=d" (edx));
ret = ((unsigned long long)eax) | (((unsigned long long)edx) << 32);
/* vm exit forced here. it uses: eax = 0; cpuid; */
__asm__ volatile("cpuid" : /* no output */ : "a"(0x00));
/**/
__asm__ volatile("rdtsc" : "=a" (eax), "=d" (edx));
ret2 = ((unsigned long long)eax) | (((unsigned long long)edx) << 32);
return ret2 - ret;
}
On a real hardware, cpuid will take significantly less time then when running in KVM.
I was playing with rdtsc offsetting and I was thinking if it is possible to just not exit on CPUID? I have tried disabling the exit and unsurprisingly the VM did not boot (UEFI firmware did not show up at all, no serial output).
I was trying to figure out why it is happening. The only thing that I can think about that could potentially cause issues is reporting of CPU cores/threads.
So the question is: Is it even possible? If not, why? If yes, are there any resources I can use to get it working?
Looking at the intel manual:
Intel® 64 and IA-32 Architectures Software Developer’s Manual Volume 3C: System Programming Guide, Part 3 Order Number: 326019-072US May 2020
Section 25.1.2: Instructions That Cause VM Exits Unconditionally
The first instruction listed in CPUID. So pretty clearly, no you cannot disable exits on CPUID. You can adjust the TSC_OFFSET field to account for this, but you will want to continuously correct towards its initial offset as they system runs. This 1 might provide you with some insights.
Additionally, there is no rule that you only interpret one opcode on an exit. You took the expense of the exit, so it might be worth stepping through a few opcodes to avoid a rapid set of exits. If you look through usenix papers, you are likely to find some suggestions on this. This is the classic: 2