Assembler → Обнаружение устройств PCI

Введение.

В данной статье мы рассмотрим вопросы нахождения и определения параметров различных устройств.

Когда у программиста возникает вопрос типа «Как определить сколько в компе оперативки?», в 90% случаев он решается тривиально – используется определенный сервис операционной системы который и отвечает на все вопросы вроде этого.

А что делать, если пользоваться сервисами нельзя, например в случае разработки собственной ОС? (звучит конечно малореально, но, тем не менее, энтузиасты всегда были, и если уж не писать свою ОС, то хотя бы разобраться как это делают уже написанные ОС, думаю, будет интересно.

На вопрос как определить установленное оборудование на полностью аппаратном уровне и призвана ответить данная статья.

Сразу определимся, что именно мы будем определять:
1 Процессор (частота, производитель, возможности)
2 Оперативная память (объем)
3 HDD (Объем).
4 Устройства PCI (производитель, модель)

1) ПРОЦЕССОР.

Определение любого существующего intel-совместимого процессора складывается из 3 основных этапов:
Определение поддержки инструкции CPUID.
Если она поддерживается - определение остальных параметров.
Определение тактовой частоты.

Процессоры поддерживают инструкцию CPUID (как intel, так и AMD), начиная с пятого поколения (Pentium) и поздних моделей 486 (чтобы TASM вас «правильно понял» при использовании CPUID, он должен быть версии 5.0 и выше).

Если она не поддерживается – определить производителя и другие параметры процессора возможно только какими-либо недокументированными путями.

Посмотрим, чем отличаются процессоры не поддерживающие CPUID (80386, 80486, более старые процессоры вроде 80286 и ниже, я думаю, рассматривать нет смысла).

Все просто – если бит 18 в EFLAGS доступен, значит процессор 486 или круче, если его невозможно изменить инструкцией POPF – 386.

В том же EFLAGS нужно попробовать изменить бит ID (21) если его можно программно изменить – процессор поддерживает инструкцию CPUID.

CPUID имеет параметр, который задается в регистре EAX.

Обычно в ответ на вызов CPUID с EAX=0 процессор возвращает в EBX:ECX:EDX некоторую строку-идентификатор производителя.

У intel это «GenuineIntel», у AMD – «AuthenticAMD», у Cyrix – «CyrixInstead».

(Обратите внимание, что размеры всех строк – 12 символов – три 4-байтных регистра).

При вызове CPUID с EAX=1 в регистре EAX возвращается информация о типе, модели и степпинге (изменения в рамках одной модели) процессора.

Эти значения расшифровываются по специальным таблицам.

EAX[00:03] – степпинг (stepping)
EAX[07:04] – модель (model)
EAX[11:08] – семейство (family)
EAX[13:12] – тип (type)
EAX[15:14] – резерв (reserved)
EAX[19:16] – расширенная модель (extended model) (только Pentium 4)
EAX[23:20] – расширенное семейство (extended family) (только Pentium 4)
EAX[31:24] – резерв (reserved)
EBX[07:00] – брэнд-индекс (brand-index)
EBX[15:08] – длина строки, очищаемой инструкцией CLFLUSH (Pentium 4)
EBX[23:16] - резерв
EBX[31:24] – идентификатор APIC процессора.
ECX – 0

EDX содержит информацию о различных расширениях архитектуры (если определенный бит равен 1 - расширение поддерживается). Ниже приведена таблица, по которой можно самостоятельно расширять прилагающуюся к статье программу.


При вызове CPUID с EAX=2 (функция появилась начиная с Pentium II, в процессорах AMD она недоступна) в регистрах EAX, EBX, ECX, EDX возвращаются так называемые «дескрипторы», которые описывают возможности кэшей и TLB буферов. Причем AL содержит число, указывающее сколько раз необходимо последовательно выполнить CPUID (с EAX=2) для получения полной информации. Дескрипторы постоены по такому принципу: никаких битов тестировать не нужно, если определенный байт просто присутствует в регистре – значит его нужно интерпретировать. На практике обычно делают так, к примеру EDX, сначала смотрят что в DL, интерпретируют его содержимое, потом делают SHR EDX,8 и смотрят опять DL и т.д. Признаком достоверности информации в регистре является бит 31, если он равен 1 – содержимое регистра достоверно. Прежде чем выполнять команду CPUID с EAX=2 сначала нужно удостовериться что текущий процессор ее подерживает.

