Bootloader在真实硬件上打印垃圾

时间:2016-01-23 17:19:20

标签: assembly boot bootloader bios osdev

我正在尝试编写自己的bootloader。虽然它在QEMU,Bochs和VirtualBox中运行良好,但我似乎无法在笔记本电脑上运行。

在我的笔记本电脑上,引导加载程序与所有模拟器的行为完全不同,挂起看似随机的地方,拒绝打印,甚至跳过一些jmp $指令。

虽然我在"真实硬件"上遇到了很多麻烦,但我认为他们都有一个原因。

以下代码是一个简短的引导加载程序,应该打印" TEST"消息3次,然后通过跳转到同一位置挂起:

[BITS 16]                                                                          
[ORG 0x7C00]                                                                                                    
    jmp 0x0000:start_16  ; In case bootloader is at 0x07C0:0x0000                                                             
start_16:                                                                          
    xor ax, ax                                                                 
    mov ds, ax                                                                 
    mov es, ax                                                                 
    cli                             ; Disable interrupts                       
    mov ss, ax                                                                 
    mov sp, 0x7C00                                                             
    sti                             ; Enable interrupts                        
    cld                             ; Clear Direction Flag                     
    ; Store the drive number                                                   
    mov [drive_number], dl                                                     
    ; Print message(s)                                                         
    mov si, msg                                                                
    call print_string                                                          
    mov si, msg                                                                
    call print_string                                                          
    mov si, msg                                                                
    call print_string                                                          

    jmp $   ; HALT                                                                                   

; print_string                                                                     
;       si      = string                                                           
print_string:                                                                      
    pusha                                                                      
    mov ah, 0x0E                                                               
.repeat:                                                                           
    lodsb                                                                      
    cmp al, 0x00                                                               
    je .done                                                                   
    int 0x10                                                                   
    jmp short .repeat                                                          
.done:                                                                             
    popa                                                                       
    ret                                                                        

; Variables                                                                        
drive_number db 0x00                                                               
msg db 'TEST', 0x0D, 0x0A, 0x00                                                    
times 510-($-$$) db 0x00                                                           
db 0x55                                                                            
db 0xAA

使用以下代码编译和模拟代码:

$ nasm -f bin bootloader.asm
$ qemu-system-x86_64 bootloader

在模拟器上,它打印" TEST"三次并挂起,在我的笔记本电脑上打印" TEST"接着是3个奇怪的字符:

Bootloader output on my laptop.

来自http://wiki.osdev.org的大多数引导加载程序代码也不起作用。例如,http://wiki.osdev.org/Babystep2中的所有代码段都不能在我的笔记本电脑上运行。

我的代码出了什么问题?我该如何解决?

其他信息

如果我删除了2个不必要的mov si, msg,那么" TEST"消息打印两次

笔记本:

  • 华硕Vivobook S200,
  • CPU:Intel i3-3217U
  • BIOS:American Megatrends,版本210。
  • 计算机可以与Grub等任何其他引导程序一起使用。

汇编和写作:

$ nasm -f bin bootloader.asm
$ qemu-system-x86_64 bootloader # TEST 1 
$ sudo dd if=/dev/zero of=/dev/sdd bs=1M count=1 # clean the USB 
$ sudo dd if=bootloader of=/dev/sdd conv=fsync # write to USB 
$ qemu-system-x86_64 /dev/sdd # TEST 2 

编辑1

罗斯里奇在评论中注意到Ω♣|是引导加载程序的前3个字节。

编辑2

更新了打印功能和字符串:

print_string:                                                                      
    pusha                                                                          
.repeat:                                                                        
    mov ah, 0x0E                                                                
    xor bx, bx                                                                  
    cld                             ; Clear Direction Flag                      
    lodsb                                                                       
    cmp al, 0x00                                                                
    je .done                                                                    
    int 0x10                                                                    
    jmp short .repeat                                                           
.done:                                                                          
    popa                                                                        
    ret 

msg db 'TEST', 0x00 

输出

一个TEST。另外两个缺失了。

编辑3

