From 1822839506d8bf8c52acc1fabfb8bfaf95ec44f2 Mon Sep 17 00:00:00 2001 From: mintsuki Date: Sat, 13 Mar 2021 05:07:18 +0100 Subject: [PATCH] fat: Initial FAT16 implementation --- Makefile | 23 ++++++++- stage23/fs/fat32.h | 6 ++- stage23/fs/fat32.s2.c | 115 ++++++++++++++++++++++++++++++++---------- 3 files changed, 114 insertions(+), 30 deletions(-) diff --git a/Makefile b/Makefile index a913321d..9f2bae99 100644 --- a/Makefile +++ b/Makefile @@ -6,7 +6,7 @@ DESTDIR = PATH := $(shell pwd)/toolchain/bin:$(PATH) -.PHONY: all clean install distclean limine-bios limine-uefi limine-bios-clean limine-uefi-clean stage23-bios stage23-bios-clean stage23-uefi stage23-uefi-clean decompressor decompressor-clean toolchain test.hdd echfs-test ext2-test fat32-test iso9660-test +.PHONY: all clean install distclean limine-bios limine-uefi limine-bios-clean limine-uefi-clean stage23-bios stage23-bios-clean stage23-uefi stage23-uefi-clean decompressor decompressor-clean toolchain test.hdd echfs-test ext2-test fat16-test fat32-test iso9660-test pxe-test uefi-test all: $(MAKE) limine-uefi @@ -138,6 +138,27 @@ ext2-test: bin/limine-install test.hdd qemu-system-x86_64 -net none -smp 4 -enable-kvm -cpu host -hda test.hdd -debugcon stdio +fat16-test: + $(MAKE) test-clean + $(MAKE) test.hdd + $(MAKE) limine-bios + $(MAKE) bin/limine-install + $(MAKE) -C test + rm -rf test_image/ + mkdir test_image + sudo losetup -Pf --show test.hdd > loopback_dev + sudo partprobe `cat loopback_dev` + sudo mkfs.fat -F 16 `cat loopback_dev`p1 + sudo mount `cat loopback_dev`p1 test_image + sudo mkdir test_image/boot + sudo cp -rv bin/* test/* test_image/boot/ + sync + sudo umount test_image/ + sudo losetup -d `cat loopback_dev` + rm -rf test_image loopback_dev + bin/limine-install test.hdd + qemu-system-x86_64 -net none -smp 4 -enable-kvm -cpu host -hda test.hdd -debugcon stdio + fat32-test: $(MAKE) test-clean $(MAKE) test.hdd diff --git a/stage23/fs/fat32.h b/stage23/fs/fat32.h index 55f401ec..030c3b5b 100644 --- a/stage23/fs/fat32.h +++ b/stage23/fs/fat32.h @@ -6,14 +6,18 @@ struct fat32_context { struct volume *part; + int type; uint8_t sectors_per_cluster; uint16_t reserved_sectors; uint8_t number_of_fats; uint32_t hidden_sectors; uint32_t sectors_per_fat; - uint32_t root_directory_cluster; uint32_t fat_start_lba; uint32_t data_start_lba; + uint32_t root_directory_cluster; + uint16_t root_entries; + uint32_t root_start; + uint32_t root_size; }; struct fat32_file_handle { diff --git a/stage23/fs/fat32.s2.c b/stage23/fs/fat32.s2.c index e27f5ee8..bbe89440 100644 --- a/stage23/fs/fat32.s2.c +++ b/stage23/fs/fat32.s2.c @@ -12,6 +12,7 @@ #define FAT32_VALID_SIGNATURE_1 0x28 #define FAT32_VALID_SIGNATURE_2 0x29 #define FAT32_VALID_SYSTEM_IDENTIFIER "FAT32 " +#define FAT16_VALID_SYSTEM_IDENTIFIER "FAT16 " #define FAT32_SECTOR_SIZE 512 #define FAT32_ATTRIBUTE_SUBDIRECTORY 0x10 #define FAT32_LFN_ATTRIBUTE 0x0F @@ -73,50 +74,81 @@ static int fat32_init_context(struct fat32_context* context, struct volume *part struct fat32_bpb bpb; volume_read(context->part, &bpb, 0, sizeof(struct fat32_bpb)); - if (bpb.signature != FAT32_VALID_SIGNATURE_1 && bpb.signature != FAT32_VALID_SIGNATURE_2) { - return 1; + if (strncmp(bpb.system_identifier, FAT32_VALID_SYSTEM_IDENTIFIER, SIZEOF_ARRAY(bpb.system_identifier)) == 0) { + if (bpb.signature == FAT32_VALID_SIGNATURE_1 || bpb.signature == FAT32_VALID_SIGNATURE_2) { + context->type = 32; + goto valid; + } } - if (strncmp(bpb.system_identifier, FAT32_VALID_SYSTEM_IDENTIFIER, SIZEOF_ARRAY(bpb.system_identifier)) != 0) { - return 1; + if (strncmp((((void *)&bpb) + 0x36), FAT16_VALID_SYSTEM_IDENTIFIER, SIZEOF_ARRAY(bpb.system_identifier)) == 0) { + context->type = 16; + goto valid; } + return 1; + +valid: context->sectors_per_cluster = bpb.sectors_per_cluster; context->reserved_sectors = bpb.reserved_sectors; context->number_of_fats = bpb.fats_count; context->hidden_sectors = bpb.hidden_sectors_count; - context->sectors_per_fat = bpb.sectors_per_fat_32; + context->sectors_per_fat = context->type == 32 ? bpb.sectors_per_fat_32 : bpb.sectors_per_fat_16; context->root_directory_cluster = bpb.root_directory_cluster; context->fat_start_lba = bpb.reserved_sectors; - context->data_start_lba = context->fat_start_lba + bpb.fats_count * bpb.sectors_per_fat_32; + context->root_entries = bpb.directory_entries_count; + context->root_start = context->reserved_sectors + context->number_of_fats * context->sectors_per_fat; + context->root_size = DIV_ROUNDUP(context->root_entries * sizeof(struct fat32_directory_entry), FAT32_SECTOR_SIZE); + switch (context->type) { + case 16: + context->data_start_lba = context->root_start + context->root_size; + break; + case 32: + context->data_start_lba = context->root_start; + break; + default: + __builtin_unreachable(); + } return 0; } -static int fat32_read_cluster_from_map(struct fat32_context* context, uint32_t cluster, uint32_t* out) { - volume_read(context->part, out, context->fat_start_lba * FAT32_SECTOR_SIZE + cluster * sizeof(uint32_t), sizeof(uint32_t)); +static int read_cluster_from_map(struct fat32_context *context, uint32_t cluster, uint32_t *out) { + switch (context->type) { + case 16: + *out = 0; + volume_read(context->part, out, context->fat_start_lba * FAT32_SECTOR_SIZE + cluster * sizeof(uint16_t), sizeof(uint16_t)); + break; + case 32: + volume_read(context->part, out, context->fat_start_lba * FAT32_SECTOR_SIZE + cluster * sizeof(uint32_t), sizeof(uint32_t)); + *out &= 0x0fffffff; + break; + default: + __builtin_unreachable(); + } - *out &= 0x0fffffff; return 0; } static uint32_t *cache_cluster_chain(struct fat32_context *context, uint32_t initial_cluster, size_t *_chain_length) { - if (initial_cluster < 0x2 || initial_cluster > 0xfffffef) + uint32_t cluster_limit = (context->type == 16 ? 0xffef : 0) + | (context->type == 32 ? 0xfffffef : 0); + if (initial_cluster < 0x2 || initial_cluster > cluster_limit) return NULL; uint32_t cluster = initial_cluster; size_t chain_length; for (chain_length = 1; ; chain_length++) { - fat32_read_cluster_from_map(context, cluster, &cluster); - if (cluster < 0x2 || cluster > 0xfffffef) + read_cluster_from_map(context, cluster, &cluster); + if (cluster < 0x2 || cluster > cluster_limit) break; } uint32_t *cluster_chain = ext_mem_alloc(chain_length * sizeof(uint32_t)); cluster = initial_cluster; for (size_t i = 0; i < chain_length; i++) { cluster_chain[i] = cluster; - fat32_read_cluster_from_map(context, cluster, &cluster); + read_cluster_from_map(context, cluster, &cluster); } *_chain_length = chain_length; return cluster_chain; @@ -134,7 +166,7 @@ static bool read_cluster_chain(struct fat32_context *context, if (chunk > block_size - offset) chunk = block_size - offset; - uint64_t base = (context->data_start_lba + (cluster_chain[block] - 2) * context->sectors_per_cluster) * FAT32_SECTOR_SIZE; + uint64_t base = ((uint64_t)context->data_start_lba + (cluster_chain[block] - 2) * context->sectors_per_cluster) * FAT32_SECTOR_SIZE; volume_read(context->part, buf + progress, base + offset, chunk); progress += chunk; @@ -183,16 +215,28 @@ static int fat32_open_in(struct fat32_context* context, struct fat32_directory_e size_t block_size = context->sectors_per_cluster * FAT32_SECTOR_SIZE; char current_lfn[FAT32_LFN_MAX_FILENAME_LENGTH] = {0}; - uint32_t current_cluster_number = directory->cluster_num_high << 16 | directory->cluster_num_low; - size_t dir_chain_len; - uint32_t *directory_cluster_chain = cache_cluster_chain(context, current_cluster_number, &dir_chain_len); + size_t dir_chain_len; + struct fat32_directory_entry *directory_entries; - if (directory_cluster_chain == NULL) - return -1; + if (directory != NULL) { + uint32_t current_cluster_number = directory->cluster_num_high << 16 | directory->cluster_num_low; - struct fat32_directory_entry *directory_entries = ext_mem_alloc(dir_chain_len * block_size); - if (!read_cluster_chain(context, directory_cluster_chain, directory_entries, 0, dir_chain_len * block_size)) - return -1; + uint32_t *directory_cluster_chain = cache_cluster_chain(context, current_cluster_number, &dir_chain_len); + + if (directory_cluster_chain == NULL) + return -1; + + directory_entries = ext_mem_alloc(dir_chain_len * block_size); + + if (!read_cluster_chain(context, directory_cluster_chain, directory_entries, 0, dir_chain_len * block_size)) + return -1; + } else { + dir_chain_len = DIV_ROUNDUP(context->root_entries * sizeof(struct fat32_directory_entry), block_size); + + directory_entries = ext_mem_alloc(dir_chain_len * block_size); + + volume_read(context->part, directory_entries, context->root_start * FAT32_SECTOR_SIZE, context->root_entries * sizeof(struct fat32_directory_entry)); + } for (size_t i = 0; i < (dir_chain_len * block_size) / sizeof(struct fat32_directory_entry); i++) { if (directory_entries[i].file_name_and_ext[0] == 0x00) { @@ -234,7 +278,10 @@ static int fat32_open_in(struct fat32_context* context, struct fat32_directory_e } } else { char fn[8+3]; - if (fat32_filename_to_8_3(fn, name) && !strncmp(directory_entries[i].file_name_and_ext, fn, 8+3)) { + if (!fat32_filename_to_8_3(fn, name)) { + continue; + } + if (!strncmp(directory_entries[i].file_name_and_ext, fn, 8+3)) { *file = directory_entries[i]; return 0; } @@ -259,7 +306,8 @@ int fat32_open(struct fat32_file_handle* ret, struct volume *part, const char* p return r; } - struct fat32_directory_entry current_directory; + struct fat32_directory_entry _current_directory; + struct fat32_directory_entry *current_directory; struct fat32_directory_entry current_file; unsigned int current_index = 0; char current_part[FAT32_LFN_MAX_FILENAME_LENGTH]; @@ -270,8 +318,18 @@ int fat32_open(struct fat32_file_handle* ret, struct volume *part, const char* p } // walk down the directory tree - current_directory.cluster_num_low = context.root_directory_cluster & 0xFFFF; - current_directory.cluster_num_high = context.root_directory_cluster >> 16; + switch (context.type) { + case 16: + current_directory = NULL; + break; + case 32: + _current_directory.cluster_num_low = context.root_directory_cluster & 0xFFFF; + _current_directory.cluster_num_high = context.root_directory_cluster >> 16; + current_directory = &_current_directory; + break; + default: + __builtin_unreachable(); + } for (;;) { bool expect_directory = false; @@ -293,12 +351,13 @@ int fat32_open(struct fat32_file_handle* ret, struct volume *part, const char* p } } - if ((r = fat32_open_in(&context, ¤t_directory, ¤t_file, current_part)) != 0) { + if ((r = fat32_open_in(&context, current_directory, ¤t_file, current_part)) != 0) { return r; } if (expect_directory) { - current_directory = current_file; + _current_directory = current_file; + current_directory = &_current_directory; } else { ret->context = context; ret->first_cluster = current_file.cluster_num_high << 16 | current_file.cluster_num_low;