Какая информация может содержаться в таблице дескрипторов прерываний

Лекция  №13

Система
прерываний 32-разрядных микропроцессоров
i80x86.

Работа системы
прерываний в защищенном режиме

Вопросы

1.   
Отличия работы системы прерываний в защищенном режиме

2.   
Классификация прерываний в защищенном режиме

3.    Таблица
дескрипторов прерываний IDT

4.    Обобщенная
схема обработки прерывания в защищенном режиме

5.    Шлюз
ловушки

6.    Шлюз
прерывания

7.    Шлюз
задачи

8.   
Резюме

Обработка прерываний в защищенном
режиме отличается от обработки в реальном режиме так же сильно, как и
защищенный режим отличается от реального.

Потому что:

1.   
В защищенном режиме немного изменено распределение
номеров векторов прерываний. (Можно самостоятельно посмотреть по таблицам,
например, в книге Аssembler Юрова,
Хорошенко на стр. 439-441)

2.   
Принципиально иным является механизм обработки
прерываний.

Классификация прерываний в защищенном режиме

Прерывания и исключения можно раз­делить
на несколько групп:

1.   
сбой;

2.   
ловушка;

3.   
аварийное завершение.

Это деление производится в
соответствии со следующими признаками:

·      какая
информация сохраняется о месте возникновения прерывания (ис­ключения)?

·      возможно
ли возобновление прерванной программы?

Исходя из этих признаков, можно
дать следующие характеристики вышепере­численным группам:

Сбой (ошибка) прерывание или исключение, при
возникновении которо­го в стек записываются значения регистров cs: ip, указывающие на команду,
вызвавшую данное прерывание. Это позволяет, получив доступ к сегменту кода,
исправить ошибочную команду в обработчике прерывания и, вернув управление
программе, фактически осуществить ее рестарт (вспомните, что в реальном режиме
при возникновении прерывания в стеке всегда запоми­нается адрес команды,
следующей за той, которая вызвала это прерывание).

Ловушка прерывание или исключение, при
возникновении которого в стек записываются значения регистров cs: ip,
указывающие на команду, сле­дующую за командой, вызвавшей данное
прерывание.

Так же, как и в случае
ошибок возможен рестарт программы. Для этого необходимо лишь испра­вить в
обработчике прерывания соответствующие код или данные, послужившие источником
ошибки. После этого перед возвратом управления нужно скорректировать значение ip в
стеке на длину команды, вызвавшей данное прерывание.

Механизм
ловушек похож на механизм прерываний в реальном режиме, хотя не во всем. Здесь есть один тонкий
момент. Если пре­рывание типа ловушки возникло в команде передачи управления jmp, то
со­держимое пары cs: ip в стеке будет отражать результат этого перехода,
то есть соответствовать команде назначения.

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

Þ
соответствующие программы-обработчики ошибок, ловушек и аварий будут отличаться
алгоритмами работы.

Микропроцессор
жестко определяет, какие прерывания являются ошибками, ловушками и авариями.

Замечания.

Для некоторых
номеров прерываний в реальном режиме возможны 2 источника прерывания
(развитие микропроцессоров).

Некоторые
прерывания при своем воз­никновении дополнительно генерируют и записывают в
стек так называемый код ошибки. Этот код
может впоследствии использоваться для установления источника прерывания. Код
ошибки записывается в стек вслед за содержимым регистров eflags, cs и eip.

Þ
можно распознать истинный источник прерывания (если есть неоднозначность).

Таблица дескрипторов прерываний IDT

Вместо таблицы векторов
прерываний система прерываний имеет дело с таблицей дескрипторов прерываний (IDT, interrupt descriptor table)

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

Элементами
таблицы IDT являются
дескрипторы – Размер дескриптора = 8 байтам.

(Т.е. формат
таблицы IDT подобен формату GDT и LDT.)

Дескрипторы
в таблице прерываний обычно называются шлюзами
(
иногда коммутаторами). 

Шлюзы
предназначены для указания точки входа в программу обработки пре­рывания.

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

·     
шлюз ловушки;

·     
шлюз прерывания;

·      шлюз
задачи. 

Физически
микропроцессор отличает шлюзы по со­держимому полей.

Особенности
таблицы IDT:

