Monday 14 March 2011

The FAT16 File System (with SD cards)

I have been working on a  project where the client requires their device to have it’s firmware updated from a Secure Digital (SD) Card.

The standard way to store and access data on SD cards is to use the MS FAT File System.

There are several libraries available dotted around the Internet which allow developers to access SD using FAT. However, if you are on a tight program space budget, such as in the Boot-Loader of an embedded Microcontroller, then you may need to program the FAT File System access manually.

This process is reasonably difficult to accomplish in raw code as there are quite a few steps to perform before you gain access to the actual file you are after.

I’ll outline here the basic features of FAT16, which for the purposes of this example, will be the simplest to begin with.


The FAT16 Layout

The layout of the MS FAT16 File System can be seen below;

FAT16 Layout

The Master Boot Record (MBR):

Offset 0x0000
Size 512 Bytes

 

 

The Master Boot Record is the first location that the Host File System parses in order to navigate the File Structure of the SD Card. It is always located at Sector zero and not only sets out the addressing parameters of the File System but also contains operating code run by the host at startup.

Address
(Offset)
Item Length Notes
0x0000 Executable Code 446 Bytes Executed by the Host system at startup
0x01BE
(00h)
Partition 1:
State
(Partition 1 Entry Begin)
1 Byte Beginning of the Partition 1 Entry. 16 Bytes Total.

The State determines if the Partition is Active (80h) or Inactive (00h)
0x01BF
(01h)
Partition 1:
Start Head
1 Byte  
0x01C0
(02h)
Partition 1:
Start Sector / Cylinder
2 Bytes Defines the Start Sector and Cylinder of Partition 1
0x01C2
(04h)
Partition 1:
Partition Type
1 Byte Defines the Type of Partition;
01h = 12bit FAT
04h = 16bit FAT (<32Mb)
05h = Ex MSDOS
06h = 16bit FAT (>32Mb)
0Bh = 32bit FAT
(<2Gb)
0x01C3
(05h)
Partition 1:
End Head
1 Byte  
0x01C4
(06h)
Partition 1:
End Sector / Cylinder
2 Bytes Defines the End Sector and Cylinder of Partition 1
0x01C6
(08h)
Partition 1:
Start Sector
4 Bytes Contains the Start Sector Address of Partition 1.
Defined as the number of Sectors between the MBR and the first Sector of the Partition.
0x01CA
(0Ch)
Partition 1:
Partition Length
(Partition 1 Entry End)
4 Bytes End of the Partition 1 Entry. 16 Bytes Total.

Defines the number of Sectors in the Partition
0x01CE Partition 2 Entry 16 Bytes Defined as above
0x01DE Partition 3 Entry 16 Bytes Defined as above
0x01EE Partition 4 Entry 16 Bytes Defined as above
0x01FE Executable Marker 2 Bytes Set as 0x55AA
 

The Boot Sector Entry (BSE):

Offset 1st sector of Partition
Size 512 Bytes
Address Location Calculation Partition Start Sector (From MBR) * Bytes / Sector

The Boot Sector Entry contains all the pertinent information regarding the Partitions’ structure. The basic layout is shown below;

Offset Item Length Notes
00h Jump Code 3 Bytes Contains executable Machine code to allow the Host System to jump past the BSE
03h OEM ID 8 Bytes The system name which formatted the device
0Bh Bytes Per Sector 2 Bytes Normally 0x2000
(512 )
0Dh Sectors Per Cluster 1 Byte  
0Eh No of Reserved Sectors 2 Bytes  
10h No of FATs 1 Byte The Number of FAT Entries
11h Max Root Directory Entries 2 Bytes For SD Cards this is usually 0x2000
(512)
13h Total Sectors in Partition 2 Bytes Only set if the device is < 32Mb.
Will likely be unset 0x0000
15h Media Type 1 Byte 0xF8 = Hard Drive
16h Sectors Per FAT 2 Bytes Used for reaching the Root Directory
18h Sectors Per Track 2 Bytes  
1Ah No of Heads 2 Bytes  
1Ch No of Hidden Sectors 4 Bytes Number of hidden sectors between the start of the disk and the BSE
20h No of Sectors in a Partition 4 Bytes Total number of Sectors in the Partition
24h Logical Drive No 1 Byte 0x80 for Hard Drives
25h Head No 1 Byte Usually 0x00
26h Extended Boot Signature 1 Byte Always 0x29 denoting that Serial No, Label and Type fields are valid
27h Serial No 4 Bytes Serial No of the Device
2Bh Partition Name 11 Bytes  
36h FAT Type 8 Bytes  
3Eh Executable Boot Code 448 Bytes Allows the host to find the first file which boots the system
0x01FE Executable Signature 2 Bytes End of BSE


FAT Entries (FAT):

Offset FAT1 = 1st sector after Reserved Sectors
Size Sectors Per FAT (From BSE)
Address Location Calculation FAT1 = (Partition Start Sector(From MBR) + Reserved Sectors(From BSE)) * Bytes / Sector

