【问题标题】:Disk Read Function not Working as Expected in Bootloader磁盘读取功能在引导加载程序中未按预期工作
【发布时间】:2020-12-05 14:22:24
【问题描述】:

我正在尝试开发一个基本的引导加载程序,但是当我尝试创建一个用于从硬盘驱动器读取其他扇区的函数时遇到了问题。我正在 NASM 中的 Kali Linux 上开发它,并使用 QEMU 作为我的模拟器。 这是我的主要引导加载程序文件:

[org 0x7c00]

mov bp, 0x8000
mov sp, bp

call read_disk

mov si, my_string
call print          ;prints a string, si points to the string to be printed

jmp $

read_disk
    mov ah, 0x02    ;read from disk
    mov al, 0x01    ;read one sector
    mov ch, 0x00    ;read from cylinder 0
    mov dh, 0x00    ;read from head 0
    mov cl, 0x02    ;read the second sector

    mov bx, 0
    mov es, bx
    mov bx, 0x7c00+512

    int 0x13

    jc disk_error   ;BIOS sets the carry flag if disk read was unsuccessful

    ret

disk_error:
    mov si, error_msg
    call print
    jmp $
;
;Functions
;
%include "functions/print.asm"
%include "functions/print_hex.asm"
%include "functions/print_nl.asm"
%include "functions/calc_len.asm"
%include "functions/find_string.asm"

;
;Data
;
error_msg:
    db 'Error reading disk', 0

times 510-($-$$) db 0    ;pad out the rest of the bootloader with zeros to increase the size to 512 bytes
dw 0xaa55                ;Magic bytes so BIOS recognizes the hard drive as bootable

;
;SECOND SECTOR
;

my_string:
    db 'Disk read successful', 0

times 512 db 0   ;need to pad out the rest of the sector with zeros since  QEMU requires it

如您所见,my_string 位于 512 字节之后,在模拟硬盘的第二个扇区中。但是当我编译并运行引导加载程序时,它不会输出任何东西。在我上面提供的代码中,我在read_disk 函数结束后打印my_string 。但奇怪的是,如果我移动打印my_string inside 函数的两行,它就可以工作。 这是有效的代码:

[org 0x7c00]

mov bp, 0x8000
mov sp, bp

call read_disk

jmp $

read_disk
    mov ah, 0x02    ;read from disk
    mov al, 0x01    ;read one sector
    mov ch, 0x00    ;read from cylinder 0
    mov dh, 0x00    ;read from head 0
    mov cl, 0x02    ;read the second sector

    mov bx, 0
    mov es, bx
    mov bx, 0x7c00+512

    int 0x13

    jc disk_error   ;BIOS sets the carry flag if disk read was unsuccessful

    mov si, my_string
    call print          ;prints a string, si points to the string to be printed

    ret

disk_error:
    mov si, error_msg
    call print
    jmp $
;
;Functions
;
%include "functions/print.asm"
%include "functions/print_hex.asm"
%include "functions/print_nl.asm"
%include "functions/calc_len.asm"
%include "functions/find_string.asm"

;
;Data
;
error_msg:
    db 'Error reading disk', 0

times 510-($-$$) db 0    ;pad out the rest of the bootloader with zeros to increase the size to 512 bytes
dw 0xaa55                ;Magic bytes so BIOS recognizes the hard drive as bootable

;
;SECOND SECTOR
;

my_string:
    db 'Disk read successful', 0

times 512 db 0   ;need to pad out the rest of the sector with zeros since  QEMU requires it

如果有人能向我解释这个奇怪的怪事,我将不胜感激。

【问题讨论】:

  • QEMU 允许您附加调试器,如 GDB;用它来查看执行的去向。这是使用模拟器的一半。它会显示ret 会去你不想要的地方,给正确的方向一个很大的提示。

标签: assembly x86 nasm bootloader osdev


【解决方案1】:

在读入内存之前,您应该设置 SS:SP 而不仅仅是 SPSS 可能为零,也可能不是。如果 SS 恰好是 0x0000,那么您的堆栈位于 0x0000:0x8000 并且它将从那里向下增长。

您的代码将磁盘上的第二个 512 字节扇区读取到内存 0x0000:0x7e00,其中包括直到并包括位于堆栈中 0x0000:0x7ffe 到 0x0000 的 disk_read 函数的返回地址的所有字节:0x7fff.

由于您破坏了堆栈,int 0x13 可能永远不会返回,因为内部数据、返回地址和标志已损坏。像这样破坏堆栈会产生不可预知的结果。考虑将堆栈放在引导加载程序下方的 0x0000:0x7c00,以免干扰引导加载程序后加载的数据和代码。

注意:您应该将所有需要的段寄存器设置为您期望的值。您不应依赖任何包含特定值的段寄存器。 BIOS 不保证它们的值,尽管在大多数模拟器中它们将是 0x0000。

【讨论】:

  • 有效!非常感谢您的帮助。但我不禁想知道为什么没有教程提到它以及为什么它对其他人有用。尽管 QEMU 非常受欢迎,但我在其他任何地方都找不到与我相同的问题。为什么它对其他人有用?
  • @0xSkid 这取决于教程。如果本教程将堆栈放置在 0x0000:0x8000 但读取到 0x8000 以上的内存,则堆栈不会被破坏。另一种可能性是教程没有将堆栈放置在 0x0000:0x8000 (我必须查看教程的链接才能知道)。另一种可能是您使用的教程有问题。
  • 这是链接:cs.bham.ac.uk/~exr/lectures/opsys/10_11/lectures/os-dev.pdf 您需要查看文档的第 28 页。该文件的作者还为同一主题制作了一个视频播放列表。我刚去看了一下,好像他把堆栈设置在0x0000:0xffff,但没有说什么,所以我没有注意到。
  • @0xSkid :第一个示例有效,因为它不会将任何 data.code 从磁盘加载到内存中,因此堆栈不会低于 0x8000。它只是打印到显示器上。如果您查看该教程中执行磁盘读取的示例,他们会将堆栈更改为 0x9000(从 0x8000)。通过这样做,磁盘读取不会破坏堆栈。
猜你喜欢
  • 1970-01-01
  • 2013-08-29
  • 2020-04-28
  • 2021-08-27
  • 1970-01-01
  • 2013-06-02
  • 2019-02-18
  • 2012-10-07
  • 2014-08-13
相关资源
最近更新 更多