无法读取启动代码上的扇区

时间:2016-10-23 12:20:27

标签: assembly x86 nasm bootloader bochs

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

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

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

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

启动代码(仅限相关功能,完整版click here):

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

1 个答案:

答案 0 :(得分: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指向使用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,这将使堆栈位于视频内存和EBDA之下的0x8c00:0x0000(0x8c000)和0x9c00:0000(0x9c000)之间。

我今天早上发现这部分属实。问题是堆栈相关,但不是因为可用空间量。当堆栈低于0x7c00时,BIOS错误最终会立即破坏堆栈。 BOCHS中的Int 0x13默认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的调用也会向前处理字符。