正如Ross Ridge所建议的那样,为了更好的调试,添加了dumpregsint 0x10执行修改任何寄存器。 经过一些测试后,我已将dumpregs函数移至drive_number分配,并在jmp $后移动$ ndisasm -b16 bootload2 -o 0x7c00。代码应打印1行寄存器转储并暂停。相反,它继续: dumpregs around drive_number assignment

完整代码: https://gist.github.com/anonymous/0ddc146f73ff3a13dd35

编辑4

使用以下命令反汇编当前的引导程序:

[ { "Stage":"birth", "Avg":19071 }, { // } ]

https://gist.github.com/anonymous/c9384fbec25513e3b815

2 个答案:

答案 0 :(得分:4)

现在看起来BIOS可能正在修改它错误地假设的BIOS parameter block是加载到内存中的引导程序的一部分。它可能感觉需要,因为它在引导时为USB设备提供的几何结构可能与引导程序及其假定的BPB写入设备时使用的几何结构不同。由于在bootsector开始时存在跳转指令是应用程序测试BPB存在的方法之一,因此您可以尝试在bootsector的开头插入一些其他指令(但不是NOP)。例如:

[BITS 16]                                                                          
[ORG 0x7C00]
    xor ax,ax                                                                                                
    jmp 0x0000:start_16  ; In case bootloader is at 0x07C0:0x0000                                                             
start_16:                       

请注意,远距离跳跃似乎不是normal indicators of the presence of a BPB之一。实际上,它是非常确凿的证据,表明BPB不存在,因为BPB从偏移量4开始,而远程跳转指令长度为5个字节。

如果这不起作用,您可以尝试为BPB保留空间,如下所示:

[BITS 16]                                                                          
[ORG 0x7C00]
    jmp start
    nop
    resb 8 + 25
start:                                                                                          
    jmp 0x0000:start_16  ; In case bootloader is at 0x07C0:0x0000                                                             
start_16:                       

这里有一些代码可用于转储寄存器以帮助调试问题:

dumpregs:
    push    es
    pusha
    push    0xb800
    pop es  
    mov di, [vidmem_ptr]
    mov bp, sp
    mov cx, 8
dump_loop:
    dec bp
    dec bp
    mov ax, [bp + 16]
    call    printhex2
    inc di
    inc di
    loop    dump_loop
    mov [vidmem_ptr], di
    popa
    pop es
    ret

printhex2:
    push    ax
    mov al, ah
    call    convhex1
    pop ax
convhex1:
    aam 16
    ; DB    0D4h, 16
    xchg    al, ah
    call    convhex
    mov al, ah
convhex:
    cmp al, 10
    jb  lessthan_10
    add al, 'A' - '0' - 10
lessthan_10:
    add al, '0'
    stosb
    mov al, 7
    stosb
    ret

vidmem_ptr dw 5 * 80 * 2 ; start at row 5

它使用直接视频写入来转储所有通用寄存器。它使用PUSHA / POPA寄存器命令:AX CX DX BX SP BP SI DI

如果通过调用此函数将原始代码中的INT 0x10指令括起来,则应得到如下输出:

Screenshot of boot with debug output

特别要确保左侧的8个数字与右侧的8个数字匹配,用于每行调试输出。您正在使用的BIOS调用不应该更改任何寄存器。

答案 1 :(得分:2)

我以前见过类似的症状。场景可能类似于:

a)您正在从USB闪存启动笔记本电脑

b)BIOS查看设备的第一个扇区,并尝试确定它是应该像软盘一样对待它还是像硬盘一样。它无法找到有效的分区表,因此它决定将其视为软盘。

c)BIOS试图"有帮助"并修补BPB中的一些字段。

d)你没有BPB,所以你的代码和/或数据被破坏,导致奇怪的事情发生(代码中的微小变化导致不同的奇怪事情发生)。

可悲的是,我自己没有硬件可以做到这一点。我不知道为什么BIOS假设有BPB,或多种不同的BPB格式中的哪一种"它假设存在,或者它认为它可能正在修补或为什么它认为修补任何东西是必要的。我只知道有些BIOS会将偏移量为0x1C到0x1F的字节和偏移量为0x24的字节丢弃。