Сейчас мы покажем, как управляющий блок процессора обрабатывает прерывания и исключения. Мы будем предполагать, что ядро уже инициализировано, и поэтому процессор работает в защищенном режиме.
После выполнения очередной инструкции пара регистров cs и eip содержит логический адрес инструкции, которая должна быть выполнена следующей. Перед тем как перейти к ней, управляющий блок проверяет, не возникло ли прерывание или исключение, пока он выполнял предыдущую. Если возникло, управляющий блок процессора выполняет следующие действия:
1. Определяет вектор (0<255), ассоциированный с этим прерыванием или исключением.
2. Читает всю запись таблицы дескрипторов прерываний, на которую указывает регистр idtr (далее мы предполагаем, что запись содержит дескриптор шлюза прерывания или ловушки).
3. Получает базовый адрес таблицы GDT из регистра gdtr и читает в ней дескриптор сегмента, идентифицируемый селектором в записи таблицы дескрипторов прерываний. Этот дескриптор содержит базовый адрес сегмента, содержащего обработчик прерывания или исключения.
4. Убеждается, что прерывание было возбуждено источником, имеющим на это право. Во-первых, управляющий блок сравнивает текущий уровень привилегий (CPL, Current Privilege Level), который хранится в двух младших битах регистра cs, с уровнем привилегий дескриптора (DPL, Descriptor Privilege Level), дескриптора сегмента, прочитанного из таблицы GDT. Затем управляющий блок возбуждает исключение "Общий сбой защиты", если текущий уровень привилегий меньше уровня привилегий дескриптора, потому что обработчик прерываний не может быть менее привилегированным, чем программа, вызвавшая прерывание. Для программных исключений управляющий блок производит дополнительную проверку: сравнивает текущий уровень привилегий с уровнем привилегий дескриптора шлюза из таблицы дескрипторов прерываний и возбуждает исключение "Общий сбой защиты", если уровень привилегий дескриптора меньше текущего уровня привилегий. Эта последняя проверка позволяет предотвратить доступ со стороны пользовательских приложений к некоторым конкретным шлюзам ловушек или прерываний.
5. Проверяет, имеет ли место изменение уровня привилегий, т. е., отличается ли текущий уровень привилегий от уровня привилегий выбранного дескриптора сегмента. Если это так, управляющий блок должен воспользоваться стеком, ассоциированным с новым уровнем привилегий. Чтобы сделать это, управляющий блок выполняет следующие действия:
• читает содержимое регистра tr, чтобы получить доступ к TSS-сегменту текущего процесса;
• загружает в регистры ss и esp соответствующие значения сегмента стека и указателя стека, ассоциированные с новым уровнем привилегий. Эти значения хранятся в сегменте TSS.
• в новом стеке сохраняет предыдущие значения регистров ss и esp, которые определяют логический адрес стека, ассоциированного со старым уровнем привилегий.
6. Если возникла ошибка, управляющий блок загружает в регистры cs и eip логический адрес инструкции, вызвавшей исключение, чтобы была возможность выполнить ее снова.
Иными словами, инструкция, выполняемая управляющим блоком после аппаратной обработки сигнала прерывания, является первой инструкцией выбранного обработчика.
Этой цели служит инструкция iret, которая заставляет управляющее устройство выполнить следующие действия:
1. Загрузить в регистры cs, eip и efiags значения, сохраненные в стеке. Если код аппаратной ошибки был помещен в стек выше содержимого регистра eip, он должен быть извлечен из стека до выполнения инструкции iret.
2. Проверить, равен ли текущий уровень привилегий обработчика значению, хранящемуся в двух младших разрядах регистра cs (это будет означать, что прерванный процесс выполнялся на том же уровне привилегий, что и обработчик). Если это так, инструкция iret завершает выполнение; в противном случае происходит переход на следующий шаг.
3. Загрузить в регистры ss и esp значения, сохраненные в стеке, и вернуться к стеку, ассоциированному с прежним уровнем привилегий.
4. Изучить содержимое сегментных регистров ds, es, fs и gs. Если какой-либо из них содержит селектор, ссылающийся на дескриптор сегмента, уровень привилегий которого ниже текущего уровня привилегий, необходимо очистить этот регистр. Управляющий блок должен сделать это, чтобы запретить программам пользовательского режима, которые работают с текущим уровнем привилегий 3, использовать сегментные регистры, которыми до этого пользовались процедуры ядра (с уровнем привилегий дескриптора, равным 0). Если эти регистры не очистить, злонамеренные программы режима пользователя могут эксплуатировать их с целью получения доступа к адресному пространству ядра.