Description:
In my efforts to create a simple standalone program I have written a simple boot loader in the first sector. Its purpose is to load the program into memory. For this purpose I am using INT 13h with AH=2. The code is:
disk_load:
push dx ; Store DX on stack so later we can recall how many sectors were requested to be read,
; even if it is altered in the meantime.
mov ah, 0x02 ; BIOS read sector.
mov al, dh ; Read DH sectors.
mov ch, 0x00 ; Select cylinder 0.
mov dh, 0x00 ; Select head 0.
mov cl, 0x02 ; Start reading from second sector (i.e. after the boot sector).
int 0x13 ; BIOS interrupt.
; <!----here
pop dx
ret
load_software:
mov bx, 0x7e0
mov es, bx
xor bx, bx
mov dh, 66
mov dl, [BOOT_DRIVE]
call disk_load
I was exercising everything in VirtualBox 5.2.8 and it worked perfectly well. Moving everything to a second machine that has VirtualBox 6.0.14 fails the experiment. The interrupt finishes with CF set, indicating a failure.
Reading the excellent answer in Boot loader doesn't jump to kernel code I have fixed the potential problem of unspecified DS value that could cause problems. If I stop and dump the CPU status just before the int 0x13
call I get a consistent state on both VirtualBoxes:
00:00:05.930849 eax=00000280 ebx=00007e00 ecx=00000002 edx=00000000 esi=00000000 edi=0000fff0
00:00:05.930857 eip=00007cc8 esp=00007bf9 ebp=00007bff iopl=0 nv up ei pl nz na po nc
00:00:05.930864 cs={0000 base=0000000000000000 limit=0000ffff flags=0000009b} dr0=00000000 dr1=00000000
00:00:05.930877 ds={0000 base=0000000000000000 limit=0000ffff flags=00000093} dr2=00000000 dr3=00000000
00:00:05.930884 es={0000 base=0000000000000000 limit=0000ffff flags=00000093} dr4=00000000 dr5=00000000
00:00:05.930891 fs={0000 base=0000000000000000 limit=0000ffff flags=00000093} dr6=ffff0ff0 dr7=00000400
00:00:05.930898 gs={0000 base=0000000000000000 limit=0000ffff flags=00000093} cr0=00000010 cr2=00000000
00:00:05.930904 ss={0000 base=0000000000000000 limit=0000ffff flags=00000093} cr3=00000000 cr4=00000000
00:00:05.930910 gdtr=00000000000fe89f:0047 idtr=0000000000000000:ffff eflags=00200246
Parsing all the values I can only conclude that all the input parameters to the interrupt are set up properly. The state after the dump has CF set and an error code:
00:00:08.984877 eax=00000900 ebx=00000000 ecx=00000002 edx=00000000 esi=00000000 edi=0000fff0
00:00:08.984887 eip=00007cca esp=00007bf9 ebp=00007bff iopl=0 nv up ei pl nz na po cy
00:00:08.984896 cs={0000 base=0000000000000000 limit=0000ffff flags=0000009b} dr0=00000000 dr1=00000000
00:00:08.984909 ds={0000 base=0000000000000000 limit=0000ffff flags=00000093} dr2=00000000 dr3=00000000
00:00:08.984917 es={07e0 base=0000000000007e00 limit=0000ffff flags=00000093} dr4=00000000 dr5=00000000
00:00:08.984925 fs={0000 base=0000000000000000 limit=0000ffff flags=00000093} dr6=ffff0ff0 dr7=00000400
00:00:08.984934 gs={0000 base=0000000000000000 limit=0000ffff flags=00000093} cr0=00000010 cr2=00000000
00:00:08.984941 ss={0000 base=0000000000000000 limit=0000ffff flags=00000093} cr3=00000000 cr4=00000000
00:00:08.984948 gdtr=00000000000fe89f:0047 idtr=0000000000000000:ffff eflags=00200247
Noting the error code AH=9
data boundary error (attempted DMA across 64K boundary or >80h sectors) lead me to https://en.wikipedia.org/wiki/INT_13H where this statement is made:
Addressing of Buffer should guarantee that the complete buffer is inside the given segment, i.e. ( BX + size_of_buffer ) <= 10000h.
This would explain my initial problems so I made another fix to set the es=0x7e0
and bx=0
. This is the state of the code that is displayed above. However even this code fails with the state described above.
Further testing shows that I can successfully read up to 65 sectors but 66 or more fails. Being it a weird number I calculated the end of the 65th sector: 0xffff. So the problem becomes a little bit more confusing.
Questions:
Should my es=0x7e0
and bx=0
solution avoid the segment crossing (as I understand it should)?
If it does, why does it seem to be the problem in crossing the linear address?
Or can one cross the segment, but not the 0xffff marker in linear address?
Thank you for your help.
lba_to_chs
function that I loop 1 sector at time to read a kernel off the disk as part of a bootloader. – Michael Petch