读取引导代码扇区失败



我编写了一个引导代码,它在屏幕上打印一些内容,然后重新定位并加载下一个引导代码(VBR)。我使用vmware在虚拟机上测试了代码,它正常工作,我看到了vbr代码的正确输出。点击这里查看代码的输出。

我希望能够调试我的代码,因为vmware没有这个功能,我想使用可以与IDA一起使用的bochs。在bochs上运行代码后,我得到了错误:

>>PANIC<< IO write(0x01f0): current command is 20h

在调用处理扩展读取(int 13h, function 42)的中断后发生错误。我检查了扩展,它们是支持的,所以我写了另一个真实模式的程序,只加载1扇区使用相同的bios功能,由于某种原因,它工作。代码之间只有很小的区别。在引导代码中,我用获取参数的函数包装了加载扇区的代码,而在第二个代码中,它没有用函数包装。我将把代码贴在这里。

启动代码(只有相关功能,完整版本点击这里):

%define AdjustAddress(addr) addr - BASE_ADDRESS + NEW_ADDRESS
%define DAP(obj, member) [obj + DiskAddressPacket. %+ member]
NEW_ADDRESS             equ         0x0600
BASE_ADDRESS            equ         0x7C00
DAP_SIZE                equ         DiskAddressPacket_size
struc DiskAddressPacket
    .size           resb 1
    .resv           resb 1
    .num_of_sectors resb 2
    .offset         resb 2
    .segment        resb 2
    .lba_start      resb 8
endstruc
main:
    ; code
    ; ....
    ; Read the VBR
    push 0x01
    mov  si, [AdjustAddress(parti_pointer)]
    push dword PE(si, starting_lba)
    push BASE_ADDRESS
    push 0x00
    call ReadSectors
    ; code
    ; ....
; void __stdcall ReadSectors(WORD segment, WORD offset, DWORD lba, WORD count)
ReadSectors: 
    push bp           ; Save bp register value
    mov  bp, sp       ; Setting up stack frame
    sub  sp, DAP_SIZE ; Allocate a buffer for the DAP data
    ; Zero out DAP buffer
    std ; Set direction flag (decrease di)
    mov   di, bp 
    xor   al, al
    mov   cx, DAP_SIZE
    repnz stosb ; di = DAP buffer at the end of this operation
    ; Initialize DAP with correct data
    mov byte DAP(di, size), DAP_SIZE
    mov bx, word [bp + 0x0C]    ; count parameter 
    mov word DAP(di, num_of_sectors), bx
    mov bx, word [bp + 0x04]    ; segment parameter 
    mov word DAP(di, segment), bx 
    mov bx, word [bp + 0x06]    ; offset parameter 
    mov word  DAP(di, offset), bx
    mov bx, word [bp + 0x08]    ; Low word of LBA parameter
    mov word DAP(di, lba_start), bx
    mov bx, word [bp + 0x0A]    ; High word of LBA parameter
    mov word DAP(di, lba_start + 0x02), bx
    ; Prepare parameters
    mov ax, 0x4200  ; Extended read sectors function of int 0x13
    mov dx, word [AdjustAddress(drive_index)] ; Drive index
    mov si, di      ; si = DAP buffer
    int 0x13        ; Read the sectors from the disk
    jc  CheckLBASupport.no_lba_support
    mov sp, bp  ; Clear the allocated memory
    pop bp      ; Restore bp register value
    ret 0x0A ; Clean the stack and return to the calling code
第二代码:

cli ; Cancel interrupts 
; Set up segments registers
xor ax, ax
mov ds, ax
mov es, ax
mov ss, ax
mov sp, 0x7c00
mov ax, 0x4100
mov bx, 0x55aa
int 0x13
; Checking returned value with the debugger
sub sp, DiskAddressPacket_size
mov bp, sp
mov byte [bp], DiskAddressPacket_size
mov byte [bp + 0x01], 0x00
mov word [bp + 0x02], 0x01
mov word [bp + 0x04], 0x7C00
mov word [bp + 0x06], 0x00
mov dword [bp + 0x08], 0x80
mov dword [bp + 0x0c], 0x00
mov ax, 0x4200
mov si, bp
int 0x13

