Как программно отключить немаскируемые прерывания?
Я читал, что для временного отключения подкачки в соответствии с руководством по системному программированию Intel (том 3, глава 9.9) я должен отключить прерывания, прежде чем делать что-либо еще. Я могу легко отключить маскируемые прерывания с помощью cli, но все руководство говорит об отключении NMI:
Прерывания NMI могут быть отключены с помощью внешней схемы (программное обеспечение должно гарантировать, что во время операции переключения режимов не генерируются исключения или прерывания).
Я нашел код, похожий на код C для отключения NMI на этой странице OSDEV, но я не совсем понимаю, что он должен означать
void NMI_enable() {
outb(0x70, inb(0x70) & 0x7F);
}
void NMI_disable() {
outb(0x70, inb(0x70) | 0x80);
}
кажется, что этот код не имеет контекста и не имеет смысла, не зная, что делают функции outb и inb.
2 ответа
ЦП имеет вывод немаскируемого прерывания (NMI) (или аппаратный эквивалент), который используется для запуска NMI. Существует внешняя схема (или аппаратный эквивалент) для предотвращения попадания NMI в CPU. Начиная с 80286 механизм использовался через порты ввода-вывода, связанные с контроллером CMOS / Realtime Clock (RTC). Этот же механизм до сих пор имитируется в аппаратном обеспечении сегодня.
Порты CMOS/RTC: 0x70 и 0x71. Порт 0x70 используется для выбора адреса CMOS/RTC для чтения или записи. Верхние 2 бита адреса CMOS/RTC не являются частью реального адреса. Самый верхний бит был переопределен как тумблер NMI. Если вы записываете байт в порт 0x70, где установлен бит 7 (старший значащий бит), NMI отключается. Если вы записываете значение, где бит 7 сброшен, то NMI включаются.
inb
а также outb
функции обертки C вокруг низкого уровня IN
(байт) и OUT
(байт) инструкции. Эти инструкции читают и записывают в пространство порта ввода-вывода. Этот код C из NMI_enable
:
outb(0x70, inb(0x70) & 0x7F);
Является эквивалентом:
uint8_t curbyte = inb(0x70); /* Read current port 0x70 state */
outb(0x70, curbyte & 0x7F); /* Update current state by clearing NMI bit */
/* and write new value back to port 0x70 */
0x7f - это битовая комбинация 01111111. ANDing 01111111 с текущим байтом очищает самый верхний бит (включая NMI).
Этот код C из NMI_disable
:
outb(0x70, inb(0x70) | 0x80);
Является эквивалентом:
uint8_t curbyte = inb(0x70); /* Read current port 0x70 state */
outb(0x70, curbyte | 0x80); /* Update current state by setting NMI bit */
/* and write new value back to port 0x70 */
0x80 - это битовая комбинация 10000000. ORing 10000000 с текущим байтом устанавливает самый старший бит (отключение NMI).
"с внешней схемой" означает, что на плате имеются выводы перед выводами NMI чипа процессора, и если эти вентили отключены (замкнуты), сигналы прерывания не достигнут выводов NMI чипа процессора.
outb
вызовы, вероятно, активируют / деактивируют эти ворота.
NMI означает немаскируемый, и это означает, что вы не можете отключить их только с помощью программного обеспечения.
Я читал, что для временного отключения подкачки в соответствии с руководством по системному программированию Intel (том 3, глава 9.9) я должен отключить прерывания, прежде чем делать что-либо еще.
Интел ошибается.
NMI обычно указывают на критические аппаратные сбои (особенно если ваше программное обеспечение не вызвало их преднамеренно, и особенно если у вас не включено "исключение проверки машины"). Игнорирование критических отказов оборудования (путем маскировки NMI) приводит к крайне неопределенному поведению; что плохо.
В идеале вам нужно гарантированное поведение. Самый простой способ сделать это - гарантировать, что ЦП выполнит тройной сбой (и перезагрузит компьютер), если есть NMI; установив предел IDT равным нулю. Это прекрасно во время загрузки (когда нет данных конечного пользователя, которые могут быть потеряны).
"Менее простой" способ - использовать временную IDT. Для отключения разбиения на страницы вы можете иметь IDT с привязкой к идентичности (и GDT, стек и т. Д.), Чтобы обработчик NMI мог быть запущен независимо от того, происходит ли NMI при включении или выключении подкачки. Подобные приемы можно использовать при переключении режима ЦП (основываясь на том факте, что размер записи IVT/IDT изменяется, что приводит к тому, что запись IVT/IDT для NMI находится по разным адресам в зависимости от "режима ЦП во время NMI").
Конечно, здравомыслящие люди не отключают временно подкачку (или переключают режимы ЦП) после загрузки; так что нет причин желать чего-то большего, чем "гарантия тройной ошибки на NMI" (во время загрузки).
В принципе, с точки зрения разработчика ОС, вы можете смело считать, что NMI нельзя отключить (даже если вы действительно нашли способ отключить его). Если NMI происходит на этапе начальной загрузки ОС, это означает, что произошло что-то серьезное (обычно оборудование, питание), машина не работает, поэтому немедленно остановите свою ОС, так как она в любом случае работать не будет.