【问题标题】:int 13h doesn't appear to read sectors containing my kernelint 13h 似乎没有读取包含我的内核的扇区
【发布时间】:2015-03-12 02:53:58
【问题描述】:

我正在尝试使用 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

我正在使用来自chrysocomedd。谁能解释这种行为?

【问题讨论】:

  • 对我来说唯一看起来“有趣”的是of=\.\e:。它必须有效,否则您根本看不到任何输出。我习惯使用的是of=/dev/fd0 - 我有一个真正的软盘驱动器。对于 USB,我会“期待”类似 of=/dev/sdb(?) 的东西。这是dd 的Windows 端口吗?这可以解释我的困惑。我在您的代码中没有发现任何错误 - 实际上非常好。祝你好运!
  • 是的,它是一个 Windows 端口。有没有其他的程序我可以试试?
  • "rawrite" 也许(我不熟悉 Windows 工具),但我认为您的 dd 必须工作,否则您根本看不到任何输出。返回int 13h。我认为检查ah 的错误号会起作用,但错误的指示是进位标志设置。你可以试试jc reset...
  • @MichaelMorrow :这是一个较老的问题,您可能已经弄清楚了,但我知道出了什么问题。为了为这个问题制定一个有用的答案,这将有助于我了解您使用的 dd 的版本。是cygwin下的dd吗?系统? msys2?还是您使用了这里的一个:chrysocome.net/dd?还是其他地方的?
  • 您好,我使用的是 chrysocome.net 版本。

标签: assembly x86 usb bootloader dd


【解决方案1】:

虽然这个问题很老,但它确实揭示了一个我知道其他人经历过的问题,确实值得一个正确的答案。

Frank Kotler 指出您的代码看起来不错。我有几个小问题:

  • 在使用 lodsb 之前,您没有使用指令 CLDSTD 指定方向标志。在您的情况下,它应该是 CLD,因为您希望 LODSB 朝着积极的方向前进。你可以在我写的另一个StackOverflow answer中看到更详尽的解释。
  • 虽然您的目标是 16 位代码,但您设置的 fsgs 段寄存器在 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= 用于输入磁盘和输出磁盘。如果是磁盘上的唯一分区,则选择整个磁盘。例如:如果你插入一个 U 盘并且它被挂载为 f: 那么 'id=f:' 将选择 U 盘(不仅仅是像 if=\.\f: 这样的分区)

要写入分区 E: 所属的 USB 驱动器的开头,您可以执行此操作(我假设您以具有管理员权限的用户身份运行命令提示符) :

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

请注意我现在如何指定 (od=e:)。 od=(output device) 只是表示我们要使用E:所属的整个物理设备分区。

观察到的行为说明

您可能感兴趣的是为什么您的引导加载程序似乎工作但打印出0x0000。我将根据对 Windows 的了解提供我的最佳猜测。

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

现在如果我们的代码写在分区中,为什么我们的引导加载程序甚至会运行,为什么它会打印错误的值 (0x0000)?

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

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

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

所以现在我们知道为什么我们的引导加载程序会运行,为什么它会打印出错误的值?现在可能更清楚了,磁盘的第二个扇区没有我们的内核,分区包含它。磁盘读取 (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)。

【讨论】:

  • 非常有趣的答案。谢谢你解释。我一定会记住这一点以供将来参考!
猜你喜欢
  • 2014-09-16
  • 1970-01-01
  • 2013-11-20
  • 2020-02-22
  • 2019-10-26
  • 2019-09-05
  • 2013-11-02
  • 2011-04-08
相关资源
最近更新 更多