Семинар 03. Прерывания в защищенном режиме. Теневые регистры. Начало MSI. - chrislvt/OS GitHub Wiki

Прерывания в защищенном режиме

Прерывания в защищенном режиме

Вектор прерывания в реальном режиме используется для получения смещения к вектору прерывания в таблице векторов прерываний DOS'а (бред какой-то).
Обратите внимание что контроллеры прерывания адресуются и это хорошо видно в ЛР2 при сохранении старой и установке новой маски, причём отдельно в ведомом и ведущем контроллерах. То есть ведомый и ведущий контроллеры имеют разные порты, разные адреса. Заметьте, EOI мы посылали по адресу 20h, а в ЛР2 используем 21h ("Интересно").


То есть контроллеры адресуются, маски посылаются отдельно на каждый контроллер и на каждом контроллере отдельно устанавливаются. Команда EOI посылается на ведущий контроллер.


В защищенном режиме, в отличие от реального режима, для адресации прерываний используется системная таблица IDT - таблица дескрипторов прерываний
В процессоре есть 32-разрядный регистр IDTR, который содержит начальный адрес этой таблицы. Нулевой дескриптор таблицы IDT значащий (не null) - Divide error (деление на 0). Первые 32 дескриптора отведены под исключения. Дескриптор таблицы IDT имеет формат 8 байт.
Дескриптор IDT
image

Поля дескриптора должны наводить на определенные мысли. Во-первых, мы видим смещение, его прибавляют к начальному адресу сегмента, этот адрес находится в дескрипторе сегмента. И мы видим поле селектора в дескрипторе таблицы IDT, его мы можем использовать чтобы обратиться к дескриптору в таблице глобальных дескрипторов (GDT), то есть этот селектор даст нам информацию, к дескриптору какого сегмента нам нужно смещение (запутано объяснила). В сегментах кода находятся коды исключений и обработчиков прерываний.

Типы дескрипторов

  • 0 - не определено
  • 1 - свободный сегмент состояния задачи (TS - segment state) 286
  • 2 - LDT (local descriptor table) то есть дескриптор таблицы дескрипторов может описывать дескриптор LDT
  • 3 - занятый сегмент состояния задачи (TSS - task segment state) 286
  • 4 - шлюз вызова (CG - call gate) 286
  • 5 - шлюз задачи (TG - task gate) 286
  • 6 - шлюз прерываний (IG - interrupt gate) 286
  • 7 - шлюз ловушки (TG - trap gate) 286
  • 8 - не определено
  • 9 - свободный сегмент состояния задачи (TS) 386, 486, Pentium Pro и т.д
  • Ah - не определено
  • Bh - занятый сегмент состояния задачи (TSS) 386, 486, Pentium Pro и т.д
  • Ch - шлюз вызова (CG - call gate) 386, 486, Pentium Pro и т.д
  • Dh - не определено
  • Eh - шлюз прерываний (IG - interrupt gate) 386, 486, Pentium Pro и т.д
  • Fh - шлюз ловушки (TG - trap gate) 386, 486, Pentium Pro и т.д

*Отсылка к ЛР2* Если в вам в вашей программе надо так или иначе описать 32 дескриптора исключения, это будут trap gate, и 2 дескриптора аппаратных прерываний это будут interrupt gate.


Типы исключений

  • 0 - Divide error источник: DIV и IDIV
  • 1 - зарезервировано FOR INTEL USE ONLY
  • 2 - NMI (none maskable internal interrupt)
  • 3 - Breakpoint
  • 4 - Overflow
  • ...
  • 7 - Device not avaliable (no mask soprocessor) источник: floating point instruction
  • 8 - Double fault тип: abort (завершение, все прерывается); какая-то инструкция, которая может генерироваться исключением, немаскируемым прерыванием или прерыванием, приводит к "панике системы"
  • ...
  • 11 - Segment not present сегмент отсутствует в оперативной памяти
  • ...
  • 13 - General protection
  • 14 - Page fault возникает, когда процессор обращается к команде, а она находится в странице, которая отсутствует в оперативной памяти
  • ...
  • 20 - 31 INTEL RESERVED, do not use
  • 32 - 255 USER DEFINED тип: interrut источник: exterlal interrupt (системные вызовы) or int (команда)

