:: commit 1822839506d8bf8c52acc1fabfb8bfaf95ec44f2

mintsuki <mintsuki@protonmail.com> — 2021-03-13 04:07

parents: 23838a4e84

fat: Initial FAT16 implementation

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 != NULL) {
+        uint32_t current_cluster_number = directory->cluster_num_high << 16 | directory->cluster_num_low;
+
+        uint32_t *directory_cluster_chain = cache_cluster_chain(context, current_cluster_number, &dir_chain_len);
 
-    if (directory_cluster_chain == NULL)
-        return -1;
+        if (directory_cluster_chain == NULL)
+            return -1;
 
-    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;
+        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, &current_directory, &current_file, current_part)) != 0) {
+        if ((r = fat32_open_in(&context, current_directory, &current_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;
tab: 248 wrap: offon