Проверка флага ID в RFLAGS с использованием встроенной сборки в C

Я написал следующую встроенную сборку, чтобы попробовать проверить флаг ID. Я знаю, что это должно быть установлено, потому что я могу использовать инструкцию cpuid, однако эта функция возвращает 0, когда я ее тестирую.

_Bool /* Checks whether the cpu has the cpuid instruction available */
cpu_has_cpuid(void) {
        uint64_t out;
        asm ("pushfq\n\t"
             "popq %[out]\n\t"
             : [out] "=r" (out)
             :: "memory");
        return out & 0x200000;
}

Кто-нибудь знает, что я сделал неправильно здесь, и вы можете мне помочь?

Если это поможет, значение, которое я получаю без битовой маски, равно 582.


person Leander    schedule 15.03.2021    source источник


Ответы (1)


Вы можете и должны использовать GNU C #include <cpuid.h> в своем собственном коде вместо встроенного ассемблера GNU C. (Как вызвать cpuid в Linux?) Пример компиляции Godbolt, включая проверку доступности CPUID в 32-битном режиме.


x86-64 гарантирует доступность CPUID; вам нужно только проверить это, если ваш код может работать на 486 или более ранней версии. Но 486 вообще не может запускать 64-битный код.

Настоящая проблема с вашим кодом заключается в том, что он не пытается перевернуть бит идентификатора. По-видимому, 0 является нормальным для его текущего состояния. https://wiki.osdev.org/CPUID#Checking_CPUID_availability показывает стандартный код обнаружения (для 32 -битовый режим Intel-синтаксис, но достаточно простой для переноса), который использует XOR для назначения памяти перед регистрацией popf / pushf / pop, чтобы увидеть, была ли принята настройка. (Затем он восстанавливает исходные EFLAGS, что, вероятно, не нужно.)

Реализацию встроенного ассемблера GNU C см. в собственном cpuid.h GCC (github), где это делается внутри блока #ifndef __x86_64__ в __get_cpuid_max. У него даже есть диалектные альтернативы для синтаксиса AT&T и Intel, поэтому код, использующий #include <cpuid.h>, не ломается, если скомпилирован с -masm=intel.

Перенос этого на x86-64 позволит вам убедиться, что он действительно работает, если вам интересно.


Кроме того, ваши push/pop небезопасны в Linux/Mac (x86-64 System V): вы наступаете на красную зону. Вам нужно add $-128, %rsp до и sub $-128, %rsp после, чтобы ваш push/pop не наступал на красную зону ниже RSP компилятора, где он может хранить локальные переменные. См. раздел Использование регистра базового указателя во встроенном ассемблере C++.

-128 подходит для imm8, а +128 нет, поэтому я предлагаю add/sub $-128 вместо sub/add $128.

person Peter Cordes    schedule 15.03.2021
comment
Хорошо, большое спасибо. - person Leander; 15.03.2021