загрузчик работает в qemu, но не работает в виртуальном боксе и на оборудовании - PullRequest
6 голосов
/ 20 июня 2020

мой загрузчик состоит из двух этапов по 512 байт. этап 1 загружается b ios в область MBR. Затем stage1 переходит к загрузке stage2 с диска и переходит к нему.

Я подтвердил с помощью шестнадцатеричного редактора, что размер конечного двоичного файла "program.bin" составляет точно 1024 байта и содержит обе "подписи" (последний два байта каждого этапа, 0xAA55 для stage1 (подпись MBR) и 0xCC77 для stage2).

ожидаемые результаты:

1 // stage1 started
0000 or 0080 // drive# in hex
CC77 // stage2 "signature" in hex
2 // stage2 started

это отлично работает в QEMU, но не работает в виртуальном боксе и на оборудовании . мне кажется, что загрузка stage2 молча терпит неудачу (ветвь ошибки не вызывается), и я безуспешно пытаюсь решить эту проблему в течение двух недель.

Вывод QEMU QEMU output

Hardware & Virtual Box output
Вывод VBox

stage1.asm:

global _start
extern _stage2
extern _stage2data

BITS 16

_start:
; init registers
    xor ax, ax
    mov es, ax
    mov gs, ax
    mov ss, ax
    mov sp, 0x7C00      ; right before MBR, counting upwards

    mov ax, 0x7C0       ; set DS to 0x7c0 so pointing at 0x0 resolves to 0x7C0:x0000 = 0x7C00
    mov ds, ax

    cld                 ; set direction flag to make string operations count forward

 ; mark start of stage 1 by printing "1"
    mov al, '1'
    call real_mode_print_char
    call real_mode_new_line

print_drive_number:
; drive# is put into DL by BIOS
    mov dh, 0x0
    mov bx, dx
    call real_mode_print_hex

load_sector2:
    mov  al, 0x01           ; load 1 sector
    mov  bx, 0x7E00         ; destination, right after your bootloader
    mov  cx, 0x0002         ; cylinder 0, sector 2
    ; mov  dl, [BootDrv]      ; boot drive
    xor  dh, dh             ; head 0
    call read_sectors_16
    jnc execute_stage2           ; if carry flag is set, disk read failed
    jmp error

execute_stage2:
    mov bx, [_stage2data]       ; print data at _stage2data to confirm stage 2 was loaded
    call real_mode_print_hex

    jmp _stage2                 ; start execude instructions of _stage2

error:
; print "E" if an error occurs
    mov al, 'E'
    call real_mode_print_char

; infinite loop
loop:
    jmp loop

; read_sectors_16
;
; Reads sectors from disk into memory using BIOS services
;
; input:    dl      = drive
;           ch      = cylinder[7:0]
;           cl[7:6] = cylinder[9:8]
;           dh      = head
;           cl[5:0] = sector (1-63)
;           es:bx  -> destination
;           al      = number of sectors
;
; output:   cf (0 = success, 1 = failure)

read_sectors_16:
    push ax
    mov si, 0x02    ; maximum attempts - 1
.top:
    mov ah, 0x02    ; read sectors into memory (int 0x13, ah = 0x02)
    int 0x13
    jnc .end        ; exit if read succeeded
    dec si          ; decrement remaining attempts
    jc  .end        ; exit if maximum attempts exceeded
    xor ah, ah      ; reset disk system (int 0x13, ah = 0x00)
    int 0x13
    jnc .top        ; retry if reset succeeded, otherwise exit
.end:
    pop ax
    retn

# print a number in hex
# IN
#   bx: the number
# CLOBBER
#   al, cx
real_mode_print_hex:
    mov cx, 4
.lp:
    mov al, bh
    shr al, 4

    cmp al, 0xA
    jb .below_0xA

    add al, 'A' - 0xA - '0'