·     нулевой
дескриптор используется для описания шлюза для программы обработки
исключительной ситуации 0 (ошибка деления);


таблице GDT отсутствует)

·     дескрипторы
в таблице IDT строго упорядочены в соответствии с номерами прерываний.

В
таблицах GDT и LDT порядок описания дес­крипторов роли не играет, хотя и
допускается наличие некоторых соглаше­ний по их упорядоченности;

·     размерность
таблицы IDT — не более
256 элементов размером по восемь байт, по числу возможных источников
прерываний.

В отдельных случаях есть
смысл описывать все 256 дескрипторов этой таблицы, формируя для неиспользуемых
номеров прерываний шлюзы-заглушки. Это позволит кор­ректно обрабатывать все
прерывания, даже если они и не планируются к использованию в данной задаче.
Если этого не сделать, то при незапланированном прерывании с номером,
превышающим пределы IDT для данной зада­чи, будет возникать исключительная
ситуация общей защиты (с номером 13 (ODh)).

Адрес и длина
таблицы IDT содержатся в регистре idtr

Заметим, что в реальном режи­ме
регистр idtr  также содержит адрес
таблицы прерываний, но при этом каждый вектор занимает 4 байта и содержит
32-разрядный адрес в формате CS:IP. Начальное значение этого
регистра равно нулю, но внего можно занести и другое значение.

Þ Возможно произвольное
размещение в памяти этой таблицы не только в за­щищенном режиме, но и реальном.

 В защищенном режиме произвести загрузку регистра idtr может только код с максимальным
уровнем привилегий.

Доступ к таблице IDT со стороны
пользовательских (прикладных) программ невозможен. 

Обобщенная схема обработки прерывания в защищенном режиме

При возникновении прерывания от
источника с номером п микропроцессор,
находясь в защищенном режиме, вы­полняет следующие действия:

1.
Определяет местонахождение таблицы IDT, адрес и размер которой содер­жится в
регистре idtr.

2.
Складывает значение адреса, по которому размещена IDT, и значение n*8. По данному смещению в
таблице IDT должен находиться 8-байтовый дес­криптор,
определяющий местоположение процедуры обработки прерывания.

3.  
Переключается на процедуру обработки прерывания.

Шлюз
ловушки

Формат шлюза ловушки —

offset_2

p

dpl

1 1 1 1

000

не используется

63            47                   
39              36                                          32

indicator

                     offset_1

31            15                                                                                     

Offset_1, offset_2 – первая и
вторая половины смещения в сегменте

indicator – Селектор,
указывающий на дескриптор в LDT
или GDT

000 – Постоянное
значение

type – 1111 – Тип шлюза —
ловушка

dpl – Определение
минимального уровня приви­легий задачи, которая может передать управление
обработчику прерываний через данный шлюз (обычно dpl =
112)

p – Бит присутствия (0 или 1)

Когда возникает
прерывание и его вектор выбирает в таблице IDT дескриптор шлюза с типом ловушки, микропроцессор сохраняет в
стеке информацию о месте, где он прервал работу текущей программы. После этого
он передает управление в соответствии с полями indicator и offset.

Поле indicator представляет
селектор одной из таблиц, GDT или LDT, в зависимос­ти от состояния бита TI в нем.

Поле offset определяет смещение в
сегменте кода. Этот сегмент кода описывается дескриптором, на который указывает
се­лектор в поле indicator.

После того как
управление было передано обработчи­ку прерывания, он выполняет свою работу до
тех пор, пока не встретит коман­ду iret. Эта команда восстанавливает из стека состояние регистров eflags, cs и eip на момент возникновения
прерывания, и работа приостановленной про­граммы продолжается.

 При подготовке выхода из программы обработки пре­рывания
имейте в виду, что команда iret ничего не знает о возможности нали­чия в стеке
кода ошибки, поэтому для корректного возврата управления не забудьте при
необходимости предварительно удалить командой pop код ошиб­ки из стека.

Шлюз
прерывания

Шлюз
прерывания  отличается
только полем типа = 1110.

При
возникновении прерывания, которому соответствует шлюз прерывания,
микропроцессор выполняет те же действия, что и для шлю­за ловушки, но с одним важным
отличием:

