简体   繁体   English

引导加载程序在 qemu 中工作,但在 virtualbox 和硬件上失败

[英]bootloader works in qemu but fails in virtualbox and on hardware

my bootloader consists of two 512 byte stages.我的引导加载程序由两个 512 字节的阶段组成。 stage 1 gets loaded by the bios into the MBR area.阶段 1 由 bios 加载到 MBR 区域。 stage1 then proceeds to load stage2 from the drive and jumps to it.然后 stage1 从驱动器加载 stage2 并跳转到它。

i confirmed with a hex editor that the size of final binary "program.bin" is exactly 1024 byte long and contains both "signatures" (last two bytes of each stage, 0xAA55 for stage1 (MBR signature) and 0xCC77 for stage2).我用十六进制编辑器确认最终二进制“program.bin”的大小正好是 1024 字节长,并且包含两个“签名”(每个阶段的最后两个字节,0xAA55 用于 stage1(MBR 签名)和 0xCC77 用于 stage2)。

expected outputs are:预期产出是:

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

this works fine in QEMU but fails in virtualbox and on hardware.这在 QEMU 中运行良好,但在 virtualbox 和硬件中失败。 it looks to me like the stage2 load fails silently (error branch doesn't get called) and i am looking to solve this for two weeks now without success.在我看来,stage2 加载无声无息地失败(错误分支没有被调用),我现在希望解决这个问题两周但没有成功。

QEMU output QEMU output
QEMU 输出

Hardware & Virtual Box output硬件和虚拟盒子 output
VBox 输出

stage1.asm:阶段1.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:阶段2.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 script "linker.ld": linker 脚本“linker.ld”:

ENTRY(_start)
OUTPUT_FORMAT(binary)

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

i use the following commands to compile and link everything together:我使用以下命令编译并将所有内容链接在一起:

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

i run the binary on QEMU with:我在 QEMU 上运行二进制文件:

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

to run it on hardware i write the binary to a USB with:要在硬件上运行它,我将二进制文件写入 USB,其中:

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

Your bootloader actually looks pretty good.您的引导加载程序实际上看起来不错。 As @jester pointed out on real hardware if you are booting USB using Floppy Disk Emulation (FDD) then you will most likely need a BPB .正如@jester 在真实硬件上指出的那样,如果您使用软盘仿真 (FDD) 启动 USB ,那么您很可能需要一个BPB There are indications in your screenshot that you are booting USB as Hard Disk Emulation (HDD) since the drive number appears to be 0x0080.您的屏幕截图中有迹象表明您正在将 USB 作为硬盘仿真 (HDD) 引导,因为驱动器号似乎是 0x0080。 If that is the case a BPB isn't necessary.如果是这种情况,则不需要 BPB。

When using USB HDD emulation you may need a partition table with one partition marked active/bootable for some BIOSes to recognize the drive as bootable.使用 USB 硬盘仿真时,您可能需要一个分区表,其中一个分区标记为活动/可引导,以便某些 BIOS 将驱动器识别为可引导。 Without it some BIOSes may refuse to recognize the drive as something it should boot even if it has the proper disk signature ( 0xaa55 ) in the last 2 bytes.如果没有它,一些 BIOS 可能会拒绝将驱动器识别为应该启动的驱动器,即使它在最后 2 个字节中具有正确的磁盘签名 ( 0xaa55 )。

I believe the real problem lies in how you are writing to the USB drive.我相信真正的问题在于您如何写入 USB 驱动器。 You are using:您正在使用:

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

/dev/sdb1 is actually the first partition, not the beginning of the drive. /dev/sdb1实际上是第一个分区,而不是驱动器的开头。 What it appears you want is to write to the beginning of the drive with:您似乎想要的是写入驱动器的开头:

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

You may ask: how is it that the bootloader you wrote was actually running if it wasn't written to the beginning of the drive?您可能会问:如果您编写的引导加载程序没有写入驱动器的开头,它是如何实际运行的? My suspicion is that your USB drive was formatted with a Master Boot Record (MBR) that chain loaded the Volume Boot Record (VBR) bootloader in partition 1 and then started executing it.我的怀疑是您的 USB 驱动器已使用主引导记录 (MBR) 格式化,该主引导记录 (MBR)在分区 1 中链式加载卷引导记录 (VBR)引导加载程序,然后开始执行它。 Such a chainloading MBR is very possible if the USB stick was formatted and partitioned in Windows.如果 USB 存储棒在 Windows 中格式化和分区,则这种链式加载 MBR 是非常可能的。 Windows usually formats USB as one large partition and places an MBR in the first sector of the drive that chain loads the VBR from the first sector of the first partition. Windows 通常将 USB 格式化为一个大分区,并在驱动器的第一个扇区中放置一个 MBR,从第一个分区的第一个扇区链接加载 VBR。


Other Observations其他观察

Since you appear to be booting everything as Hard Disk media, you may wish to consider using the Extended disk function like Int 13h/AH=42h rather than Int 13h/AH=2h .由于您似乎将所有内容都作为硬盘媒体启动,您可能希望考虑使用扩展磁盘 function ,例如Int 13h/AH=42h而不是Int 13h/AH=2h Int 13/AH=2 is very limited in what it can load with CHS addressing rather than LBA addressing when dealing with larger media (usually greater than about 8GiB).在处理较大的媒体(通常大于约 8GiB)时,Int 13/AH=2 可以通过CHS 寻址而不是LBA 寻址加载的内容非常有限。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM