我编写了一个引导代码,它在屏幕上打印一些内容,然后重新定位并加载下一个引导代码(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
的调用也在正向处理字符。