при
передаче управления обработчику прерывания через шлюз прерывания,
микропроцессор сбрасыва­ет флаг прерывания в регистре eflags в 0, запрещая тем самым обработку аппа­ратных прерываний.
Этот факт имеет важное значение для программирования обработчиков аппаратных и
программных прерываний.

Читайте также:  В каких продуктах содержится суточная норма белка

Совет. Если у вас есть сомнение в том, какой из шлюзов –
прерывания или ловушки – использовать — применяйте шлюз пре­рывания.

Шлюз задачи

Шлюз
задачи  отличается

1)   поле типа = 1110

2)   поле indicator вместо селектора сегмента кода указывает  на дескриптор сегмента состояния задачи (TSS).

Осуществляется
переход на новую задачу, которая будет осуществлять обработку прерывания. Задача является частью механизма
многозадачности. Т.е. производится переключение задач. (Текущее состояние
прерываемой задачи аппаратно сохраняется в ее собственном TSS.)

Для каждой задачи
определяется сегмент состояния задачи TSS (Task Segment Status) со строго определенной структурой. В этом сегменте
есть поля для со­хранения всех регистров общего назначения, некоторых системных
регистров и другой информации. Всю совокупность этой информации называют контек­стом задачи. Этот сегмент
описывается, подобно другим сегментам, дескрипто­ром в таблице GDT или LDT.

Если с помощью некоторого
селектора обратить­ся к такому дескриптору, то микропроцессор осуществит
переключение на соответствующую задачу. Подобные переключения могут, в частности, осущест­вляться
операционной системой, поддерживающей многозадачность, в соответ­ствии с
некоторой дисциплиной разделения времени между задачами. Пере­ключение задач
может производиться обычными командами межсегментной передачи управления
либо по возникновению прерывания при переходе к об­работчику прерывания
через шлюз задачи.

РЕЗЮМЕ

Шлюзы ло­вушки и
прерывания с помощью полей
indicator и offset
определяют адрес, по которому находится точка входа в программу обработки
прерывания.

Шлюз задачи
предназначен для реализации принципиально иного перехода к обра­ботчику
прерываний — с использованием механизма переключения задач.

 Обратим внимание, разработчики современных ОС
редко используют шлюз задачи, т.к. переключение на другую задачу требует
существенно больших затрат времени, а полное сохранение всех рабочих регистров
часто не требуется. Таким образом, используются чаще всего шлюзы прерываний.

Источник

Таблицы дескрипторов прерываний ( IDT ) представляет собой структуру данных , используемой архитектуры x86 реализовать вектор прерывания таблицу. IDT используется процессором для определения правильного ответа на прерывания и исключения .

Подробности в описании ниже относятся конкретно к архитектуре x86 и архитектуре AMD64 . Другие архитектуры имеют аналогичные структуры данных, но могут вести себя по-другому.

Использование IDT запускается тремя типами событий: аппаратными прерываниями, программными прерываниями и исключениями процессора, которые вместе называются прерываниями . IDT состоит из 256 векторов прерываний, первые 32 из которых (0–31 или 0x00–0x1F) зарезервированы для исключений процессора.

Реальный режим

В процессоре 8086 таблица прерываний называется IVT (таблица векторов прерываний). IVT всегда находится в одном и том же месте в памяти, в диапазоне от 0x0000до 0x03ff, и состоит из 256 четырехбайтовых дальних указателей реального режима (256 × 4 = 1024 байта памяти).

Указатель реального режима определяется как 16-битный сегмент и 16-битное смещение в этом сегменте. Сегмент внутренне расширяется процессором до 20 бит, сдвигая его на 4 бита влево, тем самым ограничивая обработчики прерываний реального режима первым 1 мегабайтом памяти. Первые 32 вектора зарезервированы для внутренних исключений процессора, а аппаратные прерывания могут быть отображены на любой из векторов с помощью программируемого контроллера прерываний.

На 80286 и более поздних версиях размер и расположение IVT могут быть изменены таким же образом, как это делается с IDT в защищенном режиме (т. Е. С помощью инструкции LIDT), хотя это не меняет его формат. 80286 также представил область верхней памяти , которая увеличивает ограничение адреса в реальном режиме на 65 520 байт.

