【问题标题】:fail to read sectors on boot code无法读取引导代码上的扇区
【发布时间】:2017-03-05 07:26:35
【问题描述】:

我编写了一个启动代码,它在屏幕上打印一些内容,然后重新定位并加载下一个启动代码 (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 缓冲区,结果是一样的。我无法弄清楚为什么在启动代码上我得到错误而在第二个代码上我没有。有什么我想念的吗?

【问题讨论】:

  • repnz stosb 将在 ES:DI 中存储第一个值 (0)。假设您正确设置了 ES,DI 指向 BP。 BP 实际上指向存储在堆栈中的 BP 的第一个字节(使用push bp),这意味着数据在堆栈上被清零,您不想设置为 0。在我看来,您有一个错误关闭将导致 DI 比您在 repnz stosb 完成时的预期高一。在我看来,您想在 repnz stosb 之前将 DI 减一吗?可能不是您的问题的原因,但似乎是一个错误。
  • @MichaelPetch 确实你是对的,虽然它不能解决问题。谢谢你指出来。
  • 由于您没有发布所有代码以便我们进行测试(这不是一个最小的完整可验证示例)我很好奇如果您将 repnz stosb 更改为 dec di @ 会发生什么987654333@inc di。之后的inc di 必须针对最后一个stosb 将DI 减一这一事实进行调整。
  • @MichaelPetch 我记得把inc di放在repnz stosb之后。由于代码有点大,放在原始帖子上,我不想全部复制,这样更容易集中精力解决问题,但我会放一个链接到粘贴 bin 的完整代码(@987654323 @)
  • 我最好的猜测是问题与堆栈有关。扩展的 int 13h/ah=42 可能需要比重新定位在 0x600 和 0x7c00 附近的代码末尾之间可用的堆栈更多。如果你把你的堆栈移到别处会发生什么。作为一个实验,您可以尝试将 SS:SP 设置为 0x8c00:0x0000 之类的值,这会将堆栈放置在视频内存和 EBDA 下方的 0x8c00:0x0000 (0x8c000) 和 0x9c00:0000 (0x9c000) 之间。

标签: assembly x86 nasm bootloader bochs


【解决方案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 错误

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

我最好的猜测是问题与堆栈有关。扩展的 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 的调用也可以向前处理字符。

【讨论】:

  • 感谢您的回答,在您提出更改堆栈的建议后,我注意到 VBR 被反向加载,无法找出原因。
  • @0x0066FF 是的,我昨天没有时间真正运行你的代码。今天早上当我有时间查看内存中的内容时,我真的很惊讶。
猜你喜欢
  • 1970-01-01
  • 2023-03-23
  • 2013-07-16
  • 2013-07-15
  • 2021-09-03
  • 2015-06-17
  • 1970-01-01
  • 2012-07-25
  • 1970-01-01
相关资源
最近更新 更多