Несколько лет назад в блоге появился пост с дизассемблированием микрокода 8086 — тогда Кен Ширриф прислал снимок микрокода 80386, но он казался безнадёжно большим: 94 720 бит против 10 752 у 8086. Никто не знал, с какой стороны подойти. Внутренности 80386 были чёрным ящиком.
Всё изменилось, когда на Discord подтянулись GloriousCow и Smartest Blob. Они захотели получить чёткие снимки кристалла 80386 и извлечь из них микрокод. Первая часть уже была сделана, а вот превратить картинку в бинарную последовательность и потом осмысленно дизассемблировать — задача казалась нерешаемой. Но ребята применили связку из обработки изображений, AI и ручной автоматизации — через несколько дней бинарник был извлечён и перепроверен.
Дальше пошла расшифровка. Искали закономерности, выстраивали μ-операции по одной оси, а биты — по другой. Помог блок неиспользуемых μ-опов в конце — так определили порядок чтения. Потом разбили биты на поля. Автор знал из 8086, что два поля должны быть регистрами-источником и приёмником. 80386 мог выполнять ALU-операцию за два такта, значит, было поле для второго входа ALU — загружать оба операнда на первом такте, результат на втором. Нашли повторяющийся паттерн — оказалось, он обозначает конец инструкции.
Кен Ширриф помог восстановить соединения на кристалле: проследил линии и логические блоки. Постепенно картина прояснялась: каждый новый фрагмент объяснял другие. Параллельно разбирали декодер инструкций (состоящий из нескольких маленьких PLA) и PLA теста защиты. В итоге удалось привязать инструкции 386 к фрагментам микрокода.
Микрокод 80386 содержит 215 точек входа от декодирующего ROM — огромный скачок по сравнению с 60 у 8086. Часть — новые инструкции, часть — варианты одной и той же инструкции в зависимости от режима (реальный или защищённый), типа операндов (регистры или память) и наличия префикса REP. Удивительно, но все инструкции обрабатываются микрокодом — никаких исключений, в отличие от 8086 и современных CPU.
В дизассемблировании обнаружился потенциальный баг: при 4-байтовом доступе к портам ввода-вывода микрокод проверяет только первые три адреса в битовой карте разрешений (IO permission bitmap). Если процесс с ограниченным доступом обратится на границе разрешённой области, последний байт может ошибочно пройти — злоумышленник получит доступ к неожиданному аппаратному регистру. Баг скрывался больше 40 лет, хотя микрокод, вероятно, не из самой ранней версии (в декодере нет следов XBTS/IBTS). Возможно, ошибка проявляется не во всех ревизиях CPU, но это редкий случай, когда уязвимость в массовом железе оставалась незамеченной так долго.
Дизассемблирование выложено в репозиторий на GitHub (x86 microcode repository). Подробности — в файлах parts.txt и microcode_10.txt.