Обычно используется x86 прерываний реального режима является INT 10, то Video BIOS код для обработки примитивный экран функции рисования , такие как рисование пикселей и изменение разрешения экрана.

Защищенный режим

В защищенном режиме IDT представляет собой массив дескрипторов, последовательно сохраняемых в памяти и индексированных вектором прерывания.  Длина полностью заполненного IDT составляет 2 КБ в 32-битном защищенном режиме (256 записей по 8 байтов каждая) и 4  КБ в 64-битном защищенном режиме (256 записей по 16 байтов каждая). Нет необходимости использовать все возможные записи: достаточно заполнить IDT до самого высокого используемого вектора прерывания и соответственно установить часть длины IDT IDTR.

IDT защищенного режима может находиться где угодно в физической памяти. В процессоре есть специальный регистр ( IDTR) для хранения как физического базового адреса, так и длины IDT в байтах. Когда происходит прерывание, процессор умножает вектор прерывания на размер дескриптора и добавляет результат к базовому адресу IDT. Затем с помощью длины IDT проверяется, находится ли результирующий адрес памяти в таблице; если он слишком большой, создается исключение. Если все в порядке, загружается дескриптор, хранящийся в вычисленной ячейке памяти, и предпринимаются действия в соответствии с типом и содержимым дескриптора.

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

Векторы 0-31 зарезервированы Intel для процессоров генерируются исключений ( общая ошибки защиты , неисправность страницы и т.д.). В настоящее время процессоры Intel используют только векторы 0–20, а процессоры AMD используют векторы 0–19 и 29–30. Однако будущие процессоры могут создать несовместимость для сломанного программного обеспечения, которое использует эти векторы для других целей.

Аппаратные исключения

Все INT_NUM от 0x0 до 0x1F включительно зарезервированы для исключений; INT_NUM больше 0x1F используются для обработки прерываний. (Обратите внимание, что IBM PC не всегда подчинялся этому правилу, например, используя прерывание 5, чтобы указать, что была нажата клавиша Print Screen .)

INT_NUMКраткое описание PM
0x00 Деление на ноль
0x01Одношаговое прерывание (см. Флаг прерывания )
0x02 НМИ
0x03Точка останова (вызывается специальной 1-байтовой инструкцией 0xCC, используемой отладчиками)
0x04Переполнение
0x05Границы
0x06Неверный код операции
0x07Сопроцессор недоступен
0x08 Двойная ошибка
0x09Переполнение сегмента сопроцессора (только 386 или более ранняя версия)
0x0AНедействительный сегмент состояния задачи
0x0BСегмент отсутствует
0x0CОшибка стека
0x0D Ошибка общей защиты
0x0E Ошибка страницы
0x0F зарезервированный
0x10Математическая ошибка
0x11Проверка выравнивания
0x12Проверка машины
0x13 Исключение SIMD с плавающей запятой
0x14Исключение виртуализации
0x15Исключение защиты управления

Крючок

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

Ссылки

Общее

  • Руководство разработчика программного обеспечения для архитектур Intel 64 и IA-32, Том 3: Руководство по системному программированию

внешние ссылки

  • Руководство разработчика программного обеспечения для архитектур Intel 64 и IA-32, том 3A: Руководство по системному программированию, часть 1 (см. ГЛАВУ 5, ОБРАБОТКА ПРЕРЫВАНИЙ И ИСКЛЮЧЕНИЙ и ГЛАВУ 10, РАСШИРЕННЫЙ ПРОГРАММИРУЕМЫЙ КОНТРОЛЛЕР ПРЕРЫВАНИЯ)]
  • Таблица дескрипторов прерываний на OSDev.org

Источник

Автор: sergh
The RSDN Group
Опубликовано: 02.04.2006
Версия текста: 1.0

Any time at all, any time at all, any time at all,
All you’ve gotta do is call and I’ll be there.

Читайте также:  В какой еде содержится прогестерон

John Lennon

Конечно же, вы знаете, что такое прерывание (interrupt), слышали о таблице векторов прерываний (interrupt vector table) и вообще довольно неплохо представляете, как прерывания обрабатываются в реальном режиме 🙂

СОВЕТ

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

Но, тем не менее, пару общих слов сказать нужно, так как тема важная и непростая.

Общие слова

