aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--arch/x86_64/support/grub.cfg.in1
-rw-r--r--arch/x86_64/support/modules/fat32.img3
-rw-r--r--kernel/CMakeLists.txt1
-rw-r--r--kernel/kernel/filesystems/fat32/boot_sector.hpp54
-rw-r--r--kernel/kernel/filesystems/fat32/driver_state.hpp23
-rw-r--r--kernel/kernel/filesystems/fat32/error.cpp58
-rw-r--r--kernel/kernel/filesystems/fat32/error.hpp33
-rw-r--r--kernel/kernel/filesystems/fat32/filesystem.cpp70
-rw-r--r--kernel/kernel/filesystems/fat32/filesystem.hpp8
-rw-r--r--kernel/kernel/filesystems/fat32/filesystem.tests.cpp51
-rw-r--r--kernel/kernel/main.cpp17
l---------kernel/kernel/test_support/vfs/test_assets/fat32.img1
-rwxr-xr-xscripts/qemu-wrapper.sh2
13 files changed, 318 insertions, 4 deletions
diff --git a/arch/x86_64/support/grub.cfg.in b/arch/x86_64/support/grub.cfg.in
index 06701fdc..135dc107 100644
--- a/arch/x86_64/support/grub.cfg.in
+++ b/arch/x86_64/support/grub.cfg.in
@@ -6,5 +6,6 @@ menuentry "TeachOS" {
module2 /modules/ext2_4KB_fs.img type=ramdisk
module2 /modules/ext2_1KB_fs.img type=ramdisk
module2 /modules/ext2_2KB_fs.img type=ramdisk
+ module2 /modules/fat32.img type=ramdisk
boot
} \ No newline at end of file
diff --git a/arch/x86_64/support/modules/fat32.img b/arch/x86_64/support/modules/fat32.img
new file mode 100644
index 00000000..ff9c6e08
--- /dev/null
+++ b/arch/x86_64/support/modules/fat32.img
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:1eea0fe2e0d9f93ac6a55a25c071329c038f111d085148d5f6a6d1d35e539672
+size 35651584
diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt
index e7464658..aacb5d97 100644
--- a/kernel/CMakeLists.txt
+++ b/kernel/CMakeLists.txt
@@ -86,6 +86,7 @@ target_sources("kernel_lib" PRIVATE
"kernel/filesystems/ext2/write_batch.cpp"
# fat32 Filesystem
+ "kernel/filesystems/fat32/error.cpp"
"kernel/filesystems/fat32/filesystem.cpp"
"kernel/filesystems/fat32/inode.cpp"
"kernel/filesystems/fat32/module.cpp"
diff --git a/kernel/kernel/filesystems/fat32/boot_sector.hpp b/kernel/kernel/filesystems/fat32/boot_sector.hpp
new file mode 100644
index 00000000..b5789b92
--- /dev/null
+++ b/kernel/kernel/filesystems/fat32/boot_sector.hpp
@@ -0,0 +1,54 @@
+#ifndef TEACHOS_KERNEL_FILESYSTEMS_FAT32_BOOT_SECTOR_HPP
+#define TEACHOS_KERNEL_FILESYSTEMS_FAT32_BOOT_SECTOR_HPP
+
+#include <array>
+#include <cstdint>
+
+namespace kernel::filesystems::fat32
+{
+ //! The boot sector in the fat32 filesystem.
+ //!
+ //! The layout follows the "Microsoft Extensible Firmware Initiative FAT32 File System Specification" (fatgen103).
+ //! The sector consists of the BIOS Parameter Block (BPB) common to all FAT variants, followed by the FAT32 specific
+ //! extension.
+ // cspell:ignore fatgen
+ struct [[gnu::packed]] boot_sector
+ {
+ // Common BIOS Parameter Block
+ std::array<std::uint8_t, 3> jump_boot;
+ std::array<char, 8> oem_name;
+ std::uint16_t bytes_per_sector;
+ std::uint8_t sectors_per_cluster;
+ std::uint16_t reserved_sector_count;
+ std::uint8_t number_of_fats;
+ std::uint16_t root_entry_count;
+ std::uint16_t total_sectors_16;
+ std::uint8_t media;
+ std::uint16_t fat_size_16;
+ std::uint16_t sectors_per_track;
+ std::uint16_t number_of_heads;
+ std::uint32_t hidden_sectors;
+ std::uint32_t total_sectors_32;
+
+ // FAT32 Extended BIOS Parameter Block
+ std::uint32_t fat_size_32;
+ std::uint16_t extended_flags;
+ std::uint16_t filesystem_version;
+ std::uint32_t root_cluster;
+ std::uint16_t filesystem_info_sector;
+ std::uint16_t backup_boot_sector;
+ std::array<std::uint8_t, 12> reserved; // NOLINT(readability-magic-numbers)
+ std::uint8_t drive_number;
+ std::uint8_t reserved1;
+ std::uint8_t extended_boot_signature;
+ std::uint32_t volume_id;
+ std::array<char, 11> volume_label; // NOLINT(readability-magic-numbers)
+ std::array<char, 8> filesystem_type;
+ std::array<std::uint8_t, 420> boot_code; // NOLINT(readability-magic-numbers)
+ std::uint16_t boot_signature;
+ };
+
+ static_assert(sizeof(boot_sector) == 512, "The fat32 boot sector must be exactly 512 bytes in size.");
+} // namespace kernel::filesystems::fat32
+
+#endif
diff --git a/kernel/kernel/filesystems/fat32/driver_state.hpp b/kernel/kernel/filesystems/fat32/driver_state.hpp
new file mode 100644
index 00000000..55b90724
--- /dev/null
+++ b/kernel/kernel/filesystems/fat32/driver_state.hpp
@@ -0,0 +1,23 @@
+#ifndef TEACHOS_KERNEL_FILESYSTEMS_FAT32_DRIVER_STATE_HPP
+#define TEACHOS_KERNEL_FILESYSTEMS_FAT32_DRIVER_STATE_HPP
+
+#include <kernel/filesystems/fat32/boot_sector.hpp>
+#include <kernel/vfs/driver_state.hpp>
+#include <kernel/vfs/inode.hpp>
+
+#include <kapi/tracked_mutex.hpp>
+
+#include <kstd/memory.hpp>
+#include <kstd/vector.hpp>
+
+namespace kernel::filesystems::fat32
+{
+
+ struct driver_state : vfs::driver_state
+ {
+ struct boot_sector boot_sector{};
+ };
+
+} // namespace kernel::filesystems::fat32
+
+#endif \ No newline at end of file
diff --git a/kernel/kernel/filesystems/fat32/error.cpp b/kernel/kernel/filesystems/fat32/error.cpp
new file mode 100644
index 00000000..4a18eb9e
--- /dev/null
+++ b/kernel/kernel/filesystems/fat32/error.cpp
@@ -0,0 +1,58 @@
+#include <kernel/filesystems/fat32/error.hpp>
+
+#include <kernel/vfs/error.hpp>
+
+#include <kstd/system_error.hpp>
+
+#include <string_view>
+
+namespace kernel::filesystems::fat32
+{
+
+ namespace
+ {
+ struct category_t final : kstd::error_category
+ {
+ [[nodiscard]] constexpr auto name() const noexcept -> std::string_view override
+ {
+ return "fat32";
+ }
+
+ [[nodiscard]] constexpr auto message(int value) const noexcept -> std::string_view override
+ {
+ switch (static_cast<errc>(value))
+ {
+ case errc::invalid_boot_signature:
+ return "invalid boot signature";
+ default:
+ return "unknown fat32 error";
+ };
+ }
+
+ [[nodiscard]] constexpr auto equivalent(int code, kstd::error_condition const & condition) const noexcept
+ -> bool override
+ {
+ switch (static_cast<errc>(code))
+ {
+ case errc::invalid_boot_signature:
+ if (condition.category() == kernel::vfs::category())
+ {
+ return condition.value() == static_cast<int>(kernel::vfs::errc::invalid_filesystem);
+ }
+ else if (condition.category() == kstd::generic_category())
+ {
+ return condition.value() == static_cast<int>(kstd::errc::invalid_argument);
+ }
+ break;
+ }
+ return kstd::error_category::equivalent(code, condition);
+ }
+ } constexpr inline fat32_category_instance{};
+ } // namespace
+
+ [[nodiscard]] auto category() noexcept -> kstd::error_category const &
+ {
+ return fat32_category_instance;
+ }
+
+} // namespace kernel::filesystems::fat32
diff --git a/kernel/kernel/filesystems/fat32/error.hpp b/kernel/kernel/filesystems/fat32/error.hpp
new file mode 100644
index 00000000..6955be3f
--- /dev/null
+++ b/kernel/kernel/filesystems/fat32/error.hpp
@@ -0,0 +1,33 @@
+#ifndef TEACHOS_KERNEL_FILESYSTEMS_FAT32_ERROR_HPP
+#define TEACHOS_KERNEL_FILESYSTEMS_FAT32_ERROR_HPP
+
+#include <kernel/vfs/error.hpp>
+
+#include <kstd/system_error.hpp>
+
+#include <type_traits>
+
+namespace kernel::filesystems::fat32
+{
+ enum struct errc : int
+ {
+ invalid_boot_signature = 1
+ };
+
+ [[nodiscard]] auto category() noexcept -> kstd::error_category const &;
+
+ [[nodiscard]] constexpr auto inline make_error_code(errc error) noexcept -> kstd::error_code
+ {
+ return {static_cast<int>(error), category()};
+ }
+} // namespace kernel::filesystems::fat32
+
+namespace kstd
+{
+ template<>
+ struct is_error_code_enum<kernel::filesystems::fat32::errc> : std::true_type
+ {
+ };
+} // namespace kstd
+
+#endif \ No newline at end of file
diff --git a/kernel/kernel/filesystems/fat32/filesystem.cpp b/kernel/kernel/filesystems/fat32/filesystem.cpp
index a66a9b25..30f1b286 100644
--- a/kernel/kernel/filesystems/fat32/filesystem.cpp
+++ b/kernel/kernel/filesystems/fat32/filesystem.cpp
@@ -1,5 +1,7 @@
#include <kernel/filesystems/fat32/filesystem.hpp>
+#include <kernel/filesystems/fat32/boot_sector.hpp>
+#include <kernel/filesystems/fat32/error.hpp>
#include <kernel/filesystems/fat32/inode.hpp>
#include <kernel/vfs/error.hpp>
#include <kernel/vfs/filesystem.hpp>
@@ -7,9 +9,14 @@
#include <kapi/filesystem.hpp>
#include <kstd/memory.hpp>
+#include <kstd/print.hpp>
#include <kstd/result.hpp>
+#include <kstd/span.hpp>
#include <kstd/system_error.hpp>
+#include <kstd/units.hpp>
+#include <array>
+#include <cstddef>
#include <cstdint>
#include <optional>
#include <string_view>
@@ -18,13 +25,72 @@ namespace kernel::filesystems::fat32
{
static_assert(sizeof(kernel::vfs::filesystem) == sizeof(fat32::filesystem));
+ //! Hidden implementation details.
+ namespace
+ {
+ //! Convert a fixed size, space padded character array of the boot sector into a string view.
+ //!
+ //! @param array The array to convert.
+ //! @return A view of the array contents.
+ template<std::size_t Size>
+ [[nodiscard]] constexpr auto to_string_view(std::array<char, Size> const & array) -> std::string_view
+ {
+ return {array.data(), array.size()};
+ }
+
+ //! Print all fields of a boot sector.
+ //!
+ //! @param boot_sector The boot sector to print.
+ auto print_boot_sector(boot_sector const & boot_sector) -> void
+ {
+ kstd::println("[OS:FAT32] jump_boot: {:#04X} {:#04X} {:#04X}", boot_sector.jump_boot[0], boot_sector.jump_boot[1],
+ boot_sector.jump_boot[2]);
+ kstd::println("[OS:FAT32] oem_name: '{}'", to_string_view(boot_sector.oem_name));
+ kstd::println("[OS:FAT32] bytes_per_sector: {}", boot_sector.bytes_per_sector);
+ kstd::println("[OS:FAT32] sectors_per_cluster: {}", boot_sector.sectors_per_cluster);
+ kstd::println("[OS:FAT32] reserved_sector_count: {}", boot_sector.reserved_sector_count);
+ kstd::println("[OS:FAT32] number_of_fats: {}", boot_sector.number_of_fats);
+ kstd::println("[OS:FAT32] root_entry_count: {}", boot_sector.root_entry_count);
+ kstd::println("[OS:FAT32] total_sectors_16: {}", boot_sector.total_sectors_16);
+ kstd::println("[OS:FAT32] media: {:#04X}", boot_sector.media);
+ kstd::println("[OS:FAT32] fat_size_16: {}", boot_sector.fat_size_16);
+ kstd::println("[OS:FAT32] sectors_per_track: {}", boot_sector.sectors_per_track);
+ kstd::println("[OS:FAT32] number_of_heads: {}", boot_sector.number_of_heads);
+ kstd::println("[OS:FAT32] hidden_sectors: {}", boot_sector.hidden_sectors);
+ kstd::println("[OS:FAT32] total_sectors_32: {}", boot_sector.total_sectors_32);
+ kstd::println("[OS:FAT32] fat_size_32: {}", boot_sector.fat_size_32);
+ kstd::println("[OS:FAT32] extended_flags: {:#06X}", boot_sector.extended_flags);
+ kstd::println("[OS:FAT32] filesystem_version: {:#06X}", boot_sector.filesystem_version);
+ kstd::println("[OS:FAT32] root_cluster: {}", boot_sector.root_cluster);
+ kstd::println("[OS:FAT32] filesystem_info_sector: {}", boot_sector.filesystem_info_sector);
+ kstd::println("[OS:FAT32] backup_boot_sector: {}", boot_sector.backup_boot_sector);
+ kstd::println("[OS:FAT32] drive_number: {:#04X}", boot_sector.drive_number);
+ kstd::println("[OS:FAT32] extended_boot_signature: {:#04X}", boot_sector.extended_boot_signature);
+ kstd::println("[OS:FAT32] volume_id: {:#010X}", boot_sector.volume_id);
+ kstd::println("[OS:FAT32] volume_label: '{}'", to_string_view(boot_sector.volume_label));
+ kstd::println("[OS:FAT32] filesystem_type: '{}'", to_string_view(boot_sector.filesystem_type));
+ kstd::println("[OS:FAT32] boot_signature: {:#06X}", boot_sector.boot_signature);
+ }
+ } // namespace
+
auto filesystem::probe(inode_ptr const & inode) const -> kstd::result<std::uint32_t>
{
- if (inode)
+ auto boot_sector = fat32::boot_sector{};
+ auto const bytes = kstd::raw_bytes(boot_sector);
+ auto const read_result = inode->read(bytes, constants::boot_sector_offset);
+
+ if (!read_result)
+ {
+ return kstd::failure(read_result.error());
+ }
+
+ if (boot_sector.boot_signature != constants::boot_signature)
{
- return kstd::failure(vfs::errc::invalid_argument);
+ return kstd::failure(errc::invalid_boot_signature);
}
+ print_boot_sector(boot_sector);
+
return 0;
}
diff --git a/kernel/kernel/filesystems/fat32/filesystem.hpp b/kernel/kernel/filesystems/fat32/filesystem.hpp
index 8b0b4941..54c8035c 100644
--- a/kernel/kernel/filesystems/fat32/filesystem.hpp
+++ b/kernel/kernel/filesystems/fat32/filesystem.hpp
@@ -10,6 +10,7 @@
#include <kstd/result.hpp>
#include <kstd/string.hpp>
#include <kstd/system_error.hpp>
+#include <kstd/units.hpp>
#include <kstd/vector.hpp>
#include <cstdint>
@@ -18,6 +19,13 @@
namespace kernel::filesystems::fat32
{
+ //! Constants related to the fat32 filesystem.
+ namespace constants
+ {
+ constexpr kstd::bytes inline boot_sector_offset = kstd::bytes{0};
+ constexpr std::uint16_t inline boot_signature = 0xAA55;
+ } // namespace constants
+
//! The File Allocation Table 32 Filesystem (fat32)
struct filesystem : kernel::vfs::filesystem
{
diff --git a/kernel/kernel/filesystems/fat32/filesystem.tests.cpp b/kernel/kernel/filesystems/fat32/filesystem.tests.cpp
index cc35cd74..8e4bc8c1 100644
--- a/kernel/kernel/filesystems/fat32/filesystem.tests.cpp
+++ b/kernel/kernel/filesystems/fat32/filesystem.tests.cpp
@@ -1,15 +1,25 @@
#include <kernel/filesystems/fat32/filesystem.hpp>
+#include <kernel/devices/storage.hpp>
+#include <kernel/filesystems/fat32/error.hpp>
+#include <kernel/test_support/devices/block_device.hpp>
+#include <kernel/test_support/vfs/storage_boot_module_fixture.hpp>
+#include <kernel/vfs/device_inode.hpp>
#include <kernel/vfs/filesystem.hpp>
#include <kapi/filesystem.hpp>
#include <kstd/memory.hpp>
#include <kstd/print.hpp>
+#include <kstd/units.hpp>
#include <kstd/vector.hpp>
#include <catch2/catch_test_macros.hpp>
+#include <filesystem>
+
+using namespace kstd::units_literals;
+
SCENARIO("fat32 filesystem mount and lookup", "[filesystem][fat32][filesystem]")
{
GIVEN("a mounted fat32 filesystem")
@@ -46,3 +56,44 @@ SCENARIO("fat32 filesystem mount and lookup", "[filesystem][fat32][filesystem]")
}
}
}
+
+SCENARIO_METHOD(kernel::tests::vfs::storage_boot_module_fixture, "Fat32 filesystem probe with real image",
+ "[filesystem][fat32][filesystem][img]")
+{
+ auto const image_path = std::filesystem::path{KERNEL_TEST_ASSETS_DIR} / "fat32.img";
+
+ GIVEN("a block device containing a fat32 image")
+ {
+ REQUIRE(std::filesystem::exists(image_path));
+ REQUIRE_NOTHROW(setup_modules_from_img({"test_img_module"}, {image_path}));
+
+ auto boot_device = kernel::devices::storage::determine_boot_device();
+ REQUIRE(boot_device);
+
+ auto dev_inode = kstd::make_shared<kernel::vfs::device_inode>(boot_device);
+ auto fs = kstd::make_shared<kernel::filesystems::fat32::filesystem>();
+
+ THEN("probe accepts the boot sector")
+ {
+ REQUIRE(fs->probe(dev_inode));
+ }
+ }
+}
+
+SCENARIO("Fat32 filesystem rejects invalid boot signature", "[filesystem][fat32][filesystem]")
+{
+ auto const block_size = 512_B;
+ GIVEN("a block device that does not contain a fat32 boot sector")
+ {
+ auto device = kstd::make_shared<kernel::tests::devices::block_device>("mock", block_size, 2 * block_size);
+ REQUIRE(device);
+
+ auto dev_inode = kstd::make_shared<kernel::vfs::device_inode>(device);
+ auto fs = kstd::make_shared<kernel::filesystems::fat32::filesystem>();
+
+ THEN("probe fails with invalid_boot_signature")
+ {
+ REQUIRE(fs->probe(dev_inode).error() == kernel::filesystems::fat32::errc::invalid_boot_signature);
+ }
+ }
+}
diff --git a/kernel/kernel/main.cpp b/kernel/kernel/main.cpp
index bc192950..15952547 100644
--- a/kernel/kernel/main.cpp
+++ b/kernel/kernel/main.cpp
@@ -25,6 +25,21 @@
using namespace kstd::units_literals;
+auto run_demo_fat32() -> void
+{
+ kstd::println("[OS:DEM] Running FAT32 Demo");
+
+ kstd::println("[OS:DEM] mount /dev/ram3 to /entrance");
+ if (auto result = kapi::filesystem::mount("/dev/ram3", "/entrance"))
+ {
+ kstd::println("[OS:DEM] mounted /dev/ram3 to /entrance");
+ }
+ else
+ {
+ kapi::system::panic("[OS:DEM] failed to mount /dev/ram3 on /entrance", result.error());
+ }
+}
+
auto run_demo() -> void
{
// 1) open a file
@@ -312,7 +327,7 @@ auto main() -> int
kstd::println("[OS] Virtual filesystem initialized.");
// TODO BA-FS26 remove demo code?
- run_demo();
+ run_demo_fat32();
kapi::system::panic("[OS] Returning from kernel main!");
}
diff --git a/kernel/kernel/test_support/vfs/test_assets/fat32.img b/kernel/kernel/test_support/vfs/test_assets/fat32.img
new file mode 120000
index 00000000..31031c9f
--- /dev/null
+++ b/kernel/kernel/test_support/vfs/test_assets/fat32.img
@@ -0,0 +1 @@
+../../../../../arch/x86_64/support/modules/fat32.img \ No newline at end of file
diff --git a/scripts/qemu-wrapper.sh b/scripts/qemu-wrapper.sh
index dd0200df..5e83f055 100755
--- a/scripts/qemu-wrapper.sh
+++ b/scripts/qemu-wrapper.sh
@@ -12,7 +12,7 @@ if [ -z "$ISO_PATH" ]; then
fi
ARGS=(
- "-m" "64M"
+ "-m" "128M"
"-machine" "q35"
"-smp" "4,sockets=1,cores=4,threads=1"
"-display" "curses"