Как создать минимальный загрузочный сектор BIOS hello world с GCC, который работает с USB-накопителя на реальном оборудовании?

Мне удалось создать минимальный загрузочный сектор, который работает с QEMU 2.0.0 Ubuntu 14.04:

.code16
.global _start
_start:
    cli
    mov $msg, %si
    mov $0x0e, %ah
loop:
    lodsb
    or %al, %al
    jz halt
    int $0x10
    jmp loop
halt:
    hlt
msg:
    .asciz "hello world"
.org 510
.word 0xaa55

Составлено с:

as -o main.o main.S
ld --oformat binary -o main.img -Ttext 0x7C00 main.o

Пример доступен в этом репо: https://github.com/cirosantilli/x86-bare-metal-examples/tree/2b79ac21df801fbf4619d009411be6b9cd10e6e0/no-ld-script

После:

qemu -hda main.img

это показывает hello world на экране эмулятора, как и ожидалось.

Но если я попытаюсь записать на USB:

sudo dd if=main.img of=/dev/sdb

затем подключите USB к ThinkPad T400 или T430, нажмите F12 и выберите USB:

  • некоторые загрузочные сообщения появляются быстро
  • затем экран гаснет, а в верхней части находится только курсор подчеркивания

Я также протестировал тот же USB с образом Ubuntu 14.04, и он загрузился нормально, поэтому USB работает.

Как я должен изменить этот пример, чтобы он загружался на оборудовании и отображал сообщение "Hello World"?

В чем разница между образом Ubuntu и тем, который я создал?

Где это задокументировано?

Я загрузил вывод sudo dmidecode на T400: https://gist.github.com/cirosantilli/d47d35bacc9be588009f

1 ответ

Решение

Как уже упоминалось @Jester, мне пришлось обнулить DS с:

@@ -4,2 +4,4 @@ _start:
     cli
+    xor %ax, %ax
+    mov %ax, %ds
     mov $msg, %si

Обратите внимание, что это невозможно mov немедленно к ds: мы должны пройти через ax: 8086- почему мы не можем переместить немедленные данные в сегментный регистр?

Таким образом, корень проблемы был в разнице между начальным состоянием QEMU и реальным оборудованием.

Теперь я добавляю следующий 16-битный код инициализации ко всем моим загрузчикам, чтобы гарантировать более чистое начальное состояние. Не все из них являются обязательными, как упомянуто Майклом Петчем в комментариях.

 .code16
cli
/* This sets %cs to 0. TODO Is that really needed? */
ljmp $0, $1f
1:
xor %ax, %ax
/* We must zero %ds for any data access. */
mov %ax, %ds
/* The other segments are not mandatory. TODO source */
mov %ax, %es
mov %ax, %fs
mov %ax, %gs
/*
TODO What to move into BP and SP? https://stackru.com/questions/10598802/which-value-should-be-used-for-sp-for-booting-process
Setting BP does not seem mandatory for BIOS.
*/
mov %ax, %bp
/* Automatically disables interrupts until the end of the next instruction. */
mov %ax, %ss
/* We should set SP because BIOS calls may depend on that. TODO confirm. */
mov %bp, %sp

Я также нашел этот тесно связанный вопрос: Ядро C - Хорошо работает на ВМ, но не на реальном компьютере?

Руководство по системному программированию Intel Руководство пользователя том 3 - 325384-056RU сентябрь 2015 г. 9.10.2 "Листинг STARTUP.ASM" содержит большой пример инициализации.

Другие вопросы по тегам