Программные прерывания (software generated interrupts) вызываются инструкциями int x, где x – любое число от 0 до 255 (int 3 несколько отличается от прочих, подробнее эта тема освещена в следующей главе). Это, в общем-то, почти и не прерывания вовсе, так как ничего непонятного в них нет 🙂 Фактически это просьба к процессору вызвать определённую подпрограмму, которая должна быть специальным образом зарегистрирована в системе, она называется обработчик прерывания (interrupt handler)

ПРИМЕЧАНИЕ

В защищённом режиме это не обязательно будет подпрограмма, есть ещё один вариант, он будет обсуждаться отдельно, в главе «Многозадачность».

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

  • Прерывания нумеруются, диапазон номеров от 0 до 255.
  • Обработчик прерывания это некоторая подпрограмма.
  • Где-то в памяти находится таблица, устанавливающая соотношение между номерами прерываний и их обработчиками.
  • В таблице в некотором формате хранится адрес обработчика прерывания.

Таблица дескрипторов прерываний

Аналог таблицы векторов прерываний, существующий в защищённом режиме, называется таблицей дескрипторов прерываний (Interrupt Descriptor Table, IDT).

ПРИМЕЧАНИЕ

Это термин из официального руководства Intel. В книге Дэвида Соломона и Марка Руссиновича «Внутреннее устройство Windows 2000» (Питер, Русская Редакция, 2001) упоминается IDT, но, почему-то, там она названа таблица диспетчеризации прерываний (interrupt dispatch table), причём это не ошибка русских переводчиков, в английском оригинале используется такое же название. На мой взгляд, следует придерживаться терминологии Intel.

По формату и способу инициализации IDT практически идентична GDT. Основные отличия:

  • Содержимое – в IDT находятся дескрипторы других типов. Первый будет рассмотрен ниже в этой главе, второй – в главе «Внешние прерывания», третий и последний – в главе «Многозадачность».
  • Регистр, определяющий размер и положение таблицы называется IDTR, команда для его загрузки – lidt.
  • В отличие от GDT, нулевой дескриптор IDT это обычный, полноценный дескриптор, к которому вполне возможно обращение.
  • Размер IDT не должен превышать 256 дескрипторов.

Таким образом, слегка модифицируя код из предыдущей главы, получаем пример инициализации IDT:

mov eax, 0
mov ax, ds
shl eax, 4
add eax, offset IDT

mov dword ptr offset idtr + 2, eax

lidt fword ptr idtr

IDT label byte
db …

db …

idt_len equ $ – IDT

idtr dw idt_len – 1
dd ?

ПРИМЕЧАНИЕ

Точно так же, как и в случае с lgdt, нормальные пользовательские приложения не имеют доступа к инструкции lidt. Этот факт вместе с возможностью поместить IDT в недоступную приложениям область памяти позволяет ОС контролировать обработку прерываний. Подробнее, как обычно, в главе про защиту.

Несложно заметить, что та же самая структура table_register подходит и для загрузки в IDTR.

Дескриптор шлюза ловушки

Дескриптор – не шлюза, а порядочная девушка!

Надпись маркером в лифте.

Как уже говорилось в предыдущей главе, дескриптор это структура, описывающая некую системную сущность. Очередным типом дескрипторов, с которым вам придется познакомиться, будет дескриптор шлюза ловушки (trap gate descriptor; в [Гук 1999] слово «gate» переводится как «вентиль», это стандартный перевод для схемотехники, программисты обычно говорят «шлюз»), он предназначен для IDT и имеет следующий формат:

ПоложениеНазваниеКраткое описание
Нулевой и первый байтыOffset (part 1)Младшие два байта 32-х битного поля Offset. Поле Offset содержит смещение обработчика прерывания.
Второй и третий байтыSegment SelectorСелектор сегмента, содержащего обработчик прерывания.
Четвёртый байт0, просто 0. Эта часть дескриптора не используется.
0-й – 3-й биты пятого байта??Для дескриптора шлюза ловушки значение должно быть равно 1111b. Подробности про это и следующее поле в разделе «[Лирическое отступление] Классификация дескрипторов».
4-й бит пятого байта??Устанавливайте в 0.
5-й – 7-й биты пятого байта??Пока неважно, устанавливайте в 100b.
Шестой и седьмой байтыOffset (part 2)Старшие два байта поля Offset.

