无法读取引导代码中的扇区
fail to read sectors on boot code
我写了一个引导代码,在屏幕上打印一些东西,然后重新定位并加载下一个引导代码 (VBR)。
我使用 vmware 在虚拟机上测试了代码,它正常工作,我看到了 vbr 代码的正确输出。
Click here 查看代码的输出。
我希望能够调试我的代码,由于 vmware 没有这个功能,我想使用可以与 IDA 一起使用的 bochs。
运行bochs上的代码后,报错:
>>PANIC<< IO write(0x01f0): current command is 20h
调用处理扩展读取的中断(int 13h,函数42)后发生错误。
我检查了扩展并且它们受到支持,所以我编写了另一个实模式程序,它使用相同的 bios 函数只加载 1 个扇区,并且由于某种原因它起作用了。
代码之间只有很小的区别。在引导代码中,我用获取参数的函数包装了加载扇区的代码,而在第二个代码中,它没有用函数包装。我会post这里的代码。
启动代码(只有相关功能,完整版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缓冲区,两个代码都是一样的。我无法弄清楚为什么在引导代码上我得到错误而在第二个代码上我没有。有什么我想念的吗?
您的代码中有几个问题,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 错误
在评论中我提出了一个可能的堆栈问题:
My best guess is the issue is stack related. It could be possible the extended int 13h/ah=42 requires more stack than is available between the end of your code relocated around 0x600 and 0x7c00. What happens if you move your stack elsewhere. As an experiment you could try setting SS:SP to something like 0x8c00:0x0000 which would place the stack between 0x8c00:0x0000 (0x8c000) and 0x9c00:0000 (0x9c000) below the video memory and the EBDA.
今天早上我发现这是部分正确的。该问题与堆栈相关,但不是因为可用的 space 数量。当堆栈低于 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)。 我使用 vmware 在虚拟机上测试了代码,它正常工作,我看到了 vbr 代码的正确输出。 Click here 查看代码的输出。
我希望能够调试我的代码,由于 vmware 没有这个功能,我想使用可以与 IDA 一起使用的 bochs。 运行bochs上的代码后,报错:
>>PANIC<< IO write(0x01f0): current command is 20h
调用处理扩展读取的中断(int 13h,函数42)后发生错误。 我检查了扩展并且它们受到支持,所以我编写了另一个实模式程序,它使用相同的 bios 函数只加载 1 个扇区,并且由于某种原因它起作用了。 代码之间只有很小的区别。在引导代码中,我用获取参数的函数包装了加载扇区的代码,而在第二个代码中,它没有用函数包装。我会post这里的代码。
启动代码(只有相关功能,完整版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缓冲区,两个代码都是一样的。我无法弄清楚为什么在引导代码上我得到错误而在第二个代码上我没有。有什么我想念的吗?
您的代码中有几个问题,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 错误
在评论中我提出了一个可能的堆栈问题:
My best guess is the issue is stack related. It could be possible the extended int 13h/ah=42 requires more stack than is available between the end of your code relocated around 0x600 and 0x7c00. What happens if you move your stack elsewhere. As an experiment you could try setting SS:SP to something like 0x8c00:0x0000 which would place the stack between 0x8c00:0x0000 (0x8c000) and 0x9c00:0000 (0x9c000) below the video memory and the EBDA.
今天早上我发现这是部分正确的。该问题与堆栈相关,但不是因为可用的 space 数量。当堆栈低于 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
的调用也可以向前处理字符。