Как создать минимальный загрузочный сектор 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" содержит большой пример инициализации.