Как загрузить загрузчик второго этапа с первого этапа?
Я написал простой загрузчик первого этапа, который отображает "Hello world", используя прерывания для BIOS. Теперь в качестве следующего очевидного шага, чтобы написать второй этап, но где должен существовать код для этого и как загрузить его с первого этапа?
Вот программа для первого этапа
[BITS 16] ;Tells the assembler that its a 16 bit code
[ORG 0x7C00] ;Origin, tell the assembler that where the code will
;be in memory after it is been loaded
MOV SI, HelloString ;Store string pointer to SI
CALL PrintString ;Call print string procedure
JMP $ ;Infinite loop, hang it here.
PrintCharacter: ;Procedure to print character on screen
;Assume that ASCII value is in register AL
MOV AH, 0x0E ;Tell BIOS that we need to print one charater on screen.
MOV BH, 0x00 ;Page no.
MOV BL, 0x07 ;Text attribute 0x07 is lightgrey font on black background
INT 0x10 ;Call video interrupt
RET ;Return to calling procedure
PrintString: ;Procedure to print string on screen
;Assume that string starting pointer is in register SI
next_character: ;Lable to fetch next character from string
MOV AL, [SI] ;Get a byte from string and store in AL register
INC SI ;Increment SI pointer
OR AL, AL ;Check if value in AL is zero (end of string)
JZ exit_function ;If end then return
CALL PrintCharacter ;Else print the character which is in AL register
JMP next_character ;Fetch next character from string
exit_function: ;End label
RET ;Return from procedure
;Data
HelloString db 'Hello World', 0 ;HelloWorld string ending with 0
TIMES 510 - ($ - $$) db 0 ;Fill the rest of sector with 0
DW 0xAA55 ;Add boot signature at the end of bootloader
3 ответа
На x86 вы бы сделали следующее (упрощенно):
- Пусть загрузчик загрузит n-й сектор диска / дискеты (где бы вы ни загружались) в память и выполнил его (т.е. загрузил сегмент / смещение и сделал
retf
). Лучшей альтернативой является поиск в файловой системе определенного имени файла (например, KERNEL.BIN), но вам необходимо знать тип файловой системы (например, FAT12, если вы тестируете с образа дискеты). - Затем ядро запускается в реальном режиме. Он устанавливает дескрипторы кода, GDT и т. Д., Активирует 32-битную адресацию (вы должны были слышать о "A20") и, наконец, входит в защищенный режим. Затем вам необходимо выполнить дальний переход к сегменту 32-битного кода (файл ядра должен быть связан таким образом, чтобы 32-битный код находился в абсолютной позиции, например, со смещением 512, сразу после 16-битного реального режима),
- 32-битная сборка ядра просто определяет
EXTERN _mykernel
(например) и вызывает этот символ. - Тогда вы можете начать писать свое ядро как функцию C
mykernel
,
Хорошо, это был краткий обзор того, что я сделал несколько лет назад (с большим количеством копий и вставок из Интернета;). Если это не поможет, вот несколько хороших веб-ресурсов по разработке ОС:
- http://www.brokenthorn.com/Resources/OSDevIndex.html
- http://wiki.osdev.org/Main_Page
- http://lowlevel.brainsware.org/wiki/index.php/Hauptseite (вики со многими разработчиками ОС для любителей, только на немецком языке...)
Надеюсь, что это помогает ^^
Пример минимального запуска NASM BIOS, который загружает этап 2 и переходит на него
use16
org 0x7C00
; You should do further initializations here
; like setup the stack and segment registers.
; Load stage 2 to memory.
mov ah, 0x02
; Number of sectors to read.
mov al, 1
; This may not be necessary as many BIOS set it up as an initial state.
mov dl, 0x80
; Cylinder number.
mov ch, 0
; Head number.
mov dh, 0
; Starting sector number. 2 because 1 was already loaded.
mov cl, 2
; Where to load to.
mov bx, stage2
int 0x13
jmp stage2
; Magic bytes.
times ((0x200 - 2) - ($ - $$)) db 0x00
dw 0xAA55
stage2:
; Print 'a'.
mov ax, 0x0E61
int 0x10
cli
hlt
; Pad image to multiple of 512 bytes.
times ((0x400) - ($ - $$)) db 0x00
Скомпилируйте и запустите:
nasm -f bin -o main.img main.asm
qemu-system-i386 main.img
Ожидаемый результат: a
выводится на экран, а затем программа останавливается.
Проверено на Ubuntu 14.04.
Пример Saner GAS с использованием сценария компоновщика и более правильной инициализации (регистры сегментов, стек) на моем GitHub.
Посмотрите на реализацию GRUB здесь (этап 1):
http://src.illumos.org/source/xref/illumos-gate/usr/src/grub/grub-0.97/stage1/stage1.S
Сначала заметил начальную точку 0x7c00 и конечную подпись 0xaa55 для этого первого сектора. Из разборки вы можете увидеть это:
349 copy_buffer: 350 movw ABS(stage2_segment), %es 351 352 /* 353 * We need to save %cx and %si because the startup code in 354 * stage2 uses them without initializing them. 355 */ 356 pusha 357 pushw %ds 358 359 movw $0x100, %cx 360 movw %bx, %ds 361 xorw %si, %si 362 xorw %di, %di 363 364 cld 365 366 rep 367 movsw 368 369 popw %ds 370 popa 371 372 /* boot stage2 */ 373 jmp *(stage2_address) 374 375 /* END OF MAIN LOOP */ 376
По сути, логика состоит в том, чтобы скопировать код этапа 2 в другую часть памяти, и после этого перейти непосредственно туда, и это "этап загрузки2". Другими словами, "boot stage1" эффективно запускается из BIOS после того, как он загрузил сектор в память, тогда как stage 2 - это то место, куда вы переходите - это может быть где угодно.