Содержание статьи
Описание работы программы
Прилагаемый архив содержит следующие файлы:
ASM.BAT – обеспечивает ассемблирование, линковку и запуск программы под
отладчиком. При запуске TASM и TLINK используются опции, обеспечивающие
добавление отладочной информации в EXE файл.
X86-64.ASM – основной модуль программы, 16-битный сегмент кода, запускаемый
из реального режима DOS.
CODESEGS.INC – сегменты кода, содержащие вызываемые подпрограммы: транзитный
32-битный сегмент кода, целевой 32-битный сегмент кода, целевой 64-битный
сегмент кода.
DATASEGS.INC – сегменты данных: сегмент для хранения переменных, сегменты для
построения глобальной дескрипторной таблицы (GDT) и таблицы страничной
трансляции, а также сегмент стека.
При возникновении противоречия между наглядностью и оптимальностью кода,
автор выбирал наглядность. Код может быть модифицирован и оптимизирован в
соответствии со спецификой конкретной задачи.
Рассмотрим выполнение основного модуля. Нумерация пунктов данного
описания соответствует нумерации пунктов комментариев в исходном тексте - файле
WORK\x86-64.asm.
1) Контрольная точка 00h – начало выполнения программы. Вывод в порт 80h кода
00h.
2) Установка SS:SP для адресации стека.
3) Инициализируем адреса для генерации таблицы страниц (Page Directory) в
сегменте PAGING_TABLES и ее адресации при работе в Protected Mode. Устанавливаем
значения регистров: ES=Базовый адрес сегмента PAGING_TABLES, DS=Базовый адрес,
выровненный на 4KB, EAX=CR3=Базовый физический адрес таблицы страниц.
4) Генерируем таблицу страниц. В данном примере используется режим страничной
трансляции с использованием PAE (Physical Address Extension), что является
необходимым для 64-битного режима. Для упрощения страничной трансляции,
используется режим PSE (Page Size Extension), в котором размер страниц равен
2MB, стадия 4KB страниц пропускается. Таблица страниц, построенная в данном
примере, описывает диапазон адресов 0-2MB (1 страница 2MB). При необходимости
адресоваться выше 2MB, процедуру генерации таблицы страниц потребуется
доработать.
Механизм страничной трансляции детально описан в документе [3].
5) Инициализируем адреса для генерации глобальной дескрипторной таблицы (GDT)
в сегменте GLOBAL_DESCRIPTOR_TABLE и ее адресации при работе в Protected Mode.
Устанавливаем значения регистров: DS=Базовый адрес сегмента
GLOBAL_DESCRIPTOR_TABLE, GDTR=Базовый адрес GDT. Инициализируется дескриптор
сегмента, содержащего таблицу GDT.
6) Генерируем таблицу GDT. В дескрипторах сегментов устанавливаем поля
базовых адресов в соответствии с расположением программы в памяти. Остальные
поля (лимиты сегментов и атрибуты) уже заданы в виде констант. Список сегментов,
используемых в Protected Mode, приведен в описании модуля WORK\datasegs.inc.
Механизм сегментации и назначение полей в дескрипторах сегментов детально
описаны в документах [1], [8], [11], [15].
7) В сегменте переменных (DATA_16) подготавливаем адрес для косвенного
межсегментного вызова транзитной подпрограммы Transit_32., которая включает
страничную трансляцию, режим IA-32e и вызывает целевые подпрограммы Pattern_32 и
Pattern_64.
8) В сегменте переменных (DATA_16) подготавливаем адрес для косвенного
межсегментного вызова целевой подпрограммы Pattern_32, демонстрирующей пример
32-битных операции в режиме IA-32e.
9) В сегменте переменных (DATA_16) подготавливаем адрес для косвенного
межсегментного вызова целевой подпрограммы Pattern_64, демонстрирующей пример
64-битных операции в режиме IA-32e. Так как в 64-битном режиме, в отличие от
32-битного, базовый адрес сегмента из дескриптора не используется, смещение
процедуры Pattern_64 должно содержать слагаемое, обусловленное сегментной
компонентой адреса.
10) Сохраняем регистры SS:SP (адрес стека в Real Mode) для последующего
восстановления после возврата из Protected Mode.
11) Запрещаем аппаратные прерывания.
12) Устанавливаем опции, управляющие страничной трансляцией: биты CR4.4=PSE
(Page Size Extension) и CR4.5=PAE (Physical Address Extension). Эта операция
подготовительная - данные установки вступят в силу только после включения
страничной трансляции.
Системный регистр CR4 детально описан в документах [1], [3], [8], [11].
Механизм страничной трансляции детально описан в документе [3].
13) Включаем Protected Mode (защищенный режим). После установки бита CR0.0=PE
(Protection Enable), процессор интерпретирует значения сегментных регистров не
как слагаемые при вычислении физического адреса, а как селекторы, выбирающие
дескрипторы сегментов в дескрипторной таблице, находящейся в памяти. Базовый
адрес, размер и атрибуты сегментов находятся в дескрипторах.
Protected mode (защищенный режим) детально описан в [15].
Покомандная трассировка в Turbo Debugger после перехода в Protected Mode
невозможна, так как Turbo Debugger работает в Real Mode. Поэтому необходимо
перед переключением в Protected Mode запустить программу на выполнение (Run),
предварительно установив точку останова в таком месте, куда управление приходит
после возврата в Real Mode, например, на метке B: (см. ниже).
Иначе, при попытке трассировать команду MOV CR0,EAX отладчик “зависнет”.
14) Выполняем межсегментный переход на следующую инструкцию. Это требуется
для загрузки в регистр CS селектора текущего сегмента кода.
15) Загружаем в регистры DS и ES селекторы сегмента данных.
16) Загружаем в регистр SS селектор сегмента стека. Указатель стека – SP, не
изменяется, используется значение, работавшее в Real Mode, так как сегмент стека
физически тот же, при переходе в Protected Mode изменился только метод доступа к
сегменту.
17) Контрольная точка 01h – выводится перед вызовом транзитной подпрограммы
Transit_32. Вывод в порт 80h кода 01h.
18) Вызов транзитной подпрограммы Transit_32, которая включает страничную
трансляцию и режим IA32e, после чего, вызывает целевые подпрограммы Pattern_32,
Pattern_64. Данные подпрограммы рассмотрены при описании файла WORK\codeseg.inc.
19) Контрольная точка F0h – выводится после возврата из транзитной
подпрограммы Transit_32. Вывод в порт 80h кода F0h.
20) Восстановление контекста Real Mode и возврат в Real Mode. В регистры DS,
ES, SS, FS, GS загружается селектор 16-битного сегмента DATA_16, для установки
Segment Limit=64KB, регистр CR3 перезаписывается для сброса содержимого буфера
TLB. Сбрасывается бит CR0.0=PE (Protection Enable).
21) Продолжаем восстановление контекста Real Mode. Выполняем межсегментный
переход на следующую инструкцию. Это требуется для перезагрузки регистра CS.
Восстанавливаем адрес стека в SS:SP.
22) Разрешаем аппаратные прерывания.
23) Данный блок содержит точку останова, метку B: Когда мы выполняем
программу под отладчиком, и управление пришло в эту точку, мы можем сделать дамп
16-байтного блока с адреса DS:BX. Там мы можем проконтролировать результаты
работы целевых процедур Pattern_32 и Pattern_64.
24) Контрольная точка FFh – перед завершением программы. Вывод в порт 80h
кода FFh.
25) Завершаем программу, используется функция 4Ch прерывания INT 21h. Код
завершения 00h.
Рассмотрим выполнение подпрограмм. Нумерация пунктов данного описания
соответствует нумерации пунктов комментариев в исходном тексте - файле
WORK\codeseg.inc.
1) Контрольная точка 10h – начало выполнения подпрограммы Transit_32. Вывод в
порт 80h кода 10h.
2) Установка “плоского” сегмента стека. Так как в 64-битном режиме, в отличие
от 32-битного, базовый адрес сегмента из дескриптора не используется, смещение,
загружаемое в ESP должно содержать слагаемое, обусловленное сегментной
компонентой адреса стека. Заметим, что прерывания в данный момент запрещены,
поэтому имеющая место рассогласованность значений SS и ESP в течение выполнения
нескольких инструкций, здесь допустима.
3) Устанавливаем бит LME (Long Mode Enable) в регистре IA32_EFER MSR
(Extended Feature Enable Register, Model-Specific Register). Устанавливаем бит
CR0.31=PG (Paging). Включение страничной трансляции при LME=1 вызывает активацию
режима IA32e. Процессор автоматически устанавливает бит LMA=1 (Long Mode Active)
в регистре IA32_EFER MSR, при этом приобретает функциональность ранее
зарезервированный бит 53 (бит L=Long Mode) дескриптора сегмента кода:
межсегментная передача управления вызывает переключение процессора в 64-битный
режим, если в дескрипторе целевого сегмента кода бит L=1. Иначе, если L=0,
процессор остается в 32-битном режиме.
Регистр IA32_EFER MSR детально описан в [1], [8], [11].
4) Устанавливаем регистр FS для доступа к сегменту переменных DATA_16.
5) Вызываем 32-битную целевую процедуру.
6) Вызываем 64-битную целевую процедуру.
7) Выключаем страничную трансляцию и сбрасываем бит LME.
8) Восстанавливаем 16-битный стек.
9) Контрольная точка 1Fh – возврат из подпрограммы Transit_32. Вывод в порт
80h кода 1Fh.
10) Возврат из подпрограммы Transit_32.
Подпрограмма Pattern_32 в качестве примера 32-битной операции записывает
содержимое регистра EAX по адресу FS:[EBX].
Подпрограмма Pattern_64 в качестве примера 64-битной операции записывает
содержимое регистра RAX по адресу FS:[RBX].
Если предлагаемая программа используется как шаблон для исследовательских
работ и решения различных задач, пользователь может добавлять свой код в тело
выше указанных подпрограмм.
Сегмент INTERRUPTS_16 не используется и зарезервирован для размещения
процедур обработки прерываний. Текущая реализация программы не поддерживает
обработку прерываний в Protected Mode.
Рассмотрим блоки данных. Нумерация пунктов данного описания
соответствует нумерации пунктов комментариев в исходном тексте - файле
WORK\dataseg.inc.
1) Сегмент для размещения переменных DATA_16.
2) Список селекторов сегментов:
NUL – не используется процессором (нуль-селектор)
GDTR – сегмент, содержащий GDT
IDTR – сегмент, содержащий IDT, не используется в текущей реализации программы
CODE16 – 16-битный сегмент кода
CODE32 – 32-битный сегмент кода для транзитной процедуры
DATA16 – 16-битный сегмент данных для переменных
DATA32 – 32-битный сегмент данных, не используется в текущей реализации
программы
STACK16 – 16-битный сегмент стека
INTS16 – сегмент процедур обработки прерываний, не используется в текущей
реализации программы
TARGET32 – 32-битный сегмент кода для целевой процедуры
TARGET64 – 64-битный сегмент кода для целевой процедуры
STACK64 – сегмент стека для 32- и 64-битного режимов.
3) Шаблон для генерации структуры дескрипторов сегментов.
4) Глобальная дескрипторная таблица (GDT), содержит дескрипторы сегментов,
используемых в Protected Mode.
5) Сегмент для генерации таблицы страниц (Page Table). В исходном состоянии
этот сегмент не заполнен, он декларирован для резервирования памяти. Заполняется
при работе программы.
6) Сегмент стека. Программа использует 3 метода доступа к стеку: стек Real
Mode, 16-битный стек Protected Mode 16/32 и “плоский” стек для Protected Mode
32/64. Независимо от метода доступа, физически стек всегда расположен в этом
сегменте.
Источники информации
Электронные документы, доступные на сайте
developer.intel.com.
1) 64-bit Extension Technology Software Developer’s Guide. Volume 1 of 2.
Order Number 300834-001.
2) 64-bit Extension Technology Software Developer’s Guide. Volume 2 of 2. Order
Number 300835-001.
3) TLBs, Paging-Structure Caches, and Their Invalidation. Application Note.
Document Number 317080-001.
4) Intel 64 and IA-32 Architectures Optimization Reference Manual. Order Number
248966-015.
5) Intel 64 and IA-32 Architectures Software Developer’s Manual. Volume 1: Basic
Architecture. Order Number 253665-023US.
6) Intel 64 and IA-32 Architectures Software Developer’s Manual. Volume 2A:
Instruction Set Reference, A-M. Order Number 253666-023US.
7) Intel 64 and IA-32 Architectures Software Developer’s Manual. Volume 2B:
Instruction Set Reference, N-Z. Order Number 253667-023US.
8) Intel 64 and IA-32 Architectures Software Developer’s Manual. Volume 3A:
System Programming Guide, Part 1. Order Number 253668-023US.
9) Intel 64 and IA-32 Architectures Software Developer’s Manual. Volume 3B:
System Programming Guide, Part 2. Order Number 253669-023US.
Электронные документы, доступные на сайте
developer.amd.com.
10) AMD64 Architecture Programmer’s Manual. Volume 1: Application
Programming. Publication No. 24592.
11) AMD64 Architecture Programmer’s Manual. Volume 2: System Programming.
Publication No. 24593.
12) AMD64 Architecture Programmer’s Manual. Volume 3: General-Purpose and System
Instructions. Publication No. 24594.
13) AMD64 Architecture Programmer’s Manual. Volume 4: 128-Bit Media
Instructions. Publication No. 26568.
14) AMD64 Architecture Programmer’s Manual. Volume 5: 64-Bit Media and x87
Floating-Point Instructions. Publication No. 26569.
Книги.
15) В.Л. Григорьев. Микропроцессор i486. Архитектура и программирование.
Москва ТОО “ГРАНАЛ” 1993.
Заключение
Рассматриваемая в данном материале процедура включения 64-битного режима
является частным случаем задач, для детального исследования которых автор
применил запуск ассемблерных фрагментов под DOS. Эта статья в некотором роде
пробная, при наличии читательского интереса к теме “аппаратно-приближенного
программирования” и рассмотрению современных технологий на углубленном
(ассемблерном) уровне, автор планирует продолжить цикл и выпустить новые
материалы. Вот несколько примеров:
- Запуск нескольких потоков выполнения для систем с Hyper-Threading и SMP.
- Пример бенчмарок оперативной памяти в 64-битном 2-потоковом режиме.
- Считывание параметров (разрядность и тактовая частота) соединений PCI
Express. - Считывание параметров (разрядность и тактовая частота) соединений Hyper
Transport. - Перепрограммирование микросхемы SPD на модуле DIMM средствами материнской
платы, без использования дополнительного оборудования (программаторов). - Измерение тактовой частоты процессора и системной шины.
- Использование Super VGA режимов в DOS-приложениях.
- Тестирование оперативной памяти.
- Тестирование видео памяти.
Примечание.
В данном материале рассматривается 64-битные расширения для процессоров x86:
AMD 64 и Intel EM64T (Intel 64), которые являются логическим
продолжением 32-битной архитектуры. Не следует путать данные расширения и
архитектуру IA-64, которая используется в процессорах Intel Itanium и не
является предметом рассмотрения здесь.