Таблица 1. Формат дескриптора шлюза ловушки.

То же самое на картинке:

Рисунок 1. Формат дескриптора шлюза ловушки.

Основная информация, которую несёт в себе дескриптор шлюза ловушки, это:

  • Указание на то, что данный дескриптор действительно является дескриптором шлюза ловушки.
  • Адрес обработчика прерывания в формате <селектор>:<смещение>.

Поскольку формат практически прозрачен, остаётся только привести пример:

db 04h
db 03h
db 8
db 0
db 0 db 10001111b
db 02h
db 01h

Это дескриптор шлюза ловушки, селектор сегмента указывает на первый дескриптор GDT, смещение – 01020304h. В виде структуры дескриптор шлюза выглядит так:

gate_descriptor struct
offset_low dw 0
selector dw 0
zero db 0
type_and_permit db 0

offset_high dw 0
gate_descriptor ends

[Лирическое отступление] Классификация дескрипторов

Сегмента-кода-или-данных/системный

Первый признак классификации дескрипторов – значение 4-го бита пятого байта дескриптора. Полное название этого бита – флаг «descriptor type», краткое – флаг «S» (довольно странное сокращение, но везде применяется именно оно). В соответствии с этим признаком, дескрипторы делятся на два класса:

  • Если флаг установлен, то это дескриптор сегмента кода/данных. Они были более-менее рассмотрены в предыдущей главе
  • Если флаг сброшен – системный дескриптор (system descriptor). В том числе, к этому классу относится и дескриптор шлюза ловушки.

Принципиальное отличие системных дескрипторов от дескрипторов сегмента кода/данных в том, что селекторы первых не могут быть загружены в сегментные регистры и участвовать в формировании логического адреса <сегмент>:<смещение>. Даже если системный дескриптор описывает сегмент (вы познакомитесь с таким дескриптором в главе «Первая преамбула: одна задача»), для доступа к данным придётся использовать сегмент кода/данных (с соответствующим дескриптором и селектором), проецирующийся на нужную область памяти.

Системные дескрипторы имеют различный формат и единственное общее отличие их формата от формата дескрипторов сегмента кода/данных – младшие четыре бита пятого байта. Для обоих видов дескрипторов это поле называется «Type» и уточняет тип дескриптора, но при этом используются разные подходы:

  • Для дескрипторов сегмента кода/данных поле состоит из четырёх однобитовых флагов (два из них – флаги C/D и W/R – были рассмотрены в предыдущей главе).
  • Для системных дескрипторов это просто номер, задающий тип дескриптора. Как уже было сказано, для дескриптора шлюза ловушки значение должно быть равно 1111b.

Сегмента/шлюза

Второй признак классификации – характер сущности, описываемой дескриптором. Это может быть:

  • Сегмент. О том, что такое сегмент было подробно написано в предыдущей главе, соответствующий дескриптор называется дескриптором сегмента. К ним относятся дескрипторы сегмента кода/данных и несколько типов системных дескрипторов.
  • Шлюз. В данном случае это слово следует трактовать как «точка входа», то есть (несколько упрощая) адрес начала некоторой подпрограммы. Дескриптор называется дескриптором шлюза (gate descriptor), к ним относятся остальные системные дескрипторы, в частности дескриптор шлюза ловушки.
Читайте также:  Опытным путем докажите в каких пробирках содержаться растворы

Дескрипторы, описывающие похожие сущности, имеют близкие форматы. Так, любой дескриптор сегмента по формату похож на дескриптор сегмента кода/данных, а любой дескриптор шлюза – на дескриптор шлюза ловушки.

Всё вместе

– Каждому прерыванию по шлюзу!
– Ура!!!
– Каждому шлюзу по обработчику!
– Ура!!!
– Каждому обработчику по прерыванию!
[вопрос с места]
– А правда, что прерываний только 255 и их не хватит всем пятистам обработчикам?
– Нет!
– Ура!!!

Популистское выступление продолжается.
Докладчику пока удаётся не врать…

Объединим всё, уже известное про прерывания и добавим недостающие детали.

Вызов обработчика прерывания

