У меня возникают проблемы с компенсацией TSC для работы с моим гипервизором. Согласно руководствам Intel, вы должны убедиться, что виртуальная машина не завершает работу на rdtsc
, поэтому я отключил rdtsc_exiting
. Я также включил элемент управления use tsc offsetting
. Настройка VMCS
таким образом должна позволить мне писать в поле управления tsc_offset
, чтобы изменить способ чтения TSC виртуальной машиной (гостевой ОС).
Что я сделал, так это прямо перед тем, как вернуть управление виртуальной машине в обработчике выхода, я написал -2000
в поле смещения TSC.
__vmx_vmwrite(Vmcs::kTscOffset, -2000);
Я использую это как свою реализацию VmWrite
:
inline unsigned char __vmx_vmwrite(_In_ size_t field, _In_ size_t field_value) {
FlagRegister flags = {};
__asm {
pushad
push field_value
mov eax, field
_emit 0x0F
_emit 0x79
_emit 0x04
_emit 0x24 // VMWRITE EAX, [ESP]
pushfd
pop flags.all
add esp, 4
popad
}
if (flags.fields.cf) {
return 2;
}
if (flags.fields.zf) {
return 1;
}
return 0;
}
Согласно руководствам Intel, этого должно быть достаточно, чтобы иметь возможность использовать смещение TSC. Итак, для проверки я написал эту небольшую тестовую программу (в моем случае CPUID выполняет VMEXIT):
auto a1 = __rdtsc();
__cpuid(cpuInfo, 0);
auto a2 = __rdtsc();
result = static_cast<int>(a2 - a1);
На базовой машине это занимает примерно 120 циклов процессора. При запуске из моей виртуальной машины и выключении rdtsc (смещение не включено) для завершения требуется 2200 циклов.
Затем последний тест реализует то, что я описал выше, и запускает тот же тест. Что в моем случае заканчивается теми же 2200 циклами.
Есть идеи, почему гостевая ОС игнорирует любые смещения, которые я ввожу?
a1
иa2
в равной степени, поэтому разница не изменится. (Аппаратное обеспечение поддерживает оба, с масштабированием только в более новом оборудовании. См. -rdtsc" title="есть какой-либо способ активировать устаревший режим для rdtsc">stackoverflow.com/questions/35137786/ для смещения и масштабирования, в частности, ссылка на www-ssl.intel.com/content/www /нас/ru/процессоры/). - person Peter Cordes   schedule 17.07.2017__rdtsc()
сам по себе не вызывает выход из виртуальной машины (как, я полагаю, это делает CPUID), вы должны измерить то же время, что и на «голом железе», что было бы хорошим способом убедиться, чтоrdtsc
не является вызывая выходы ВМ. Циклfor(int i=0; i<1000; i++){}
должен выполняться почти точно за 1000 циклов, при условии, что он компилируется в.loop:
inc ecx
/cmp ecx,1000
/jne .loop
или подобное. - person Peter Cordes   schedule 17.07.2017vmwrite(tscOffset, vmread(tscOffset) - vmExitCycles)
. Он всегда должен компенсировать количество циклов vmexit, и гость не будет знать, что на самом деле это заняло больше времени. На самом деле это работает, но гостевая ОС мерцает, и через несколько мгновений она зависает. Должен ли я использовать масштабирование вместо смещения в моем случае? - person Steffen Brem   schedule 17.07.2017