当磁盘不是硬盘驱动器时,读取磁盘时出错。 Int 0x13 ah 0x02

时间:2019-08-30 16:54:51

标签: assembly x86 x86-16 bootloader osdev

我正在编写一个简单的操作系统,但从磁盘读取时遇到很多问题。我使用int 0x13和ah = 0x02从驱动器读取数据,并且收到了一些不同的错误消息。当我与

一起跑步时
$ qemu-system-x86_64 -drive file=os.bin,if=floppy,index=0,media=disk,format=raw

效果很好。当我这样做

$ qemu-system-x86_64 -drive file=os.bin,format=raw

已设置进位标志,并且ah为0x20。根据{{​​3}},这是“控制器故障”错误。这没有多大意义,因为它是在vm中运行的,所以我很确定这是我的代码错了。

当我将启动映像写入磁盘(dd写入闪存驱动器上的分区)时,它会引导并成功启动程序,但在相同的磁盘负载下失败,ah为0x01。同一站点说这是“ AH中的函数无效或参数无效”错误,它进一步确认了问题出在我的代码中。我不得不拼凑出一个糟糕的print_hex解决方案,该解决方案只能打印出X个数字,因为我没有动力将更好的东西放在一起。

这是我的load_disk.asm文件:

disk_load:
  pusha
  push bx
  mov bx, DISK_START
  call print_string
  pop bx   

  push dx
  mov ah, 0x02
  mov al, dh
  mov cl, 0x02
  mov ch, 0x00
  mov dh, 0x00


  int 0x13
  jc disk_error0
  pop dx
  cmp dh, al
  jne disk_error1

  push bx
  mov bx, DISK_SUCC
  call print_string
  pop bx  
  popa
  ret

disk_error0:
  loopY:
  cmp ah, 0x0
  je cont
  mov bx, STARTING_DISK_ERROR
  call print_string  
  sub ah, 1
  jmp loopY
cont:
; print a nice little counter
  mov bx,NEWLINE
  call print_string 
  mov ah,8 ; 80 character screen, 10 characters
loopS:
  cmp ah,0x0
  je cont2
  mov bx, NUMBERS
  call print_string
  sub ah, 1
  jmp loopS

cont2:
  mov bx,NEWLINE
  call print_string
  mov bx, DISK_ERROR_0
  call print_string
  jmp $

disk_error1:
  mov bx, DISK_ERROR_1
  call print_string
  jmp $

STARTING_DISK_ERROR : db "X",0
NEWLINE: db 10,13,0
NUMBERS: db "1234567890",0
DISK_ERROR_0 : db "Error0",10,13, 0
DISK_ERROR_1 : db "Error1",10,13, 0
DISK_START : db "Startingdisk", 10,13, 0
DISK_SUCC : db "Loadeddisk", 10,13,0

我已将字符串截断以在512字节的调试代码中腾出空间。这段代码是从boot.asm中调用的,它是

[bits 16]
[org 0x7c00]
  jmp 0x0000:main_entry    ; ensures cs = 0x0000

main_entry:
  xor ax, ax
  mov ds, ax
  mov es, ax
  KERNAL_OFFSET equ 0x1000

  mov [BOOT_DRIVE], dl

  mov bp, 0x9000
  mov sp, bp


  mov bx, MSG_REAL_MODE
  call print_string



  call load_kernal
  call switch_to_pm 

  ;this line will never execute
  jmp $

%include "src/print_string.asm"
%include "src/disk_load.asm"
%include "src/gdt.asm"
%include "src/print_string_pm.asm"
%include "src/switch_to_pm.asm"
%include "src/print_hex.asm" ; this is broken, don't use

[bits 16]
load_kernal:
  mov bx, MSG_LOAD_KERNAL
  call print_string

  mov bx, KERNAL_OFFSET

  mov dh, 31            ; load 31 sectors. gives plenty of room
  mov dl, [BOOT_DRIVE]
  call disk_load 
;  mov bx, MSG_LOAD_DISK
;  call print_string
  ret

[bits 32]

BEGIN_PM:
  mov ebx, MSG_PROT_MODE
  call print_string_pm
  call KERNAL_OFFSET 
  mov ebx, 0x5000
  call print_string_pm
  jmp $ ; if the kernal returns, stay here



BOOT_DRIVE      db 0
MSG_REAL_MODE   db "Started in 16-bit", 10, 13, 0
MSG_PROT_MODE   db "Sted in 32-bit", 0
;MSG_SHOULD_NEVER_PRINT db "Frack",10,13, 0
MSG_LOAD_KERNAL db "Loding kernal",10,13, 0
;MSG_LOAD_DISK   db "Loaded disk!", 10,13,0
MSG_KERNAL_EXIT db "kernal has exited",10,13,0



times 510-($-$$) db 0

dw 0xaa55

我一直在寻找http://www.ctyme.com/intr/rb-0606.htm#Table234作为该项目的基础。但是,由于它是一张软盘,因此在这种情况下它的帮助有限。

