I wrote a boot code that print some stuff on the screen then relocate itself and load the next boot code (VBR). I tested the code on virtual machine using vmware and it's working as it should, I see the right output from the vbr code. Click here to see the output of the code.
I want to be able to debug my code and since vmware doesn't have this feature, I want to use bochs which can be used with IDA. After running the code on bochs, I got the error:
>>PANIC<< IO write(0x01f0): current command is 20h
The error occurred after calling the interrupt that handles extended reading (int 13h, function 42). I checked for extensions and they are supported so I wrote another real mode program which only load 1 sector using the same bios function, and for some reason it worked. There is only small difference between the codes. In the boot code I wrapped the code that loads the sector with function that gets the parameters, and in the second code it wasn't wrapped with a function. I will post here the codes.
Boot code (only the relevant function, for a full version 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
Second 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
I checked the DAP buffer in the memory for both code, and it is the same. I cant figure out why on the boot code I get the error and on the second code I dont. Is there something I miss?
A couple of issues in your code, and a bug in BOCHS are causing issues.
There is an off by one error in your ReadSectors
function:
You have:
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
The problem is that mov di, bp
is pointing at the least significant byte of BP that was pushed on the stack with push bp
. The last byte in the disk access packet (DAP) is actually bp-1
. As well after the last stosb
instruction executes DI will actually be one byte below where your buffer starts. To fix these problems you can modify your code to look something like:
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
In some places after you relocate the code to the memory area at 0x600 you fail to adjust the addresses. For example in CheckBootSignature
this code:
.error:
mov si, boot_sig_error
call PrintString
int 0x18
Should be:
.error:
mov si, AdjustAddress(boot_sig_error)
call PrintString
int 0x18
There is a similar issue in FindBootablePartition
In a comment I suggested a possible stack issue:
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.
I discovered this morning this is partially true. The issue is stack related, but not because of the amount of space available. When the stack is beneath 0x7c00 a BIOS bug ends up clobbering the stack almost immediately. Int 0x13
in BOCHS default BIOS implementation is actually not setting the direction flag when it does processing. It relies on the code calling Int 0x13
to set the direction forward. In your code the direction is backward (You used STD to set the direction flag bit).
With the direction flag set (backward) BOCHS BIOS Int 0x13/AH=0x42 starts writing DWORDs it reads from the disk into 0x0000:0x7c00 and proceeds to write future data below that offset. Since the stack was placed just below 0x0000:0x7c00 the first sector that gets read pretty much obliterates what is on the stack including the disk access packet (DAP). When this happens any number of bizarre behaviors may occur.
To fix this make sure that when you call Int 0x13
that you set the direction flag forward with 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
By doing this you get around this BOCHS bug, and you ensure that future calls to PrintString
also process characters in the forward direction.