引导加载程序的问题

时间:2015-09-23 11:20:12

标签: assembly bootloader

我正在学习Assembly,我尝试编写自己的bootloader。它在VirtualBox上运行良好,但它不适用于实际的PC。 在电脑'Hello World!'不打印。

这是代码使用:

BITS 16
ORG 0x7C00

jmp boot_sector

;------------------------------

OEMLabel            db "FLOPPYDR"
BytesPerSector      dw 512
SectorsPerCluster   db 1
ReservedForBoot     dw 1
NumberOfFats        db 2
RootDirEntries      dw 224
LogicalSectors      dw 2880
MediumByte          db 0xF0
SectorsPerFat       dw 9
SectorsPerTrack     dw 18
Sides               dw 2
HiddenSectors       dd 0
LargeSectors        dd 0
DriveNo             dw 0
Signature           db 41
VolumeID            dd 0x00
VolumeLabel         db "FLOPPYDRIVE"
FileSystem          db "FAT12"

;##############################
          boot_sector:
;##############################

  mov ax, 0x0000            ; Set up the stack
  mov ss, ax                ; Is this done correctly?
  mov sp, 0x7C00            ; (I dont quite understand)

  int 0x10                  ; Set video mode

  int 0x13                  ; Reset the drive

  mov ah, 0x02              ; Read more sectors
  mov al, 2                 ; Read two extra sectors,
  mov bx, main_sector       ; starting from the second.
  mov ch, 0                 ;
  mov cl, 2                 ; dl has been set already (?)
  mov dh, 0                 ;
  int 0x13                  ;

  mov [bootdev], dl         ; Store original dl in bootdev

  jmp main_sector           ; Go to the main sector (0x200 I think)

  times 510 - ($ - $$) db 0 ; Fill in the rest of the sector with 0s
  dw 0xAA55                 ; and 0xAA55 at the end for signature

;##############################
          main_sector:
;##############################

jmp Start

;------------------------------

bootdev db 0
msg db 'Hello World!', 10, 13, 0

;------------------------------

print_string:
  mov ah, 0x0E
  mov bh, 0
  cmp al, 0
  jne .loop
  mov bl, 0x0F
  .loop:
    lodsb
    cmp al, 0
    je .end
    int 0x10
    jmp .loop
  .end:
    ret

;------------------------------

  Start:
    mov si, msg
    call print_string
    hlt

    times 512 - ($ - main_sector) db 0

我也在一些问题上发表了评论,但这些不是我的主要问题(好吧,也许我不知道答案会导致问题)。为什么这不适用于真正的PC?

要编译我使用nasm -f bin boot.asm -o boot.bin并创建虚拟的floppydisk文件我使用mkfile 1474560 floppy.flp

然后我使用HexEdit打开floppy.flp并将前64行(0x00 - 0x3F)替换为boot.bin文件的内容(使用HexEdit打开)。

