本文介绍了当磁盘不是硬盘驱动器时,读取磁盘时出错.整数0x13啊0x02的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm writing a simple operating system and I'm having a lot of problems with reading from the disk. I use int 0x13 and ah=0x02 to read data from the drive and I've been getting several different error messages. When I run with

$ 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.根据 http://www.ctyme.com/intr/rb-0606. htm#Table234 ,这是控制器故障"错误.这没有多大意义,因为它是在vm中运行的,所以我很确定这是我的代码错了.

the carry flag is set and ah is 0x20. According to http://www.ctyme.com/intr/rb-0606.htm#Table234, it's a "controller failure" error. This doesn't make much sense, as it's running in a vm, so I'm pretty sure that it's my code that is wrong.

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

When I write my boot image to a disk (dd to a partition on a flashdrive) it boots and successfully starts my program but fails at the same disk load, with ah being 0x01. The same site says that this is a "invalid function in AH or invalid parameter" error, which further confirms that the problem is in my code. I've had to throw together a poor print_hex solution that prints ah number of X's, because I haven't had the motivation to put something better together.

这是我的load_disk.asm文件:

Here is my load_disk.asm file:

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中调用的,它是

I've truncated my strings to make room in the 512 bytes for debug code. This code is called from boot.asm, which is

[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

我一直在浏览 https://www.cs.bham.ac.uk/~exr/lectures/opsys/10_11/lectures/os-dev.pdf 作为该项目的基础.但是,由于它是一张软盘,因此在这种情况下它的帮助有限.

I've been looking through https://www.cs.bham.ac.uk/~exr/lectures/opsys/10_11/lectures/os-dev.pdf as my foundation for this project. However, it assumes a floppy disk, so it is of limited help in this situation.

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

I thought I got all the relevant files, and it appears I didn't :(Here's 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,即不受支持的曲目或无效的媒体".

After a comment mentioned it, I added a push dx/pop dx to that file. ah is now 12, or 0x0C, which is "unsupported track or invalid media".

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

There's a chance that it's an issue with how hard drives are structured or something. I'm using cat to assemble my final os.bin file, which doesn't make that much sense to me. Here's my Makefile line (I can post the entire makefile if that would be helpful)

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

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

build/boot.bin is all of my assembly that is loaded in the first 512 bytes. kernal.bin is my C code that I should be loading from the disk

推荐答案

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

You don't show your kernel but I can make some educated guesses. Although it may vary on some versions of QEMU, you will find when booting as floppy from disk images that you are allowed to read sectors past the end of the file, but booting as a hard drive is less forgiving.

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

Your code reads 31 sectors starting from from CHS(0,0,2) when it loads the kernel. You don't show your kernel (kernel.bin) but I suspect that it's less than 31 sectors in size.

当您这样做:

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

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

you boot as the first floppy disk. Since QEMU generally allows you to read past the end of a floppy disk image the Int 13h/AH=2 succeeds.

当您这样做:

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

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

you boot as the first hard disk. QEMU is likely complaining because you have requested to read 31 sectors worth of data but there isn't that much data in the disk image os.bin. I believe the general rule is that for QEMU's hard disk read to work there has to be at least 1 byte of data in a sector for the read to succeed. That would mean that at a minimum you'd have to have an os.bin that is at least 512 bytes (bootsector) + 30 * 512 bytes (kernel) + 1 (at least 1 byte in the 31st sector) = 15873 bytes in size. I would expect then that if your image file is less than 15873 bytes, reading 31 sectors from CHS(0,0,2)/LBA(Logical Block Address)=1 will fail. That is likely why you are getting the error:

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

The fix is rather simple. Make sure your os.bin is at least 32 sectors (boot sector + maximum of 31 sectors for the kernel) or a file size of 32*512=16384. You can use the DD program to build a 16384 byte image and then use DD to place the boot.bin and kernel.bin files inside of it.

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

Your Makefile entry for building os.bin could probably look like:

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

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

The first command creates a zero filled file called os.bin using a block size (bs) of 512 and generating a file with 32 blocks. 32 * 512 = 16384. The second command writes boot.bin to the beginning of the file to block 0 (first block). The conv=notrunc says that after writing boot.bin to os.bin that we don't want the file to be truncated. The last line is similar but it writes kernal.bin to os.bin but tells DD to seek to block 1 on disk and write the file and not to truncate os.bin when finished.

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

After this Makefile recipe is complete you should have an os.bin file that is 16384 bytes long containing your bootloader and kernel. This should keep QEMU happy whether it is reading as a floppy or hard disk image when using Int 13h/AH=2.

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

When it comes to booting as USB on a real machine using Floppy Disk Drive (FDD) emulation you may find a bootloader starts running but doesn't appear to work correctly. This is because that many BIOSes that boot USB media as a floppy assume that a Bios Parameter Block (BPB) is present and blindly updates drive geometry fields after your boot sector is read into memory and before control is transferred to physical address 0x07c00. Without a BPB those changes could overwrite data and/or instructions causing the code to not work as expected. More information on this phenomenon and a sample BPB can be found in one of my other Stackoverflow answers.

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

It is also a good idea on real hardware to retry a disk operation a few times if it fails. You can do that by calling BIOS function Int 13h/AH=0 (Disk Reset) before retrying the operation again. If it fails more than a few times then you can abort. I don't believe this is your problem though, but I mention it for completeness.

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

Your bootloader starts with:

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(请参阅我的常规引导程序提示 Stackoverflow回答以获取更多信息和建议).由于您的代码甚至似乎都不使用 BP (通常是堆栈帧指针),因此无需将其设置为0x9000.只需将 SS:SP 设置为0x0000:0x9000.代码如下:

Your code only sets SS, and not SP. SS:SP combined create the stack pointer. Since SS hasn't been set we really don't know where in memory the stack is. There is no guarantee SS is 0 upon entry to our bootloader (see my general bootloader tips Stackoverflow answer for more information and suggestions). Since your code doesn't even seem to use BP (usually a stack frame pointer) there is no need to set it to 0x9000. Just set SS:SP to 0x0000:0x9000. The code could look like:

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

这篇关于当磁盘不是硬盘驱动器时,读取磁盘时出错.整数0x13啊0x02的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

10-10 07:59