Help regarding FAT16 cluster reading

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Post Reply
mohammedisam
Member
Member
Posts: 32
Joined: Tue Jan 06, 2015 5:15 pm

Help regarding FAT16 cluster reading

Post 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, &sector);  //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
User avatar
thepowersgang
Member
Member
Posts: 734
Joined: Tue Dec 25, 2007 6:03 am
Libera.chat IRC: thePowersGang
Location: Perth, Western Australia
Contact:

Re: Help regarding FAT16 cluster reading

Post 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.
Kernel Development, It's the brain surgery of programming.
Acess2 OS (c) | Tifflin OS (rust) | mrustc - Rust compiler
Currently Working on: mrustc
User avatar
JAAman
Member
Member
Posts: 879
Joined: Wed Oct 27, 2004 11:00 pm
Location: WA

Re: Help regarding FAT16 cluster reading

Post 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)
mohammedisam
Member
Member
Posts: 32
Joined: Tue Jan 06, 2015 5:15 pm

Re: Help regarding FAT16 cluster reading

Post by mohammedisam »

Hi, and thanx 4 ur replies :D
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 #-o

PS: BTW, it might be a stupid question, but how can i check the "type" of the drive for LBA or CHS addressing?
thanx
User avatar
BrightLight
Member
Member
Posts: 901
Joined: Sat Dec 27, 2014 9:11 am
Location: Maadi, Cairo, Egypt
Contact:

Re: Help regarding FAT16 cluster reading

Post 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! :)
You know your OS is advanced when you stop using the Intel programming guide as a reference.
User avatar
Combuster
Member
Member
Posts: 9301
Joined: Wed Oct 18, 2006 3:45 am
Libera.chat IRC: [com]buster
Location: On the balcony, where I can actually keep 1½m distance
Contact:

Re: Help regarding FAT16 cluster reading

Post by Combuster »

boot_secto.
Was that copy-pasted correctly? :?
"Certainly avoid yourself. He is a newbie and might not realize it. You'll hate his code deeply a few years down the road." - Sortie
[ My OS ] [ VDisk/SFS ]
User avatar
JAAman
Member
Member
Posts: 879
Joined: Wed Oct 27, 2004 11:00 pm
Location: WA

Re: Help regarding FAT16 cluster reading

Post 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
User avatar
JAAman
Member
Member
Posts: 879
Joined: Wed Oct 27, 2004 11:00 pm
Location: WA

Re: Help regarding FAT16 cluster reading

Post 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 #-o
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...)
azblue
Member
Member
Posts: 147
Joined: Sat Feb 27, 2010 8:55 pm

Re: Help regarding FAT16 cluster reading

Post by azblue »

As JAAman said, you want to get away from BIOS. This tutorial is probably the easiest I've seen.
Post Reply