Если первые 32 исключения зарезервированы и определены документацией, а у нас базовый вектор 8-го прерывания равен 8. 8 + 0 = 8, вектор прерывания мы хотим использовать как смещение в IDT, куда мы попадем? В double fault, значит так нельзя.
Для того чтобы использовать вектор прерывания как смещение к дескрипторам аппаратных прерываний, нам надо перепрограммировать контроллер прерываний на другой базовый вектор. Благо PIC позволяет это делать.

Перепрограммирование контроллеров


*Отсылка к ЛР2*
Наша программа это ОС, которую мы устанавливаем на голое железо, то есть у нас есть только аппаратная поддержка, которой мы можем пользоваться. Понятно что машинные команды нам даны по определению. У нас есть только 2 прерывания (в рамках ЛР), фактически мы обрабатываем только IRQ0 и IRQ1. Поэтому все прерывания кроме прерываний, приходящих на IRQ0 и IRQ1, мы не обрабатываем, поэтому нам надо игнорировать все кроме IRQ0 и IRQ1. Нам надо установить соответствующие маски. Для ведомого контроллера мы запрещаем все прерывания, а на ведущем - все кроме IRQ0 и IRQ1.
Последовательность действий:

  • Сохранить старые маски прерываний
  • Установить новые маски
  • Установить новый базовый вектор прерываний для ведущего контроллера в 20h
  • При возвращении в реальный режим восстановить старые маски
  • Восстановить старый базовый вектор прерываний в 8h

Сохранение масок прерываний ведущего и ведомого контроллеров

in al, 21h  
mov MaskMaster, al  
in al, 0A1h  
mov MaskSlave, al  

Установка новых масок

mov al, 0FCh  
out 21h, al  
mov al, 0FFh  
out 0A1h, al  

Восстановление старых масок

mov al, MaskMaster  
out 21h, al  
mov al, MaskSlave  
out 0A1h, al  

Макрос для установки другого базового вектора прерываний

set_interrupt_base MACRO base: req
    mov al, 11h
    out 20h, al
    mov al, base
    out 21h, al
    mov al, 4
    out 21h, al
    mov al, 1
    out 21h, al
ENDM
; Вызов set_interrupt_base 20h или 8h

Теневые регистры

Изучая, регистры режима long и все рассуждения, связанные с переводом компьютера из защищенного режима и режим long, Рязанова сделала вывод, что они (INTEL) часть с сегментацией не трогали, то есть таблица глобальных дескрипторов адресуется, работает, но сегментация в long'е отсутствует, они ее игнорируют. Они в начальный адрес записывают все нули, а в лимит мб все F (она не уверена, надо почитать) и используют эти дескрипторы для установки определенных флагов всего-навсего, но они имеют место быть. Соответственно теневые регистры там тоже остались, то есть вот эту часть процессора они почему-то не трогают. Наверное это непросто - взять и переделать процессор целиком. При этом они вводят так называемые архитектурно зависимые регистры, которые располагаются в оперативной памяти. Она думает, что это восходит корнями к IBM360, где такие вещи использовались.
Теневые регистры находятся в процессоре. С каждым сегментным регистром сопоставлен теневой регистр. Кстати, регистры FS и GS являются особыми, у них и в режиме long особая роль.
Можно предположить, что размер теневого регистра равен размеру дескриптора. С той же степенью вероятности можно предположить, что он меньше на 2 байта (байты атрибутов). Но абсолютно уверенно можно сказать, что в теневом регистре находится начальный адрес сегмента и, вероятнее всего, там находится лимит, он необходим в сегментации для проверки выхода за сегмент. Почему так сделано? В управлении памятью сегментами или, как просто говорят, при сегментации адреса, необходимо выполнять преобразование сегмент-смещение, надо понимать сто это другое преобразование, не такое как в реальном режиме, там адресация называется сегмент:смещение, для защищенного режима лучше использовать "сегментация".
Мы понимаем, что GDT находится в оперативной памяти. Для формирования адреса нам нужно обратиться к дескриптору сегмента и считать из него начальный адрес сегмента, после этого к начальному адресу сегмента прибавляется смещение. То есть сначала идет обращение к памяти, это затратно, называется цикл обращения к памяти.

