#include "boot_fat32.h" #include #include #include #include #include #include /* #include #include */ #define FAT_MAGIC_VALUE 0x12345678 // FAT descriptor initialized /**************************************************************************** * Global variables. * ****************************************************************************/ // FAT32 descriptor fat_desc_t boot_fat __attribute__((aligned(64))); // Buffer used for FAT scanning uint32_t buffer_fat[1024] __attribute__((aligned(64))); // Buffer used for directory scanning unsigned char buffer_dir[4096] __attribute__((aligned(64))); // LBA of cluster currently stored in buffer_fat uint32_t buffer_fat_lba; // LBA of cluster currently stored in buffer_dir uint32_t buffer_dir_lba; /**************************************************************************** * Internal functions. * ****************************************************************************/ /**************************************************************************** * This function returns the offset (in bytes) of the field defined by * * 'offset' and 'size'. * * @ offset : offset of the field from the beginning of the sector (in * * bytes). * * @ size : length of the field (in bytes). * * * * @ returns the field offset. * ****************************************************************************/ static inline int get_offset(int offset, int size) { return offset; } // get_offset() /**************************************************************************** * This function returns the length (in bytes) of the field defined by * * 'offset' and 'size'. * * @ offset : offset of the field from the beginning of the sector (in * * bytes). * * @ size : length of the field (in bytes). * * * * @ returns the field length. * ****************************************************************************/ static inline int get_size(int offset, int size) { return size; } // get_size() /**************************************************************************** * This function reads a data field (less than 4 bytes) from 'buffer', * * taking endianness into account. The field to be analyzed is defined by * * 'offset' and 'size'. * * @ offset : offset (in bytes) from the beginning of the buffer * * @ size : size (in bytes) of the field to be read * * @ buffer : base address of the buffer * * @ little_endian : 1 if buffer is in little-endian format / 0 otherwise * * * * @ returns the value read from the data field. * ****************************************************************************/ static uint32_t read_field( uint32_t offset, uint32_t size, unsigned char* buffer, uint32_t little_endian ) { uint32_t res = 0; uint32_t i; for (i = 0; i < size; i++) { if (little_endian) // Least significant bytes are stored first { res |= buffer[offset+i] << (8*i); } else // Most significant bytes are stored first { res |= buffer[offset+i] << 8*(size - i - 1); } } return res; } // read_field() /**************************************************************************** * This function transfers 'count' sectors from the block device and a * * memory buffer by calling the relevant driver. * * @ lba : first sector address on the block device. * * @ buff_addr : memory buffer physical address. * * @ count : number of sectors to be transfered. * * @ returns 0 on success, -1 on error. * ****************************************************************************/ static int fat_ioc_access( uint32_t lba, xptr_t buf_paddr, uint32_t count) { // Call the appropriate driver #if USE_IOC_BDV return ( boot_bdv_access( lba, buf_paddr, count) ); #elif USE_IOC_HBA return ( boot_hba_access( lba, buf_paddr, count) ); /* #elif USE_IOC_SDC return ( boot_sdc_access( lba, buf_paddr, count) ); #elif USE_IOC_SPI return ( boot_spi_access( lba, buf_paddr, count) ); #elif USE_IOC_RDK return ( boot_rdk_access( lba, buf_paddr, count) ); */ #else boot_printf("\n[BOOT ERROR] in fat_ioc_access(): IOC driver not defined\n"); return 1; #endif } // fat_ioc_access() /**************************************************************************** * This function directly accesses the FS Information Sector on the block * * device to set the free_cluster_hint and free_clusters_nr fields of * * the FAT32 descriptor. * * @ returns 0 on success, -1 on error. * ****************************************************************************/ static int set_fsi() { #if DEBUG_BOOT_FAT32 boot_printf("\n[BOOT INFO] %s enters at cycle %d\n", __FUNCTION__ , boot_get_proctime() ); #endif // Load FS Information Sector into the FAT buffer if ( fat_ioc_access( boot_fat.fsi_lba, XPTR( BOOT_CORE_CXY , boot_fat.block_buffer ), 1 ) ) { boot_printf("\n[BOOT ERROR] %s: Cannot load FS Information Sector\n", __FUNCTION__ ); return -1; } #if DEBUG_BOOT_FAT32 boot_printf("\n[BOOT INFO] %s : FSI Sector loaded at cycle %d\n", __FUNCTION__ , boot_get_proctime() ); #endif boot_fat.block_buffer_lba = boot_fat.fsi_lba; // Get free_clusters_nr field from FS Information Sector boot_fat.free_clusters_nr = read_field(FSI_FREE_COUNT, boot_fat.block_buffer, 1); // check free clusters number no larger than total number of clusters if (boot_fat.free_clusters_nr >= (boot_fat.data_sectors >> 3)) { boot_printf("\n[BOOT ERROR] %s: FSI_FREE_COUNT in FSI sector (%x)\n", "\texceeds number of data clusters (%x)\n", __FUNCTION__ , boot_fat.free_clusters_nr , (boot_fat.data_sectors >> 3)); return -1; } // Get free_cluster_hint field from FS Information Sector boot_fat.free_cluster_hint = read_field(FSI_NXT_FREE, boot_fat.block_buffer, 1); // check free_cluster_hint no larger than total number of clusters if (boot_fat.free_cluster_hint >= (boot_fat.data_sectors >> 3)) { boot_printf("\n[BOOT ERROR] %s: FSI_NXT_FREE in FSI sector (%x)\n", "\texceeds number of data clusters (%x)\n", __FUNCTION__ , boot_fat.free_cluster_hint , (boot_fat.data_sectors >> 3)); return -1; } #if DEBUG_BOOT_FAT32 boot_printf("\n[BOOT INFO] %s : free_clusters_nr = %x / free_cluster_hint = %x\n", __FUNCTION__ , boot_fat.free_clusters_nr , boot_fat.free_cluster_hint ); #endif return 0; } // set_fsi() /**************************************************************************** * This debug function displays the FAT32 File System descriptor content. * ****************************************************************************/ #if DEBUG_BOOT_FAT32 static void fat32_desc_display() { boot_printf("\n############### FAT32 DESCRIPTOR ####################" "\nFAT initialized: %x" "\nSector size (in bytes): %x" "\nCluster size (in bytes): %x" "\nFAT Region LBA: %x" "\nFAT Region size (in sectors): %x" "\nDATA Region LBA: %x" "\nDATA Region size (in sectors): %x" "\nCount of free clusters: %x" "\nMost recently allocated cluster number: %x" "\n#####################################################\n", boot_fat.initialized, boot_fat.sector_size, boot_fat.cluster_size, boot_fat.fat_lba, boot_fat.fat_sectors, boot_fat.data_lba, boot_fat.data_sectors, boot_fat.free_clusters_nr, boot_fat.free_cluster_hint ); } // fat32_desc_display() #endif /**************************************************************************** * This function computes the logical block address (LBA) of the data * * cluster whose number is 'cluster_number'. It exits if 'cluster_number' * * value is smaller than 2. * * @ cluster_number : number of the cluster whose LBA is desired. * * * * @ returns the LBA of the cluster. * ***************************************************************************/ static uint32_t cluster_to_lba(uint32_t cluster_number) { /* * The clusters begin their numbering at 2, so there is no cluster #0 * or cluster #1. */ if (cluster_number < 2) { boot_printf("\n[BOOT ERROR] cluster_to_lba(): " "Cluster number smaller than 2\n"); boot_exit(); } /* * LBA = boot_fat.data_lba + ((cluster_number - 2) * * (boot_fat.cluster_size /boot_fat.sector_size)); */ return (boot_fat.data_lba + ((cluster_number - 2) << 3)); } // cluster_to_lba() /**************************************************************************** * This function directly looks up the FAT to find the entry corresponding * * to 'cur_cluster' and return the value stored in this entry (usually the * * index of the next cluster in the cluster chain). * * @ cur_cluster : index of current cluster. * * @ nxt_cluster : pointer to the Rbuffer for the next cluster index. * * @ returns 0 on success, -1 on error. * **************************************************************************** * Implementation note * There is two versions: * - In the "software engineer" version, the FAT is seen as a set of sectors * containing 128 FAT entries each. * + : buffer of only 512 bytes is needed (we only read a sector). * - : nonetheless, I find it less elegant than the other one: * divisions and multiplications using MULT and DIV instructions are * usually slower. * - In the "hardware engineer" version, the FAT is seen as a set of clusters * containing 1024 FAT entries each. * + : divisions and multiplications are actually performed via SHIFT * operations, which are much faster on 2s complement architectures. * Personally, I pretty like this "hardware" approach. * - : on current Intel X86 processors, MULT and DIV instructions are * heavily optimized for multiplication and division by powers of 2. * Moreover, since we read a cluster of FAT entries, the buffer needs * to be of 4096 bytes. ****************************************************************************/ /* static int get_next_cluster_soft(uint32_t cur_cluster, uint32_t* nxt_cluster) { uint32_t fat_region_offset; // Offset of 'cur_cluster' in the // FAT Region (in bytes) uint32_t fat_sec_lba; // LBA of the FAT sector that // contains the entry for // 'cur_cluster' in the FAT uint32_t fat_entry_offset; // Offset of the entry // corresponding to 'cur_cluster' // in 'fat_sec_num' // Initialize the variables fat_region_offset = cur_cluster * FAT_ENTRY_SIZE; fat_sec_lba = boot_fat.fat_lba + fat_region_offset / boot_fat.sector_size; fat_entry_offset = fat_region_offset % boot_fat.sector_size; // Read the FAT sector containing the FAT entry for 'cur_cluster' if (buffer_fat_lba != fat_sec_lba) { if ( fat_ioc_access( fat_sec_lba, (uint32_t)buffer_fat, 1 ) ) { boot_printf("\n[BOOT ERROR] get_next_cluster_soft(): " "Cannot load sector of LBA %x into buffer_fat\n", fat_sec_lba); return -1; } buffer_fat_lba = fat_sec_lba; } // Fetch the content of the entry *nxt_cluster = *(uint32_t*)&buffer_fat[fat_entry_offset] & 0x0FFFFFFF; // Check the previously read value of the next cluster number if ((*nxt_cluster < USED_MIN) || (*nxt_cluster > USED_MAX)) { boot_printf("\n[BOOT ERROR] get_next_cluster_soft(): " "Illegal next cluster number (%x)\n", *nxt_cluster); return -1; } return 0; } // get_next_cluster_soft() */ ///////////////////////////////////////////////////////// static int get_next_cluster_hard(uint32_t cur_cluster, uint32_t * nxt_cluster) { uint32_t fat_cluster; // Index of cluster containing the FAT entry uint32_t fat_cluster_offset; // Offset of FAT entry in fat_cluster uint32_t fat_cluster_lba; // LBA for fat_cluster // Compute the reqired variables fat_cluster = cur_cluster >> 10; fat_cluster_offset = cur_cluster & 0x3FF; fat_cluster_lba = boot_fat.fat_lba + (fat_cluster << 3); // Read the FAT cluster containing the FAT entry if required if (buffer_fat_lba != fat_cluster_lba) { if ( fat_ioc_access( fat_cluster_lba, XPTR( BOOT_CORE_CXY , buffer_fat ), 8 ) ) { boot_printf("\n[BOOT ERROR] get_next_cluster_hard(): " "Cannot load cluster of LBA %x into buffer_fat\n", fat_cluster_lba); return -1; } buffer_fat_lba = fat_cluster_lba; } // returns the FAT entry *nxt_cluster = buffer_fat[fat_cluster_offset] & 0x0FFFFFFF; return 0; } // get_next_cluster_hard() /**************************************************************************** * This function breaks a 'pathname' pathname into a sequence of path * * components which are separated by the delimiting character "/". Each * * call to the function gets the next path component and places it in the * * buffer pointed to by 'path_component'. The result does not include the * * "/" separator. * * A sequence of calls to the function that operate on the same pathname * * maintains a pointer 'nb_read' that determines the point from which to * * start searching for the next path component. * * @ pathname : pathname to be analyzed. * * @ path_component : pointer to the buffer for a path component. * * @ nb_read : number of characters already read from the pathname. * * @ returns 0 on success, -1 on error. * ****************************************************************************/ static int get_path_component(char* pathname, char* path_component, uint32_t * nb_read) { uint32_t pathname_offset; // index used to scan the LFN entry uint32_t path_comp_offset; // index used to write to the buffer // Initialize the variables pathname_offset = *nb_read; path_comp_offset = 0; // Skip the delimiting character if (pathname[pathname_offset] == '/') pathname_offset++; // Get a path component while ((pathname[pathname_offset] != '/') && (pathname[pathname_offset] != '\0')) { path_component[path_comp_offset++] = pathname[pathname_offset++]; if (path_comp_offset > NAME_MAX_SIZE) { boot_printf("\n[BOOT ERROR] get_path_component(): " "File/directory name is too long\n"); return -1; } } path_component[path_comp_offset] = '\0'; // Update 'nb_read' for the next path component *nb_read = pathname_offset; #if DEBUG_BOOT_FAT32 boot_printf("\n[BOOT INFO] %s : returns <%s> from <%s> at cycle %d\n", __FUNCTION__ , path_component , pathname , boot_get_proctime() ); #endif return 0; } // get_path_component() /**************************************************************************** * This function analyzes a Long File Name entry pointed to by 'lfn_entry' * * to get a portion of a file name and stores it in the temporary buffer * * pointed to by 'lfn_buffer'. * * @ lfn_entry : pointer to a LFN entry. * * @ lfn_buffer : pointer to the temporary buffer for a portion of the * * full long name. * ****************************************************************************/ static void get_name_from_long(unsigned char* lfn_entry, char* lfn_buffer) { uint32_t entry_offset; /* Index used to scan the LFN entry. */ uint32_t buffer_offset; /* Index used to write to the buffer. */ uint32_t lfn_name1_end; /* End of the first part of this entry name portion. */ uint32_t lfn_name2_end; /* End of the second part of this entry name portion. */ uint32_t lfn_name3_end; /* End of the third part of this entry name portion. */ /* Initializing the variables. */ buffer_offset = 0; entry_offset = get_offset(LDIR_NAME1); lfn_name1_end = get_offset(LDIR_ATTR); lfn_name2_end = get_offset(LDIR_FSTCLUSLO); lfn_name3_end = DIR_ENTRY_SIZE; /* Iterating through the first part of this entry name portion. */ while (entry_offset != lfn_name1_end) { // If this is the last portion of a file name (file names are also NUL // terminated), we can stop the LFN entry analyzing process. if (lfn_entry[entry_offset] == '\0') goto exit; // Writing to the name buffer. lfn_buffer[buffer_offset] = lfn_entry[entry_offset]; // Preparing variables for the next iteration. buffer_offset++; entry_offset += 2; } /* Getting to the next part of the name portion. */ entry_offset = get_offset(LDIR_NAME2); /* Iterating through the second part of this entry name portion. */ while (entry_offset != lfn_name2_end) { // If this is the last portion of a file name (file names are also NUL // terminated), we can stop the LFN entry analyzing process. if (lfn_entry[entry_offset] == '\0') goto exit; // Writing to the name buffer. lfn_buffer[buffer_offset] = lfn_entry[entry_offset]; // Preparing variables for the next iteration. buffer_offset++; entry_offset += 2; } /* Getting to the next part of the name portion. */ entry_offset = get_offset(LDIR_NAME3); /* Iterating through the last part of this entry name portion. */ while (entry_offset != lfn_name3_end) { // If this is the last portion of a file name (file names are also NUL // terminated), we can stop the LFN entry analyzing process. if (lfn_entry[entry_offset] == '\0') break; // Writing to the name buffer. lfn_buffer[buffer_offset] = lfn_entry[entry_offset]; // Preparing variables for the next iteration. buffer_offset++; entry_offset += 2; } exit: /* Appending the trailing NUL to the buffer. */ lfn_buffer[buffer_offset] = '\0'; } // get_name_from_long() /**************************************************************************** * This function analyzes a standard 8.3 entry pointed to by 'entry' to * * get the name of the file/directory corresponding to this entry and * * stores it in the buffer pointed to by 'buffer'. * * @ entry : pointer to a standard 8.3 entry. * * @ buffer : pointer to the buffer for the entry name. * ****************************************************************************/ static void get_name_from_short(unsigned char* entry, char* buffer) { uint32_t entry_offset; /* Index used to scan the 8.3 entry. */ uint32_t buffer_offset; /* Index used to write to the buffer. */ /* Initializing the variables. */ entry_offset = 0; buffer_offset = 0; /* Getting the file name. */ while ((entry_offset < 8) && (entry[entry_offset] != ' ')) buffer[buffer_offset++] = boot_to_lower(entry[entry_offset++]); /* Appending the dot to the name buffer. */ buffer[buffer_offset++] = '.'; /* Getting the file extension. */ while ((entry_offset < 11) && (entry[entry_offset] != ' ')) buffer[buffer_offset++] = boot_to_lower(entry[entry_offset++]); /* Appending the trailing NUL to the buffer. */ buffer[buffer_offset++] = '\0'; } // get_name_from_short() /**************************************************************************** * This function searches for the a file identifid by its pathname. * * It returns the first cluster index and the file size. * * @ pathname : searched file pathname. * * @ first_cluster : pointer to the first cluster index * * @ file_size : pointer to the file size. * * @ returns 0 on success, -1 on error. * ****************************************************************************/ static int fat_file_search(char* pathname, uint32_t* first_cluster, uint32_t* file_size) { char path_comp[PATH_MAX_SIZE]; // Buffer for a path component char buffer_lfn[16]; // Buffer for a portion of the LFN char name[NAME_MAX_SIZE]; // Buffer for a full name uint32_t nb_read; // Number of characters already read uint32_t parent_cluster; // Cluster of the parent directory uint32_t next_cluster; // Next cluster number uint32_t child_cluster; // Cluster of searched file/directory uint32_t child_size; // Size of searched file/directory uint32_t child_is_dir; // Type of searched file/directory uint32_t cluster_lba; // LBA of current cluster uint32_t offset; // Offset in cluster buffer uint32_t ord; // First byte of a directory entry uint32_t attr; // Attribute of a directory entry uint32_t lfn_seq_elem_nr; // Number of elements in a LFN uint32_t lfn_seq_order; // Order of this entry in LFN uint32_t found; unsigned char* entry; #if DEBUG_BOOT_FAT32 boot_printf("\n[BOOT INFO] %s enters for <%s> file at cycle %d\n", __FUNCTION__ , pathname, boot_get_proctime()); #endif // Initialize some variables before getting into the search loop nb_read = 0; child_cluster = 0; child_size = 0; child_is_dir = 0; parent_cluster = boot_fat.root_cluster; // this first loop is on components in the pathname while ( pathname[nb_read] != '\0' ) { // Parse the file pathname. if ( get_path_component( pathname, path_comp, &nb_read) ) return -1; // scan one directory for one component in pathname // this second loop is on clusters // (found = 1 if success / found = 2 if failure) found = 0; while ( found == 0 ) { cluster_lba = cluster_to_lba( parent_cluster ); // Load the cluster containing the parent directory if (buffer_dir_lba != cluster_lba) { if ( fat_ioc_access( cluster_lba, XPTR( BOOT_CORE_CXY , buffer_dir ), boot_fat.cluster_size / boot_fat.sector_size ) ) { boot_printf("\n[BOOT ERROR] %s: Cannot load cluster at lba %x\n", __FUNCTION__ , cluster_lba); return -1; } buffer_dir_lba = cluster_lba; } // this third loop is on entries in this cluster for ( offset = 0, lfn_seq_elem_nr = 0; (offset < boot_fat.cluster_size) && (found == 0); offset += DIR_ENTRY_SIZE) { entry = buffer_dir + offset; ord = read_field(LDIR_ORD, entry, 1); attr = read_field(DIR_ATTR, entry, 1); if (ord == LAST_ENTRY) // no more entry in this directory { found = 2; } else if (ord == FREE_ENTRY) // unused, check the next entry { continue; } else if (attr == ATTR_LONG_NAME) // LFN entry { // Get the order of this entry in the long file name // as well as its number of elements. lfn_seq_order = ord & 0x3F; lfn_seq_elem_nr = (ord & LAST_LONG_ENTRY) ? lfn_seq_order : lfn_seq_elem_nr; // Load the portion of the long file name into temporary buffer get_name_from_long(entry, buffer_lfn); // Append this portion of the name to the full name buffer boot_strcpy(name + 13 * (lfn_seq_order-1) , buffer_lfn); // Append the trailing NUL if last LFN entry if (lfn_seq_order == lfn_seq_elem_nr) name[13 * (lfn_seq_order-1) + boot_strlen(buffer_lfn)] = '\0'; } else // Normal entry (standard 8.3 entry) { if (lfn_seq_elem_nr == 0) get_name_from_short(entry, name); // check if the full name is what we are looking for. if (boot_strcmp(name, path_comp) == 0) { found = 1; // Get the first cluster for this entry. child_cluster = (read_field(DIR_FSTCLUSHI, entry, 1) << 16) | (read_field(DIR_FSTCLUSLO, entry, 1)); // Test if this entry is a directory. child_is_dir = (attr & ATTR_DIRECTORY); // Get its size. child_size = read_field(DIR_FILESIZE, entry, 1); } // Reset lfn_seq_elem_nr for the next LFN lfn_seq_elem_nr = 0; } } // end loop on entries in current cluster // Compute next cluster index if not found in current cluster if ( found == 0 ) { if ( get_next_cluster_hard( parent_cluster, &next_cluster ) ) { boot_printf("\n[BOOT ERROR] %s: Cannot get next cluster for cluster %x\n", __FUNCTION__ , parent_cluster ); return -1; } parent_cluster = next_cluster; } } // end second while for one component in pathname // Check the result of this path component search. if (found == 2) { boot_printf("\n[BOOT ERROR] %s: <%s> not found\n", path_comp); return -1; } // check type for each pathname component if (((pathname[nb_read] == '\0') && (child_is_dir != 0)) || ((pathname[nb_read] != '\0') && (child_is_dir == 0))) { boot_printf("\n[BOOT ERROR] %s: Illegal type for <%s>" " nb_read = %d / last_char = %x / child_is_dir = %x\n", path_comp , nb_read , pathname[nb_read] , child_is_dir ); return -1; } // prepare for the next iteration. parent_cluster = child_cluster; } // end first while on the complete pathname // return file information *first_cluster = child_cluster; *file_size = child_size; #if DEBUG_BOOT_FAT32 boot_printf("\n[BOOT INFO] %s : <%s> file found at cycle %d\n" " fat_cluster = %x / size = %x\n", __FUNCTION__ , pathname , boot_get_proctime() , *first_cluster , *file_size ); #endif return 0; } // fat_file_search() /**************************************************************************** * API functions. * ****************************************************************************/ ///////////////////// int boot_fat32_init() { // FAT32 initialization should be done only once if (boot_fat.initialized == FAT_MAGIC_VALUE) { boot_printf("\n[BOOT WARNING] %s: FAT32 already initialized\n", __FUNCTION__ ); return 0; } #if DEBUG_BOOT_FAT32 boot_printf("\n[BOOT INFO] %s: Enters at cycle %d\n", __FUNCTION__ , boot_get_proctime() ); #endif // Load Boot Sector (VBR) into FAT buffer if ( fat_ioc_access( 0, XPTR( BOOT_CORE_CXY , boot_fat.block_buffer ), 1 ) ) { boot_printf("\n[BOOT ERROR] %s: Cannot load VBR\n", __FUNCTION__ ); return -1; } boot_fat.block_buffer_lba = 0; #if DEBUG_BOOT_FAT32 boot_printf("\n[BOOT INFO] %s: Boot Sector loaded at cycle %d\n", __FUNCTION__ , boot_get_proctime() ); #endif // Check assumptions on the Boot Sector uint32_t bytes_per_sector = read_field( BPB_BYTSPERSEC, boot_fat.block_buffer, 1 ); if ( bytes_per_sector != 512 ) { boot_printf("\n[BOOT ERROR] boot_fat32_init(): sector size = %x / must be Ox200\n", bytes_per_sector ); return -1; } uint32_t sectors_per_cluster = read_field(BPB_SECPERCLUS, boot_fat.block_buffer, 1); if ( sectors_per_cluster != 8 ) { boot_printf("\n[BOOT ERROR] boot_fat32_init(): Cluster size = %d / must be 8 sectors \n"); return -1; } uint32_t nb_fat_copies = read_field(BPB_NUMFATS, boot_fat.block_buffer, 1); if ( nb_fat_copies != 1 ) { boot_printf("\n[BOOT ERROR] boot_fat32_init(): number of FAT copies must be 1 \n"); return -1; } uint32_t nb_fat_sectors = read_field(BPB_FATSZ32, boot_fat.block_buffer, 1); if ( (nb_fat_sectors & 0xF) != 0 ) { boot_printf("\n[BOOT ERROR] boot_fat32_init(): FAT size must be multiple of 16 sectors\n"); return -1; } uint32_t root_cluster = read_field(BPB_ROOTCLUS, boot_fat.block_buffer, 1); if ( root_cluster != 2 ) { boot_printf("\n[BOOT ERROR] boot_fat32_init(): Root directory must be at cluster #2\n"); return -1; } uint32_t fs_info_sector = read_field(BPB_FSINFO, boot_fat.block_buffer, 1); if ( fs_info_sector != 1 ) { boot_printf("\n[BOOT ERROR] boot_fat32_init(): FS Information Sector must be 1\n"); return -1; } uint32_t reserved_sectors = read_field(BPB_RSVDSECCNT, boot_fat.block_buffer, 1); uint32_t nb_total_sectors = read_field(BPB_TOTSEC32, boot_fat.block_buffer, 1); // Initialize FAT32 descriptor from Boot Sector boot_fat.sector_size = bytes_per_sector; boot_fat.cluster_size = bytes_per_sector * sectors_per_cluster; boot_fat.fat_sectors = nb_fat_sectors; boot_fat.fat_lba = reserved_sectors; boot_fat.data_sectors = nb_total_sectors - (nb_fat_sectors + reserved_sectors); boot_fat.data_lba = nb_fat_sectors + boot_fat.fat_lba; boot_fat.root_cluster = root_cluster; boot_fat.fsi_lba = fs_info_sector; boot_fat.initialized = FAT_MAGIC_VALUE; // Set information from FS Information Sector if (set_fsi()) return -1; // Initialize FAT and DIR buffers buffer_fat_lba = 0xFFFFFFFF; buffer_dir_lba = 0xFFFFFFFF; #if DEBUG_BOOT_FAT32 fat32_desc_display(); boot_printf("\n[BOOT INFO] %s : FAT32 File System initialized at cycle %d\n", __FUNCTION__ , boot_get_proctime() ); #endif return 0; } // boot_fat32_init() /////////////////////////////////////// int boot_fat32_load( char* pathname, uint32_t buff_addr, uint32_t buff_size ) { uint32_t cur_cluster; uint32_t nxt_cluster; uint32_t size; uint32_t nb_clusters; uint32_t buff_offset; uint32_t cluster_lba; // Checking FAT32 initialization if (boot_fat.initialized != FAT_MAGIC_VALUE) { boot_printf("\n[BOOT ERROR] %s: FAT not initialized\n", __FUNCTION__ ); return -1; } #if DEBUG_BOOT_FAT32 boot_printf("\n[BOOT INFO] %s enters for file <%s> at cycle %d\n", __FUNCTION__ , pathname, boot_get_proctime() ); #endif // Search file if (fat_file_search(pathname, &cur_cluster, &size)) { boot_printf("\n[BOOT ERROR] in %s : File <%s> not found\n", __FUNCTION__ , pathname); return -1; } /* Checking buffer size. */ if (size > buff_size) { boot_printf("\n[BOOT ERROR] in %s : file <%s> is too large (%x bytes) / " "buffer size = %x bytes\n", __FUNCTION__ , pathname , size , buff_size ); return -1; } /* Computing number of clusters to read. */ // nb_clusters = size / boot_fat.cluster_size nb_clusters = size >> 12; // if ((size % boot_fat.cluster_size) != 0) if (size & 0xFFF) nb_clusters++; /* Following the cluster chains in the FAT. */ buff_offset = buff_addr; while (nb_clusters > 0) { cluster_lba = cluster_to_lba(cur_cluster); /* Loading the current cluster. */ if ( fat_ioc_access( cluster_lba, XPTR( BOOT_CORE_CXY , buff_offset ), boot_fat.cluster_size / boot_fat.sector_size ) ) { boot_printf("\n[BOOT ERROR] in %s : cannot load cluster at LBA %x\n", __FUNCTION__ , cluster_lba ); return -1; } /* Computing next cluster number. */ if ( get_next_cluster_hard( cur_cluster , &nxt_cluster ) ) { boot_printf("\n[BOOT ERROR] in %s : cannot get next cluster for cluster %x\n", __FUNCTION__ , cur_cluster ); return -1; } /* Getting prepared for the next iteration. */ nb_clusters--; buff_offset += boot_fat.cluster_size; cur_cluster = nxt_cluster; } #if DEBUG_BOOT_FAT32 boot_printf("\n[BOOT INFO] %s : file <%s> loaded at cycle %d\n" " address = %x , size = %x\n", __FUNCTION__ , pathname , boot_get_proctime() , buff_addr , size ); #endif return 0; } // boot_fat32_load()