我检查了内存中两个代码的DAP缓冲区,它是相同的。我无法弄清楚为什么在启动代码上我得到了错误,而在第二个代码上我没有。我错过什么了吗?

代码中的几个问题以及BOCHS中的一个bug导致了问题。


您的ReadSectors函数中有一个关闭错误:

你有

:

push bp           ; Save bp register value
mov  bp, sp       ; Setting up stack frame
sub  sp, DAP_SIZE ; Allocate a buffer for the DAP data
; Zero out DAP buffer
std ; Set direction flag (decrease di)
mov   di, bp
xor   al, al
mov   cx, DAP_SIZE
repnz stosb ; di = DAP buffer at the end of this operation

问题是mov di, bp指向与push bp一起压入堆栈的BP的最低有效字节。磁盘访问包(DAP)中的最后一个字节实际上是bp-1。同样,在最后一个stosb指令执行后,DI实际上将位于缓冲区开始位置的一个字节以下。要解决这些问题,您可以将代码修改为如下内容:

push bp           ; Save bp register value
mov  bp, sp       ; Setting up stack frame
sub  sp, DAP_SIZE ; Allocate a buffer for the DAP data
; Zero out DAP buffer
std ; Set direction flag (decrease di)
lea   di, [bp - 1]
xor   al, al
mov   cx, DAP_SIZE
repnz stosb ; di = DAP buffer at the end of this operation
inc   di

在某些地方,当您将代码重新定位到0x600的内存区域后,您无法调整地址。例如在CheckBootSignature中这段代码:

 .error:
    mov  si, boot_sig_error
    call PrintString
    int  0x18
应:

 .error:
    mov  si, AdjustAddress(boot_sig_error)
    call PrintString
    int  0x18

FindBootablePartition

也有类似的问题

BOCHS BIOS bug

在评论中,我提出了一个可能的堆栈问题:

我最好的猜测是这个问题与堆栈有关。扩展的int 13h/ah=42可能需要比在0x600和0x7c00附近重新定位的代码末尾之间可用的堆栈更多的堆栈。如果将堆栈移到其他地方会发生什么。作为一个实验,您可以尝试将SS:SP设置为0x8c00:0x0000,这将使堆栈位于0x8c00:0x0000 (0x8c000)和0x9c0000 (0x9c000)之间,位于显存和EBDA下方。

我今天早上发现这是部分正确的。这个问题与堆栈有关,但与可用空间大小无关。当堆栈低于0x7c00时,BIOS错误几乎会立即破坏堆栈。在BOCHS的默认BIOS实现中,Int 0x13在进行处理时实际上没有设置方向标志。它依赖于调用Int 0x13的代码来设置前进的方向。在您的代码中,方向是向后的(您使用STD来设置方向标志位)。

设置方向标志(反向)后,BOCHS BIOS Int 0x13/AH=0x42开始写入DWORDs,它从磁盘读取到0x000:0x7c00,并继续写入该偏移量以下的未来数据。由于堆栈位于0x0000:0x7c00以下,因此读取的第一个扇区几乎会删除堆栈上的内容,包括磁盘访问数据包(DAP)。当这种情况发生时,可能会出现许多奇怪的行为。

要解决这个问题,请确保当您调用Int 0x13时,您将方向标志设置为CLD

; Zero out DAP buffer
std ; Set direction flag (decrease di)
lea   di, [bp - 1] 
xor   al, al
mov   cx, DAP_SIZE
repnz stosb ; di = DAP buffer at the end of this operation
inc   di
cld         ; Forward direction for Int 0x13 and future calls to PrintString

通过这样做,您可以绕过这个BOCHS错误,并确保将来对PrintString的调用也在正向处理字符。

相关内容

  • 没有找到相关文章