Цикл обращения к памяти (на пяльцах)

  • На шину адреса должен быть выставлен адрес байта памяти
  • На шину управления должен быть выставлен сигнал read/write (два управляющих сигнала, две отдельные линии).
  • Если передан управляющий сигнал read, контроллер памяти должен выставить на шину памяти считываемые данные, которые по шине данных поступают в регистры процессора.
  • Если передан сигнал write, процессор выставляет на шину данных данные, которые должны быть записаны в память и контроллер памяти считывает с шины памяти эти данные и записывает их в указанный адрес

Этот цикл - затратное действие, связанное с несколькими тактовыми импульсами. Как часто надо выполнять эти действия? Для того, чтобы считать из памяти что-то, надо считать базовый адрес, преобразовать, добавить смещение, получить линейный адрес и по нему обратиться в память. Команда считана и, например, надо записать что-то в память, значит опять надо сформировать адрес, то есть сделать точно такое же преобразование и только тогда можно что-то записать в память. То есть идет постоянное обращение в память за базовым адресом - трата времени. На каждой команде, а то и несколько раз. То есть вот эта формула очень сжато характеризует те накладные расходы, которые выполняются в системе. Поэтому информация из дескриптора сегмента записана в теневой регистр непосредственно в процессоре, оттуда берется базовый адрес, прям из теневого регистра. Не происходит обращение к памяти. Таким образом резко сокращается время формирования адреса.
Самое смешное, что они вот сделали эти вот навороты, она так предполагает, что они думали, что сегментация будет основной, но "далеко смотрели" и включили в процессор регистр CR3 - регистр начального адреса каталога таблиц страниц. И в общем-то сейчас сегментация не используется, у нее очень много проблем (мы рассмотрели их на лекции по управлению памятью). Используется страничное преобразование и в защищенном режиме страничное преобразование используется с моделью памяти flat. В long'е только страничное преобразование, там сегментация игнорируется. НО мы же говорим с вами о защищенном режиме и, соответственно, такой же теневой регистр есть и у LDTR, и у task register.

MSI - message signaled interrupts

Уже в компьютерах с шиной PCI express прерывания передаются как MSI, то есть для PCI express MSI - обязательная способность устройств. Для просто PCI шины, то MSI - опциональная возможность. MSI позволяет функциям устройства запрашивать сервис путем посылки записи в память как транзакцию MSI. Транзакция - неделимое действие, прервать ее выполнение нельзя, если транзакция прерывается, то выполняется откат к начальному состоянию до транзакции. Так как MSI генерируется в форме записи в память, то поддерживаются все условия транзакции

Условия транзакции

  • Retry - повторная передача
  • Master-abort - прерывание ведущего
  • Targert-abort - прерывание цели
  • Normal - нормальное завершение

Устройства PCI, которые поддерживают MSI, должны также поддерживать механизм pinIRQ (pin - штырь, вот эти IRQ это pin'ы) для обеспечения обратной совместимости. В системах, которые поддерживают MSI, драйвер шины способен инициализировать адрес сообщения и данные сообщения для функции устройства.
MSI

Обратите внимание, DUAL PIC - каскадное соединение PIC'ов. На смену пришел APIC - расширенный контроллер прерываний с большим количеством входов, но он поддерживал функции PIC'а. Эмулируется DUAL PIC, но если у вас шина PCI express, то прерывания передаются как MSI. PIC присутствует и его можно отключить, в магистратуре этот PIC и отключают. Мы с вами работаем через PIC. Функция внешнего устройства с поддержкой MSI указывает на поддержку MSI путем реализации структуры возможностей в своем списке возможностей PCI (ее "кривой" перевод), то есть существует соответствующая структура, с ее помощью функция устройства может сообщить шине, что она поддерживает MSI и будет передавать прерывания как MSI. Данная информация - анонс.