【问题标题】:Bootloader printing garbage on real hardware [duplicate]引导加载程序在真实硬件上打印垃圾[重复]
【发布时间】:2016-04-30 05:14:17
【问题描述】:

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

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

虽然我对“真正的硬件”有很多麻烦,但我认为它们都有一个原因。

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

[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 个奇怪的字符:

http://wiki.osdev.org 中的大多数引导加载程序代码也不起作用。例如,http://wiki.osdev.org/Babystep2 中的任何 code-sn-ps 都不能在我的笔记本电脑上运行。

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


其他信息

如果我删除了 2 个不必要的 mov si, msg,“TEST”消息将打印两次

笔记本电脑:

  • 华硕 Vivobook S200,
  • CPU:英特尔 i3-3217U
  • BIOS:美国大趋势,版本 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

Ross Ridge 在 cmets 中注意到 Ω♣| 是引导加载程序的前 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 的建议,已添加 dumpregs 以进行更好的调试。 int 0x10 确实修改任何寄存器。 经过一些测试,我将dumpregs 函数移动到drive_number 分配和jmp $ 之后。该代码应打印 1 行寄存器转储并停止。相反,它继续:

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

编辑 4

当前引导加载程序的反汇编使用:

$ ndisasm -b16 bootload2 -o 0x7c00

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

【问题讨论】:

  • 这不是一个答案,但我发现华硕机器在使用自定义引导加载程序时可能会出现奇怪的行为。我们的一个项目涉及自定义磁盘格式和初始加载程序。在除华硕之外的所有设备上都运行良好。华硕的行为与您描述的相同。我并不是说它本身就是华硕……这可能是我们做错了……但硬件似乎不如其他硬件那么宽容。 :)
  • 尝试禁用中断?还尝试将mov ah, 0x0E 放入循环中。和push si/pop siint 0x10 周围。
  • @Jester 我已经尝试了你的建议。唯一改变的是打印的字符串:“TEST ≡S”
  • 出于好奇,如果将cld 移动到 print_string 之后 repeat: 会发生什么?这似乎是一个奇怪的测试,但我想知道在某些情况下华硕int 0x10 是否正在设置方向标志,这可能会导致后续写入打印垃圾(它不会解释 jmps 受到影响)。我会尝试的另一件事是从字符串中删除 0x0D, 0x0A 作为测试。可能存在处理这些特殊字符之一的错误。
  • 字符 Ω♣| 是您程序的前三个字节,但我看不出该代码将如何打印它们。

标签: assembly x86 bootloader bios osdev


【解决方案1】:

现在看起来 BIOS 可能会修改它错误地认为是加载到内存中的引导扇区的一部分的 BIOS parameter block。它可能会觉得需要这样做,因为它在引导 USB 设备时提供给它的几何图形可能与将引导扇区及其假定的 BPB 写入设备时使用的几何图形不同。由于在引导扇区开始处存在跳转指令是应用程序测试是否存在 BPB 的方法之一,因此您可以尝试在引导扇区的开头插入一些其他指令(但不是 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 指令与对该函数的调用括起来,则应该得到如下输出:

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

【讨论】:

  • 我一定要疯了,我希望你已经准备好了。我已经复制了您的代码并尝试了它。虽然它只打印了两次“TEST”,但两边的所有寄存器都匹配。玩弄你的函数,我通过删除drive_number 变量及其赋值来清理代码,然后正确打印了测试!。为了验证这一点,我将您的dumpregs 函数放在mov [drive_number], dl 周围,然后添加了jmp $,但代码仍然打印了“TEST”一次。您可以在 OP 中的 Edit 3 下看到完整修改后的代码和屏幕截图。
  • 这解决了我的问题 - BIOS 用 BPB 覆盖了我的引导加载程序的一部分,因此,一些指令被覆盖了。这会导致各种问题,例如覆盖jmp $
【解决方案2】:

我以前见过类似的症状。场景大概是这样的:

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

b) BIOS 查看设备的第一个扇区并尝试决定是否应将其视为软盘或硬盘驱动器。它无法找到有效的分区表,因此决定将其视为软盘。

c) BIOS 尝试“提供帮助”并修补 BPB 中的某些字段。

d) 您没有 BPB,因此您的代码和/或数据会被丢弃,从而导致奇怪的事情发生(代码中的微小更改会导致不同的奇怪事情发生)。

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

【讨论】:

  • 我猜它错误地识别了一个 BPB,因为第一个字节表示一个 jmp。我的猜测是 BIOS 代码没有被编码为仅寻找相对跳转。它的假设很糟糕,感觉有一个 BPB 并对其进行了修改
  • 在这种情况下,far jmp 并不是一个延伸,因为许多引导加载程序都会将 cs:ip 设置为预期值。
  • @MichaelPetch:我不知道。它可能只是if( no partition table) { make false assumptions and trash things },它不查看分区表以外的任何内容。
  • 您好,您的“0x1C 到 0x1F 和偏移量 0x24 处的字节”的来源是什么?
猜你喜欢
  • 1970-01-01
  • 2016-03-09
  • 1970-01-01
  • 2013-07-07
  • 2019-05-09
  • 2015-09-20
  • 1970-01-01
  • 1970-01-01
  • 2016-04-15
相关资源
最近更新 更多