Программа для определения установленного компьютерного оборудования

Тип работы:
Контрольная
Предмет:
Программирование


Узнать стоимость

Детальная информация о работе

Выдержка из работы

Введение

В рамках достижения поставленной цели автором были поставлены и решены следующие задачи:

1. Изучить теоретические аспекты.

2. Изучить структуру и методы написания программ на языке Assembler.

3. Реализовать программу, пользуясь пунктами 1 и 2.

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

А что делать, если пользоваться сервисами нельзя, например, в случае разработки собственной ОС?

В данной работе будет проверено:

1. Процессор (частота, производитель, возможности).

2. Оперативная память (объем).

3. HDD (объем, тип).

4. Устройства PCI (производитель, модель).

Следовательно, необходимо понимать устройство всей системы, под которую мы пишем программу (в нашем случае это MS-DOS). Как видим, написание программы для определения установленного оборудования требует обширных знаний в сфере организации ЭВМ.

1. Процессор

1.1 Определение семейства

Процессоры поддерживают инструкцию CPUID (как Intel, так и AMD), начиная с пятого поколения (Pentium) и поздних моделей 486.

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

Посмотрим, чем отличаются процессоры не поддерживающие CPUID. Все просто — если бит 18 в EFLAGS доступен, значит процессор 486 или круче, если его невозможно изменить инструкцией POPF — 386.

В том же EFLAGS нужно попробовать изменить бит ID (21) если его можно программно изменить — процессор поддерживает инструкцию CPUID. CPUID имеет параметр, который задается в регистре ЕАХ.

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

У Intel это «Genuinelntel», у AMD — «AuthenticAMD», у Cyrix — «Cyrixlnstead».

Пример:

mov eax, 0 cpuid

mov dword ptr [stm], EBX; Genu

mov dword ptr [stm+4], EDX; inel

mov dword ptr [stm+8], ECX; ntel

mov ah, 9

lea dx, stm; Genuinelntel

int 21h

1.2 Определение функций

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

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

ЕАХ[00: 03] - степпинг (stepping)

ЕАХ[07: 04] - модель (model)

ЕАХ[11: 08] - семейство (family)

ЕАХ[ 13: 12] - тип (type)

ЕАХ[ 15: 14]- резерв (reserved)

ЕАХ[19: 16] - расширенная модель (только Pentium 4)

ЕАХ[23: 20] - расширенное семейство (только Pentium 4)

ЕАХ[31: 24] - резерв (reserved)

ЕВХ[07: 00] - брэнд-индекс (brand-index)

ЕВХ[ 15: 08] - длина строки, очищаемой инструкцией CLFLUSH (Pentium4)

ЕВХ[23: 16] - резерв

ЕВХ[31: 24] - идентификатор APIC процессора.

ЕСХ-0

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

Таблица 1 — значения основных бит регистра EDX

Бит

Описание

0

Наличие сопроцессора

1

Расширение для режима V86, наличие флагов VIP и VIF в EFLAGS

2

Расширения отладки (останов по обращению к портам)

3

Возможности расширения размера страниц до 4Мб

4

Наличие счетчика меток реального времени (и инструкции RDTSC)

5

Поддержка модельно-специфических регистров в стиле Pentium

6

Расширение физического адреса до 36 бит

7

Поддержка Machine Check Exception

8

Инструкция CMPXCHG8B

9

Наличие APIC

10

RESERVED

11

Поддержка инструкций SYSENTER и SYSEXIT

12

Регистры управления кэшированием (MTRR)

13

Поддержка бита глобальности в элементах каталога страниц

14

Поддержка архитектуры машинного контроля

15

Поддержка инструкций условной пересылки CMOVxx

16

Поддержка атрибутов страниц

17

Возможность использования режима PSE-36 для страничной адресации

18

Поддержка серийного номера процессора

19

Поддержка инструкции CLFLUSH

20

RESERVED

21

Поддержка отладочной записи истории переходов

22

Наличие управления частотой синхронизации (АСР1)

23

Поддержка ММХ

24

Поддержка инструкций сохранениявосстановления контекста FPU

25

SSE

26

SSE2

27

Самослежение (Self Snoop)

28

RESERVED

29

Автоматическое снижение производительности при перегреве

30

Начилие AMD 3DNow!

