Старый загрузчик BIOS для загрузки кода реального режима на втором этапе - PullRequest
0 голосов
/ 27 февраля 2019

Я работаю над написанием собственной операционной системы.Пока мой код превышает 512 байт, что слишком велико для того, чтобы поместиться в простой загрузочный сектор.

Я понимаю, что теперь мне нужно написать загрузчик, который читает произвольный код, который может быть больше или меньше, чемодин 512-байтовый сектор.

Загрузчик должен:

  • функционировать как загрузочная запись с подписью диска 0xaa55.
  • прочитать второй этап (тесткод) начинаться с LBA 1 (LBA 0 - загрузочный сектор) произвольной длины, начиная с адреса памяти 0x7E00.
  • Передать ему управление с помощью FAR JMP на 0x0000: 0x7E00.
  • Может использоваться какобраз дискеты 1,44 МБ для использования в эмуляторах, таких как QEMU, BOCHS, VirtualBox и т. д.
  • Может быть передан и использован на USB-накопителе для тестирования на реальном оборудовании с BIOS, настроенным на загрузку USB с помощью дисковода гибких дисков (FDD) эмуляция. Примечание : Некоторые загрузчики не работают должным образом при установке на USB-накопители.
  • Передайте загрузочный диск на второй этап в DL.
  • Обнулите все регистры сегментов и установите для SS: SP значение 0x0000: 0x7C00 (растет из-под загрузчика).

Это также послужит хорошей отправной точкой для вопросов о переполнении стека, связанных с разработкой ОС.Программисты часто пытаются создать минимальный, полный и проверяемый пример .Обычный шаблон / шаблон позволил бы другим пользователям Stack Overflow, желающим помочь протестировать код с ограниченным количеством суеты.

Как мне поступить при создании такого многоразового загрузчика?

Ответы [ 2 ]

0 голосов
/ 26 июля 2019

Я изменил свой загрузчик загрузочного сектора, чтобы добавить новый протокол.Он устанавливает значение es = ds = ss = 0 и загружает весь файл загрузки по адресу 07E00h, переходя к этому в 0000h: 7E00h.Однако sp остается направленным чуть ниже 7C00h.

И есть большая разница с требованиями в вопросе: этот загрузчик использует файловую систему (FAT12 или FAT16) для загрузки следующего этапа.Он загружается из файла с именем KERNEL7E.BIN, если найден.Имя файла, как и весь протокол загрузки, можно изменить, отредактировав исходный файл или передавая определения в командной строке NASM.

Ограничение из-за размера кода состоит в том, что выводятся только односимвольные сообщения об ошибкахпри возникновении ошибки: R означает ошибку чтения диска, M означает, что загружаемый файл слишком велик (недостаточно памяти).Другое ограничение заключается в том, что протокол RPL (Remote Program Loader) не используется, так как ему требуется еще несколько байтов.

Чтобы уменьшить объемное давление, загрузчик может быть собран с -D_CHS=0 -D_QUERY_GEOMETRY=0 (если загружать через ROM-BIOS-интерфейс LBA) или -D_LBA=0 (если загружать через интерфейс CHS).

Чтобы собрать загрузчик, клонируйте репозитории lmacros и ldosboot и поместитеони рядом друг с другом.Загрузчик должен быть собран из каталога ldosboot с NASM следующим образом для FAT12:

$ nasm -I ../lmacros/ boot.asm -l boot7e12.lst -D_MAP=boot7e12.map -o boot7e12.bin -D_COMPAT_KERNEL7E

Или так для FAT16:

$ nasm -I ../lmacros/ boot.asm -l boot7e16.lst -D_MAP=boot7e16.map -o boot7e16.bin -D_FAT16 -D_COMPAT_KERNEL7E

Вот как установить загрузчик в существующий уже отформатированный образ файловой системы FAT12 или FAT16:

dd if=boot7e12.bin of=floppy.img bs=1 count=11 conv=notrunc
dd if=boot7e12.bin of=floppy.img bs=1 count=$((512 - 0x3e)) seek=$((0x3e)) skip=$((0x3e)) conv=notrunc

Вместо использования существующего изображения NASM может создать все изображение.Я написал такую ​​программу на https://bitbucket.org/ecm/bootimg/src/default/ Она строится так:

nasm -I ../lmacros/ -D_BOOTFILE="'../ldosboot/boot12.bin'" -D_MULTIPAYLOADFILE="'../ldebug/bin/ldebug.com','../ldebug/bin/lddebug.com'" bootimg.asm -o bootimg.img

Обратите внимание, что длинное определение имеет двойные кавычки вокруг записей в одинарных кавычках.Каждая запись в списке обрезается до базового имени (после последней косой черты или обратной косой черты), ее содержимое добавляется в область данных, а запись каталога добавляется в корневой каталог.Имена файлов ASCII и allcaps.

Репозиторий ldosboot также содержит двухсекторный загрузчик FAT32, но я пока не модифицировал его для поддержки этого протокола.При перемещении буфер FAT уже должен быть в верхней части памяти.Это означает, что файл может быть загружен в 07E00h.Тем не менее, сс будет на высоком сегменте, а не ноль.Помимо этой разницы, протокол может быть указан с коммутаторами.Команда для его создания: nasm -I ../lmacros/ boot32.asm -l boot7e32.lst -D_MAP=boot7e32.map -o boot7e32.bin -D_RELOCATE -D_MEMORY_CONTINUE=0 -D_ZERO_DS -D_ZERO_ES -D_SET_BL_UNIT=0 -D_SET_DL_UNIT=1 -D_LOAD_ADR=07E00h -D_EXEC_SEG_ADJ=-7E0h -D_EXEC_OFS=7E00h -D_OEM_NAME="'KERNEL7E'" -D_LOAD_NAME="'KERNEL7E'" -D_LOAD_EXT="'BIN'"

Существует также программа instsect (в своем репо) для DOS, которая создается с образами загрузчиков и устанавливает их на диск DOS.

0 голосов
/ 27 февраля 2019

Я написал такой код как часть других ответов, но никогда не имел возможности представить простой тестовый комплект, на который можно ссылаться из других вопросов Stackoverflow.То, что вы просите, довольно тривиально.Это можно сделать, написав загрузчик в NASM, который содержит двоичный образ собранного кода, который вы хотите протестировать.Этот образ будет считан с диска, начиная с LBA 1 (первый сектор после загрузчика), используя функцию BIOS Int 13 / ah = 2 .Управление будет затем передано ему через FAR JMP на 0x0000: 0x7e00.

Код загрузчика будет выглядеть следующим образом:

bpb.inc :

%define WITH_BPB 1
global bpb_disk_info

    jmp boot_start
    TIMES 3-($-$$) DB 0x90   ; Support 2 or 3 byte encoded JMPs before BPB.

    bpb_disk_info:

        ; Dos 4.0 EBPB 1.44MB floppy
        OEMname:           db    "mkfs.fat"  ; mkfs.fat is what OEMname mkdosfs uses
        bytesPerSector:    dw    512
        sectPerCluster:    db    1
        reservedSectors:   dw    1
        numFAT:            db    2
        numRootDirEntries: dw    224
        numSectors:        dw    2880
        mediaType:         db    0xf0
        numFATsectors:     dw    9
        sectorsPerTrack:   dw    18
        numHeads:          dw    2
        numHiddenSectors:  dd    0
        numSectorsHuge:    dd    0
        driveNum:          db    0
        reserved:          db    0
        signature:         db    0x29
        volumeID:          dd    0x2d7e5a1a
        volumeLabel:       db    "NO NAME    "
        fileSysType:       db    "FAT12   "

boot.asm :

STAGE2_ABS_ADDR  equ 0x07e00
STAGE2_RUN_SEG   equ 0x0000
STAGE2_RUN_OFS   equ STAGE2_ABS_ADDR
                                ; Run stage2 with segment of 0x0000 and offset of 0x7e00

STAGE2_LOAD_SEG  equ STAGE2_ABS_ADDR>>4
                                ; Segment to start reading Stage2 into
                                ;     right after bootloader

STAGE2_LBA_START equ 1          ; Logical Block Address(LBA) Stage2 starts on
                                ;     LBA 1 = sector after boot sector
STAGE2_LBA_END   equ STAGE2_LBA_START + NUM_STAGE2_SECTORS
                                ; Logical Block Address(LBA) Stage2 ends at
DISK_RETRIES     equ 3          ; Number of times to retry on disk error

bits 16
ORG 0x7c00

; Include a BPB (1.44MB floppy with FAT12) to be more comaptible with USB floppy media
%include "bpb.inc"

