Коротко: QEMU сильно мягчит ошибки (и по‑умолчанию отличается по CPU, графике, MTRR/PCI и поведению BIOS). Чтобы понять, почему ваш код валится именно на Celeron D, нужно последовательно сузить область ответственности и добавить надёжные средства ранней отладки. Ниже — практичный чек‑лист с приоритетными шагами и конкретными приёмами.
1) Организуйте надёжный вывод ранней отладки (serial)
- Сделайте вывод всех ранних сообщений на COM1 (UART 16550) — это самый надёжный способ поймать крах до графики/видео.
- Подключите USB–TTL адаптер к реальной машине и снимайте лог в терминале (minicom, screen).
- Пример: инициализация 0x3F8, baud 115200, запись байта через outb(0x3F8, c).
2) Минимизируйте и бинарно/поэтапно локализуйте проблему
- Соберите «минимальный» образ, который только инициализирует память, выключает прерывания и печатает строку. Если он работает — добавляйте блоки кода по одному (память, графика, multitasking, драйверы), чтобы увидеть, при добавлении чего ломается.
- Это быстрый способ обнаружить, какой функционал вызывает расхождение QEMU ↔ реальная плата.
3) Воспроизведите поведение QEMU как можно точнее
- Запускайте QEMU с тем же типом CPU: qemu-system-i386 -cpu pentium4 (или core2, pentium3 — подберите ближе к Celeron D). QEMU по умолчанию может эмулировать более современный CPU.
- Используйте -serial stdio и -monitor stdio, -S -s для отладки через gdb.
- Сравните CPUID, MSR и состояние регистров в QEMU и на реальном устройстве (выведите cpuid информация при загрузке).
4) Проверки, которые почти всегда спасают
- A20, GDT/IDT, CR0/CR4/CR3: убедитесь, что вы корректно включаете/выключаете нужные биты (WP, NE, PG и т.д.). QEMU может маскировать мелкие ошибки.
- Инициализируйте FPU/SSE: fninit/fnstcw/сброс CR0.EM и CR4.OSFXSR? BIOS может по‑разному оставлять состояние FPU/SSE — некоторые операции с SSE приведут к падению, если не инициализировать.
- Выключите прерывания до полной установки IDT/TSS. На реальном железе таймер и прочие прерывания могут прибыть раньше, чем вы готовы.
- Стек/выравнивание стекa: для SSE инструкции и для некоторых ABI нужен 16‑байтный выравненный стек при вызове функций (movaps/lddqu и т.п.) — QEMU иногда не проявляет проблему, а реальный CPU — да. Добавьте выравнивание или компиляторские опции/атрибуты.
- Инициализируйте/проверьте TSS и переключение стека при прерываниях, если вы используете отдельные стеки.
5) Графика / VESA / MMIO / MTRR
- QEMU VGA / VBE гораздо проще. Интегрированная Intel‑графика может предъявлять особые требования: нужно правильно получить линейный framebuffer через VESA или через PCI BAR MMIO и учесть кеширование (MTRR или PAT/WC).
- Если вы рисуете в LFB — убедитесь, что адрес действительный и что установлен правильный тип памяти (write‑combining), либо используйте простой VGA текст/planar режим для отладки графики.
- Проблемы с MTRR/WC часто проявляются как «коррупция» или зависания при интенсивной записи в фрейм‑буфер.
6) APIC/IRQ и мультипроцессор
- Проверьте, как настроен APIC: QEMU может предоставлять APIC иначе, чем на плате. Если у вас код работает с LAPIC, убедитесь, что правильно установлены MSR IA32_APIC_BASE и что вы корректно обрабатываете IRQ.
- Если у вас однопоточный код, временно выключите/не инициализируйте APIC и переключитесь на PIC, чтобы проверить влияние.
7) Сборка и оптимизации
- Скомпилируйте версию с -O0 и без агрессивных оптимизаций (-fno-omit-frame-pointer) и попробуйте. Часто баги проявляются только при оптимизациях (выравнивание, реорганизация стека).
- Попробуйте собрать без использования SSE (-mno-sse/-mno-sse2) или явно выровнять критические структуры ( __attribute__((aligned(16))) ).
- Добавьте «canaries»/проверки переполнения стека/heap, заполнение освобождённой памяти 0xAA/0x55, чтобы поймать коррумпцию.
8) Ловите исключения и регистры
- Установите обработчики для всех исключений (DF, GP, PF, UD, etc.), которые печатают регистры (EIP, CS, EFLAGS, CR2 и т.д.) на serial перед halt. Это даст точку входа для диагностики.
- Если у вас падает в графическом режиме, переведите обработчик исключений на COM1 ещё до настройки видеорежима.
9) Используйте внешние средства отладки
- Подключите аппаратный USB‑UART, используйте внешнюю машину с gdb + kgdb по serial если возможно.
- Попробуйте протестировать образ на других реальных машинах (старые Pentium/Atom/ноутбуки), чтобы понять, специфично ли это именно Celeron D.
10) Логика плохих мест, на которые обратить внимание в первую очередь
- Неправильная работа с видеопамятью (неправильный LFB, MTRR, кеширование).
- SSE/выравнивание стека или FPU не инициализирован.
- Прерывания приходят раньше готовности IDT/TSS.
- Неправильная обработка PCI/BIOS данных (BAR, MMIO), адреса за пределами доступной RAM.
- Использование инструкции, которую CPU не поддерживает (или поддерживает иначе) — проверьте CPUID и MSR.
11) Практические команды / примеры
- QEMU: qemu-system-i386 -cpu pentium4 -m 512M -serial stdio -S -s
(запустите gdb на порт 1234: target remote :1234)
- Serial capture: screen /dev/ttyUSB0 115200
Если хотите — пришлите:
- логи serial с реальной машины (до и после падения),
- вывод CPUID (в виде hex),
- маленький минимальный образ/код, который стабильно ломается на Celeron D, но работает в QEMU.
Могу помочь проанализировать конкретные логи/дампы и подсказать, где искать дальше.