Как загрузить загрузчик второго этапа с первого этапа? - PullRequest
9 голосов
/ 14 января 2010

Я написал простой загрузчик первого этапа, который отображает «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 ]

6 голосов
/ 16 января 2010

На x86 вы должны сделать следующее (упрощенно):

  • Пусть загрузчик загрузит в память n-й сектор диска / дискеты (откуда бы вы ни загружались) и запустил его (т.е. загрузите сегмент / смещение и выполните retf). Лучшей альтернативой является поиск в файловой системе определенного имени файла (например, KERNEL.BIN), но вам необходимо знать тип файловой системы (например, FAT12, если вы тестируете с образа дискеты).
  • Затем ядро ​​запускается в реальном режиме. Он устанавливает дескрипторы кода, GDT и т. Д., Активирует 32-битную адресацию (вы должны были слышать о «A20») и, наконец, входит в защищенный режим. Затем вам необходимо выполнить дальний переход к сегменту 32-битного кода (файл ядра должен быть связан так, чтобы 32-битный код находился в абсолютной позиции, например, со смещением 512, сразу после 16-битного реального режима) .
  • 32-битная сборка ядра просто определяет EXTERN _mykernel (например) и вызывает этот символ.
  • Затем вы можете начать писать свое ядро ​​как функцию C mykernel.

Хорошо, это был краткий обзор того, что я сделал несколько лет назад (с большим количеством копий и вставок из Интернета;). Если это не поможет, вот несколько хороших веб-ресурсов по разработке ОС:

Надеюсь, это поможет ^^

3 голосов

Пример минимального запуска 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 .

1 голос
/ 18 февраля 2012

Посмотрите на реализацию 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 после того, как он загрузил сектор в память, тогда как stage2 - это то место, куда вы переходите - это может быть где угодно.

...