首页 > 解决方案 > 为什么这种使用 BIOS 磁盘服务读取扇区不起作用?

问题描述

我正在尝试使用 BIOS 磁盘服务读取扇区功能读取扇区,但无论我做什么,进位标志都已设置,AH 为 0x01,这意味着它认为存在无效参数。我已经验证了寄存器的内容与我的分区表相对应,我还将在下面粘贴。磁盘映像只是在 Arch Linux 上使用 fdisk 创建的普通映像,没有什么特别之处。我在 QEMU 中运行它,因此这是 SeaBIOS。

我什至查看了 SeaBIOS 代码,据我所知,它返回无效参数错误的唯一方法是计数 (AL) 等于 0 或大于或等于 128,如果扇区(即CL&0x3f) 为 0,或者如果柱面、磁头或扇区大于磁盘具有的每个数量。据我所知,这一切似乎都不是真的。这个 CHS 地址应该是十进制的 (0, 32, 33) [我将在最后粘贴一个 C 程序来验证] 这应该对应于绝对扇区 2048。最后一个扇区是 (0, 65, 1) 所以第一个扇区不应超出范围。是的,DL 是 0x80,第一个驱动器。

我正在运行 qemu 像这样:qemu -gdb tcp::26000 -S -hda disk.img

那么发生了什么?

.equ MBR_ADDRESS_RESIDENT,      0x0600  # Where the MBR will reside             
.equ MBR_ADDRESS_ORIGINAL,      0x7c00  # Where the MBR was loaded              
.equ MBR_OFFSET_PT,             0x01be  # Offset of the MBR Partition Table     
.equ MBR_OFFSET_SIGNATURE,      0x01fe  # Offset of the MBR Signature           
.equ MBR_SIZE,                  0x0200  # Size of the MBR, 512B                 
                                                                                
.equ STACK_ADDRESS,             0x8000  # at the 32KB mark                      
.equ STACK_SIZE,                0x0200  # 512B                      

    cli         # Disable interrupts                                            
                                                                                
    xor ax, ax                                                                  
    mov ds, ax                      # DS := 0x0                                 
    mov es, ax                      # ES := 0x0                                 
    mov ss, ax                      # SS := 0x0                                 
                                                                                
    mov sp, STACK_ADDRESS   # Use the 512B at 0x7e00-0x800 for the stack        
    mov bp, sp                                                                  
                                                                                
    push dx                                                                     
                                                                                
    # Relocate this MBR sector                                                  
    mov si, MBR_ADDRESS_ORIGINAL    # Where this sector is located              
    mov di, MBR_ADDRESS_RESIDENT    # Where this sector will be relocated       
    mov cx, MBR_SIZE                # The size of this sector, 512B             
    cld                                                                         
    rep movsb                                                                   
                                                                                
    sti                                                                         
                                                                                
    ljmp 0x0000, main       # Initialize the code segment register              
                                                                  


    # --- [[SNIP CODE] ---
                                                

    # SI does point to the partition table. Even hardcoding the values of DX and CX changes nothing.
    mov dx, [si+0x00]                                                           
    # DL Verified to equal 0x80                                                 
    # DH Verified to equal 0x20                                                 
    mov cx, [si+0x02]                                                           
    # CL Verified to equal 0x21                                                 
    # CH Verified to equal 0x00                                                 
    mov bx, 0x7c00              # Address to load sector                        
    mov al, 0x01                # Read one sector                               
    mov ah, 0x02                # Read Sector Function                          
    int 0x13                    # BIOS Disk Service                             
    jc error                                                               
    jmp ok   

这是我的分区表

   $ dd if=disk.img of=disk.img.ptable bs=1 count=64 skip=446
   64+0 records in
   64+0 records out
   64 bytes copied, 0.000883791 s, 72.4 kB/s
   $ xxd disk.img.ptable 
   00000000: 8020 2100 0141 0100 0008 0000 0008 0000  . !..A..........
   00000010: 0041 0200 830a 0802 0010 0000 0070 0000  .A...........p..
   00000020: 0000 0000 0000 0000 0000 0000 0000 0000  ................
   00000030: 0000 0000 0000 0000 0000 0000 0000 0000  ................

这是 fdisk 认为的样子:

$ fdisk -l disk.img
Disk disk.img: 16 MiB, 16777216 bytes, 32768 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0xfdd440d0

Device     Boot Start   End Sectors Size Id Type
disk.img1  *     2048  4095    2048   1M  1 FAT12
disk.img2        4096 32767   28672  14M 83 Linux

根据寄存器的值验证 CHS 地址...

$ cat main.c 
#include <stdint.h>
#include <stdio.h>

int main(int argc, char *argv[]) {

    uint8_t
        DL = 0x80,
        DH = 0x20,  /* H */
        CL = 0x21,  /* Bits 7-6 are higher 2 bits of C,
                       Bits 5-0 are S */
        CH = 0x00;  /* Lower 8 bits of C */
    uint16_t C, H, S;

    C = CH | (((uint16_t)CL << 2) & 0x0300);
    H = DH;
    S = CL & 0x3f;
    
    printf("C/H/S = %d, %d, %d\n", C, H, S);

    return (0);
}
$ ./a.out 
C/H/S = 0, 32, 33

以下是我如何创建磁盘映像

    $ dd if=/dev/zero of=disk.img bs=1M count=16
    $ dd conv=notrunc if=MBR.bin of=disk.img bs=512 count=1 seek=0
    $ printf ",1M,01\n;" | sfdisk disk.img
    $ sfdisk -A disk.img 1

标签: assemblyx86bios

解决方案


推荐阅读