Счастливые обладатели процессоров Pentium III (только их) могут определить серийный номер своего процессора (предварительно разрешив в BIOS его сообщение процессором, которое по умолчанию отключено) при помощи CPUID с EAX=3.

В регистрах EDX:ECX возвращаются младшие 64 бита номера, вместе с тем, что возвращается в EAX при CPUID (EAX=1), они составляют уникальный 96-битный идентификатор процессора (о котором, в свое время, было столько разговоров).

Кроме того, процессоры AMD имеют возможности вызова функций EAX=80000005h и 80000006h по ним сообщается такая информация как ассоциативность TLB и элементов кэша, но в такие дебри мы сейчас углубляться не будем.

В процессорах AMD (начиная с K5) и Pentium4 имеются возможности сообщения некоторой 48-символьной строки (не той что по CPUID(0)) эти возможности также задействуются с помощью номеров функций более 80000000h.

Инструкция CPUID доступна в любом режиме процессора и с любым уровнем привилегий.

К мануалу прилагается исходник посвященный использованию инструкции CPUID, программа определяет поддержку MMX, SSE, SSE2. Там используются только случаи с EAX=0 и EAX=1, причина этого проста - начиная с EAX=2 начинаются очень большие разночтения между intel и AMD, а я не хочу делать мануал заточенный под intel (так же как и под AMD). Предусмотреть оба случая - значит усложнить программу и найти себе проблемы с тестированием на разных процессорах.

Частоту процессора можно определить многими путями, в былые времена измеряли время выполнения циклов, но ,надо сказать, что этот метод весьма неточный и применим не ко всем процессорам.

Начиная с Pentium в архитектуру был введен счетчик тактов (вообще говоря интел его так не называет, и утвеждает что в будущем он может считать не такты, гарантируется лишь, что счетчик будет монотонно возрастать) мы будем определять частоту процессора используя именно этот счетчик. Для начала немного о нем самом: Счетчик тактов имеет разрядность 64 бита и увеличивается на 1 с каждым тактом процессора начиная с сигнала RESET#, он продолжает счет при выполнении инструкции HLT (собственно при выполнении этой инструкции процессор вовсе не останавливается, а всего-навсего непрерывно выполняет инструкцию NOP, которая ,в свою очередь , является закамуфлированной инструкцией XCHG AX,AX (опкод NOP – 10010000b, опкод XCHG AX,reg – 10010reg, что при использовании регистра AX (000) дает 10010000b, интересно, что фактически существует 32-разрядный аналог NOP-а – XCHG EAX,EAX, на кодовую последовательность 66h,90h процессор реагирует нормально). Считывание счетчика тактов можно запретить для прикладных программ (CPL=3) уставнокой в 1 бита TSD в CR4 (в win считываение запрещено). После выполнения инструкции RDTSC (у кого на нее ругается компилятор – db 0fh,031h) регистры EDX:EAX содержат текущее значение счетчика. Измерение частоты при помощи RDTSC происходит следующим образом:
Маскируются все прерывания кроме таймерного.
Делается HLT.
Считывается и сохраняется значение счетчика.
Снова HLT.
Считывается значение счетчика.
Разность значений считанных в пунктах 3 и 5 есть количество тактов за 1 тик таймера (частота прерываний таймера примерно 18,2Гц).

На первый взгляд ничего непонятно. Посмотрим на временную диаграмму.



Момент запуска программы обозначен как t0, штрихи на оси – моменты, когда происходит прерывание от таймера. Первый HLT в листинге нужен для того чтобы преодолеть время t1, которое неизвестно заранее, так как программа может быть запущена в произвольное время. Затем, в момент между t1 и t2 считывается значение счетчика, оно сохраняется и снова делается HLT, процессор будет простаивать до первого прерывания, то есть практически ровно период t2, который и равен периоду прерываний от таймера. Таким образом, при известном значении периода таймера 18,2 Гц, а также количества тактов за этот период можно узнать точную тактовую частоту.



Автор: Dark_Master@tut.by

На этом пока все! Конец 1 части
Добавил: javavirys ( 2017-03-07 13:54:31 )
Теги:Asm PCI
Рейтинг: + 1 -
Просмотров: 1830

Специальные предложения