为啥我的引导加载程序无法在最近的笔记本电脑上运行?

Posted

技术标签:

【中文标题】为啥我的引导加载程序无法在最近的笔记本电脑上运行?【英文标题】:Why is my bootloader not working on recent laptops?为什么我的引导加载程序无法在最近的笔记本电脑上运行? 【发布时间】:2019-09-29 22:31:52 【问题描述】:

我想从头开始做一个简单的引导加载程序。我希望它能够在最近的笔记本电脑上通过 USB 启动,这意味着它具有 UEFI 启动但理论上它支持 Legacy 启动。

所以我写了一小段汇编代码,它只打印“hello world”,然后用 nasm -f bin boot.asm -o boot.com 编译它 然后我用 dd 从二进制文件构建了软盘。 该软盘在 qemu-system-x86_64 上运行良好。我使用 dd if=boot.bin of=/dev/sda 将其传输到格式化为 FAT32 的 U 盘的 MBR。 但是当用我最近的电脑启动它时,USB 没有出现在可启动设备中。

我试图: - 在 Qemu 上成功启动它(使用 qemu-system-x86_64 -hdb /dev/sda) - 在具有英特尔奔腾 4 处理器的非常旧的计算机上启动它,一切都按预期工作。 - 在 Bios 选项中启用传统模式 - 禁用安全启动 - 在另一台计算机上启动它,USB 棒显示在可启动设备中,但没有打印任何内容。

我在网上找到的,所以组装应该是正确的

;; A tiny, working bootloader for x86 PCs. Has a few subroutines
;; so it's slightly less useless than just printing "hello world".
;;
;; writeup here: http://joebergeron.io/posts/post_two.html
;;
;; Joe Bergeron, 2016.
;;
    bits 16

    mov ax, 07C0h
    mov ds, ax
    mov ax, 07E0h       ; 07E0h = (07C00h+200h)/10h, beginning of stack segment.
    mov ss, ax
    mov sp, 2000h       ; 8k of stack space.

    call clearscreen

    push 0000h
    call movecursor
    add sp, 2

    push msg
    call print
    add sp, 2

    cli
    hlt

clearscreen:
    push bp
    mov bp, sp
    pusha

    mov ah, 07h     ; tells BIOS to scroll down window
    mov al, 00h     ; clear entire window
        mov bh, 07h         ; white on black
    mov cx, 00h         ; specifies top left of screen as (0,0)
    mov dh, 18h     ; 18h = 24 rows of chars
    mov dl, 4fh     ; 4fh = 79 cols of chars
    int 10h         ; calls video interrupt

    popa
    mov sp, bp
    pop bp
    ret

movecursor:
    push bp
    mov bp, sp
    pusha

    mov dx, [bp+4]      ; get the argument from the stack. |bp| = 2, |arg| = 2
    mov ah, 02h         ; set cursor position
    mov bh, 00h     ; page 0 - doesn't matter, we're not using double-buffering
    int 10h

    popa
    mov sp, bp
    pop bp
    ret

print:
    push bp
    mov bp, sp
    pusha
    mov si, [bp+4]      ; grab the pointer to the data
    mov bh, 00h         ; page number, 0 again
    mov bl, 00h     ; foreground color, irrelevant - in text mode
    mov ah, 0Eh         ; print character to TTY
 .char:
    mov al, [si]        ; get the current char from our pointer position
    add si, 1       ; keep incrementing si until we see a null char
    or al, 0
    je .return          ; end if the string is done
    int 10h             ; print the character if we're not done
    jmp .char       ; keep looping
 .return:
    popa
    mov sp, bp
    pop bp
    ret


msg:    db "Oh boy do I sure love assembly!", 0

    times 510-($-$$) db 0
    dw 0xAA55

我希望能够在我最近的计算机上启动 USB,运行 i7-8550U 处理器和 UEFI/Legacy 兼容。 目前,USB 记忆棒并未出现在可启动设备中。

编辑: 谢谢您的回复 ! 我尝试通过添加这段代码来设置 Bios 参数块 (BPB):


ORG 0
BITS 16

jmp near start

db "MYBOOT  "      
dw 512           
db 1                
dw 1          
db 2             
dw 512                    
dw 65535          
db 0xf8     
dw 20      
dw 63            
dw 16    
dd 0            
dd 0        
db 0x29  
dd 0xffff  
db 0         
db 0         
db "NO NAME    "
db "FAT32   "

start:
   blablabla (see above)

USB 棒现在出现在 ubuntu 桌面上(我注意到如果我使用“jmp near start”以外的其他东西,比如“jmp short start”,它就会消失) 但它仍然不想出现在我的 BIOS 启动菜单中。 我真的不知道为什么它现在不想看到它。 我有 F.23 版的 Bios,也许这个 bios 有什么特殊的方法可以做到这一点?

【问题讨论】:

并非您在 Internet 上找到的所有内容都是正确的 :) 请参阅 duplicate 如何使用 BPB 创建引导扇区。这可能会解决您的问题。 首先,您是将 USB 引导为软盘驱动器 (FDD) 仿真或硬盘驱动器磁盘仿真 (HDD)。您应该能够在 USB 相关启动的 BIOS 中看到此设置。我假设从您的 QEMU 命令行中您正在 LInux 上进行开发? 您有机会试用当前答案中的代码吗? 【参考方案1】:

我不能将其放在评论中,如果有任何帮助,我会修改它以成为正确的答案。提供的代码实际上也可能是解决问题的方法。这是基于几个月前在OSDev Forum discussion 上发现的类似情况下使用较新的 BIOS。

您能否尝试使用它编译和构建 MBR,并查看您的 BIOS 是否将驱动器识别为可引导驱动器?如果您碰巧在分区媒体上启动,它包含一个自引用分区表。不确定您的 BIOS 正在模拟什么 USB 介质,希望您简单地测试一下它是否可以打印:

你好,世界!

代码:

bits 16
org 0x7c00

boot_start:
    xor ax, ax                  ; DS=0 since we use ORG 0x7c00. 0x0000<<4+0x7c00=0x7c00
    mov ds, ax
    mov es, ax

    ; If you will be reading data into memory outside of 0x7c00 to 0x7dff
    ; then you want to set the stack SS:SP - uncomment these lines
    ; mov ss, ax                ; Stack at 0x0000:0x7c00
    ; mov sp, 0x7c00            ;     Just below bootloader

    cld                         ; Forward movement of string instructions
                                ;     (MOVSB, SCASB, etc)

    mov si, HelloWorldMsg       ; Print hello world
    call print_string

end_loop:                       ; Loop forever to terminate
    hlt
    jmp end_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

HelloWorldMsg:   db "Hello, world!", 0x0d, 0x0a, 0

times 446-($-$$) db 0   ; Pad with 0s up until first partition entry
part1_entry:
db 0x80                 ; 0x80 = Active boot partition, 0x00=inactive
db 0x00, 0x01, 0x00     ; CHS of first absolute sector (MBR) of hard drive
                        ;     Head=0, Sector=1, Cylinder=0
db 0x0c                 ; Partition type (has to be non-zero)
                        ;     0x0c = Win 95 FAT32 (LBA)
db 0x00, 0x01, 0x00     ; CHS of last absolute sector (MBR) of hard drive
                        ;     Head=0, Sector=1, Cylinder=0
                        ;     We are effectively saying Size of partition is 1 sector
dd 0x0                  ; LBA of first absolute sector (0=MBR)
dd 0x1                  ; Number of sectors in partition. We set it to 1 but if you
                        ;     wish you could set it to the number of sectors on the disk

times 510-($-$$) db 0   ; Pad remainder of boot sector up to boot signature. This zeroes
                        ;     partition entries 2,3,4 effectively making them inactive

dw 0xAA55               ; The standard PC boot signature after partition table

此代码可能不会使您的驱动器可见,甚至无法启动和输出任何内容,但结果可以帮助缩小问题范围。考虑到您的 BIOS 的反应,我的一部分认为可能不仅仅是 BPB 问题。

【讨论】:

这没有提供问题的答案。要批评或要求作者澄清,请在他们的帖子下方留下评论。 - From Review @Rob :问题在于,要缩小问题的范围非常困难,因为这样做实际上需要提供不适合注释的代码级别。这实际上是一个很好的问题,希望通过 OP 的一些输入和测试,这个问题可以 a)正确复制另一个问题,或者 b)这个非答案可以作为涉及分区媒体的解决方案的潜在字符串点。与许多琐碎的问题不同,对于尚未在 So 上捕捉到的棘手问题,有一个潜在的机会来回答。 我相信你的话,这是评论而不是问题的答案,但答案部分不允许使用 cmets。 我知道这不属于典型的解决方案,但它实际上可能是一个解决方案。我们只能知道 OP 是否取得了一些成功。这是一个非答案,我愿意接受成堆的反对票而不是删除,希望它最终可以变成有用的东西。 @rob :这个问题中的代码实际上可能是解决方案。他可能只需要一个自引用分区表来欺骗 BIOS。该代码实际上是一个完整的功能 MBR,它打印 hello world。如果可行,这可以很容易地转化为带有解释的正确答案。在这种情况下,如果您对该主题知之甚少,它可能看起来一无所有,但我是 [bootloader] 标签中排名最高的回答者之一。我希望评论者可以放纵我。这有可能成为规范的问答。我会尊重投票并希望答案可以避免此时被删除

以上是关于为啥我的引导加载程序无法在最近的笔记本电脑上运行?的主要内容,如果未能解决你的问题,请参考以下文章

为啥我的电脑不能运行java web start

为啥我的笔记本上安装RStudio运行不了,一打开就出现下面的错误

“无法加载文件或程序集”错误。可以在我的电脑上运行,但不能在其他电脑上运行

Eclipse“错误:无法找到或加载主类”

为啥 json-server 只能在 localhost 服务器上工作?

无线wifi开启后,为啥笔记本上不了网了?