编辑:我以为我得到了所有相关文件,但看来我没有:( 这是print_string.asm

; prints the string at location BX
print_string:
  push ax
  push bx
  push dx ; NEW
  mov ah, 0x0e
  loop1:
    mov al, [bx]
    int 0x10
    add bx, 1
    mov dl, [bx]
    cmp dl, 0x0
    jne loop1
  pop dx ; NEW
  pop bx
  pop ax
  ret

在评论提到它之后,我向该文件添加了push dx / pop dx。 ah现在为12,或0x0C,即“不受支持的曲目或无效的媒体”。

这可能与硬盘的结构或其他问题有关。我正在使用cat来组装最终的os.bin文件,这对我来说意义不大。这是我的Makefile行(如果有帮助,我可以发布整个makefile)

os.bin : build/boot.bin build/kernal.bin
    cat build/boot.bin build/kernal.bin > $@

build / boot.bin是我的所有程序集,均以前512个字节加载。 kernal.bin是我应该从磁盘加载的C代码

1 个答案:

答案 0 :(得分:1)

您没有显示内核,但是我可以做出一些有根据的猜测。尽管在某些版本的QEMU上可能有所不同,但从磁盘映像以软盘启动时,您会发现可以读取文件末尾的扇区,但是作为硬盘驱动器启动的容忍度较低。

您的代码在加载内核时从CHS(0,0,2)开始读取31个扇区。您没有显示内核(kernel.bin),但我怀疑它的大小小于31个扇区。

当您这样做:

qemu-system-x86_64 -drive file=os.bin,if=floppy,index=0,media=disk,format=raw

您作为第一张软盘启动。由于QEMU通常允许您读取软盘映像末尾的内容,因此Int 13h/AH=2成功。

当您这样做:

qemu-system-x86_64 -drive file=os.bin,format=raw

您作为第一个硬盘启动。 QEMU可能会抱怨,因为您已请求读取31个扇区的数据,但是磁盘映像os.bin中没有那么多数据。我相信一般规则是,要使QEMU的硬盘读取正常工作,必须在一个扇区中至少有1个字节的数据才能成功读取。这意味着您至少必须拥有一个{strong>至少的os.bin 512字节(引导扇区)+ 30 * 512字节(内核)+1(至少1字节)在第31个扇区中)= 15873个字节。我希望然后,如果您的图像文件小于15873字节,则从CHS(0,0,2)/ LBA(Logical Block Address)= 1读取31个扇区将失败。这可能是您收到错误的原因:

  

不受支持的曲目或无效的媒体

修复非常简单。确保您的os.bin至少为32个扇区(引导扇区+内核最多31个扇区)或文件大小为32 * 512 = 16384。您可以使用DD程序生成16384字节的图像,然后使用DD将boot.binkernel.bin文件放入其中。

您用于构建os.bin的Makefile条目可能类似于:

os.bin : build/boot.bin build/kernal.bin    
    dd if=/dev/zero of=$@ bs=512 count=32
    dd if=build/boot.bin of=$@ bs=512 conv=notrunc
    dd if=build/kernal.bin of=$@ bs=512 seek=1 conv=notrunc

第一个命令使用块大小(os.bin)为512创建一个名为bs的零填充文件,并生成一个包含32个块的文件。 32 * 512 =16384。第二个命令将boot.bin写入文件的开头到块0(第一个块)。 conv=notrunc表示将boot.bin写入os.bin后,我们不希望文件被截断。最后一行是相似的,但是它将kernal.bin写入os.bin,但是告诉DD试图在磁盘上阻止1并写入文件,并且不要在完成后截断os.bin

此Makefile配方完成后,您应该拥有一个16384字节长的os.bin文件,其中包含引导加载程序和内核。使用Int 13h / AH = 2时,无论是作为软盘或硬盘映像进行读取,这都应使QEMU保持满意。


使用USB FDD仿真在真实硬件上引导

在使用软盘驱动器(FDD)仿真在真实计算机上作为USB引导时,您可能会发现引导加载程序开始运行,但似乎无法正常工作。这是因为,许多将USB介质作为软盘启动的BIOS都假定存在BIOS参数块(BPB),并且在将引导扇区读入内存之后以及将控制权转移到物理地址0x07c00之前盲目更新驱动器几何形状字段。没有BPB,这些更改可能会覆盖数据和/或指令,从而导致代码无法按预期运行。有关此现象的更多信息和示例BPB可以在我的其他Stackoverflow answers中找到。

在真实硬件上,如果磁盘操作失败,请重试几次也是一个好主意。您可以通过在重新尝试该操作之前调用BIOS函数Int 13h/AH=0(磁盘重置)来完成此操作。如果失败多次,则可以中止。我不相信这是您的问题,但出于完整性考虑,我会提及它。


其他观察结果

您的引导程序以以下内容开头:

main_entry:
  xor ax, ax
  mov ds, ax
  mov es, ax
  KERNAL_OFFSET equ 0x1000

  mov [BOOT_DRIVE], dl

  mov bp, 0x9000
  mov sp, bp

您的代码仅设置 SS ,而不设置 SP 。结合 SS:SP 创建堆栈指针。由于尚未设置 SS ,因此我们真的不知道堆栈在内存中的哪个位置。进入自举程序时,不能保证 SS 为0(有关更多信息和建议,请参见我的general bootloader tips Stackoverflow答案)。由于您的代码甚至似乎都不使用 BP (通常是堆栈帧指针),因此无需将其设置为0x9000。只需将 SS:SP 设置为0x0000:0x9000。代码如下:

main_entry:
  xor ax, ax
  mov ds, ax
  mov es, ax
  mov ss, ax               ; Set SS to 0
  mov sp, 0x9000           ; Set SP right after SS (see my bootloader tips for reason why) 

  mov [BOOT_DRIVE], dl

  KERNAL_OFFSET equ 0x1000