Page 1 of 1

Assembly + INT 13 AH 42

Posted: Fri Sep 14, 2012 11:27 pm
by mark3094
I've been working on my Kernel, but I've had to take a step back to look at my bootloader.
I have found out that my bootloader can load a 40KB kernel, but hangs if it tries to load anything over 40KB.

I am using INT 13, AH=42 in real mode to read from a FAT32 partition. I read the kernel to 0000:5500
After the jump to protected mode, the boot loader copies the kernel from 0x5500 to 0x100000

I have found that if the kernel is larger than 40KB, it will not fit into the real mode segment that I am using as the INT 13 buffer.

I have considered using 0550:0000 as the buffer (same physical memory location), which would give me up to 64KB for the Kernel before the segment is full, however, (1) I can't get this to work, and (2) I'll probably hit the limit for this again sometime in the near future.

I figure I can use 0550:0000 as the buffer, and when it gets full, move to the next segment. However, as I've said, I can't get it to work.

This is the DAP I'm using:

Code: Select all

DAP:
	.Size		DB 0x10
	.NULL		DB 0x00
	.Sectors	DW 0x00
	.Offset		DW 0x00
	.Segment	DW 0x00
	.LBA		DD 0x00
	.LBA48		DD 0x00
If I use code like this, it works:

Code: Select all

	mov word [DAP.Segment], 0x0000
	mov word [DAP.Offset], 0x5500
If I change it, I get problems:

Code: Select all

	mov word [DAP.Segment], 0x0550
	mov word [DAP.Offset], 0x0000
I'm using VMWare Player to test with.


Any ideas what I may be doing wrong here?

Re: Assembly + INT 13 AH 42

Posted: Sat Sep 15, 2012 12:03 am
by Brendan
Hi,
mark3094 wrote:I have found that if the kernel is larger than 40KB, it will not fit into the real mode segment that I am using as the INT 13 buffer.
If the kernel is larger than 64 KiB, then you have to split it into multiple reads (where each read is less than 64 KiB). If the kernel is larger than about 600 KiB (e.g. Linux is typically about 5 MiB) then you need a loop that reads (up to) 64 KiB then copies it somewhere else (e.g. to 0x00100000).

Also, for FAT32 the file may be fragmented, and you'd have to deal with that too.

The simplest way would be something like:

Code: Select all

    while(not_all_loadeed) {
        find_next_cluster();
        read_cluster_into_buffer();
        copy_cluster_to_memory_above_0x00100000();
    }
You can improve performance a little by reading more than one cluster at a time (where possible):

Code: Select all

    while(not_all_loadeed) {
        find_next_cluster();
        sectors = find_contiguous_clusters() * sectors_per_cluster;
        if(sectors > buffer_size / bytes_per_sector) {
            sectors = buffer_size / bytes_per_sector;
        }
        read_contiguous_sectors_into_buffer(count);
        copy_sectors_to_memory_above_0x00100000(count);
    }
Also, for the last cluster in the file you may be able to skip some sectors. For example, if a cluster is 2 KiB (4 sectors) and the end of the file only consumes 123 bytes of the last cluster, then you could only load 1 sector of the last cluster.

mark3094 wrote:Any ideas what I may be doing wrong here?
Not really. 0x0550:0x0000 is the same physical address as 0x0000:0x5500, and there's no other information that we can check. :)


Cheers,

Brendan

Re: Assembly + INT 13 AH 42

Posted: Sat Sep 15, 2012 1:12 am
by mark3094
I have found out why I was having problems with the Segment:Offset, and that is because another function was overwriting some values. I have put in some PUSH and POPs, and solved that part.

However, my Kernel still won't load if it's over 40KB. It seems to hang when INT 13 is called the 11th time (8 sectors per cluster, one read for each cluster, iw 4KB per INT 13).

Any thoughts on how I can troubleshoot this? I tried to read AH after INT is used, but it has already hung, so I can't print it to screen.


EDIT:
I have found that this problem happens as soon as I try to write 4KB to 0550:A000 (0xF500 linear). Does this overwrite reserved memory? As far as I can see I don't have anything loaded there.

Stage 2 is at 0x0500
Kernel (attempting) at 0x5500
Stack at 0x0050
Memory map at 0x3500
FAT/RD/etc at 0x2500

Can't see anything on the Wiki either
http://wiki.osdev.org/Memory_Map_%28x86%29


EDIT 2:
I have also found that I can use INT 13h AH 42h to store sectors in memory addresses up to 0x10499 (linear). As soon as I write to 0x104A0, it hangs or faults. Any idea what this means?

Re: Assembly + INT 13 AH 42

Posted: Sat Sep 15, 2012 4:23 am
by Brendan
Hi,

Is this right?

Code: Select all

0x0050 to 0x04FF is Stack
0x0500 to 0x14FF is Stage 2
0x2500 to 0x34FF is FAT/RD/etc
0x3500 to 0x54FF is Memory map
0x5500 to ?????? is kernel
Now look at the first 4 rows of the memory map at http://wiki.osdev.org/Memory_Map_%28x86%29, and rearrange it a little:

Code: Select all

0x0000 to 0x03FF is Real Mode IVT (Interrupt Vector Table)
0x0400 to 0x04FF is BDA (BIOS data area)
0x0500 to ?????? is Conventional memory
Now, let's merge these into one:

Code: Select all

0x0050 to 0x04FF is Real Mode IVT that is TRASHED BY YOUR STACK
0x0400 to 0x04FF is BDA (BIOS data area) that is TRASHED BY YOUR STACK
0x0500 to 0x14FF is Stage 2
0x2500 to 0x34FF is FAT/RD/etc
0x3500 to 0x54FF is Memory map
0x5500 to ?????? is kernel
Cheers,

Brendan

Re: Assembly + INT 13 AH 42

Posted: Sat Sep 15, 2012 4:33 am
by mark3094
Sorry, my mistake, my stack is not at 0x50, it is at 0xFFFF (not sure why I said 0x50... #-o )

Anyway, it appears I am trashing my stack with my Kernel, which is very bad of course.
I tried loading at 0x11000, and it worked just fine.


Thanks again for your help. Much appreciated