Page 1 of 1
Help regarding FAT16 cluster reading
Posted: Tue Jan 06, 2015 5:32 pm
by mohammedisam
Hi all
I am working on a small OS kernel of my own. I am having a trouble accessing the "right" cluster for a given subdirectory on a FAT16 harddisk.
I got so far the grips on reading the OEM parameter block, and extracting the root directory info. But when I try to access a directory in the root (drive C) I get a wrong offset for the sector on the disk.
I am using the following code, which first calculates the absolute cluster from the current cluster number, then adds the root directory sectors (32 in my case), then adds the LBA of the start of the disk (I am using a partitioned drive, starting at cluster 0, head 1, sector 1 = LBA 63). then it adds the first data sector (amounts to 513, as I am using FAT16 with 2 tables, 256 sector each, and one reserved sector).
Code: Select all
uint32_t tmp = (pfile->current_cluster-2)*mount_info.sectors_per_cluster; //get absolute cluster number
tmp += (mount_info.num_root_entries*32)/mount_info.bytes_per_sector; //skip root directory
tmp += mount_info.lba; //add partition starting lba
tmp += mount_info.first_data_sector; //add first data sector
LBA_to_CHS(tmp, &cylinder, &head, §or); //convert into CHS
All the results I am getting are offset by 3-4 heads (e.g. on my disk, Windows dir is actually at cluster 3, head 37, sector 38, but my code gets me into cluster 3, head 40, sector 38... offset by 3 heads!!), hence I get garbage whenever I try to list the contents of a directory or read a file.
My question is: are the above calculations right? in other words, how can I correctly convert a relative cluster number into an absolute CHS to read it from the disk? remember I need to add the offset into the partition..
Thanx
Re: Help regarding FAT16 cluster reading
Posted: Tue Jan 06, 2015 9:02 pm
by thepowersgang
First off - Why are you using CHS addressing (it shouldn't need to be used for hard disks, only for floppies).
Secondly, are you double-accounting for the root directory? (I don't see a mention of the FAT size, so I assume first_data_sector is a calculated value, which might already include the root directory size)
Thirdly, have you checked your CHS code, it might have errors.
Re: Help regarding FAT16 cluster reading
Posted: Wed Jan 07, 2015 9:56 am
by JAAman
agreed -- it is actually dangerous to use CHS to access a drive formatted using LBA -- the CHS values are not guaranteed to be correct, and the drive may translate the CHS/LBA values differently than you did (the drive always translates CHS values into LBA values internally) -- the "proper" way is to check the MBR for the "type" of the partition, and use LBA if it is a LBA "type", and CHS if it is a CHS "type" -- however, just using LBA will almost always be correct for any drive that supports LBA and was formatted in the last 15 years
I also agree that you are probably counting the root directory twice (since the data area is supposed to start after the root directory on FAT16)
Re: Help regarding FAT16 cluster reading
Posted: Wed Jan 07, 2015 5:51 pm
by mohammedisam
Hi, and thanx 4 ur replies
I actually calculate the first data sector as:
Code: Select all
uint32_t first_data_sector = boot_secto.reserved_sectors+(boot_sector.FATs*boot_sector.sectors_per_fat_12_16);
So i don't think i double added the root dir to the calculation (the formulae i am using are from the FAT page on the WiKi).
I triple checked the LBA to CHS code with three different formulae i found online, all with the same results. Here is the code (i put the info about the HDDs in an array) i am using the second formula, i included the other two for comparison:
Code: Select all
void LBA_to_CHS(unsigned long LBA,
unsigned int *cylinder,
unsigned int *head,
unsigned int *sector,
unsigned char drive_index)
{
/*unsigned int t = LBA/hdd_drives[drive_index].sectors;
*sector = (LBA%(hdd_drives[drive_index].sectors))+1;
*head = t%(hdd_drives[drive_index].heads);
*cylinder = t/hdd_drives[drive_index].heads;*/ <==== same results with this code
*cylinder = LBA/(hdd_drives[drive_index].heads*hdd_drives[drive_index].sectors);
unsigned t = LBA%(hdd_drives[drive_index].heads*hdd_drives[drive_index].sectors);
*head = t/hdd_drives[drive_index].sectors;
*sector = t%hdd_drives[drive_index].sectors+1;
/**sector = LBA%hdd_drives[drive_index].sectors+1;
*head = (LBA%(hdd_drives[drive_index].sectors*2))/(hdd_drives[drive_index].sectors);
*cylinder = LBA/(hdd_drives[drive_index].heads*2);*/ <==== same results with this code
}
Lastly, i am using CHS because i am reading the HDD through int 0x13 function 02h. I tried using int 0x13 function 42h but it needs the address of the packet in DS:SI, and i am using v86 mode to access the function (my kernel is in protected mode). if i am to use 42h, i can declare the address packet as a buffer in my C code, but how can i convert its memory address to real mode address and pass it to the function in DS:SI? It is really confusing
PS: BTW, it might be a stupid question, but how can i check the "type" of the drive for LBA or CHS addressing?
thanx
Re: Help regarding FAT16 cluster reading
Posted: Wed Jan 07, 2015 6:01 pm
by BrightLight
mohammedisam wrote:PS: BTW, it might be a stupid question, but how can i check the "type" of the drive for LBA or CHS addressing?
thanx
You can use BIOS INT 13h extensions "function 0x41" this here tests if LBA is supported or not:
Code: Select all
test_lba:
mov ax, 0x41
mov bx, 0x55aa
mov dl, 0x80
int 13h
jc .no
.yes:
mov ax, 0
ret
.no:
mov ax, 1
ret
Which returns 1 in AX if there is no LBA or 0 if there is LBA.
Happy coding!
Re: Help regarding FAT16 cluster reading
Posted: Thu Jan 08, 2015 1:15 am
by Combuster
boot_secto.
Was that copy-pasted correctly?
Re: Help regarding FAT16 cluster reading
Posted: Thu Jan 08, 2015 8:17 am
by JAAman
omarrx024 wrote:mohammedisam wrote:PS: BTW, it might be a stupid question, but how can i check the "type" of the drive for LBA or CHS addressing?
thanx
You can use BIOS INT 13h extensions "function 0x41" this here tests if LBA is supported or not:
[edit]
that is the correct way to determine if you should format it using LBA, but once it is formatted, you
must use the same method as was used to format it, other methods can give incorrect results
[/edit]
nope, that can give incorrect results (although it is highly unlikely that it will) -- the proper way to test whether you should use CHS or LBA is to read the partition table, and look at the "file system type" byte in the partition table -- there are separate entries for LBA and CHS versions of FAT
you may be "correctly" converting LBA into CHS, but there are multiple "correct" ways, and you cannot know how the drive itself does the conversion (well, its not really different ways", its different number of heads/sectors/cylinders -- the only thing you can do, is rely on the information stored in the bpb -- which might be incorrect, and even if it is correct, it still might not be the same values that the drive uses -- gets even more complicated if you are using BIOS to load the sector, since the BIOS may or may not translate the CHS you give it into a different CHS sent to the drive (this is mostly the case if you are either using larger heads value, or a larger cylinder value... which is quite common on any disk larger than 400MB, and many smaller than that), plus if the drive was formatted using LBA, the BPB values for CHS may well be completely wrong -- wouldn't matter since the partition is expected to use LBA anyway
Re: Help regarding FAT16 cluster reading
Posted: Thu Jan 08, 2015 8:36 am
by JAAman
mohammedisam wrote:
Lastly, i am using CHS because i am reading the HDD through int 0x13 function 02h. I tried using int 0x13 function 42h but it needs the address of the packet in DS:SI, and i am using v86 mode to access the function (my kernel is in protected mode). if i am to use 42h, i can declare the address packet as a buffer in my C code, but how can i convert its memory address to real mode address and pass it to the function in DS:SI? It is really confusing
the easiest way, would be just to store it on the v86 stack (in reverse order, of course), and then pass SS:SP, alternatively, you could reserve a specific area of lower memory for this use, and store it in that fixed location...
in general though, you should move away from using BIOS as soon as possible, because there are many things that can go wrong when using the BIOS (brenden would insert a long list of reasons here...)
Re: Help regarding FAT16 cluster reading
Posted: Sun Jan 11, 2015 9:14 am
by azblue
As JAAman said, you want to get away from BIOS.
This tutorial is probably the easiest I've seen.