В данной курсовой работе проверка значений бит, реализована с помощью макроса bit_test:

bit test MACRO num., mess

local ml, m2

mov ah, 9

lea dx, ext& mess

int 21h

bt edi, num

jnc ml

lea dx, yes

int 21h

jmp m2

ml:

lea dx, no int 21h m2:

ENDM

Mess -- номер строковой переменной для вывода

Num -- номер проверяемого бита

При вызове CPUID с ЕАХ=2 (функция появилась, начиная с Pentium II, в процессорах AMD она недоступна) в регистрах ЕАХ, ЕВХ, ЕСХ, EDX возвращаются так называемые «дескрипторы», которые описывают возможности КЭШей и TLB буферов. Причем AL содержит число, указывающее сколько раз необходимо последовательно выполнить CPUID (с ЕАХ=2) для получения полной информации. Дескрипторы построены по такому принципу: никаких битов тестировать не нужно, если определенный байт просто присутствует в регистре — значит его нужно интерпретировать. На практике обычно делают так, к примеру EDX, сначала смотрят что в DL, интерпретируют его содержимое, потом делают SHR EDX, 8 и смотрят опять DL и т. д. Признаком достоверности информации в регистре является бит 31, если он равен 1 — содержимое регистра достоверно. Прежде чем выполнять команду CPUID с ЕАХ=2 сначала нужно удостовериться, что текущий процессор ее поддерживает.

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

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

Кроме того, процессоры AMD имеют возможности вызова функций EAX=8 000 0005h и 8 000 0006h, по ним сообщается такая информация как ассоциативность TLB и элементов КЭШа, но этот вопрос не затрагивается в данной работе.

В процессорах AMD (начиная с К5) и Pentium4 имеются возможности сообщения некоторой 48-символьной строки (не той что по CPUID (O)) эти возможности также задействуются с помощью номеров функций более 8 000 0000b.

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

В данной работе программа определяет поддержку MMX, SSE, SSE2 и.т.д. Там используются только случаи с ЕАХ=0 и ЕАХ=1, причина этого проста — начиная с ЕАХ=2, начинаются очень большие разночтения между Intel и AMD. Предусмотреть оба случая — значит усложнить программу и найти себе проблемы с тестированием на разных процессорах.

1.3 Определение частоты

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

Начиная с Pentium в архитектуру был введен счетчик тактов (вообще говоря Intel его так не называет, и утверждает что в будущем он может считать не такты, гарантируется лишь, что счетчик будет монотонно возрастать) мы будем определять частоту процессора используя именно этот счетчик. Счетчик тактов имеет разрядность 64 бита и увеличивается на 1 с каждым тактом процессора, начиная с сигнала RESET#. Он продолжает счет при выполнении инструкции HLT (собственно при выполнении этой инструкции процессор вовсе не останавливается, а всего-навсего непрерывно выполняет инструкцию NOP, которая, в свою очередь, является закамуфлированной инструкцией XCHG АХ, АХ (опкод NOP — 1 001 0000b, опкод XCHG AX, reg — 10010reg, что при использовании регистра АХ (000) дает 1 001 0000b, интересно, что фактически существует 32- разрядный аналог NOP-a — XCHG ЕАХ, ЕАХ, на кодовую последовательность 66h, 90h процессор реагирует нормально). Считывание счетчика тактов можно запретить для прикладных программ (CPL=3) установкой в 1 бита TSD в CR4 (в win считывание запрещено). После выполнения инструкции RDTSC (если происходить ошибка компиляции — db 0fh, 031h) регистры EDX: EAX содержат текущее значение счетчика. Измерение частоты при помощи RDTSC происходит следующим образом:

1. Маскируются все прерывания, кроме таймерного.

2. Делается HLT.

3. Считывается и сохраняется значение счетчика.

4. Снова делается HLT.

5. Считывается значение счетчика.

Разность значений, считанных в пунктах 3 и 5, есть количество тактов за 1 тик таймера (частота прерываний таймера примерно 18,2Гц).

Для большей наглядности представим действия на временной диаграмме:

Рисунок 1 — Временная диаграмма

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

2. Оперативная память

2.1 Методы определения объема оперативной памяти

Перечислим функции BIOS для определения оперативной памяти:

int 12h — получить размер базовой памяти в кб (640 кб). Входных параметров не имеет.

На выходе:

-- АХ — количество памяти.

Функция 88h прерывания INT 15h сообщает объём имеющейся оперативной памяти свыше 1 Мбайта, т. е. начиная с адреса 1 OOOOOh.

На входе:

-- АН — 88h

На выходе:

-- АХ — количество памяти в диапазоне от 1 до 16 Мб в килобайтах.

Функция C7h прерывания INT 15h возвращает карту распределения памяти. (является необязательной)

На входе:

-- АН — C7h

-- DS: SI — адрес карты памяти

На выходе:

-- CF — сброшен, если успешно

-- Заполненная карта памяти

Формат карты памяти представлен в таблице 2:

Таблица 2 — формат карты памяти

Смещение

Размер

Описание

0

WORD

Объём в килобайтах локальной памяти в

пределах от 1 до 16 Мбайт

2

DWORD

Объём в килобайтах локальной памяти в

пределах от 16 Мбайт до 4 Гбайт

6

DWORD

Объём в килобайтах системной памяти в

пределах от 1 до 16 Мбайт

10

DWORD

Объём в килобайтах системной памяти в

пределах от 1 до 16 Мбайт

14

DWORD

Объём в килобайтах системной памяти в

пределах от 16 Мбайт до 4 Гбайт

18

DWORD

Объём в килобайтах кэшируемой памяти в

пределах от 1 до 16 Мбайт

22

DWORD

Объём в килобайтах кэшируемой памяти в

пределах от 16 Мбайт до 4 Гбайт

26

DWORD

Объём в килобайтах перед началом

несистемной памяти в пределах от 1 до 16 Мбайт

30

DWORD

Объём в килобайтах перед началом

несистемной памяти в пределах от 16 Мбайт до 4 Гбайт

34

WORD

Начальный сегмент крупнейшего свободного

блока в диапазоне адресов от COOOOh до DFFFFh

36

WORD

Размер крупнейшего свободного блока

38

DWORD

Зарезервировано

компьютерное оборудование программная проверка

Функция E801h прерывания INT 15h

На входе:

-- AX -E801h

На выходе:

-- CF — сброшен, если успешно

-- AX — размер памяти в диапазоне от 1 до 16 Мбайт, выраженный в килобайтах.

-- BX — размер памяти свыше 16 Мбайт, выраженный в блоках по 64 Кбайта.

-- CX — размер сконфигурированной памяти в диапазоне от 1 до 16 Мбайт, выраженной в килобайтах.

-- DX — размер сконфигурированной памяти свыше 16 Мбайт, выраженный в блоках по 64 Кбайта.

Данные методы не являются точными, и не работают на процессорах, которые не поддерживают функцию APIC. Поэтому, существует более точный метод рассмотренный далее.

2.2 Классический метод

Ставший уже классическим метод определения объема заключается в следующем принципе:

Если что-то записать по несуществующему физически адресу, а потом прочитать что-то с этого же адреса — записанное и прочитанное значения естественно не совпадут (в 99,(9) процентах случаев прочитаются нули). Сам алгоритм такой:

1. Инициализировать счетчик.

2. Сохранить в регистре значение из памяти по адресу [счетчик].

3. Записать в память тестовое значение (в нашем случае это будет AAh)

4. Прочитать из памяти.

5. Восстановить старое значение по этому адресу.

6. Сравнить записанное и прочитанное значение.

7. Если равны — счетчик = счетчик+1, если нет — выход из цикла.

8. JMP пункт 2.

На первый взгляд все очень просто, при практической же реализации приведенного алгоритма возникает множество проблем: во-первых сама программа считающая память расположена в этой самой памяти и рано или поздно она сама себя перезапишет тестовым значением. Этот нюанс обычно решается так: программа выполняется в реальном режиме в пределах первого мегабайта, счет же начинается с адресов выше мегабайта.

Этот метод порождает другую проблему — в реальном режиме непосредственно доступен только этот самый один мегабайт. Эта проблема решается путем применения «нереального» режима, он же Big real-mode.

