1

我写了一个启动代码,在屏幕上打印一些东西,然后重新定位并加载下一个启动代码(VBR)。我使用 vmware 在虚拟机上测试了代码,它可以正常工作,我看到了 vbr 代码的正确输出。 单击此处查看代码的输出。

我希望能够调试我的代码,并且由于 vmware 没有此功能,我想使用可与 IDA 一起使用的 bochs。在 bochs 上运行代码后,我得到了错误:

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

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

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

%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 缓冲区中的两个代码,它是相同的。我无法弄清楚为什么在启动代码上我得到错误而在第二个代码上我没有。有什么我想念的吗?

4

1 回答 1

1

您的代码中有几个问题,而BOCHS中的一个错误会导致问题。


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指向BP的最低有效字节被压入堆栈push 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 错误

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

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

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

设置方向标志(向后)BOCHS BIOS Int 0x13/AH=0x42开始将它从磁盘读取的 DWORD 写入 0x0000: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也可以向前处理字符。

于 2016-10-24T16:12:27.703 回答