Нужно ли извлекать код ошибки, помещенный в стек с определенными исключениями, прежде чем вернуться из обработчика прерываний? - PullRequest
6 голосов
/ 29 января 2009

Я загрузил таблицу idt с 256 записями, все они указывают на похожие обработчики:

  • для исключений 8 и 10-14, нажмите номер исключения (эти исключения автоматически вводят код ошибки)
  • для остальных нажмите «фиктивный» код ошибки и номер исключения;
  • затем перейти к общему обработчику

Итак, когда входит общий обработчик, стек правильно выровнен и содержит номер исключения / прерывания, код ошибки (который может быть просто фиктивным), eflags, cs и eip.

Мой вопрос касается возврата из обработчика прерываний. Я использую iret для возврата после извлечения номера исключения и кода ошибки из стека, но это не работает для исключения № 8; если я оставлю код ошибки в стеке, он вернется нормально!

Вопросы:

  • Должен ли я оставить код ошибки в стеке для исключений, которые помещают туда код ошибки? Если да, то как iret определяет, должен ли он выдавать код ошибки или нет?
  • как только я включаю прерывания, я всегда получаю исключение 8 (двойной отказ), но затем все работает нормально (я разрабатываю хобби ОС). Это нормальное поведение или у меня где-то ошибка?

Ответы [ 4 ]

13 голосов
/ 29 января 2009

Если ЦП автоматически выдал код ошибки, обработчик должен выдвинуть его до iret. Инструкция iret не знает, откуда вы, если это ошибка, ловушка или внешнее прерывание. Он всегда делает то же самое и предполагает, что в стеке нет кода ошибки.

Цитирование из SDM (Руководство разработчика программного обеспечения), том 3, глава 5, раздел 5.13, под названием Код ошибки:

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

Справочник разработчика программного обеспечения IA-32 можно найти здесь : http://www.intel.com/products/processor/manuals/

Том 3, часть 1, глава 5, описывает обработку исключений и прерываний. Том 2, часть 1, содержит спецификацию для инструкции iret.

1 голос
/ 25 января 2015

У меня была похожая проблема с «двойными ошибками», как только я включил прерывания. Ну, они выглядели как двойные ошибки, но они действительно были прерываниями по таймеру!

Двойные ошибки - номер прерывания 8.

К сожалению, таймер сигналов конфигурации PIC по умолчанию прерывает как номер прерывания (DEFAULT_PIC_BASE + TIMER_OFFSET) = (8 + 0) = 8.

Маскировка всех моих прерываний PIC (пока я не был готов правильно настроить PIC) заставила замолчать эти двойные прерывания, похожие на таймеры.

(PIC требуют, чтобы ЦП подтвердил прерывания, прежде чем они произведут следующее. Поскольку ваш код не подтверждал первоначальное прерывание по таймеру, PIC больше не давал вам! Вот почему у вас есть только один, а не миллионный мог ожидать.)

1 голос
/ 29 января 2009

Некоторое время назад я написал маленькую ОС x86 . Взгляните на файл isr.asm в репозитории cvs.

Обратите внимание на то, как мы настроили обработчики, большинство из них помещают фиктивные двойные слова в стек, чтобы учесть несколько обработчиков, которые автоматически получают выдаваемый код ошибки. Затем, когда мы возвращаемся через iret, мы всегда можем взять 2 стека в стеке независимо от прерывания и выполнить add esp, 8 перед iret, чтобы хорошо очистить вещи.

Это должно ответить на ваш первый вопрос.

Что касается вашего второго вопроса: двойная ошибка при включении прерываний, ... хммм может быть проблемой с подкачкой, если вы не настроили ее правильно. Может быть и миллион других вещей:)

0 голосов

Нужно ли оставлять код ошибки в стеке для исключений, которые помещают туда код ошибки?

Как уже упоминалось, вы должны сделать либо:

pop %eax
/* Do something with %eax */
iret

Или, если вы хотите игнорировать код ошибки:

add $4, %esp
iret

Если вы этого не сделаете, iret будет интерпретировать код ошибки как новый CS, и вы, вероятно, получите ошибку общей защиты, как указано в: Почему iret из обработчика ошибок страницы генерирует прерывание 13 (общая ошибка защиты) и код ошибки 0x18?

Минимальный Работающий обработчик этой страницы , который я создал, чтобы проиллюстрировать это. Попробуйте закомментировать pop и посмотрите, как он взорвется.

Сравните вышеприведенное с Исключением ошибки деления , которое не выводит стек.

Обратите внимание, что если вы просто сделаете int $14, лишний байт не будет выдвинут: это происходит только с фактическим исключением.

Руководство Intel Том 3 Руководство по системному программированию - 325384-056RU Сентябрь 2015 г. Таблица 6-1. Столбец «Исключения и прерывания защищенного режима» «Код ошибки» содержит список прерываний, которые выдвигают код ошибки или нет.

38.9.2.2 «Коды ошибок страницы» объясняет, что означает ошибка.

Хороший способ справиться с этим - поместить в стек фиктивный код ошибки 0 для прерываний, которые этого не делают, чтобы сделать вещи единообразными. Учебник Джеймса Моллоя делает именно это .

Кажется, что ядро ​​Linux 4.2 делает нечто подобное. В arch / x86 / entry / entry64.S он моделирует прерывания с has_error_code:

trace_idtentry page_fault do_page_fault has_error_code=1

и затем использует его в том же файле, что и:

.ifeq \has_error_code
pushq $-1 /* ORIG_RAX: no syscall to restart */
.endif

, который делает толчок, когда has_error_code=0.

...