boot_start:
    xor ax, ax                  ; DS=SS=ES=0 for stage2 loading
    mov ds, ax
    mov ss, ax                  ; Stack at 0x0000:0x7c00
    mov sp, 0x7c00
    cld                         ; Set string instructions to use forward movement

    ; Read Stage2 1 sector at a time until stage2 is completely loaded
load_stage2:
    mov [bootDevice], dl        ; Save boot drive
    mov di, STAGE2_LOAD_SEG     ; DI = Current segment to read into
    mov si, STAGE2_LBA_START    ; SI = LBA that stage2 starts at
    jmp .chk_for_last_lba       ; Check to see if we are last sector in stage2

.read_sector_loop:
    mov bp, DISK_RETRIES        ; Set disk retry count

    call lba_to_chs             ; Convert current LBA to CHS
    mov es, di                  ; Set ES to current segment number to read into
    xor bx, bx                  ; Offset zero in segment

.retry:
    mov ax, 0x0201              ; Call function 0x02 of int 13h (read sectors)
                                ;     AL = 1 = Sectors to read
    int 0x13                    ; BIOS Disk interrupt call
    jc .disk_error              ; If CF set then disk error

.success:
    add di, 512>>4              ; Advance to next 512 byte segment (0x20*16=512)
    inc si                      ; Next LBA

.chk_for_last_lba:
    cmp si, STAGE2_LBA_END      ; Have we reached the last stage2 sector?
    jl .read_sector_loop        ;     If we haven't then read next sector

.stage2_loaded:
    mov ax, STAGE2_RUN_SEG      ; Set up the segments appropriate for Stage2 to run
    mov ds, ax
    mov es, ax

    ; FAR JMP to the Stage2 entry point at physical address 0x07e00
    xor ax, ax                  ; ES=FS=GS=0 (DS zeroed earlier)
    mov es, ax
    mov fs, ax
    mov gs, ax
    ; SS:SP is already at 0x0000:0x7c00, keep it that way
    ; DL still contains the boot drive number
    ; Far jump to second stage at 0x0000:0x7e00
    jmp STAGE2_RUN_SEG:STAGE2_RUN_OFS

.disk_error:
    xor ah, ah                  ; Int13h/AH=0 is drive reset
    int 0x13
    dec bp                      ; Decrease retry count
    jge .retry                  ; If retry count not exceeded then try again

error_end:
    ; Unrecoverable error; print drive error; enter infinite loop
    mov si, diskErrorMsg        ; Display disk error message
    call print_string
    cli
.error_loop:
    hlt
    jmp .error_loop

; Function: print_string
;           Display a string to the console on display page 0
;
; Inputs:   SI = Offset of address to print
; Clobbers: AX, BX, SI

print_string:
    mov ah, 0x0e                ; BIOS tty Print
    xor bx, bx                  ; Set display page to 0 (BL)
    jmp .getch
.repeat:
    int 0x10                    ; print character
.getch:
    lodsb                       ; Get character from string
    test al,al                  ; Have we reached end of string?
    jnz .repeat                 ;     if not process next character
.end:
    ret

;    Function: lba_to_chs
; Description: Translate Logical block address to CHS (Cylinder, Head, Sector).
;              Works for all valid FAT12 compatible disk geometries.
;
;   Resources: http://www.ctyme.com/intr/rb-0607.htm
;              https://en.wikipedia.org/wiki/Logical_block_addressing#CHS_conversion
;              https://stackoverflow.com/q/45434899/3857942
;              Sector    = (LBA mod SPT) + 1
;              Head      = (LBA / SPT) mod HEADS
;              Cylinder  = (LBA / SPT) / HEADS
;
;      Inputs: SI = LBA
;     Outputs: DL = Boot Drive Number
;              DH = Head
;              CH = Cylinder (lower 8 bits of 10-bit cylinder)
;              CL = Sector/Cylinder
;                   Upper 2 bits of 10-bit Cylinders in upper 2 bits of CL
;                   Sector in lower 6 bits of CL
;
;       Notes: Output registers match expectation of Int 13h/AH=2 inputs
;
lba_to_chs:
    push ax                     ; Preserve AX
    mov ax, si                  ; Copy LBA to AX
    xor dx, dx                  ; Upper 16-bit of 32-bit value set to 0 for DIV
    div word [sectorsPerTrack]  ; 32-bit by 16-bit DIV : LBA / SPT
    mov cl, dl                  ; CL = S = LBA mod SPT
    inc cl                      ; CL = S = (LBA mod SPT) + 1
    xor dx, dx                  ; Upper 16-bit of 32-bit value set to 0 for DIV
    div word [numHeads]         ; 32-bit by 16-bit DIV : (LBA / SPT) / HEADS
    mov dh, dl                  ; DH = H = (LBA / SPT) mod HEADS
    mov dl, [bootDevice]        ; boot device, not necessary to set but convenient
    mov ch, al                  ; CH = C(lower 8 bits) = (LBA / SPT) / HEADS
    shl ah, 6                   ; Store upper 2 bits of 10-bit Cylinder into
    or  cl, ah                  ;     upper 2 bits of Sector (CL)
    pop ax                      ; Restore scratch registers
    ret

