- PVSM.RU - https://www.pvsm.ru -
В первой части [1] я рассказал о необходимости идентификации расширений, присутствующих на конкретном процессоре. Это нужно для того, чтобы исполняющийся код (операционная система, компилятор или пользовательское приложение) смог надёжно определить, какие возможности аппаратуры он может задействовать. Также в предыдущей статье я сравнил несколько популярных архитектур центральных процессоров общего назначения. Возможности по идентификации между ними сильно разнятся: некоторые предоставляют полную информацию о расширениях ISA, тогда как другие ограничиваются парой чисел для различения вендора и ревизии.
В этой части я расскажу об одной инструкции архитектуры Intel IA-32 — CPUID, введённой специально для перечисления декларируемых процессором расширений. Немного о том, что было до её появления, что она умеет сообщать, какие неожиданности могут поджидать и какой софт позволяет интерпретировать её вывод.
Источник изображения: [1]
Как я уже утверждал в первой части, присутствует следующая тенденция: чем более «встраиваемая» природа у процессора, тем меньше возможностей для идентификации заложено в его архитектуру. Создатели встраиваемых систем почему-то не волнуются о переносимости двоичного кода.
Не являлся исключением и Intel 8086 — микропроцессор 1970-х годов, выросший из «калькуляторной» серии 8008, 8080, 8085. Изначально в него не было заложено никаких средств идентификации.
Начиная с 808386 сведения о модели, степпинге и семействе стали сообщаться в регистре EDX сразу после перезагрузки (получения сигнала RESET). Инструкция CPUID, кодируемая байтами 0x0f 0xa2, была введена в процессорах 80486. Наличие CPUID можно было распознать по возможности записи в бит 21 регистра флагов. Для поддержки работы на более старых ЦПУ приходилось идти на очень изощрённые методы для того, чтобы различать процессоры серий от 8086 до 80386.
Для получения информации о степпинге на 80386 необходимо прочитать значение EDX сразу после RESET'а. Но в этот момент начинает работать BIOS, который наверняка перезапишет этот регистр задолго до того, как управление будет передано пользовательскому коду! Однако и тут Роберт придумывает и описывает хитрую схему с манипуляцией печально известной линией A20 для того, чтобы обмануть процесс перезагрузки и получить управление.
Перечисленные в оригинальной статье техники были опробованы преимущественно на ЦПУ от Intel. В статье автор признаёт, что они не позволяют надёжно классифицировать клоны x86 других производителей.
Для системного программиста работа по идентификации некоторого расширения обычно заключается в установке входных значений в регистрах EAX (лист, англ. leaf) и ECX (подлист, англ. subleaf), исполнению CPUID и прочтению результата в четырёх регистрах: EAX, EBX, ECX, EDX. Отдельные битовые поля выходных регистров будут содержать информацию о значениях связанных с ними архитектурных параметров конкретного ядра процессора.
Все валидные сочетания входных листов-подлистов и четвёрок регистров на выходе формируют таблицу CPUID. Для современных процессоров она содержит около двух десятков строк по четыре 32-битных столбца.
Я не буду расписывать детально все официально описанные поля этой таблицы. Интересующиеся всегда могут найти их в Intel SDM [1] (рекомендую запастись терпением — около 40 страниц текста только про CPUID). Болеее того, для уже заявленных, но ещё не выпущенных в физических продуктах расширений ISA соответствующие им новые поля CPUID могут быть найдены в [3]. Вместо этого я классифицирую информацию, которую можно извлечь из вывода этой инструкции. Для обозначения битовых полей таблицы я буду использовать принятую для этого нотацию: CPUID.leaf.subleaf.reg[bitstart:bitend]. Например, CPUID.0.EBX[31:0] — это биты с 0 по 31 выходного регистра EBX после исполнения CPUID, которая на вход получила лист 0 (EAX = 0); подлист (входное значение ECX) игнорируется, поэтому он не указан.
Неподдерживаемые значения входных EAX и ECX не приводят к возникновению исключений, а вместо этого возвращают нули во всех четырёх регистрах, либо «мусор» (значения другого листа согласно спецификации). Допустимые же сочетания листов и подлистов образуют три непрерывных региона.
Наиболее важные для практиков данные, закодированные в CPUID — это флаги поддерживаемых наборов инструкций. Традиционно для этого отводились биты в составе CPUID.1.ECX и CPUID.1.EDX. Несколько примеров ниже.
Однако в настоящее время расширения ISA могут быть обозначены битами в других листах, так как ёмкость листа 1 исчерпана.
Конечно же, ни один вендор не упустит возможности увековечить своё имя в идентификационных данных своего продукта. Причём желательно сделать это не просто в виде числа, а впечатать ASCII-строку (хорошо хоть, что не Unicode).
В IA-32 CPUID текст можно найти минимум в двух группах листов. CPUID.0.EBX, ECX, EDX содержат 12 байт ASCII-строки, специфичной для каждого вендора. Для Intel это, конечно же, «GenuineIntel». А три листа CPUID.0x80000002–0x80000004 предоставляют аж 48 байт для кодирования в ASCII так называемой Brand String. Именно её видно при распечатке cat /proc/cpuinfo в Linux. И, хотя формат её более-менее стандартизован: «вендор марка серия CPU @ частота», я настоятельно не рекомендую по её содержимому принимать решения в программном коде. Слишком значительно её содержимое может варьироваться: частота может быть указана в МГц или в ГГц (а в реальности быть совсем иной из-за динамической подстройки), пробелы могут менять положение, а симулятор или виртуальная машина могут подставить туда вообще что угодно. Вся информация из brand string может быть найдена программно более надёжными способами.
Информация о кэшах, такая как их тип, количество, ёмкость, геометрия, разделяемость между ядрами полезна для тюнинга высокопроизводительного математического софта, например, библиотек BLAS (basic linear algebra system).
Изначально конфигурацию кэшей описывал лист 2. Спроектировали его не очень дальновидно. Формат кодирования информации в нём был выбран не самый гибкий, он не смог в будущем поддержать постоянные изменения в объёме и конфигурации нескольких уровней кэшей. В настоящее время использование информации из листа 2 не рекомендуется, там могут стоять 0xFF-ки.
Судя по тому, что лист 0x80000006 входит в расширенный диапазон (хотя я не уверен, документальных доказательств пока что не нашёл), он был добавлен не Intel. С помощью него была сделана попытка информацию листа 2 дополнить данными о строении кэшей, которые потребовались разработчикам софта. При этом опять же не было намерения предоставить пространство для роста.
Лист 4 — последнее и пока что наиболее гибкое представление данных о кэшах. Цена этому — добавление концепции подлистов, кодируемых в ECX. Каждый подлист описывает один кэш: данных, кода или совмещённый, определяет его уровень, ёмкость и т.д. Хватит ли четвёртого листа надолго — поживём, увидим.
Под «топологией» здесь подразумевается, конечно, не раздел математики, а информация о взаимном расположении отдельных ядер и гипер-потоков (если поддерживается Intel HyperThreading) в составе текущего процессора. Для современных серверных процессоров Intel выделяются следующие уровни иерархии.
Понятие «логический процессор» соответствует самому нижнему из присутствующих в системе уровней. Именно их видит операционная система. От того, являются ли два логических процессора родственниками (т.е. входят в состав одного ядра или пакета), зависит стоимость миграции процессов между ними, задержки при передаче данных, эффекты кэшей, конфигурация NUMA-памяти и т.п. Именно поэтому данные о топологии содержатся в CPUID листе 0xB и его подлистах.
Кроме того, для задач адресации для доставки прерываний от периферийных устройств и других процессоров, каждый логический процессор имеет т.н. APIC ID — уникальное в составе системы число. Топология влияет на то, по какому закону выдаются эти числа множеству активных ядер. Они не всегда последовательны; например, в системе с выключенным HyperThreading все APIC ID будут чётными.
Классический APIC ID хранится в поле CPUID.1.EBX[31:24]. Это всего 8 бит, что ограничивает число логических процессоров 256, что, конечно, недостаточно в современных реалиях. Поэтому существует его расширение — X2APIC ID, хранимое в CPUID.0xB.ED[31:0]. Думаю, что этих 32 бит хватит на более длительный период.
«Координаты» каждого логического процессора в топологии его пэкажда уникальны. По этой причине неплохо бы озаботиться обеспечением affinity для потока, читающего несколько листов CPUID подряд, иначе он рискует получить значения с разных ядер.
Если проблем с топологией кажется мало, то спешу сообщить, что само содержимое таблицы CPUID может меняться динамически во время работы системы. Не все поля, конечно, можно изменить; и всё же из настроек BIOS можно напрямую влиять на то, увидит ли ОС некоторые возможности используемого ЦПУ. Приведу лишь некоторые из них.
В этой секции я собрал прочие интересные моменты, связанные с историей и работой команды CPUID.
Во времена Pentium III каждый процессор получил уникальный серийный номер, содержавшийся в CPUID.3.ECX и CPUID.3.EDX [7]. Легко представить, насколько такая фича была бы удобна для нужд защиты ПО от копирования. Однако в 1999 году Европейское сообщество запротестовало [4], разумно опасаясь, что подобная функциональность повредит приватности пользователей таких систем. Уже в Intel Pentium IV серийный номер был убран, сейчас лист 3 возвращает нули.
Очень интересная таблица [5] повествует о том, что хранят (или в прошлом хранили) в разных листах CPUID разные вендоры. Например, описывается некий mystery level 0x8fffffff, в котором процессоры AMD K8 возвращали строку IT'S HAMMER TIME [5].
История появления расширений набора инструкций IA-32 в условиях конкурентной борьбы нескольких компаний [4]. Добавление новых инструкций всегда влияло на CPUID, и не всегда все могли договориться о том, как это сделать правильно.
Инструкция CPUID всегда нравилась мне лаконичностью своего интерфейса и отсутствием неожиданностей в работе: один регистр на входе и четыре на выходе. В её работе нет генерации исключений, нет обращений к памяти, нет чтения/модификации регистра флагов, на неё не влияют префиксы, она работает во всех режимах процессора. По сравнению с зоопарком CISC-команд IA-32 это был почти идеал.
… пока не оказалось, что иногда на вход необходимо подать два регистра для кодирования листа и подлиста. Окей, не так всё хорошо. Ну хотя бы выходные регистры заранее известны и всегда изменяются…
И тут оказалось, что иногда CPUID изменяет ещё один регистр — а именно IA32_BIOS_SIGN_ID, — и сохраняет в нём сигнатуру текущей программы микрокода процессора. Происходит это, если до этого было произведено обновление прошивки процессора. По каким-то причинам информация об этой процедуре была раскидана по мануалу [1] на тысячу страниц, и потому она ускользала от меня очень долго.
В отличие от ряда других архитектур, в IA-32 инструкция CPUID непривилегированная, т.е. может быть исполнена пользовательским ПО, а не только ядром ОС. Поэтому обыкновенные программы могут свободно исследовать, какие возможности имеет ЦПУ, на котором они запускаются. Конечно же, было написано множество инструментов для представления запутанной двоичной информации CPUID в удобном для человека виде. Перечислю здесь некоторые.
Автор: Atakua
Источник [18]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/news/59795
Ссылки в тексте:
[1] части: http://habrahabr.ru/company/intel/blog/220203/
[2] возвращают: http://www.mjmwired.net/kernel/Documentation/kvm/cpuid.txt
[3] транзакционной памяти: http://software.intel.com/node/475111
[4] запротестовало: http://en.wikipedia.org/wiki/Pentium_III#Controversy_about_privacy_issues
[5] IT'S HAMMER TIME: http://rutube.ru/video/f177a08f6624db5577efb14398826daa/
[6] www.cpuid.com/softwares/cpu-z.html: http://www.cpuid.com/softwares/cpu-z.html
[7] www.flounder.com/cpuid_explorer2.htm: http://www.flounder.com/cpuid_explorer2.htm
[8] www.intel.com/support/processors/tools/piu/sb/CS-014921.htm: http://www.intel.com/support/processors/tools/piu/sb/CS-014921.htm
[9] 01.org/msr-tools: https://01.org/msr-tools
[10] www.etallen.com/cpuid.html: http://www.etallen.com/cpuid.html
[11] www.intel.com/content/www/us/en/processors/architectures-software-developer-manuals.html: http://www.intel.com/content/www/us/en/processors/architectures-software-developer-manuals.html
[12] www.drdobbs.com/database/cpuid-algorithm-wars/184410005: http://www.drdobbs.com/database/cpuid-algorithm-wars/184410005
[13] software.intel.com/en-us/intel-isa-extensions: https://software.intel.com/en-us/intel-isa-extensions
[14] www.agner.org/optimize/blog/read.php?i=25: http://www.agner.org/optimize/blog/read.php?i=25
[15] sandpile.org/x86/cpuid.htm: http://sandpile.org/x86/cpuid.htm
[16] github.com/grigory-rechistov/ggg-cpuid: https://github.com/grigory-rechistov/ggg-cpuid
[17] netwinder.osuosl.org/pub/misc/docs/i386/24161812.pdf: http://netwinder.osuosl.org/pub/misc/docs/i386/24161812.pdf
[18] Источник: http://habrahabr.ru/post/220851/
Нажмите здесь для печати.