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

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

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

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

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

Вопросы:

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

person Joao da Silva    schedule 29.01.2009    source источник
comment
Также были бы очень желательны указатели на руководства Intel :) Я еще не нашел там ничего по поводу этих проблем.   -  person Joao da Silva    schedule 29.01.2009


Ответы (4)


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

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

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

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

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

person Nathan Fellman    schedule 29.01.2009
comment
Я как-то пропустил этот абзац :) Итак, теперь я знаю, что это ошибка в моем коде. Большое спасибо за указание на это! - person Joao da Silva; 29.01.2009

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

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

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

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

person QAZ    schedule 29.01.2009
comment
Привет, Стив, спасибо за ответ :) У меня не включен пейджинг, и у меня есть только 3 действительных записи в GDT (для кода и данных в кольце 0, и еще один для использования в реальном режиме). Обычно я нахожусь в защищенном режиме, но я возвращаюсь в реальный режим, чтобы использовать некоторые подпрограммы BIOS (в основном чтение с диска). Какие-нибудь подсказки? :) - person Joao da Silva; 29.01.2009
comment
Извините, я ничего не могу придумать, возможно, переключение обратно в реальный режим вызывает проблемы - person QAZ; 29.01.2009
comment
Ссылка на вашу ОС мертва, может у вас версия на GitHub? Составляю сборник образовательных операционок. - person Ciro Santilli 新疆再教育营六四事件ۍ 28.10.2015

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

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

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

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

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

person stalepretzel    schedule 25.01.2015

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

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

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

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

add $4, %esp
iret

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

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

Сравните вышеприведенное с norerupt.asm#L38"> делает именно то, что rel= а>.

Ядро 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.

person Ciro Santilli 新疆再教育营六四事件ۍ    schedule 28.10.2015