Page 1 of 1

INT 13H only working on QEMU

Posted: Tue Jan 25, 2022 7:18 am
by Barry
Hello,

I recently decided to test my OS on VirtualBox and Real Hardware. Before this I'd only used QEMU.
My bootloader works perfectly when running on QEMU and loads my kernel/initramfs into memory at 0x0FE0:0x0000 and then runs it in protected mode.
But when I run on VirtualBox or actual hardware, it just gets stuck when trying to read the drive.

I'm using INT 13H with AH=42H. I've also used AH=02H but that also fails. It can read Up to 63 sectors and then it just stops. The routine doesn't even complete. INT 13H never gets returned from after trying to load passed 63 sectors.

I'm not sure what's causing this, so if anyone has some idea I'd be grateful to hear it.

The issue seems to be related to where I'm writing to in memory, since changing 0xFE00 to a lower address allows INT 13H to get more sectors read, but I don't understand why there would be some memory limit (the VM is running with 4GB or RAM).

Thanks

Re: INT 13H only working on QEMU

Posted: Tue Jan 25, 2022 8:20 am
by Klakap
Hello, without code we can not help you. But I have two notes. int 13h ah=42h work only for hard disk/usb emulated as hard disk. If you connect your OS as floppy, or your computer is emulating it as floppy, it is reason why it do not work. And in real mode there is memory limit - one segment has size 64 KB.

Re: INT 13H only working on QEMU

Posted: Tue Jan 25, 2022 8:50 am
by Barry
Thanks for the advice, I'll probably switch to using AH=02H so I can support other devices too.
The memory limit wasn't the issue, although I did initially suspect it, because I was enabling the A20 gate, I thought I might have been doing it wrong, but it was being enabled.
I have now solved the issue, my stack was setup at 0x07C0:0x0000, I wanted it to grow down from where the bootloader starts, but I'd done it with segmentation, so it actually couldn't grow downwards. Apparently QEMU doesn't check memory segment permissions properly.
Thanks for the help thought, and thanks for the INT 13H tip too :)

Re: INT 13H only working on QEMU

Posted: Tue Jan 25, 2022 3:51 pm
by Barry
Okay, so I don't want to start a new thread, because this one is very small and related.

I've got my OS running on VirtualBox and Real Hardware. BUT I've hit an issue when adding more code to my kernel. It's now grown over 64KB (128 sectors) which appears to be an issue. My bootloader will (try to) load the kernel at 0x10000, or 0x1000:0x0000 with segmentation. It has worked so far, but when I hit 128 sectors, it fails. 128 * 512 = 0x10000, which means the disk driver can't load here anymore. I changed my bootloader code so that each sector gets loaded to 0xFE00 and then copied to the relevant memory area by a rep movsb, but I'm still having the same issue.

I took the advice so far in the thread and switched to using INT 13H with AX=02H, and I wrote a little function to switch an LBA address to CHS, and it works just fine... until >128 sectors.

Does anyone know of a way I can load past 0x10000 or have some alternate way of loading the kernel. I only moved it up high so it wouldn't interfere with BIOS things.

I'd prefer to not have to switch into PM and back to RM for the copying, since that'd make the bootloader larger than I'd really like.

Re: INT 13H only working on QEMU

Posted: Tue Jan 25, 2022 4:31 pm
by Octocontrabass
You seem very confused about which addresses are accessible to the INT 0x13 handler and the CPU in real mode. 1MB is 0x100000, not 0x10000.

Did you try loading 64kB at 0x1000:0x0000 and the rest at 0x2000:0x0000? (Of course by passing these addresses directly to INT 0x13, not using REP MOVSB.)

Re: INT 13H only working on QEMU

Posted: Tue Jan 25, 2022 5:03 pm
by Barry
I can do that, yes. But it's not desirable, since I'd have to hard code that into the bootloader, but my kernel might change size. Currently I'm getting the size (in sectors) from the drive, then loading that many sectors. I can do it one by one and just count, but I don't really want to have to be changing the segment when the count exceeds 128, since that complicated the routine slightly. If that's the best way forward I'll do it, but if there's an easier way to read more than a segment limit into memory, I'd rather do that. That's really what I'm asking here. Sorry if my previous post wasn't very clear.
I'm aware I can access up to 1MB in the BIOS, and more if I enable the A20 gate (which I've done), but that's not the limit issue here. INT 13H can only put into memory up to a segment limit.
Wikipedia wrote:Addressing of Buffer should guarantee that the complete buffer is inside the given segment, i.e. ( BX + size_of_buffer ) <= 10000h. Otherwise the interrupt may fail with some BIOS or hardware versions.

Re: INT 13H only working on QEMU

Posted: Tue Jan 25, 2022 5:08 pm
by Octocontrabass
Barry wrote:I don't really want to have to be changing the segment when the count exceeds 128, since that complicated the routine slightly.
There's no way around it. You have to change the segment.

Re: INT 13H only working on QEMU

Posted: Tue Jan 25, 2022 5:12 pm
by Barry
Alright, thanks for the quick response.
I'll see what I can do with it. Thinking about it, it shouldn't be that difficult, I just need segment = ((current_LBA - kernel_offset)/128)*512 + 0x1000
I think my initial design much over-complicated the booting process.

Re: INT 13H only working on QEMU

Posted: Wed Jan 26, 2022 7:31 pm
by BenLunt
Are you using 32-bit registers?

Might I suggest that you pass three parameters to your read routine?
Starting LBA -> (edx:)eax
Count of sectors to read -> (e)cx
Physical address to read to -> ebx

Then in your read routine, you can easily change the physical address to a segmented address.

offset = ebx & 0xF
segment = ebx >> 4

Place the offset in BX and the segment in ES and you're done. Some code I have does just this.

Also, I wanted to comment on:
I have now solved the issue, my stack was setup at 0x07C0:0x0000, I wanted it to grow down from where the bootloader starts,
If you initially set ss:sp to 0x07C0:0x0000, you realize that even though your stack grows down, it grows down from 0x07C0:0xFFFF + 1, 65536 bytes above where you think it does.

Next,
I'm using INT 13H with AH=42H. I've also used AH=02H but that also fails.
You can check to see if the BIOS supports the Extended Services for that drive, reverting to the CHS services if needed. Most BIOSes support the Extended Services, as long as it isn't a floppy. (Fun fact: I have a machine with an odd-ball BIOS that supports the Extended Services for the floppy as well.)

Anyway, I may be wrong, so I apologize if I am, but I think you don't quite understand segmentation. Segments aren't a consecutive set of 64k blocks, one right after the other. Segments can reside at any 16-byte boundary.

For example, the following segmented addresses all point to the same physical address:

Code: Select all

0x07C0:0x0000
0x0600:0x1C00
0x0555:0x26B0
0x0000:0x7C00
Anyway, I suggest that you split up your code so that you call a routine to read from the disk. This routine can decide which BIOS service to call, leaving your initial code to not have any concern which was used. Simply pass the Staring LBA, the count of sectors to read, and the physical address to place the data. Let the called routine worry about how to read from the disk.

Hope this helps,
Ben
- https://www.fysnet.net/osdesign_book_series.htm