E9 38 00 46 4C 4F 50 50 59 44 52 00 02 01 01 00
02 E0 00 40 0B F0 09 00 12 00 02 00 00 00 00 00
00 00 00 00 00 00 29 00 00 00 00 46 4C 4F 50 50
59 44 52 49 56 45 46 41 54 31 32 B8 00 00 8E D0
BC 00 7C CD 10 CD 13 B4 02 B0 02 BB 00 7E B5 00
B1 02 B6 00 CD 13 88 16 03 7E E9 A3 01 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 55 AA <-- End of first sector
E9 24 00 00 48 65 6C 6C 6F 20 57 6F 72 6C 64 21
0A 0D 00 B4 0E B7 00 3C 00 75 02 B3 0F AC 3C 00
74 04 CD 10 EB F7 C3 BE 04 7E E8 E6 FF F4 00 00 (The rest is just 0's).

这是我将floppy.flp刻录到USB驱动器时的终端:

Last login: Wed Sep 23 12:10:48 on ttys000
MacBook-Air:~ sasha$ cd ~/Desktop
MacBook-Air:Desktop sasha$ diskutil list
/dev/disk0
   #:                       TYPE NAME                    SIZE       IDENTIFIER
   0:      GUID_partition_scheme                        *121.3 GB   disk0
   1:                  Apple_HFS                         209.7 MB   disk0s1
   2:          Apple_CoreStorage                         120.5 GB   disk0s2
   3:                 Apple_Boot Recovery HD             650.0 MB   disk0s3
/dev/disk1
   #:                       TYPE NAME                    SIZE       IDENTIFIER
   0:                  Apple_HFS Macintosh HD           *120.1 GB   disk1
                                 Logical Volume on disk0s2
                                 8CD6A846-395D-4C97-A5DE-0A7ABA9F1C99
                                 Unencrypted
/dev/disk2
   #:                       TYPE NAME                    SIZE       IDENTIFIER
   0:     Apple_partition_scheme                        *17.1 MB    disk2
   1:        Apple_partition_map                         32.3 KB    disk2s1
   2:                  Apple_HFS Flash Player            17.1 MB    disk2s2
/dev/disk3
   #:                       TYPE NAME                    SIZE       IDENTIFIER
   0:                            FLOPPYDRIVE            *1.0 GB     disk3
MacBook-Air:Desktop sasha$ diskutil unmountdisk /dev/disk3
Unmount of all volumes on disk3 was successful
MacBook-Air:Desktop sasha$ sudo dd bs=512 if=floppy.flp of=/dev/disk3
Password:
2880+0 records in
2880+0 records out
1474560 bytes transferred in 0.843982 secs (1747146 bytes/sec)
MacBook-Air:Desktop sasha$ 

提前谢谢。

2 个答案:

答案 0 :(得分:2)

当启动引导加载程序时,几乎所有寄存器(包括段寄存器)的内容都是&#34;未定义&#34;。实际上唯一具有已知值的寄存器是DL(其中包含BIOS&#39;磁盘的驱动器号)。

所有引用内存的指令都使用隐含或显式段寄存器。例如,mov [bootdev], dllodsb都依赖于(隐含的)DS段寄存器,该寄存器从未设置且仍未定义。

与所有未定义的值一样,它们可能(由于纯粹的运气)使它们成为使事情无意中发挥作用的价值。如果BIOS碰巧在DS中保留值0x0000,那么您的代码将起作用。

答案 1 :(得分:2)

我最近在Stackoverflow上了解了一些关于引导加载程序的问题written。大多数问题涉及它在一个仿真器或VM上工作而在另一个(或物理硬件)上工作的情况,通常归结为当BIOS跳转到您的代码时对段寄存器的状态做出错误的假设。在某些仿真器下,段寄存器可能具有更多合理的值,但通常情况并非如此。根据我之前的回答,我有两个似乎适用的提示:

  1. 当BIOS跳转到您的代码时,您无法依赖 DS ES SS SP < / em>寄存器具有有效或预期的值。应该在启动引导程序启动时正确设置它们。
  2. 可以设置或清除lodsbmovsb等使用的方向标记。如果方向标志设置不正确 SI / DI 寄存器可能会以错误的方向调整。使用STD / CLD将其设置为您希望的方向(CLD =向前/ STD =向后)。在这种情况下,代码假定向前移动,因此应使用CLD。有关详细信息,请参阅instruction set reference
  3. 您的汇编代码设置为编译和链接,假设原点为0x7C00(通过ORG 0x7C00)。访问 msg bootdev 等变量的代码将假设其内存地址在段( DS )中是绝对的。这意味着如果您的 DS 段无效,那么您可能会在错误的位置处理变量,数据和标签。举个例子:

    mov [bootdev], dl 
    

    隐含引用 DS ,可以写成:

    mov DS:[bootdev], dl
    

    如果 DS 中有一些随机值,那么您可能会在您不期望的地方访问内存。对于某些环境, DS 可能只是零,因此您的代码可以正常工作。

    您如何知道要使用哪个细分?引导加载程序由BIOS在物理内存0x0000:0x7C00(段:偏移)加载。您的原点(使用ORG指令设置)与偏移量匹配,以便在您的情况下 DS 中的含义应设置为零。

    在您的代码中, ES 也应设置为零。原因是INT 0x13 AH=0x02(磁盘读取)说:

      

    ES:BX缓冲区地址指针

    想象一下,如果将 ES 设置为随机垃圾,则磁盘读取可能会读入您不想要的内存中。因此,就像 DS 一样,还必须设置 ES 。您已将引导加载程序和内核写入原点为0x7C00的同一文件中,因此您只需要将 ES 段设置为零。

    设置堆栈时,您可以适当地设置 ES DS

    mov ax, 0x0000            ; Set up the stack
    mov ss, ax                ; Is this done correctly?
    mov sp, 0x7C00            ; (I dont quite understand)
    mov ds, ax                ; Set DS to 0 because that is what your code needs
    mov es, ax                ;     ES the same as DS.
    cld                       ; Read my tip #2
    

    你确实问过你是否正确设置了堆栈。没有什么问题。您的指令有效地设置了一个堆栈,该堆栈从引导加载程序占用区域下方的0x0000:0x7C00向下增长。这留下了大约27kb(0x7C00-0x1000)的堆栈空间。 4k足以用于BIOS调用和您当前的代码。第一个0x1000内存通常用于中断表/ BIOS数据区等。

    我在代码中注意到的另一个错误是当您尝试重置磁盘驱动器时:

    int 0x10                  ; Set video mode
    int 0x13                  ; Reset the drive
    

    您将 AX 设置为这两行以上的零。 INT 0x10 AH=0x00(设置视频模式)具有在 AX 中返回信息的副作用。由于 AX 可能遭到破坏,因此您对INT 0x13 AH=0x00的调用可能会出错。在调用int 0x13之前,您需要清除 AH (或所有 AX )以重置驱动器。代码应如下所示:

    int 0x10                  ; Set video mode
    xor ax,ax                 ; clear AX (AH=0)
    int 0x13                  ; Reset the drive
    

    程序顶部存在一个小问题,如果您将此引导加载程序放在格式正确的FAT12磁盘映像上并尝试将其安装在您的操作系统中,则可能只会出现问题。你有:

    jmp boot_sector
    ;------------------------------    
    OEMLabel            db "FLOPPYDR"
    

    引导加载程序中的磁盘结构应该从第4个字节开始OEMLabeljmp boot_sector是一个3字节的指令。在 JMP 之后放置NOP(1字节指令)会将OEMLabel推送到文件中的第4个字节。它应该是这样的:

    jmp boot_sector
    nop
    ;------------------------------    
    OEMLabel            db "FLOPPYDR"
    

    避免使用hexedit在磁盘映像的开头手动插入引导加载程序代码的技巧是使用dd。您可以使用dd覆盖前1024个字节并保持其余部分不变。试试dd if=boot.bin of=floppy.flp bs=512 count=2 conv=notrunc。这应该打开 floppy.flp 写入包含来自 boot.bin 的1024个字节的2个512字节扇区,而不截断文件( conv = notrunc