Целью всего механизма обработки прерываний является своевременный вызов обработчика. В нашем случае (остальные случаи – в остальных главах, описывать всё сразу совершенно ни к чему) вызов происходит примерно так:

  1. По номеру прерывания в IDT отыскивается нужный дескриптор, из него извлекается селектор сегмента и смещение начала обработчика.
  2. Проверяется корректность селектора (указывает на дескриптор в пределах GDT, это дескриптор сегмента кода, и т.п.) и смещения (попадает в сегмент).
  3. В стеке сохраняются регистр EFLAGS и дальний указатель на команду, следующую за int x, то есть регистр CS и значение регистра EIP, увеличенное на размер команды int x. На каждый из регистров в стеке выделяется по 4 байта, рядом с CS находятся два «лишних» байта.
  4. CS и EIP загружаются новыми значениями.
  5. Начинается выполнение обработчика. Дескриптор шлюза ловушки не предназначен для обработки внешних (аппаратных) прерываний, поэтому аппаратные прерывания не маскируются автоматически при входе в обработчик.
  6. После своего завершения обработчик возвращает управление командой iretd. В отличии от iret, она предназначена для случая, когда в стеке сохранены 32-х разрядные EIP, CS и EFLAGS, а не 16-ти разрядные IP, CS и FLAGS (подробнее про 16-ти и 32-х разрядность см. в приложении).
  7. Возврат к исходной программе. В регистры EFLAGS, CS и EIP загружается значение из стека, при этом происходит обращение к GDT и проверка корректности устанавливаемого селектора и смещения.
ПРЕДУПРЕЖДЕНИЕ

Ещё раз обращаю ваше внимание: независимо от префиксов команд, разрядности сегмента стека, разрядности целевого и исходного сегмента кода и т.п. (всё это будет обсуждаться в приложении в разделе про разрядность), под EFLAGS, CS, EIP в стеке выделяется 12 байт, по 4 байта на каждый регистр.

Это связано с тем, что тип 1111b соответствует 32-х разрядному дескриптору шлюза ловушки. Существует полностью аналогичный 16-и разрядный, но он в курсе не рассматривается.

Упрощённая версия алгоритма в виде комикса:

Рисунок 2. Немного упрощенный алгоритм вызова обработчика прерывания.

Ключевые признаки, отделяющие «наш» случай от «не нашего»:

  • Происходит программное прерывание.
  • Соответствующая ячейка IDT содержит именно дескриптор шлюза ловушки.
  • Несколько условий, связанных с защитой. Если дескрипторы в GDT и IDT такие, как рекомендовано (то есть, в описанные части дескрипторов записано что-то разумное, для «пока неважных» частей взяты значения, приведённые в таблицах), все условия выполняются. Подробнее эта тема рассмотрена в главах, посвящённых защите.

Переключение режимов: инициализация IDT

Добавим к алгоритмам переключения ещё несколько шагов. Из реального режима в защищённый:

  1. Запретить маскируемые и немаскируемые прерывания.
  2. Инициализировать GDT и загрузить её адрес в GDTR.
  3. Инициализировать IDT и загрузить её адрес в IDTR.
  4. Установить флаг PE (младший бит регистра CR0).
  5. Выполнить дальний переход (jmp или call). Это нужно для перезагрузки регистра CS.
  6. Перезагрузить все сегментные регистры.
ПРЕДУПРЕЖДЕНИЕ

В данном случае нельзя использовать CS, оставшийся «в наследство» от реального режима, так как при возврате из обработчика прерывания сохранённое в стеке значение CS будет толковаться как селектор сегмента.

ПРИМЕЧАНИЕ

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

Обратное переключение:

  1. Сделать текущим сегментом кода какой-либо доступный для чтения сегмент с пределом FFFFh байт.
  2. Загрузить во все сегментные регистры селекторы дескрипторов доступных для записи сегментов данных с пределом FFFFh.
  3. Сбросить флаг PE
  4. Выполнить дальний переход (jmp или call).
  5. Перезагрузить сегментные регистры.
  6. Восстановить в IDTR значение для реального режима.
  7. Разрешить прерывания.

Сохранение значения IDTR

Для того, чтобы при переключении в реальный режим восстановить значение IDTR, при старте программы его необходимо сохранить. Для этого предназначена команда sidt.

