int 13h似乎没有读取包含我的内核的扇区

时间:2015-03-12 02:53:59

标签: assembly x86 usb bootloader dd

我正在尝试使用USB上的引导加载程序加载一些数据,但显然int 13h不起作用!

引导器:

[bits 16]
[ORG 0x7c00]

jmp 0x0000:start
start:

cli
xor ax, ax
mov ss, ax
mov sp, 0x7c00

mov ax, cs
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
sti

mov [driveno], dl

reset:
    ;reset drive
xor ax, ax
mov dl, [driveno]
int 0x13
or ah, ah
jnz reset


mov ah, 0x02
mov al, 0x01
mov bx, 0x0000
mov es, bx
mov bx, 0x7e00

mov dl, [driveno]

xor dh, dh

mov cx, 0x0002

int 0x13
or ah, ah
jnz reset


mov dx, [0x7e00]   ;So i can check and see if it has been loaded
call printhex

cli
hlt
jmp $

print:
loop:
lodsb
or al, al
jz done
mov ah, 0x0e
mov bx, 0x0003 ;page 0 and default color
int 0x10
jmp loop
done:
ret

printhex:
push bx
push si
mov si, hex_template

mov bx, dx
shr bx, 12
mov bx, [bx+hexabet]
mov [hex_template+2], bl

mov bx, dx
shr bx, 8
and bx, 0x000f
mov bx, [bx+hexabet]
mov [hex_template+3], bl

mov bx, dx
shr bx, 4
and bx, 0x000f
mov bx, [bx+hexabet]
mov [hex_template+4], bl

mov bx, dx
and bx, 0x000f
mov bx, [bx+hexabet]
mov [hex_template+5], bl

call print
pop si
pop bx
ret

hex_template db '0x????',0
hexabet db '0123456789abcdef'

driveno db 0      ;Storing dl

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

dw 0x1234  ;This wont load!!!!

times 510 db 0

我希望我的十六进制转储方法将0x1234打印到屏幕,但它会打印0x0000而不是!我知道我的十六进制转储方法有效,问题是0x1234永远不会被加载到第一位。有什么想法吗?

我在Windows上运行。我编译并生成一个图像:

nasm -f bin -o boot.bin boot.asm
dd if=boot.bin of=\\.\e: bs=512 count=2

我正在使用chrysocome中的dd。任何人都可以解释这种行为吗?

1 个答案:

答案 0 :(得分:3)

虽然这个问题已经很久了,但它确实揭示了我所知道的其他人应该遇到的问题。

正如 Frank Kotler 指出你的代码看起来很好。我有几个小的挑剔:

  • 在使用 lodsb 之前,您没有使用指令 CLD STD 指定方向标志。在您的情况下,它应该是 CLD ,因为您希望 LODSB 向正方向前进。你可以在我写的另一篇StackOverflow answer中看到更全面的解释。
  • 虽然您的目标是16位代码,但您正在设置 fs gs 段寄存器,这些寄存器不可用8086/8088/80286。

问题和解决方案

问题是您使用 DD 程序。你这样做:

dd if=boot.bin of=\.\e: bs=512 count=2

通常,您使用of=\\.\e:使用双反斜杠,但这不是问题的原因。 \.\e:实际上并未指向磁盘(或USB驱动器)的开头。它指向 E:指向的分区的开头。因此,您在分区数据的开头编写了引导加载程序,而不是主引导记录(MBR)。较新版本的DD支持指定整个设备开头的选项(不仅仅是分区):

  

版本0.6beta1的变化

     

新功能id = / od =用于输入磁盘和输出磁盘。如果是磁盘上的唯一分区,则选择整个磁盘。例如:如果您插入一个USB磁盘并将其安装为f:那么' id = f:'将选择USB磁盘(不仅仅是if = \。\ f:将要执行的分区)

要写入USB驱动器的开头,分区 E:是其中的一部分,您可以这样做(我假设您以具有Admin的用户身份运行命令提示符权限):

dd if=boot.bin od=e: bs=512 count=2

注意我现在如何指定(od=e:)。 od=(输出设备)只是意味着我们要使用整个物理设备分区 E:

观察到的行为的解释

可能感兴趣的是为什么你的bootloader似乎工作但打印出0x0000。基于对Windows的了解,我会提出最佳猜测。

由于 DD 写入分区开头(而不是驱动器的开头)的问题,扇区#2(基于1)实际上并不包含我们的小内核。扇区#1甚至不包含我们编写的引导加载程序!

现在如果我们的代码是在分区中编写的,为什么我们的bootloader甚至会运行,为什么它会打印错误的值(0x0000)?

如果您使用Windows格式化USB驱动器,则会发生这种情况。您可能没有意识到的是,默认情况下,USB驱动器的格式类似于具有单个分区的硬盘驱动器,并且该一个分区被标记为可引导。 Microsoft在MBR(磁盘的第一个扇区)中安装引导加载程序。这个Microsoft引导加载程序是一个链式加载器,它通常具有这样的功能(一些细节可能会有所不同):

  • 正确设置细分和堆叠
  • 保存 DL [我们启动的驱动器]
  • 将自己从物理地址0x00007C00移到其他地方。
  • 跳转到我们应该继续的新内存位置的偏移量
  • 查看分区表中标记为bootable的分区
  • 可引导分区(非磁盘)的第一扇区(512字节)读入内存0x00007C00
  • 使用第一步中保存的值
  • 恢复 DL
  • FAR JMP到0x0000:0x7C00
  • 分区引导加载程序的执行就像BIOS已加载并直接跳转到它一样。

我们现在拥有的是Microsoft MBR引导加载程序读取USB驱动器上可引导分区的第一个扇区并执行它。分区的第一个扇区恰好是我们的引导加载程序,因为我们使用了dd if=boot.bin of=\.\e: bs=512 count=2。我们实际上写了两个部门。 分区的第二个扇区(不是磁盘)包含我们的内核。所以我们的引导加载程序运行得很有效!

所以现在我们知道为什么我们的bootloader运行了,为什么它打印出错误的值?现在可能更清楚的是磁盘的第二个扇区没有我们的内核,分区包含它。磁盘读取( int 13h )代码执行此操作:

mov ah, 0x02            ; Disk Read
mov al, 0x01            ; Number of Sectors to read
mov bx, 0x0000
mov es, bx
mov bx, 0x7e00          ; ES:BX location to read to 0x0000:0x7E00
mov dl, [driveno]
xor dh, dh
mov cx, 0x0002          ; Read sector #2 (1-based, not 0-based)
int 0x13

我们只读取磁盘的第二个扇区,而不是分区。很可能第二个扇区被清零,因此我们的引导加载程序读取值为0x0000的原因。

结论

如果我们正确地写了磁盘的前2个扇区(1024字节)( DD ),那么我们的引导加载程序和内核就能正常工作..

感谢Microsoft及其链式加载器 - 我们的引导加载程序运行但是内核位于磁盘上的错误位置,我们打印出一个可能充满零的扇区。这一系列事件恰好使我们的引导程序运行并使其看起来 int 13h 失败。它可能根本没有失败,它只是读取了一个没有包含我们内核的扇区。

注意:我使用单词kernel,但在此问题的上下文中,它指的是存储在第二个扇区中的数据(0x1234)。