Проблемы с работой компенсации Intel TSC

У меня возникают проблемы с компенсацией 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 циклами.

Есть идеи, почему гостевая ОС игнорирует любые смещения, которые я ввожу?


person Steffen Brem    schedule 15.07.2017    source источник
comment
Вы уверены, что устанавливаете коэффициент масштабирования, а не просто смещение? Смещение повлияет на a1 и a2 в равной степени, поэтому разница не изменится. (Аппаратное обеспечение поддерживает оба, с масштабированием только в более новом оборудовании. См. -rdtsc" title="есть какой-либо способ активировать устаревший режим для rdtsc">stackoverflow.com/questions/35137786/ для смещения и масштабирования, в частности, ссылка на www-ssl.intel.com/content/www /нас/ru/процессоры/).   -  person Peter Cordes    schedule 17.07.2017
comment
Кстати, если код между функциями __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.2017
comment
В моем случае rdtsc не вызывает выхода. CPUID делает. Моя цель — настроить TSC таким образом, чтобы гость никогда не подумал, что CPUID занимает больше циклов, чем обычно. Я попытался сделать следующее в конце обработчика vmexit: vmwrite(tscOffset, vmread(tscOffset) - vmExitCycles). Он всегда должен компенсировать количество циклов vmexit, и гость не будет знать, что на самом деле это заняло больше времени. На самом деле это работает, но гостевая ОС мерцает, и через несколько мгновений она зависает. Должен ли я использовать масштабирование вместо смещения в моем случае?   -  person Steffen Brem    schedule 17.07.2017
comment
Ага, понятно. Ваш код смещения TSC должен работать в vmexit, вызванном CPUID, поэтому он выглядит на 2000 циклов короче, чем был. Это имеет смысл, если это работает. Я просто не понял, что вы пытались сделать, когда я впервые прочитал это. Может быть, поместите свою реальную цель в начало вопроса, чтобы люди знали, для чего она нужна, читая код. Я не возился с гипервизорами, поэтому у меня нет хороших идей, извините.   -  person Peter Cordes    schedule 17.07.2017