Пример использования:

sidt fword ptr old_idtr

old_idtr table_register <>

Пример

Обработчик int 0

Практически минимальная программа, устанавливающая обработчик нулевого прерывания и вызывающая его командой int 0.

ПРИМЕЧАНИЕ

Программа написана на основе примера cs64kb.asm из предыдущей главы.

.model tiny
.code
.386p
org 100h

segment_descriptor struct
limit_low dw 0
base_low dw 0
base_high0 db 0
type_and_permit db 0
flags db 0
base_high1 db 0
segment_descriptor ends

gate_descriptor struct
offset_low dw 0
selector dw 0
zero db 0
type_and_permit db 0
offset_high dw 0
gate_descriptor ends

table_register struct
limit dw 0
base dd 0
table_register ends

start:

push cs
pop ds

push 0b800h
pop es

mov ax, cs
mov cs:rm_cs, ax

call cs_to_eax
mov cs_dsc.base_low, ax
shr eax, 16
mov cs_dsc.base_high0, al

sidt fword ptr old_idtr

call disable_interrupts

call initialize_gdt

call initialize_idt

call set_PE

db 0EAh
dw $ + 4
dw 8

int 0

call clear_PE

db 0EAh
dw $ + 4
rm_cs dw 0

lidt fword ptr old_idtr

call enable_interrupts

ret

int0_handler:

push eax
push ecx

mov eax, 0
mov ecx, 80 * 25

screen_loop:
inc byte ptr es:[eax]
inc eax
inc byte ptr es:[eax]
inc eax
loop screen_loop

pop ecx
pop eax
iretd

GDT label byte

segment_descriptor <>

cs_dsc segment_descriptor <0ffffh, 0, 0, 10011010b, 0, 0>

gdtr table_register <$ – GDT – 1, 0>

IDT label byte

gate_descriptor <int0_handler, 8, 0, 8Fh, 0>

idtr table_register <$ – IDT – 1, 0>

old_idtr table_register <>

initialize_idt:

call cs_to_eax
add eax, offset IDT

mov idtr.base, eax

lidt fword ptr idtr
ret

initialize_gdt:

call cs_to_eax
add eax, offset GDT

mov gdtr.base, eax

lgdt fword ptr gdtr
ret

disable_interrupts:
cli
in al, 70h
or al, 80h
out 70h, al
ret

enable_interrupts:
in al, 70h
and al, 7Fh
out 70h, al
sti
ret

set_PE:
mov eax, cr0
or al, 1
mov cr0, eax
ret

clear_PE:
mov eax, cr0
and al, 0FEh
mov cr0, eax
ret

cs_to_eax:
mov eax, 0
mov ax, cs
shl eax, 4
ret

end start

Задания

  • Перепишите программу так, чтобы вызывалось 29-е прерывание, но она продолжала бы работать.
  • Вызовите прерывание, номер которого выходит за границы IDT
  • Объявите IDT, содержащую более 256 дескрипторов. Есть ли в этом смысл?
  • Запишите в IDT нулевой дескриптор, дескриптор сегмента данных, сегмента кода, вызовите соответствующие прерывания.
  • Запишите в IDT дескриптор шлюза ловушки такой, что содержащийся в нём селектор: нулевой, ссылается за пределы GDT, ссылается на сегмент данных. Вызовите прерывание.
  • Запишите в IDT дескриптор шлюза ловушки такой, что: содержащееся в нём смещение выходит за пределы сегмента, не соответствует началу обработчика. Вызовите прерывание.
  • Замените в обработчике прерывания iretd на iret.
  • Посмотрите, что находится в IDTR в реальном режиме. Запишите туда что-нибудь другое.
  • Реализуйте в обработчике аналог 9-й функции стандартного DOS-прерывания int 21h (вывод на экран строки, завершённой символом «$»). Для простоты не заботьтесь об управляющих символах (табуляции, перевода строки и т.п.).
  • В обработчике выведите на экран значения EIP, CS и EFLAGS из стека. Проверьте, что это именно они.


Любой из материалов, опубликованных на этом сервере, не может быть воспроизведен в какой бы
то ни было форме и какими бы то ни было средствами без письменного разрешения владельцев авторских
прав.

Источник