Проверка за 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 inline asm. (Как да извикам cpuid в Linux?) Пример в Godbolt за това как се компилира, включително проверка на 32-битов режим за наличност на CPUID.


x86-64 гарантира наличието на CPUID; трябва само да го проверите дали вашият код може да работи на 486 или по-стара версия. Но 486 не може да изпълнява 64-битов код на първо място.

Истинският проблем с вашия код е, че той не се опитва да преобърне ID бита. Очевидно 0 е нормално за текущото си състояние. https://wiki.osdev.org/CPUID#Checking_CPUID_availability показва стандартен код за откриване (за 32 -битов режим Intel-синтаксис, но достатъчно лесен за пренасяне), който използва XOR за местоназначение на паметта преди popf / pushf / pop reg, за да види дали настройката е приета. (След това възстановява оригиналния EFLAGS, който вероятно е ненужен.)

За внедряване на GNU C inline asm вижте собствения cpuid.h на GCC (github), където се прави вътре в блок #ifndef __x86_64__ в __get_cpuid_max. Той дори има диалектни алтернативи за синтаксис на AT&T срещу Intel, така че кодът, който използва #include <cpuid.h>, не се поврежда, ако се компилира с -masm=intel.

Пренасянето на това към x86-64 ще ви позволи да проверите дали наистина работи, ако сте любопитни.


Също така, вашето натискане/изскачане не е безопасно на Linux / Mac (x86-64 System V): прекрачвате червената зона. Трябва да add $-128, %rsp преди и sub $-128, %rsp след, за да избегнете натискането/изскачането на червената зона под RSP на компилатора, където може да се пазят локални променливи. Вижте Използване на регистър на базов указател в C++ inline asm

-128 се побира в imm8, но +128 не, затова предлагам add/sub $-128 вместо sub/add $128.

person Peter Cordes    schedule 15.03.2021
comment
Добре, благодаря ти много. - person Leander; 15.03.2021