; Uncomment these lines if not using a BPB (via bpb.inc)
%ifndef WITH_BPB
numHeads:        dw 2         ; 1.44MB Floppy has 2 heads & 18 sector per track
sectorsPerTrack: dw 18
%endif

bootDevice:      db 0x00
diskErrorMsg:    db "Unrecoverable disk error!", 0

; Pad boot sector to 510 bytes and add 2 byte boot signature for 512 total bytes
TIMES 510-($-$$) db  0
dw 0xaa55

; Beginning of stage2. This is at 0x7E00 and will allow your stage2 to be 32.5KiB
; before running into problems. DL will be set to the drive number originally
; passed to us by the BIOS.

NUM_STAGE2_SECTORS equ (stage2_end-stage2_start+511) / 512
                                ; Number of 512 byte sectors stage2 uses.

stage2_start:
    ; Insert stage2 binary here. It is done this way since we
    ; can determine the size(and number of sectors) to load since
    ;     Size = stage2_end-stage2_start
    incbin "stage2.bin"

; End of stage2. Make sure this label is LAST in this file!
stage2_end:

; Fill out this file to produce a 1.44MB floppy image
TIMES 1024*1440-($-$$) db 0x00

Чтобы использовать это, вы first сгенерируете двоичный файл с именем stage2.bin.Затем вы должны создать образ диска размером 1,44 МБ с помощью этой команды:

nasm -f bin boot.asm -o disk.img

Код в stage2.bin должен быть сгенерирован с допущением, что ORG (начальная точка) 0x07e00 в памяти.


Пример использования / Пример

Пример кода, сгенерированного для файла с именем stage2.bin, который можно загрузить с помощью этого тестового набора:

testcode.asm:

ORG 0x7e00

start:
    mov si, testCodeStr
    call print_string

    cli
.end_loop:
    hlt
    jmp .end_loop

testCodeStr: db "Test harness loaded and is executing code in stage2!", 0

; Function: print_string
;           Display a string to the console on display page 0
;
; Inputs:   SI = Offset of address to print
; Clobbers: AX, BX, SI

print_string:
    mov ah, 0x0e                ; BIOS tty Print
    xor bx, bx                  ; Set display page to 0 (BL)
    jmp .getch
.repeat:
    int 0x10                    ; print character
.getch:
    lodsb                       ; Get character from string
    test al,al                  ; Have we reached end of string?
    jnz .repeat                 ;     if not process next character
.end:
    ret

Примечание : вверху есть ORG 0x7e00.Это важно.Чтобы собрать этот файл в stage2.bin, используйте:

nasm -f bin testcode.asm -o stage2.bin

Затем создайте образ диска 1.44MiB с:

nasm -f bin boot.asm -o disk.img

Результатом должен быть образ диска размером точно 1.44MiB,содержит копию stage2.bin и загрузочный сектор нашего тестового жгута.

Файл stage2.bin может быть любым, с двоичным кодом, записанным для загрузки и запуска в 0x0000: 0x7e00.Язык (C, сборка и т. Д.), Используемый для создания кода в stage2.bin, не имеет значения.Я использую NASM для этого примера.Когда этот тестовый код выполняется в QEMU с использованием qemu-system-i386 -fda disk.img, он будет выглядеть примерно так:

enter image description here


Специальное примечание: : Если вы не хотите использовать bpb.inc и не собираетесь загружаться с USB с помощью эмуляции FDD, вы можете закомментировать или удалить эту строку в boot.asm

%include "bpb.inc"
...