.below_0xA:
    add al, '0'

    call real_mode_print_char

    shl bx, 4
    loop .lp

    call real_mode_new_line

    ret

real_mode_new_line:
    mov al, 0x0D
    call real_mode_print_char
    mov al, 0x0A
    call real_mode_print_char
    ret

real_mode_print_char:
    push bx
    xor bx, bx              ; Attribute=0/Current Video Page=0
    mov ah, 0x0e
    int 0x10                ; Display character
    pop bx
    ret

; boot signature
TIMES 510-($-$$) db 0

mbr_id:
dw 0xAA55

stage2.asm:

global _stage2
global _stage2data

BITS 16

_stage2:
    mov al, '2'
    call bios_print_char

loop:
    jmp loop

    bios_print_char:
    push bx
    xor bx, bx              ; Attribute=0/Current Video Page=0
    mov ah, 0x0e
    int 0x10                ; Display character
    pop bx
    ret

; boot signature
TIMES 510-($-$$) db 0
_stage2data:
dw 0xCC77

скрипт компоновщика "linker.ld":

ENTRY(_start)
OUTPUT_FORMAT(binary)

SECTIONS
{
  output :
    {
      stage1.elf(.text)
      stage2.elf(.text)
    }
}

Я использую следующие команды для компиляции и компоновки всего вместе:

nasm -f elf64 stage1.asm -o stage1.elf
nasm -f elf64 stage2.asm -o stage2.elf
ld -m elf_x86_64 -o program.bin stage2.elf stage1.elf -nostdlib -T linker.ld

Я запускаю двоичный файл на QEMU с помощью:

qemu-system-x86_64 -drive format=raw,file=program.bin

, чтобы запустить его на оборудовании, я записываю двоичный файл в USB с:

dd if=program.bin of=/dev/sdb1 && sync

1 Ответ

7 голосов
/ 20 июня 2020

Ваш загрузчик выглядит неплохо. Как отметил @jester на реальном оборудовании, если вы загружаете USB с помощью эмуляции гибкого диска (FDD), вам, скорее всего, понадобится BPB . На вашем снимке экрана есть указания на то, что вы загружаете USB в качестве эмуляции жесткого диска (HDD), поскольку номер диска выглядит как 0x0080. В этом случае BPB не нужен.

При использовании эмуляции жесткого диска USB вам может потребоваться таблица разделов с одним разделом, помеченным как активный / загрузочный, чтобы некоторые BIOS распознали диск как загрузочный. Без него некоторые BIOS могут отказаться распознавать диск как то, что он должен загружать, даже если он имеет правильную подпись диска (0xaa55) в последних 2 байтах.

Я считаю, что настоящая проблема заключается в том, как вы запись на USB-накопитель. Вы используете:

dd if=program.bin of=/dev/sdb1 && sync 

/dev/sdb1 фактически первый раздел, а не начало диска. Похоже, вы хотите записать в начало диска с помощью:

dd if=program.bin of=/dev/sdb && sync

Вы можете спросить: как получилось, что загрузчик, который вы написали, действительно работал, если он не был записан в начало привод? Я подозреваю, что ваш USB-накопитель был отформатирован с использованием главной загрузочной записи (MBR) , что загружена цепочка загрузочная запись тома (VBR) загрузчик в разделе 1 и затем начал его выполнять. Такая последовательная загрузка MBR очень возможна, если USB-накопитель был отформатирован и разбит на разделы Windows. Windows обычно форматирует USB как один большой раздел и помещает MBR в первый сектор диска, который загружает VBR из первого сектора первого раздела.

Другие наблюдения

Поскольку кажется, что вы загружаете все как с жесткого диска, вы можете sh рассмотреть возможность использования функции расширенного диска, например Int 13h / AH = 42h , а чем Int 13h / AH = 2h . Int 13 / AH = 2 очень ограничен в том, что он может загружать с помощью CHS-адресации , а не LBA-адресации при работе с большими носителями (обычно более 8 ГБ).

...