Что такое стандартная функция CPUID 01H?

Мне нужно проверить ECX на наличие 30-го бита, который является функцией ЦП, необходимой для RDRAND. Из RDRAND Wiki,

Если поддерживается, бит 30 регистра ECX устанавливается после вызова стандартной функции CPUID 01H.

Я точно не знаю, что это значит. "Стандартная функция 01H"?

Означает ли это EAX=80000001h? Я не совсем уверен, как поступить.


person 8protons    schedule 21.07.2016    source источник
comment
Нет, это означает EAX=1. Выполнение инструкции CPUID с EAX=0x80000001 помещает другой набор информации в ea/b/c/dx, который не включает ничего о RDRAND.   -  person Peter Cordes    schedule 21.07.2016
comment
@PeterCordes Большое спасибо.   -  person 8protons    schedule 22.07.2016


Ответы (1)


Я думаю, это означает, что %eax должен быть равен 1 при вызове функции cpuid. У специалистов по аппаратному обеспечению странные соглашения, поэтому они говорят 01H вместо 1 или 0x1.

См. руководство Intel Vol. 2. Гл. 3. Таблица 3.8. Начальное значение eax равно 01H. ECX по возврату содержит полную информацию об особенностях в таблице 3.10. Внизу этой таблицы для бита 30 указано, что значение 1 означает, что процессор поддерживает RDRAND.

Если у вас есть gcc, вы можете попробовать что-то вроде этого:

evaitl@bb ~/se $ cat foo.c 
#include <stdio.h>
#include <cpuid.h>
int main(){
    unsigned eax=0, ebx=0, ecx=0, edx=0;
    unsigned max_lvl=__get_cpuid_max(0,&ebx);
    printf("Max level: %d sig %x\n",max_lvl,ebx);
    ebx=0;
    for(int lvl=0; lvl<=max_lvl; ++lvl){
        __get_cpuid(lvl,&eax, &ebx, &ecx, &edx);
        printf("lvl %-2d eax %08x ebx %08x ecx %08x edx %08x\n",
               lvl, eax, ebx, ecx, edx);
        eax=ebx=ecx=edx=0;
    }
    return 0;
}
evaitl@bb ~/se $ ./foo 
Max level: 13 sig 756e6547
lvl 0  eax 0000000d ebx 756e6547 ecx 6c65746e edx 49656e69
lvl 1  eax 000306e4 ebx 07200800 ecx 7fbee3bf edx bfebfbff
lvl 2  eax 76036301 ebx 00f0b2ff ecx 00000000 edx 00ca0000
lvl 3  eax 00000000 ebx 00000000 ecx 00000000 edx 00000000
lvl 4  eax 00000000 ebx 00000000 ecx 00000000 edx 00000000
lvl 5  eax 00000040 ebx 00000040 ecx 00000003 edx 00001120
lvl 6  eax 00000077 ebx 00000002 ecx 00000009 edx 00000000
lvl 7  eax 00000000 ebx 00000000 ecx 00000000 edx 00000000
lvl 8  eax 00000000 ebx 00000000 ecx 00000000 edx 00000000
lvl 9  eax 00000001 ebx 00000000 ecx 00000000 edx 00000000
lvl 10 eax 07300403 ebx 00000000 ecx 00000000 edx 00000603
lvl 11 eax 00000000 ebx 00000000 ecx 0000006e edx 00000007
lvl 12 eax 00000000 ebx 00000000 ecx 00000000 edx 00000000
lvl 13 eax 00000000 ebx 00000000 ecx 00000000 edx 00000000

Чтобы все раскодировать, потребуется довольно много времени, но в ‹cpuid.h› есть определения, которые могут помочь:

/* %ecx */
#define bit_SSE3    (1 << 0)
#define bit_PCLMUL  (1 << 1)
#define bit_LZCNT   (1 << 5)
#define bit_SSSE3   (1 << 9)
#define bit_FMA     (1 << 12)
#define bit_CMPXCHG16B  (1 << 13)
#define bit_SSE4_1  (1 << 19)
#define bit_SSE4_2  (1 << 20)
#define bit_MOVBE   (1 << 22)
#define bit_POPCNT  (1 << 23)
#define bit_AES     (1 << 25)
#define bit_XSAVE   (1 << 26)
#define bit_OSXSAVE (1 << 27)
#define bit_AVX     (1 << 28)
#define bit_F16C    (1 << 29)
#define bit_RDRND   (1 << 30)

В заголовочном файле есть еще куча. Вы заметите, что 30-й бит моего ecx (7fbee3bf) установлен на уровне 1, поэтому у меня есть доступная инструкция RDRND.

Более подробную информацию можно получить с помощью этой инструкции. Существует расширенная информация о том, что eax имеет установленный старший бит при вызове cpuid. Также ряд уровней имеет разные «листья» в зависимости от значения ecx при вызове cpuid. Кто-то, кому скучно, может потратить день или два на программирование чего-то, чтобы выполнить извлечение всех функций и сделать это красиво. В качестве альтернативы можно выполнить «grep flags /proc/cpuinfo» и получить что-то вроде этого:

флаги: FPU VME-де-псевдоэфедрин TSC MSR пае MCE CX8 APIC Сентябрь MTRR PGE MCA CMOV погладить pse36 clflush DTs Acpi MMX fxsr ße SSE2 сс ХТЫ тм РОВ системного вызовы пй pdpe1gb rdtscp лй constant_tsc arch_perfmon УИБ БТС rep_good nopl xtopology nonstop_tsc aperfmperf eagerfpu ПНИ PCLMULQDQ dtes64 монитор ds_cpl vmx est tm2 ssse3 cx16 xtpr pdcm pcid dca sse4_1 sse4_2 x2apic popcnt tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm epb tpr_shadow vnmi flexpriority ept vpid fsgsbase smep erms xsaveopt dtherm idapts pln

person evaitl    schedule 21.07.2016
comment
Я думаю, что eax выбирает уровень, но для некоторых уровней ecx используется для получения листа. - person evaitl; 21.07.2016
comment
о, да, я не смотрел на это какое-то время и ошибся в терминологии. - person Peter Cordes; 21.07.2016
comment
Большое спасибо - это был феноменальный ответ. Я был удивлен, обнаружив, что чип моего ноутбука 2012 года не поддерживает RDRAND. - person 8protons; 22.07.2016
comment
@8protons: если это ответило на ваш вопрос, вы должны принять его, установив флажок под стрелками вверх/вниз. - person Peter Cordes; 22.07.2016
comment
Кроме того, в отношении вашего редактирования: 01H не является аппаратным соглашением AFAIK, это соглашение Intel/DOS asm, чтобы поместить основание числа как суффикс. Такие ассемблеры, как NASM и GAS, принимают 0xDEADBEEF. NASM также принимает 0DEADBEEFh. Обратите внимание, что программирование на ассемблере и документация по x86 — это не аппаратное обеспечение, это документирование того, что вам нужно для написания программного обеспечения, а не для создания аппаратного обеспечения. - person Peter Cordes; 22.07.2016
comment
@evaitl Итак, я был в тупике, как мне проверить бит. #define bit_RDRAND говорит (1 << 30). Разве это не сдвигает 1 на 31-й битный слот? Мы хотим проверить, установлен ли бит 30, так что не должен ли он быть (1 << 29)? В итоге это будет выглядеть как ECX & (1 << 29) - person 8protons; 22.07.2016
comment
Ах, в руководстве указан бит 00, так что это означает, что бит 30 на самом деле является 31-м битом, поэтому сдвиг на 30 даст результат! - person 8protons; 22.07.2016