Как известно в процессоре каждый сегментный регистр имеет скрытые или теневые (shadow parts) части, в которых в защищенном режиме кэшируется дескриптор сегмента, для программиста они невидимы. В защищенном режиме эти части обновляются всякий раз, когда в сегментный регистр загружается новое значение, в реальном же режиме обновляются только поля базового адреса сегмента. Если в защищенном режиме создать сегмент с лимитом в 4Гб и загрузить в сегментный регистр такой селектор, после чего переключиться в реальный режим, и, не следуя рекомендациям Intel, оставить предел равным 4Гб — значение лимита сегмента сохранится, позволяя использовать 32-битные смещения. Алгоритм перехода в нереальный режим:

1. Создать дескриптор с базой равной 0.

2. Установить предел сегмента в 4Гб.

3. Переключиться в защищенный режим.

4. Загрузить селектор сегмента в какой-либо сегментный регистр.

5. Переключиться в реальный режим.

После этих действий можно в реальном режиме использовать конструкции типа:

mov ax, word ptr fs: [edx]

Где EDX может изменяться от нуля до 4Гб не вызывая никаких исключений защиты (в «настоящем» реальном режиме превышение 64Кб вызывает исключение GP#) Фактически ЕDХ = целевой адрес, поскольку база сегмента в FS=0

В защищенном режиме при включенной страничной адресации считать память этим методом бесполезно, потому что кроме основной память будет считаться еще и файл подкачки на винчестере, и в перспективе можно всегда получать значение около 4Гб (зависит от ОС).

Нужно учесть один важный момент: в некоторых книгах пишется, что в качестве «нереального» сегментного регистра надо использовать FS или GS, так как другие регистры часто перезагружаются и процессор якобы сбрасывает лимит в 64Кб после перезагрузки сегментного регистра в реальном режиме. На практике оказывается совсем не так. Процессор не использует поля лимитов в реальном режиме.

Для того чтобы избежать дополнительных проблем, приведем пример с регистром FS:

1. Установить «нереальный режим».

2. Открыть старшие адресные линии (GateA20).

3. Установить счетчик в 1 048 576 (1Mb).

4. Цикл записи-чтения.

5. Вывести значение счетчика.

6. Закрыть вентиль А20.

7. Выход.

После запуска программы следует немного подождать, примерно в 2 раза больше времени, чем время, за которое считает оперативную память BIOS при загрузке.

Существует способ многократного увеличения скорости программы. Дело в том, что данный алгоритм считает память с точностью до байта, такая точность вообще говоря не нужна, т.к. размер современной планки памяти не может быть некратным мегабайту, поэтому можно наращивать счетчик сразу прибавляя к нему значение 1 048 576, чего можно достичь заменив в цикле записи-чтения команду inc есх на add есх, 1 048 576.

3. HDD

3.1 Определение объема

Стандартный IDE контроллер, применяемый в PC, поддерживает 2 канала, на каждом из которых, может быть 2 устройства АТА (то есть всего может быть 4 устройства). Каждый канал имеет свою собственную часть пространства ввода-вывода. Для первого канала — lF0h-lF7h для второго — 170h-177h. На данном этапе надо ввести понятие базового порта: в общем, это лучше всего пояснить на примере:

Адреса портов формируются следующим образом: базовый порт+смещение. Загрузив в базовый порт значение lFOh или 170h можно больше не думать, о том с каким каналом нужно работать, потому что функции портов, к примеру lF3h и 173h совпадают для разных каналов IDE.

То есть для первого канала базовым портом является lFOh, для второго — 170h, загрузив один раз эти значения в качестве базы остальные порты можно адресовать по смещениям относительно этих, т. е. сделать так, что один и тот же код сможет работать с двумя каналами. В нагрузку к этому контроллером используется еще пара портов 3F6h-3F7h для первого канала и 376h-377h для его коллеги.

Теперь рассмотрим команды АТА. Существует около 20 команд ATA, но на практике, обычно используются всего 10.

В данной работе рассматриваются команды чтения/записи одного сектора, идентификации накопителя, а также команду остановки винчестера (команда в стандарт не входит, но поддерживается абсолютным большинством винчестеров), которая используются всеми ОС при переводе компьютера в режим пониженного энергопотребления. Все примеры относятся к Master устройству первого канала, но их можно легко адаптировать к любой конфигурации.

Чтение секторов с винчестера происходит следующим образом:

1. Запретить прерывания с записью 3F6h.

2. Дождаться готовности канала читая бит BSY в порту lF7h.

3. Выбрать устройство на канале записью в lF6h.

4. Дождаться DRDY=1 и BSY=0.

5. Загрузить LBA адрес.

6. Послать команду чтения (20h).

7. Дождаться BSY=0.

8. Дождаться готовности обмена данными (DRQ=1).

9. Принять данные от устройства через lFOh строковой операцией ввода из порта.

10. Разрешить прерывания от устройства.

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

4. PCI

4.1 Нахождение PCI устройств

Сначала введем фундаментальное понятие — конфигурационное пространство PCI (PCI configuration space).

Так называется массив регистров, который имеется у каждого PCI- устройства, через них задаются различные параметры (номера прерываний для устройства и т. д.). Общение с PCI-устройствами происходит в основном через 2 32-битных порта 0CF8h и OCFCh. Через них можно читать и писать в это самое конфигурационное пространство определенного устройства.

Происходит этот процесс следующим образом:

В регистре 0CF8h задается адрес устройства на шине, после чего из OCFCh считываются (записываются) данные.

Координаты устройства на шине (формат 0CF8h) выглядят так:

Рисунок 2 — Координаты устройства на PCI шине

31-й бит показывает достоверность информации в регистре, там должен быть 1.

Bus Number — номер шины PCI. (их вполне может быть несколько, например порт AGP использует не ту шину, к которой подключены слоты PCI).

Device Number — номер устройства на шине

Function Number — номер функции устройства (здесь надо немного определится с терминологией, дело в том что под функцией и подразумевается устройство, тогда так под устройством (device) подразумевается абонент шины, то есть, если, например, есть карта в которой совмещены 2 каких-либо устройства, то она будет восприниматься как одно устройство с двумя функциями, причем даже такое «однофункциональное» как видеокарта может иметь множество функций). Это деление на устройства и функции в большинстве случаев просто логическое, «основное» устройство соответствует функции 0.

Register Number — номер регистра конфигурационного пространства который следует прочитать (записать). (Вообще используется все поле до 0- го бита, но поскольку обмен производится двойными словами (4 байта) то получается что младшие 2 бита всегда нулевые).

Для того чтобы определить производителя нужно посмотреть на карту конфигурационного пространства:

Рисунок 3 — Карта конфигурационного пространства

Поля обозначенные желтым цветом должны присутствовать у всех

устройств, именно там и хранится информация о том, что это за устройство и кто его производитель. Нас будут интересовать 2 поля:

VendorlD — код производителя.

DevicelD — код устройства.

Если же что-то прочитать из пространства реально не существующего устройства, то прочитается специально зарезервированное для этой цели значение OFFFFFFFFh.

Из этого всего можно сделать такой вывод: чтобы найти все устройства нужно в цикле (изменяя Bus от нуля до 255, dev от 0 до 31, func от нуля до 7) читать их конфигурационные пространства, если прочиталось OFFFFFFFFh, значит устройства нет, если же прочиталось что-то другое — устройство присутствует.

Вот пример процедуры используемой в данной работе и читающей из конфигурационного пространства PCI.

Номер функции задается в BL, номер устройства в ВЫ, функция в CL, и смещение (номер регистра) в СН.

RDJPCI PROC NEAR mov dx, 0CF8h xor eax, eax mov al, bl

or ah, 80h; Бит достоверности в 1

shl eax, 16

mov ah, bh

shl ah, 3

or ah, cl

mov al, ch

and al, 0FCh; Сбросить 2 младших бита

out dx, eax

mov dx, 0CFCh

in eax, dx

ret

RD PCIENDP

К курсовой работе прилагается файл в котором описаны коды VendorlD и DevicelD

Заключение

Данную работу можно считать полноценной программой. Для ее написания необходимо понимание структуры ЭВМ и ОС, в которой она должна работать, понимание принципа работы аппаратных устройств.

Во время написания программы возникали проблемы связанные с различным типом оборудования, и устройством ОС.

В итоге получилась простая программа для определения подключенного к ЭВМ оборудования, которая, естественно, нуждается в доработке и оптимизации.

Список литературы

1. www. wasm. ru «Программирование архитектуры ЭВМ»

2. В. Юров. «Assembler», 2-е изд. — СПб. :Питер, 2006. — 534 с.

3. Эндрю Таненбаум «Архитектура компьютера» 5-е изд. — СПб.: Питер, 2007. -- 844 с.

ПоказатьСвернуть
Заполнить форму текущей работой