The FAT Entries lay out how the Partitions’ identically sized clusters are laid out. There are normally two FAT Entries in order to help prevent FAT corruption problems.

The FAT is located in the first sector after the Reserved Sectors. All sectors between Partition Start Sector and the FAT, which includes the Boot Sector Entry, are contained within the Reserved Sectors Allocation.

So, to find the first FAT we navigate from the beginning of the Device, adding together the Partition Start Sector and the Number of bytes contained in the Reserved Sectors section.

Root Directory Entry (RDE):

Offset After last FAT
Size No of Root Entries(From BSE) * 32
Address Location Calculation RDE = FAT1 Start Address +
((No of FATS(From BSE) *
Sectors Per FAT(From BSE)) * Bytes Per Sector)

The Root Directory contains the Main Directory and File Structure for the entire Partition

The Root directory is almost identical to a standard File System Directory with only a few differences, namely; The first byte is the first character of the Partition Name. The Create Date and Time are set to 0x00.

The Root directory appears after the last FAT, and so can be found by adding the Start Address of the first FAT to the number of Bytes occupied by all FAT’s, which is invariably 2 for SD Cards. Thus the formula can be simplifed to;

RDE = FAT1 Start Address +
      ((2 * Sectors Per FAT) * 512)

Each entry in the Root Directory consumes a total of 32 Bytes as shown below;

Offset Item Length Notes
00h DOS Filename 8 Bytes For all other Directories the first byte has special values.
08h DOS File Extension 3 Bytes  
0Bh File Attributes 1 Byte 8 bits to denote various attributes
0Ch NT Case Info 1 Byte Used for Windows NT Casing Info
0Dh Create Time (ms) 1 Byte 10ms Units
0Eh Create Time (Hrs/Mins/Secs) 2 Bytes  
10h Create Date 2 Bytes  
12h Last Access Date 2 Bytes  
14h File / Folder
Start Cluster (High)
2 Bytes Only used in FAT32 Systems
16h Last Modified Time 2 Bytes  
18h Last Modified Date 2 Bytes  
1Ah File / Folder
Start Cluster (Low)
2 Bytes  
1Ch File Size (Bytes) 4 Bytes Folders will have a File Size of 0x0000


Sub Directory / Files:

Offset After Root Directory
Size N/a
Address Location Calculation Root Folder Address +
(Max Root Directory Entries(From BSE) * 32) +
((( File / Folder Start Cluster(From RDE) – 2) *
Sectors Per Cluster(From BSE) ) *
Bytes Per Sector(From BSE) )

Normally For SD Cards using FAT16;

Max Root Directory Entries = 512
Bytes Per Sector = 512

A Sub Directory takes on almost exactly the same format as the root Directory above.

All data stored after the Root Directory is located in the third Data Cluster, thus in order to find the location of all data we must subtract these two clusters from the Start Cluster given in the File / Folder Start Cluster Field.

The first data is found using the formula above, however for SD Cards using FAT16, the maximum Root Directory Entries and Bytes Per Sector are invariably 512. Thus the formula can be simplified to the following;

First Data = Root Folder Address + 16384 +
             (((File/Folder St Cluster – 2) *
             Sectors Per Cluster) * 512)

A Worked Example:

Card Type Sandisk 2Gb SD
MBR Length 512
Partition 1 Start Sector 129
Bytes Per Sector 512
Sectors Per Cluster 64
Reserved Sectors 2
Number of FAT’s 2
Sectors Per FAT 239
Hidden Sectors 129
BSE Length 512
Number of Root Entries 512

The table above gives all the information taken from the MBR and BSE of the Card.

Calculating the BSE Address:

BSE = Partition Start Sector *
        
Bytes Per Sector

BSE = 129 * 512

BSE = 66048 = 0x10200

Calculating the FAT1 Address:

FAT1 = (Partition Start Sector +
      
Reserved Sectors) * Bytes Per Sector)

FAT1 = (129 + 2) * 512

FAT1 = 131 * 512

FAT1 = 67072 = 0x10600

Calculating the Root Directory Address:

RDE = FAT1 Start Address +
      ((No of FATS * Sectors Per FAT) *
     
Bytes Per Sector)

RDE = 67072 +
      ((2 * 239) *
      512)

RDE = 67072 + (478 * 512)

RDE = 67072 + 244736

RDE = 311808 = 0x4C200

Calculating the First File Address:

For a file or folder which has a starting cluster value of 0x04, then the following will locate the start address;

File = Root Folder Address +
      (Max Root Entries * 32) +
      (((File/Folder St Cluster – 2) *
      Sectors Per Cluster) *
      Bytes Per sector)

File = 311808 +
       (512 * 32) +
       (((4 – 2) *
       64) *
       512)

File = 311808 +
       16384 +
       ((2 * 64) * 512)

File = 311808 + 16384 + (128 * 512)

File = 311808 + 16384 + 65536

File = 393728 = 0x60200

No comments:

Post a Comment