Browse Source

Merge branch 'special-device-file' into ata

greatbridf 2 years ago
parent
commit
6627cd11bd

+ 31 - 10
CMakeLists.txt

@@ -19,6 +19,12 @@ set(BOOTLOADER_SOURCES src/boot.s
 
 add_library(bootloader STATIC ${BOOTLOADER_SOURCES})
 
+add_custom_command(OUTPUT mbr.bin
+    DEPENDS ${PROJECT_SOURCE_DIR}/src/mbr.S ${PROJECT_SOURCE_DIR}/src/mbr.ld
+    COMMAND ${CMAKE_ASM_COMPILER} -m32 -c ${PROJECT_SOURCE_DIR}/src/mbr.S -o mbr.o
+    COMMAND ${CMAKE_LINKER} -T ${PROJECT_SOURCE_DIR}/src/mbr.ld mbr.o -o mbr.bin
+)
+
 add_custom_command(OUTPUT extracted_bootloader
     DEPENDS bootloader
     COMMAND ${CMAKE_AR} xf ${PROJECT_BINARY_DIR}/libbootloader.a --output=${EXTRACT_DIR}
@@ -27,7 +33,7 @@ add_custom_command(OUTPUT extracted_bootloader
 project(kernel_main)
 
 set(CMAKE_C_FLAGS "-nostdinc -m32 -nostdlib -W -Wall -Wextra -Wno-builtin-declaration-mismatch -Wno-format -Werror=implicit-int -Werror=implicit-function-declaration -Werror=strict-aliasing -fverbose-asm -fno-exceptions -fno-pic -fno-stack-protector")
-set(CMAKE_CXX_FLAGS "-nostdinc -m32 -nostdlib -W -Wall -Wextra -Wno-builtin-declaration-mismatch -Wno-format -fverbose-asm -fno-use-cxa-atexit -fno-exceptions -fno-pic -fno-stack-protector")
+set(CMAKE_CXX_FLAGS "-nostdinc -m32 -nostdlib -W -Wall -Wextra -Wno-builtin-declaration-mismatch -Wno-format -fverbose-asm -fno-use-cxa-atexit -fno-exceptions -fno-pic -fno-stack-protector -fno-rtti")
 set(CMAKE_CXX_STANDARD 17)
 
 if (CMAKE_BUILD_TYPE STREQUAL "Debug")
@@ -40,7 +46,8 @@ endif()
 
 include_directories(${PROJECT_SOURCE_DIR}/include ${PROJECT_SOURCE_DIR}/user-space-program/build)
 
-set(KERNEL_MAIN_SOURCES src/kernel_main.c
+set(KERNEL_MAIN_SOURCES src/fs/fat.cpp
+                        src/kernel_main.c
                         src/kernel/errno.c
                         src/kernel/interrupt.cpp
                         src/kernel/process.cpp
@@ -60,6 +67,7 @@ set(KERNEL_MAIN_SOURCES src/kernel_main.c
                         include/asm/boot.h
                         include/asm/port_io.h
                         include/asm/sys.h
+                        include/fs/fat.hpp
                         include/kernel/event/event.h
                         include/kernel/errno.h
                         include/kernel/tty.h
@@ -69,7 +77,7 @@ set(KERNEL_MAIN_SOURCES src/kernel_main.c
                         include/kernel/syscall.hpp
                         include/kernel/mem.h
                         include/kernel/mm.hpp
-                        include/kernel/vfs.h
+                        include/kernel/vfs.hpp
                         include/kernel/vga.h
                         include/kernel/hw/ata.hpp
                         include/kernel/hw/keyboard.h
@@ -80,6 +88,7 @@ set(KERNEL_MAIN_SOURCES src/kernel_main.c
                         include/kernel/input/input_event.h
                         include/types/bitmap.h
                         include/types/buffer.h
+                        include/types/hash_map.hpp
                         include/types/types.h
                         include/types/size.h
                         include/types/status.h
@@ -99,8 +108,9 @@ add_custom_command(OUTPUT extracted_kernel_main
     COMMAND ${CMAKE_AR} xf ${PROJECT_BINARY_DIR}/libkernel_main.a --output=${EXTRACT_DIR}
 )
 
-add_custom_command(OUTPUT user_space_programs
-    COMMAND make -C ${CMAKE_SOURCE_DIR}/user-space-program all
+add_custom_target(user_space_programs
+    COMMAND cp ${CMAKE_SOURCE_DIR}/user-space-program/Makefile.src ${CMAKE_SOURCE_DIR}/user-space-program/Makefile
+    COMMAND make -C ${CMAKE_SOURCE_DIR}/user-space-program CROSS_COMPILE=${TOOLCHAIN_PATH_AND_PREFIX} all
     COMMAND mkdir -p ${CMAKE_SOURCE_DIR}/user-space-program/build
     COMMAND rm -f ${CMAKE_SOURCE_DIR}/user-space-program/build/*
     COMMAND cp ${CMAKE_SOURCE_DIR}/user-space-program/*.res ${CMAKE_SOURCE_DIR}/user-space-program/build
@@ -110,15 +120,26 @@ add_custom_target(kernel.out
     DEPENDS extracted_bootloader
     DEPENDS extracted_kernel_main
     DEPENDS user_space_programs
-    DEPENDS ${CMAKE_SOURCE_DIR}/ldscript.ld
-    COMMAND ${CMAKE_LINKER} -T ${CMAKE_SOURCE_DIR}/ldscript.ld ${EXTRACT_DIR}/*.o ${CMAKE_SOURCE_DIR}/user-space-program/user.sym
+    DEPENDS ${CMAKE_SOURCE_DIR}/src/kernel.ld
+    COMMAND ${CMAKE_LINKER} -T ${CMAKE_SOURCE_DIR}/src/kernel.ld ${EXTRACT_DIR}/*.o ${CMAKE_SOURCE_DIR}/user-space-program/user.sym
     -melf_i386 -o ${CMAKE_BINARY_DIR}/kernel.out
 )
 
-add_custom_target(boot.img
+add_custom_target(mbr_hole.bin
     DEPENDS kernel.out
-    COMMAND ${CMAKE_OBJCOPY} --strip-debug -O binary
-    ${CMAKE_BINARY_DIR}/kernel.out boot.img)
+    COMMAND ${CMAKE_OBJCOPY} --strip-debug -O binary ${CMAKE_BINARY_DIR}/kernel.out mbr_hole.bin
+)
+
+add_custom_target(boot.img
+    DEPENDS mbr.bin
+    DEPENDS mbr_hole.bin
+    COMMAND dd if=mbr.bin of=boot.img
+    COMMAND cat mbr_hole.bin >> boot.img
+    COMMAND dd if=/dev/zero of=boot.img bs=`expr 512 \\* 1024 \\* 1024` count=0 seek=1
+    COMMAND sh -c \"echo n\; echo\; echo\; echo\; echo\; echo a\; echo w\" | fdisk boot.img
+    COMMAND mkfs.fat --offset=2048 -v -n SYSTEM boot.img
+    COMMAND mcopy -i boot.img@@1M ${CMAKE_SOURCE_DIR}/user-space-program/user.sym ::user.sym
+)
 
 add_custom_command(OUTPUT run
     POST_BUILD

+ 16 - 0
configure

@@ -95,6 +95,22 @@ else
     CROSS_COMPILE_FLAG=
 fi
 
+event "checking mkfs tool"
+if ! which mkfs.fat > /dev/null 2>&1; then
+    echo "no"
+    exit 1
+else
+    echo `which mkfs.fat`
+fi
+
+event "checking mtools"
+if ! which mcopy > /dev/null 2>&1; then
+    echo "no"
+    exit 1
+else
+    echo `which mcopy`
+fi
+
 cp Makefile.src Makefile
 sed -i '' -e "s/##PLACEHOLDER_1##/$QEMU/" Makefile > /dev/null 2>&1
 sed -i '' -e "s/##PLACEHOLDER_2##/$GDB/" Makefile > /dev/null 2>&1

+ 162 - 0
include/fs/fat.hpp

@@ -0,0 +1,162 @@
+#pragma once
+
+#include <kernel/mem.h>
+#include <kernel/vfs.hpp>
+#include <types/size.h>
+#include <types/stdint.h>
+
+namespace fs::fat {
+using cluster_t = uint32_t;
+
+// for FAT32
+struct PACKED old_boot_sector {
+    uint8_t jmp_instruction[3];
+    char oem_name[8];
+    // usually 512
+    uint16_t bytes_per_sector;
+    uint8_t sectors_per_cluster;
+    // 32 for FAT32
+    uint16_t reserved_sectors;
+    // usually 2
+    uint8_t fat_copies;
+    // 0 for FAT32
+    uint16_t root_directory_entries;
+    // valid before FAT32
+    uint16_t _sectors_cnt;
+    // 0xf8 for hard disk
+    uint8_t type;
+    // valid before FAT32
+    uint16_t _sectors_per_fat;
+    // 12
+    uint16_t sectors_per_track;
+    // 2
+    uint16_t heads;
+    // 0
+    uint16_t hidden_sectors;
+};
+
+// for FAT32
+struct PACKED ext_boot_sector {
+    struct old_boot_sector old;
+    // 0
+    uint16_t hidden_sector_ext;
+    uint32_t sectors_cnt;
+    uint32_t sectors_per_fat;
+    uint16_t mirror_flags;
+    uint16_t fs_version;
+    // 2
+    cluster_t root_directory;
+    // 1
+    uint16_t fs_info_sector;
+    // usually at 6, 0x0000 or 0xffff if none
+    uint16_t backup_boot_sector;
+    uint8_t _reserved[12];
+    // for int $0x13
+    uint8_t drive_number;
+    uint8_t _reserved_for_current_head;
+    // 0x29
+    uint8_t ext_signature;
+    uint32_t serial_number;
+    char label[11];
+    char fs_type[8];
+    uint8_t _reserved_blank[420];
+    // 0x55, 0xaa
+    uint16_t magic;
+};
+
+struct PACKED fs_info_sector {
+    // 0x41615252
+    uint32_t signature_one;
+    uint8_t _reserved[480];
+    // 0x61417272
+    uint32_t signature_two;
+    // may be incorrect
+    uint32_t free_clusters;
+    // hint only
+    uint32_t next_free_cluster;
+    uint8_t _reserved_two[12];
+    // 0xaa550000
+    uint32_t sector_signature;
+};
+
+struct PACKED directory_entry {
+    char filename[8];
+    char extension[3];
+    struct PACKED {
+        uint8_t ro : 1;
+        uint8_t hidden : 1;
+        uint8_t system : 1;
+        uint8_t volume_label : 1;
+        uint8_t subdir : 1;
+        uint8_t archive : 1;
+        uint8_t _reserved : 2;
+    } attributes;
+    uint8_t _reserved;
+    uint8_t c_time_date[5];
+    uint16_t access_date;
+    uint16_t cluster_hi;
+    uint8_t m_time_date[4];
+    uint16_t cluster_lo;
+    uint32_t size;
+};
+
+class fat32 : public virtual fs::vfs {
+private:
+    constexpr static uint32_t SECTOR_SIZE = 512;
+    constexpr static cluster_t EOC = 0xffffff8;
+
+private:
+    uint32_t sector_cnt;
+    uint32_t sectors_per_fat;
+    uint32_t serial_number;
+    uint32_t free_clusters;
+    uint32_t next_free_cluster_hint;
+    cluster_t root_dir;
+    cluster_t data_region_offset;
+    inode* device;
+    uint16_t reserved_sectors;
+    uint8_t fat_copies;
+    uint8_t sectors_per_cluster;
+    char label[12];
+    cluster_t* fat;
+
+    // buf MUST be larger than 512 bytes
+    inline void read_sector(void* buf, uint32_t sector_no);
+
+    // buf MUST be larger than 4096 bytes
+    inline void read_cluster(void* buf, cluster_t no);
+
+    static inline cluster_t cl(const inode* ind)
+    {
+        return reinterpret_cast<cluster_t>(ind->impl);
+    }
+
+    static inline cluster_t _rearrange(directory_entry* d)
+    {
+        return (((cluster_t)d->cluster_hi) << 16) + d->cluster_lo;
+    }
+
+    static inline size_t _write_buf_n(char* buf, size_t buf_size, const char* src, size_t n)
+    {
+        if (n <= buf_size) {
+            memcpy(buf, src, n);
+            return n;
+        } else {
+            memcpy(buf, src, buf_size);
+            return buf_size;
+        }
+    }
+
+protected:
+    virtual int load_dentry(dentry* ent) override;
+
+public:
+    fat32(const fat32&) = delete;
+    explicit fat32(inode* _device);
+    ~fat32();
+
+    virtual size_t inode_read(inode* file, char* buf, size_t buf_size, size_t offset, size_t n) override;
+    virtual int inode_stat(dentry* ent, stat* st) override;
+};
+
+}; // namespace fs::fat

+ 7 - 2
include/kernel/hw/ata.hpp

@@ -1,8 +1,8 @@
 #pragma once
 
 #include <asm/port_io.h>
-#include <kernel/syscall.hpp>
 #include <kernel/hw/port.hpp>
+#include <kernel/syscall.hpp>
 #include <types/cplusplus.hpp>
 
 constexpr port_id_t ATA_PRIMARY_BUS_BASE = 0x1f0;
@@ -40,16 +40,21 @@ private:
     p8r stats;
     p8w comms;
 
+    uint8_t slave_flag;
+
 public:
     ata(port_id_t port_base);
 
     stat_t status(void) const;
 
-    void identify(void) const;
+    bool identify(void) const;
+    int select(bool master);
 
     size_t read_data(char* buf, size_t n) const;
+    size_t write_data(const char* buf, size_t n) const;
 
     int read_sector(char* buf, uint32_t lba_low, uint16_t lba_high) const;
+    int write_sector(const char* buf, uint32_t lba_low, uint16_t lba_high) const;
 };
 
 void init_ata(void* data);

+ 21 - 20
include/kernel/hw/port.hpp

@@ -10,44 +10,41 @@ private:
     uint16_t mp;
 
 public:
-    port(uint16_t p)
+    explicit port(uint16_t p)
         : mp(p)
-    {}
+    {
+    }
 
     port_size_t operator*(void) const
     {
         static_assert(
-                types::is_same<port_size_t, uint8_t>::value ||
-                types::is_same<port_size_t, uint16_t>::value,
-                "this type is not implemented yet.");
+            types::is_same<port_size_t, uint8_t>::value || types::is_same<port_size_t, uint16_t>::value,
+            "this type is not implemented yet.");
         port_size_t ret;
         if constexpr (types::is_same<port_size_t, uint8_t>::value)
             asm("inb %1, %0"
                 : "=a"(ret)
-                : "d"(mp)
-                );
+                : "d"(mp));
         if constexpr (types::is_same<port_size_t, uint16_t>::value)
             asm("inw %1, %0"
                 : "=a"(ret)
-                : "d"(mp)
-                );
+                : "d"(mp));
         return ret;
     }
 
     port_size_t operator=(port_size_t n) const
     {
         static_assert(
-                types::is_same<port_size_t, uint8_t>::value ||
-                types::is_same<port_size_t, uint16_t>::value,
-                "this type is not implemented yet.");
+            types::is_same<port_size_t, uint8_t>::value || types::is_same<port_size_t, uint16_t>::value,
+            "this type is not implemented yet.");
         if constexpr (types::is_same<port_size_t, uint8_t>::value)
             asm("outb %1, %0"
-                : : "d"(mp), "a"(n)
-                );
+                :
+                : "d"(mp), "a"(n));
         if constexpr (types::is_same<port_size_t, uint16_t>::value)
             asm("outw %1, %0"
-                : : "d"(mp), "a"(n)
-                );
+                :
+                : "d"(mp), "a"(n));
         return n;
     }
 };
@@ -59,8 +56,12 @@ using p16 = port<uint16_t>;
 using p16r = port<uint16_t, true, false>;
 using p16w = port<uint16_t, false, true>;
 
-template <> uint8_t p8r::operator=(uint8_t n) const = delete;
-template <> uint8_t p8w::operator*(void) const = delete;
-template <> uint16_t p16r::operator=(uint16_t n) const = delete;
-template <> uint16_t p16w::operator*(void) const = delete;
+template <>
+uint8_t p8r::operator=(uint8_t n) const = delete;
+template <>
+uint8_t p8w::operator*(void) const = delete;
+template <>
+uint16_t p16r::operator=(uint16_t n) const = delete;
+template <>
+uint16_t p16w::operator*(void) const = delete;
 } // namespace hw

+ 3 - 3
include/kernel/mm.hpp

@@ -1,7 +1,7 @@
 #pragma once
 
 #include <kernel/mem.h>
-#include <kernel/vfs.h>
+#include <kernel/vfs.hpp>
 #include <types/allocator.hpp>
 #include <types/list.hpp>
 #include <types/types.h>
@@ -33,7 +33,7 @@ public:
     struct mm_attr attr;
     page_directory_entry* pd;
     page_arr* pgs;
-    struct inode* mapped_file;
+    fs::inode* mapped_file;
     size_t file_offset;
 
 public:
@@ -113,7 +113,7 @@ int k_map(
 int mmap(
     void* hint,
     size_t len,
-    struct inode* file,
+    fs::inode* file,
     size_t offset,
     int write,
     int priv);

+ 0 - 89
include/kernel/vfs.h

@@ -1,89 +0,0 @@
-#pragma once
-
-#include <types/types.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-struct inode;
-struct inode_flags;
-struct inode_ops;
-struct stat;
-struct dirent;
-
-typedef size_t (*inode_read)(struct inode* file, char* buf, size_t buf_size, size_t offset, size_t n);
-typedef size_t (*inode_write)(struct inode* file, const char* buf, size_t offset, size_t n);
-typedef int (*inode_readdir)(struct inode* dir, struct dirent* entry, size_t i);
-typedef struct inode* (*inode_findinode)(struct inode* dir, const char* filename);
-typedef int (*inode_mkfile)(struct inode* dir, const char* filename);
-typedef int (*inode_rmfile)(struct inode* dir, const char* filename);
-typedef int (*inode_mkdir)(struct inode* dir, const char* dirname);
-typedef int (*inode_stat)(struct inode* dir, struct stat* stat, const char* dirname);
-
-typedef uint32_t ino_t;
-typedef uint32_t blksize_t;
-typedef uint32_t blkcnt_t;
-
-struct inode_flags {
-    uint32_t file : 1;
-    uint32_t directory : 1;
-    uint32_t mount_point : 1;
-};
-
-struct inode_ops {
-    inode_read read;
-    inode_write write;
-    inode_readdir readdir;
-    inode_findinode findinode;
-    inode_mkfile mkfile;
-    inode_rmfile rmfile;
-    inode_mkdir mkdir;
-    inode_stat stat;
-};
-
-struct fs_info {
-    const struct inode_ops* ops;
-    void* impl;
-};
-
-struct inode {
-    struct inode_flags flags;
-    uint32_t perm;
-    void* impl;
-    ino_t ino;
-    struct fs_info* fs;
-};
-
-struct stat {
-    ino_t st_ino;
-    blksize_t st_blksize;
-    blkcnt_t st_blocks;
-};
-
-struct dirent {
-    char name[128];
-    uint32_t ino;
-};
-
-extern struct inode* fs_root;
-
-void init_vfs(void);
-
-size_t vfs_read(struct inode* file, char* buf, size_t buf_size, size_t offset, size_t n);
-size_t vfs_write(struct inode* file, const char* buf, size_t offset, size_t n);
-int vfs_readdir(struct inode* dir, struct dirent* entry, size_t i);
-struct inode* vfs_findinode(struct inode* dir, const char* filename);
-int vfs_mkfile(struct inode* dir, const char* filename);
-int vfs_rmfile(struct inode* dir, const char* filename);
-int vfs_mkdir(struct inode* dir, const char* dirname);
-
-// @return pointer to the inode if found, nullptr if not
-struct inode* vfs_open(const char* path);
-
-// @return GB_OK if succeed, GB_FAILED if failed and set errno
-int vfs_stat(struct stat* stat, const char* path);
-
-#ifdef __cplusplus
-}
-#endif

+ 186 - 0
include/kernel/vfs.hpp

@@ -0,0 +1,186 @@
+#pragma once
+
+#include <types/allocator.hpp>
+#include <types/hash_map.hpp>
+#include <types/list.hpp>
+#include <types/stdint.h>
+#include <types/types.h>
+#include <types/vector.hpp>
+
+#define INODE_FILE (1 << 0)
+#define INODE_DIR (1 << 1)
+#define INODE_MNT (1 << 2)
+#define INODE_NODE (1 << 3)
+
+namespace fs {
+using ino_t = size_t;
+using blksize_t = size_t;
+using blkcnt_t = size_t;
+
+class vfs;
+
+union inode_flags {
+    uint32_t v;
+    struct {
+        uint32_t file : 1;
+        uint32_t directory : 1;
+        uint32_t mount_point : 1;
+        uint32_t special_node : 1;
+    } in;
+};
+
+struct inode {
+    inode_flags flags;
+    uint32_t perm;
+    void* impl;
+    ino_t ino;
+    vfs* fs;
+    size_t size;
+};
+
+union node_t {
+    uint32_t v;
+    struct {
+        uint32_t major : 16;
+        uint32_t minor : 16;
+    } in;
+};
+
+struct special_node;
+
+typedef size_t (*special_node_read)(special_node* sn, char* buf, size_t buf_size, size_t offset, size_t n);
+typedef size_t (*special_node_write)(special_node* sn, const char* buf, size_t offset, size_t n);
+
+struct special_node_ops {
+    special_node_read read;
+    special_node_write write;
+};
+
+struct special_node {
+    special_node_ops ops;
+    uint32_t data1;
+    uint32_t data2;
+};
+
+struct stat {
+    ino_t st_ino;
+    node_t st_rdev;
+    size_t st_size;
+    blksize_t st_blksize;
+    blkcnt_t st_blocks;
+};
+
+class vfs {
+public:
+    struct dentry {
+    public:
+        using name_type = types::string<>;
+
+    private:
+        types::list<dentry> children;
+        types::hash_map<name_type, dentry*, types::string_hasher<const name_type&>> idx_children;
+
+    public:
+        dentry* parent;
+        inode* ind;
+        // if the entry is not a file, this flag is ignored
+        union {
+            uint32_t v;
+            struct {
+                uint32_t present : 1;
+                uint32_t dirty : 1;
+            } in;
+        } flags;
+        name_type name;
+
+        explicit dentry(dentry* parent, inode* ind, const name_type& name);
+        explicit dentry(dentry* parent, inode* ind, name_type&& name);
+        dentry(const dentry& val) = delete;
+        dentry(dentry&& val);
+
+        dentry& operator=(const dentry& val) = delete;
+        dentry& operator=(dentry&& val) = delete;
+
+        dentry* append(inode* ind, const name_type& name);
+        dentry* append(inode* ind, name_type&& name);
+
+        dentry* find(const name_type& name);
+
+        dentry* replace(dentry* val);
+
+        void invalidate(void);
+    };
+
+private:
+    // TODO: use allocator designed for small objects
+    using inode_list = types::list<inode>;
+    using inode_index_cache_list = types::hash_map<ino_t, inode*, types::linux_hasher<ino_t>>;
+
+private:
+    inode_list _inodes;
+    inode_index_cache_list _idx_inodes;
+    types::hash_map<dentry*, dentry*, types::linux_hasher<dentry*>> _mount_recover_list;
+    ino_t _last_inode_no;
+
+private:
+    ino_t _assign_inode_id(void);
+
+protected:
+    dentry _root;
+
+protected:
+    inode* cache_inode(inode_flags flags, uint32_t perm, size_t size, void* impl_data);
+    inode* get_inode(ino_t ino);
+    void register_root_node(inode* root);
+
+    virtual int load_dentry(dentry* ent);
+
+public:
+    explicit vfs(void);
+    vfs(const vfs&) = delete;
+    vfs& operator=(const vfs&) = delete;
+    vfs(vfs&&) = delete;
+    vfs& operator=(vfs&&) = delete;
+
+    constexpr dentry* root(void)
+    {
+        return &_root;
+    }
+
+    int mount(dentry* mnt, vfs* new_fs);
+
+    virtual size_t inode_read(inode* file, char* buf, size_t buf_size, size_t offset, size_t n);
+    virtual size_t inode_write(inode* file, const char* buf, size_t offset, size_t n);
+    virtual int inode_mkfile(dentry* dir, const char* filename);
+    virtual int inode_mknode(dentry* dir, const char* filename, union node_t sn);
+    virtual int inode_rmfile(dentry* dir, const char* filename);
+    virtual int inode_mkdir(dentry* dir, const char* dirname);
+    virtual int inode_stat(dentry* dir, stat* stat);
+};
+
+extern fs::vfs::dentry* fs_root;
+
+void register_special_block(uint16_t major,
+    uint16_t minor,
+    special_node_read read,
+    special_node_write write,
+    uint32_t data1,
+    uint32_t data2);
+
+vfs* register_fs(vfs* fs);
+
+size_t vfs_read(inode* file, char* buf, size_t buf_size, size_t offset, size_t n);
+size_t vfs_write(inode* file, const char* buf, size_t offset, size_t n);
+int vfs_mkfile(fs::vfs::dentry* dir, const char* filename);
+int vfs_mknode(fs::vfs::dentry* dir, const char* filename, node_t sn);
+int vfs_rmfile(fs::vfs::dentry* dir, const char* filename);
+int vfs_mkdir(fs::vfs::dentry* dir, const char* dirname);
+int vfs_stat(const char* filename, stat* stat);
+int vfs_stat(fs::vfs::dentry* ent, stat* stat);
+
+// @return pointer to the dentry if found, nullptr if not
+fs::vfs::dentry* vfs_open(const char* path);
+
+} // namespace fs
+
+extern "C" void init_vfs(void);

+ 37 - 4
include/types/cplusplus.hpp

@@ -48,6 +48,32 @@ struct add_reference {
     using type = T&;
 };
 
+template <typename T>
+struct remove_cv {
+    using type = T;
+};
+template <typename T>
+struct remove_cv<const T> {
+    using type = T;
+};
+template <typename T>
+struct remove_cv<volatile T> {
+    using type = T;
+};
+template <typename T>
+struct remove_cv<const volatile T> {
+    using type = T;
+};
+
+template <typename T>
+struct decay {
+private:
+    using U = remove_reference<T>;
+
+public:
+    using type = typename remove_cv<U>::type;
+};
+
 } // namespace types::traits
 
 namespace types {
@@ -69,13 +95,20 @@ struct constant_value {
 using true_type = constant_value<bool, true>;
 using false_type = constant_value<bool, false>;
 
+template <typename>
+struct template_true_type : public true_type {
+};
+template <typename>
+struct template_false_type : public false_type {
+};
+
 template <typename, typename>
-struct is_same : false_type
-{};
+struct is_same : false_type {
+};
 
 template <typename T>
-struct is_same<T, T>: true_type
-{};
+struct is_same<T, T> : true_type {
+};
 
 } // namespace types
 

+ 284 - 0
include/types/hash_map.hpp

@@ -0,0 +1,284 @@
+#pragma once
+
+#include <types/allocator.hpp>
+#include <types/cplusplus.hpp>
+#include <types/list.hpp>
+#include <types/stdint.h>
+#include <types/string.hpp>
+#include <types/types.h>
+#include <types/vector.hpp>
+
+namespace types {
+
+// taken from linux
+constexpr uint32_t GOLDEN_RATIO_32 = 0x61C88647;
+// constexpr uint64_t GOLDEN_RATIO_64 = 0x61C8864680B583EBull;
+
+static constexpr uint32_t _hash32(uint32_t val)
+{
+    return val * GOLDEN_RATIO_32;
+}
+
+static constexpr uint32_t hash32(uint32_t val, uint32_t bits)
+{
+    // higher bits are more random
+    return _hash32(val) >> (32 - bits);
+}
+
+template <typename T>
+struct linux_hasher {
+    static constexpr uint32_t hash(const T& val, uint32_t bits)
+    {
+        return hash32(val, bits);
+    }
+};
+
+template <typename T>
+struct linux_hasher<T*> {
+    static constexpr uint32_t hash(T* val, uint32_t bits)
+    {
+        return hash32(reinterpret_cast<uint32_t>(val), bits);
+    }
+};
+
+template <typename T>
+struct string_hasher {
+    static constexpr uint32_t hash(T, uint32_t)
+    {
+        static_assert(types::template_false_type<T>::value, "string hasher does not support this type");
+        return (uint32_t)0;
+    }
+};
+template <>
+struct string_hasher<const char*> {
+    static constexpr uint32_t hash(const char* str, uint32_t bits)
+    {
+        constexpr uint32_t seed = 131;
+        uint32_t hash = 0;
+
+        while (*str)
+            hash = hash * seed + (*str++);
+
+        return hash32(hash, bits);
+    }
+};
+template <template <typename> class Allocator>
+struct string_hasher<const types::string<Allocator>&> {
+    static inline constexpr uint32_t hash(const types::string<Allocator>& str, uint32_t bits)
+    {
+        return string_hasher<const char*>::hash(str.c_str(), bits);
+    }
+};
+template <template <typename> class Allocator>
+struct string_hasher<types::string<Allocator>&&> {
+    static inline constexpr uint32_t hash(types::string<Allocator>&& str, uint32_t bits)
+    {
+        return string_hasher<const char*>::hash(str.c_str(), bits);
+    }
+};
+
+template <class Hasher, typename Value>
+struct hasher_traits {
+    using hash_t = size_t;
+    using length_t = size_t;
+    static constexpr hash_t hash(Value val, length_t bits)
+    {
+        return Hasher::hash(val, bits);
+    }
+};
+
+template <typename Key, typename Value, typename Hasher, template <typename _T> class Allocator = types::kernel_allocator>
+class hash_map {
+public:
+    struct pair;
+    template <typename Pointer>
+    class iterator;
+
+    using key_type = Key;
+    using value_type = Value;
+    using pair_type = pair;
+    using size_type = size_t;
+    using difference_type = ssize_t;
+    using iterator_type = iterator<pair_type*>;
+    using const_iterator_type = iterator<const pair_type*>;
+
+    using bucket_type = list<pair, Allocator>;
+    using bucket_array_type = vector<bucket_type, Allocator>;
+
+    static constexpr size_type INITIAL_BUCKETS_ALLOCATED = 64;
+
+public:
+    struct pair {
+        const key_type key;
+        value_type value;
+
+        pair(void) = delete;
+        pair(const key_type _key, value_type _val)
+            : key(_key)
+            , value(_val)
+        {
+        }
+        bool operator==(const pair& p)
+        {
+            return key == p.key;
+        }
+    };
+
+    template <typename Pointer>
+    class iterator {
+    public:
+        using _Value = typename traits::remove_pointer<Pointer>::type;
+        using Reference = typename traits::add_reference<_Value>::type;
+
+    public:
+        iterator(const iterator& iter) noexcept
+            : p(iter.p)
+        {
+        }
+
+        iterator(iterator&& iter) noexcept
+            : p(iter.p)
+        {
+            iter.p = nullptr;
+        }
+
+        iterator& operator=(const iterator& iter)
+        {
+            p = iter.p;
+            return *this;
+        }
+
+        explicit iterator(Pointer p) noexcept
+            : p(p)
+        {
+        }
+
+        bool operator==(const iterator& iter) noexcept
+        {
+            return this->p == iter.p;
+        }
+
+        bool operator!=(const iterator& iter) noexcept
+        {
+            return !(*this == iter);
+        }
+
+        bool operator!()
+        {
+            return !p;
+        }
+
+        Reference operator*() const noexcept
+        {
+            return *p;
+        }
+        Pointer operator->() const noexcept
+        {
+            return p;
+        }
+
+    protected:
+        Pointer p;
+    };
+
+private:
+    bucket_array_type buckets;
+
+protected:
+    constexpr uint32_t hash_length(void) const
+    {
+        switch (buckets.capacity()) {
+        case 32:
+            return 5;
+        case 64:
+            return 6;
+        case 128:
+            return 7;
+        case 256:
+            return 8;
+        // TODO
+        default:
+            return 9;
+        }
+    }
+
+public:
+    explicit hash_map(void)
+        : buckets(INITIAL_BUCKETS_ALLOCATED)
+    {
+        for (size_type i = 0; i < INITIAL_BUCKETS_ALLOCATED; ++i)
+            buckets.emplace_back();
+    }
+
+    hash_map(const hash_map& v)
+        : buckets(v.buckets)
+    {
+    }
+
+    hash_map(hash_map&& v)
+        : buckets(move(v.buckets))
+    {
+    }
+
+    ~hash_map()
+    {
+        buckets.clear();
+    }
+
+    void insert(const pair& p)
+    {
+        auto hash_value = hasher_traits<Hasher, key_type>::hash(p.key, hash_length());
+        buckets.at(hash_value).push_back(p);
+    }
+    void insert(pair&& p)
+    {
+        auto hash_value = hasher_traits<Hasher, key_type>::hash(p.key, hash_length());
+        buckets.at(hash_value).push_back(move(p));
+    }
+    void insert(const key_type& key, const value_type& val)
+    {
+        insert(pair { key, val });
+    }
+
+    void remove(const key_type& key)
+    {
+        auto hash_value = hasher_traits<Hasher, key_type>::hash(key, hash_length());
+        auto& bucket = buckets.at(hash_value);
+        for (auto iter = bucket.begin(); iter != bucket.end(); ++iter) {
+            if (iter->key == key) {
+                bucket.erase(iter);
+                return;
+            }
+        }
+    }
+
+    iterator_type find(const key_type& key)
+    {
+        auto hash_value = hasher_traits<Hasher, key_type>::hash(key, hash_length());
+        auto& bucket = buckets.at(hash_value);
+        for (auto& item : bucket) {
+            if (key == item.key)
+                return iterator_type(&item);
+        }
+        return iterator_type(nullptr);
+    }
+
+    const_iterator_type find(const key_type& key) const
+    {
+        auto hash_value = hasher_traits<Hasher, key_type>::hash(key, hash_length());
+        const auto& bucket = buckets.at(hash_value);
+        for (const auto& item : bucket) {
+            if (key == item.key)
+                return const_iterator_type(&(*item));
+        }
+        return const_iterator_type(nullptr);
+    }
+
+    void clear(void)
+    {
+        for (size_t i = 0; i < buckets.size(); ++i)
+            buckets.at(i).clear();
+    }
+};
+
+} // namespace types

+ 15 - 9
include/types/string.hpp

@@ -27,14 +27,6 @@ public:
     {
         this->append(str, n);
     }
-    string(const string& str)
-        : inner_vector_type((const inner_vector_type&)str)
-    {
-    }
-    string(string&& str)
-        : inner_vector_type((inner_vector_type &&) move(str))
-    {
-    }
     string& append(const char* str, size_type n = npos)
     {
         this->pop_back();
@@ -57,7 +49,8 @@ public:
     }
     string& operator+=(const char c)
     {
-        *this->back() = c;
+        this->pop_back();
+        this->push_back(c);
         this->push_back(0x00);
         return *this;
     }
@@ -73,6 +66,10 @@ public:
     {
         return this->append(move(str));
     }
+    bool operator==(const string& rhs) const
+    {
+        return strcmp(c_str(), rhs.c_str()) == 0;
+    }
     string substr(size_type pos, size_type n = npos)
     {
         return string(this->m_arr + pos, n);
@@ -96,14 +93,23 @@ public:
     }
     typename inner_vector_type::iterator_type back(void)
     {
+        // TODO: assert
+        if (this->empty())
+            return typename inner_vector_type::iterator_type((void*)0xffffffff);
         return --inner_vector_type::back();
     }
     typename inner_vector_type::const_iterator_type back(void) const
     {
+        // TODO: assert
+        if (this->empty())
+            return typename inner_vector_type::iterator_type((void*)0xffffffff);
         return --inner_vector_type::back();
     }
     typename inner_vector_type::const_iterator_type cback(void) const
     {
+        // TODO: assert
+        if (this->empty())
+            return typename inner_vector_type::iterator_type((void*)0xffffffff);
         return --inner_vector_type::cback();
     }
 };

+ 19 - 0
include/types/vector.hpp

@@ -140,6 +140,25 @@ public:
         arr.m_size = 0;
     }
 
+    vector& operator=(vector&& arr)
+    {
+        resize(0);
+        m_arr = arr.m_arr;
+        m_capacity = arr.m_capacity;
+        m_size = arr.m_size;
+
+        arr.m_arr = nullptr;
+        arr.m_capacity = 0;
+        arr.m_size = 0;
+
+        return *this;
+    }
+
+    vector& operator=(const vector& arr)
+    {
+        return operator=(vector(arr));
+    }
+
     ~vector() noexcept
     {
         resize(0);

+ 9 - 64
src/boot.s

@@ -1,59 +1,5 @@
-.section .text.bootsect
-.code16
-
-# mbr
-
-.globl _start
-
-_start:
-    jmp $0x07c0, $(real_start-_start)
-
-real_start:
-    movw %cs, %ax
-    movw %ax, %ds
-    movw %ax, %es
-    movw %ax, %ss
-
-# perform a temporary stack
-    movw $(stack_base-_start), %ax
-    movw %ax, %bp
-    movw %ax, %sp
-
-    call read_data
-
-    ljmp $0x07e0, $(loader_start-loader_start)
-
-die:
-    hlt
-    jmp die
-
-read_data:
-    movw $(read_data_pack-_start), %si
-    mov $0x42, %ah
-    mov $0x80, %dl
-    int $0x13
-    ret
-
-string_hello:
-.string "Hello World!"
-
-read_data_pack:
-    .byte 0x10, 0
-# TODO!!!
-# read more!
-    .word 128    # block count (read 64k)
-    .word 0x0000 # offset address
-    .word 0x07e0 # segment address
-    .long 1      # LBA to read
-
-stack_edge:
-.space 128
-stack_base:
-
 .section .text.loader
-
-# loader
-
+.code16
 loader_start:
 # set segment registers
     movw %cs, %ax
@@ -83,7 +29,7 @@ _get_memory_size:
     movw %dx, %bx
 
 _get_memory_size_use_ax:
-    movl $(asm_mem_size_info-loader_start), %edx
+    movl $asm_mem_size_info, %edx
     movw %ax, (%edx)
     addw $2, %dx
     movw %bx, (%edx)
@@ -101,7 +47,7 @@ _e820_mem_map_load:
     movw %cs, %ax
     movw %ax, %es
 
-    movl $(asm_e820_mem_map-loader_start), %edi
+    movl $asm_e820_mem_map, %edi
 
     # clear ebx
     xorl %ebx, %ebx
@@ -128,24 +74,24 @@ _e820_mem_map_load_loop:
 
 _e820_mem_map_load_fin:
     movl (%esp), %eax
-    movl $(asm_e820_mem_map_count-loader_start), %edi
+    movl $asm_e820_mem_map_count, %edi
     movl %eax, (%edi)
 
-    movl $(asm_e820_mem_map_entry_size-loader_start), %edi
+    movl $asm_e820_mem_map_entry_size, %edi
     movl %ecx, (%edi)
 
     jmp _load_gdt
 
 _load_gdt:
     cli
-    lgdt (asm_gdt_descriptor-loader_start)
+    lgdt asm_gdt_descriptor
 
 # enable protection enable (PE) bit
     movl %cr0, %eax
     orl $1, %eax
     movl %eax, %cr0
 
-    ljmp $0x08, $0x7e00 + (start_32bit-loader_start)
+    ljmp $0x08, $start_32bit
 
 .code32
 
@@ -230,9 +176,8 @@ start_move_kernel:
     movl $__loader_end, %eax
     movl $__real_kernel_start, %ebx
 
-    movl $__kernel_text_and_data_size_offset, %ecx
+    movl $__p_kernel_text_and_data_size_addr, %ecx
     movl (%ecx), %ecx
-    addl $__loader_end, %ecx
     movl (%ecx), %ecx
 
 _move_kernel:
@@ -252,7 +197,7 @@ loader_halt:
 
 asm_gdt_descriptor:
     .word (5 * 8) - 1 # size
-    .long 0x7e00+(asm_gdt_table-loader_start)  # address
+    .long asm_gdt_table  # address
 
 .globl asm_gdt_descriptor
 .type asm_gdt_descriptor @object

+ 190 - 0
src/fs/fat.cpp

@@ -0,0 +1,190 @@
+#include <fs/fat.hpp>
+#include <kernel/mem.h>
+#include <kernel/mm.hpp>
+#include <kernel/syscall.hpp>
+#include <kernel/vfs.hpp>
+#include <types/allocator.hpp>
+#include <types/hash_map.hpp>
+#include <types/status.h>
+#include <types/stdint.h>
+
+namespace fs::fat {
+// buf MUST be larger than 512 bytes
+inline void fat32::read_sector(void* buf, uint32_t sector_no)
+{
+    if (vfs_read(
+            device,
+            (char*)buf,
+            SECTOR_SIZE,
+            sector_no * SECTOR_SIZE,
+            SECTOR_SIZE)
+        != SECTOR_SIZE) {
+        syscall(0x03);
+    }
+}
+
+// buf MUST be larger than 4096 bytes
+inline void fat32::read_cluster(void* buf, cluster_t no)
+{
+    // data cluster start from cluster #2
+    no -= 2;
+    for (int i = 0; i < sectors_per_cluster; ++i) {
+        // skip reserved sectors
+        read_sector((char*)buf + SECTOR_SIZE * i, data_region_offset + no * sectors_per_cluster + i);
+    }
+}
+
+int fat32::load_dentry(dentry* ent)
+{
+    cluster_t next = cl(ent->ind);
+    auto buf = (char*)k_malloc(4096);
+    do {
+        read_cluster(buf, next);
+        auto* d = reinterpret_cast<directory_entry*>(buf);
+        for (; d->filename[0]; ++d) {
+            if (d->attributes.volume_label)
+                continue;
+            auto* ind = cache_inode({ .in {
+                                        .file = !d->attributes.subdir,
+                                        .directory = d->attributes.subdir,
+                                        .mount_point = 0,
+                                        .special_node = 0,
+                                    } },
+                0777, d->size, (void*)_rearrange(d));
+            types::string<> fname;
+            for (int i = 0; i < 8; ++i) {
+                if (d->filename[i] == ' ')
+                    break;
+                fname += d->filename[i];
+            }
+            if (d->extension[0] != ' ') {
+                fname += '.';
+                fname += d->extension[0];
+            }
+            for (int i = 1; i < 3; ++i) {
+                if (d->extension[i] == ' ')
+                    break;
+                fname += d->extension[i];
+            }
+            ent->append(ind, fname);
+        }
+        next = fat[next];
+    } while (next < EOC);
+    k_free(buf);
+    return GB_OK;
+}
+
+fat32::fat32(inode* _device)
+    : device(_device)
+    , label { 0 }
+{
+    char* buf = (char*)k_malloc(SECTOR_SIZE);
+    read_sector(buf, 0);
+
+    auto* info = reinterpret_cast<ext_boot_sector*>(buf);
+
+    sector_cnt = info->sectors_cnt;
+    sectors_per_fat = info->sectors_per_fat;
+    sectors_per_cluster = info->old.sectors_per_cluster;
+    serial_number = info->serial_number;
+    root_dir = info->root_directory;
+    reserved_sectors = info->old.reserved_sectors;
+    fat_copies = info->old.fat_copies;
+
+    data_region_offset = reserved_sectors + fat_copies * sectors_per_fat;
+    fat = (cluster_t*)k_malloc(SECTOR_SIZE * sectors_per_fat);
+    // TODO: optimize
+    for (uint32_t i = 0; i < 4; ++i)
+        read_sector((char*)fat + i * SECTOR_SIZE, reserved_sectors + i);
+    for (uint32_t i = 4; i < sectors_per_fat; ++i)
+        memset((char*)fat + i * SECTOR_SIZE, 0x00, SECTOR_SIZE);
+
+    int i = 0;
+    while (i < 11 && info->label[i] != 0x20) {
+        label[i] = info->label[i];
+        ++i;
+    }
+    label[i] = 0x00;
+
+    read_sector(buf, info->fs_info_sector);
+
+    auto* fsinfo = reinterpret_cast<fs_info_sector*>(buf);
+    free_clusters = fsinfo->free_clusters;
+    next_free_cluster_hint = fsinfo->next_free_cluster;
+
+    k_free(buf);
+
+    size_t _root_dir_clusters = 1;
+    cluster_t next = root_dir;
+    while ((next = fat[next]) < EOC)
+        ++_root_dir_clusters;
+    auto* n = cache_inode({ INODE_MNT | INODE_DIR }, 0777, _root_dir_clusters * sectors_per_cluster * SECTOR_SIZE, (void*)root_dir);
+    register_root_node(n);
+}
+
+fat32::~fat32()
+{
+    k_free(fat);
+}
+
+size_t fat32::inode_read(inode* file, char* buf, size_t buf_size, size_t offset, size_t n)
+{
+    cluster_t next = reinterpret_cast<cluster_t>(file->impl);
+    uint32_t cluster_size = SECTOR_SIZE * sectors_per_cluster;
+    auto* b = (char*)k_malloc(cluster_size);
+    size_t orig_n = n;
+
+    do {
+        if (offset == 0) {
+            if (n > cluster_size) {
+                read_cluster(buf, next);
+                buf_size -= cluster_size;
+                buf += cluster_size;
+                n -= cluster_size;
+            } else {
+                read_cluster(b, next);
+                auto read = _write_buf_n(buf, buf_size, b, n);
+                k_free(b);
+                return orig_n - n + read;
+            }
+        } else {
+            if (offset > cluster_size) {
+                offset -= cluster_size;
+            } else {
+                read_cluster(b, next);
+
+                auto to_read = cluster_size - offset;
+                if (to_read > n)
+                    to_read = n;
+
+                auto read = _write_buf_n(buf, buf_size, b + offset, to_read);
+                buf += read;
+                n -= read;
+
+                if (read != to_read) {
+                    k_free(b);
+                    return orig_n - n;
+                }
+
+                offset = 0;
+            }
+        }
+        next = fat[next];
+    } while (n && next < EOC);
+
+    k_free(b);
+    return orig_n - n;
+}
+
+int fat32::inode_stat(dentry* ent, stat* st)
+{
+    st->st_size = ent->ind->size;
+    st->st_blksize = 4096;
+    st->st_blocks = (ent->ind->size + 4095) / 4096;
+    st->st_ino = ent->ind->ino;
+    if (ent->ind->flags.in.special_node) {
+        st->st_rdev.v = reinterpret_cast<uint32_t>(ent->ind->impl);
+    }
+    return GB_OK;
+}
+} // namespace fs::fat

+ 10 - 27
ldscript.ld → src/kernel.ld

@@ -3,39 +3,25 @@ OUTPUT_ARCH(i386:i386)
 
 MEMORY
 {
-    WHOLE : org = 0x00000000, l = 4M
+    MEM : org = 0x00000000, l = 4096M
 }
 
 SECTIONS
 {
-    .text.bootsect : AT(0x00)
-    {
-        *(.text.bootsect)
-    } > WHOLE
-
-    .magicnumber : AT(0x1fe)
-    {
-        BYTE(0x55);
-        BYTE(0xaa);
-    } > WHOLE
-
-    .text.loader 0x7e00 : AT(0x200)
+    .text.loader 0x7e00 : AT(0x00000000)
     {
         *(.text.loader)
-        __kernel_text_and_data_size_offset = .;
-        LONG(LOADADDR(.data) - LOADADDR(.text));
+        __p_kernel_text_and_data_size_addr = .;
+        LONG(__loader_end + kernel_text_and_data_size - __real_kernel_start);
         __loader_end = .;
-    } > WHOLE
+    } > MEM
 
     .text 0x100000 : AT(LOADADDR(.text.loader) + SIZEOF(.text.loader))
     {
         __real_kernel_start = .;
         *(.text*)
         *(.rodata*)
-    } > WHOLE
 
-    .data : AT(LOADADDR(.text) + ADDR(.data) - ADDR(.text))
-    {
         kernel_text_and_data_size = .;
         LONG(__kernel_text_and_data_end - ADDR(.text));
         asm_kernel_size = .;
@@ -55,7 +41,7 @@ SECTIONS
         *(.data)
         *(.data*)
         __kernel_text_and_data_end = .;
-    } > WHOLE
+    } > MEM
 
     .bss ALIGN(0x1000) :
     {
@@ -63,12 +49,12 @@ SECTIONS
         *(.bss)
         *(.bss*)
         __bss_end = ALIGN(0x1000);
-    } > WHOLE
+    } > MEM
 
     .kernel_end :
     {
         __real_kernel_end = .;
-    } > WHOLE
+    } > MEM
 
     /* Stabs debugging sections.  */
     .stab          0 : { *(.stab) }
@@ -104,14 +90,11 @@ SECTIONS
     .debug_typenames 0 : { *(.debug_typenames) }
     .debug_varnames  0 : { *(.debug_varnames) }
 
-    .end : AT(0x0ffffff)
-    {
-        BYTE(0x00);
-    } > WHOLE
-
     /DISCARD/ :
     {
         *(.fini_array*)
         *(.eh_frame*)
+        *(.text.bootsect)
+        *(.note*)
     }
 }

+ 157 - 17
src/kernel/hw/ata.cpp

@@ -1,8 +1,13 @@
 #include <asm/port_io.h>
+#include <fs/fat.hpp>
+#include <kernel/hw/ata.hpp>
 #include <kernel/stdio.h>
 #include <kernel/syscall.hpp>
 #include <kernel/tty.h>
-#include <kernel/hw/ata.hpp>
+#include <kernel/vfs.hpp>
+#include <types/allocator.hpp>
+#include <types/status.h>
+#include <types/stdint.h>
 
 hw::ata::ata(port_id_t p)
     : data(p)
@@ -15,18 +20,20 @@ hw::ata::ata(port_id_t p)
     , drive(p + 6)
     , stats(p + 7)
     , comms(p + 7)
-{}
+    , slave_flag(0x00)
+{
+}
 
 hw::ata::stat_t hw::ata::status(void) const
 {
     return hw::ata::stat_t { *stats };
 }
 
-void hw::ata::identify(void) const
+bool hw::ata::identify(void) const
 {
     char buf[512] {};
 
-    drive = 0xa0;
+    drive = 0xa0 | slave_flag;
     count = 0;
     lbalo = 0;
     lbami = 0;
@@ -38,12 +45,25 @@ void hw::ata::identify(void) const
         ;
 
     if (stat.in.err)
-        syscall(0x03);
+        return false;
 
     read_data(buf, 512);
 
     if (!status().in.rdy)
-        syscall(0x03);
+        return false;
+
+    return true;
+}
+
+int hw::ata::select(bool master)
+{
+    if (master)
+        slave_flag = 0x00;
+    else
+        slave_flag = 0x10;
+
+    drive = 0xe0 | slave_flag;
+    return GB_OK;
 }
 
 size_t hw::ata::read_data(char* buf, size_t n) const
@@ -57,18 +77,28 @@ size_t hw::ata::read_data(char* buf, size_t n) const
     return orig_n - n * 2;
 }
 
+size_t hw::ata::write_data(const char* buf, size_t n) const
+{
+    size_t orig_n = n;
+    n /= 2;
+    while (status().in.drq && n--) {
+        data = *(uint16_t*)buf;
+        buf += sizeof(uint16_t);
+    }
+    return orig_n - n * 2;
+}
+
 int hw::ata::read_sector(char* buf, uint32_t lba_low, uint16_t lba_high) const
 {
-    drive = 0x40;                   // SELECT MASTER DRIVE AND LBA48
-    count = 0x00;                   // HIGH BYTE
+    count = 0x00; // HIGH BYTE
     lbalo = (lba_low >> 24) & 0xff;
     lbami = lba_high & 0xff;
     lbahi = (lba_high >> 8) & 0xff;
-    count = 0x01;                   // LOW BYTE
+    count = 0x01; // LOW BYTE
     lbalo = lba_low & 0xff;
     lbami = (lba_low >> 8) & 0xff;
     lbahi = (lba_low >> 16) & 0xff;
-    comms = 0x24;                   // READ SECTORS EXT
+    comms = 0x24; // READ SECTORS EXT
 
     while (status().in.bsy)
         ;
@@ -77,16 +107,126 @@ int hw::ata::read_sector(char* buf, uint32_t lba_low, uint16_t lba_high) const
     return GB_OK;
 }
 
+int hw::ata::write_sector(const char* buf, uint32_t lba_low, uint16_t lba_high) const
+{
+    count = 0x00; // HIGH BYTE
+    lbalo = (lba_low >> 24) & 0xff;
+    lbami = lba_high & 0xff;
+    lbahi = (lba_high >> 8) & 0xff;
+    count = 0x01; // LOW BYTE
+    lbalo = lba_low & 0xff;
+    lbami = (lba_low >> 8) & 0xff;
+    lbahi = (lba_low >> 16) & 0xff;
+    comms = 0x24; // READ SECTORS EXT
+
+    while (status().in.bsy)
+        ;
+    if (status().in.drq)
+        write_data(buf, 512);
+    return GB_OK;
+}
+
+static hw::ata* ata_pri;
+static hw::ata* ata_sec;
+constexpr hw::ata** p_ata_pri = &ata_pri;
+constexpr hw::ata** p_ata_sec = &ata_sec;
+
+// data1: offset sectors
+// data2: limit sectors
+template <hw::ata** ata_bus>
+size_t _ata_read(fs::special_node* sn, char* buf, size_t buf_size, size_t offset, size_t n)
+{
+    // TODO: check buf_size
+    char b[512] {};
+    char* orig_buf = buf;
+    size_t start = sn->data1 + offset / 512;
+    size_t end = sn->data1 + (offset + n + 511) / 512;
+    if (end > sn->data1 + sn->data2)
+        end = sn->data1 + sn->data2;
+    offset %= 512;
+    for (size_t i = start; i < end; ++i) {
+        (*ata_bus)->read_sector(b, i, 0);
+        size_t to_copy = 0;
+        if (offset)
+            to_copy = 512 - offset;
+        else
+            to_copy = n > 512 ? 512 : n;
+        memcpy(buf, b + offset, to_copy);
+        offset = 0;
+        buf += to_copy;
+        n -= to_copy;
+    }
+    return buf - orig_buf;
+}
+
+struct PACKED mbr_part_entry {
+    uint8_t attr;
+    uint8_t chs_start[3];
+    uint8_t type;
+    uint8_t chs_end[3];
+    uint32_t lba_start;
+    uint32_t cnt;
+};
+
+struct PACKED mbr {
+    uint8_t code[440];
+    uint32_t signature;
+    uint16_t reserved;
+    struct mbr_part_entry parts[4];
+    uint16_t magic;
+};
+
+static inline void mbr_part_probe(fs::inode* drive, uint16_t major, uint16_t minor)
+{
+    struct mbr hda_mbr { };
+    auto* dev = fs::vfs_open("/dev");
+
+    fs::vfs_read(drive, (char*)&hda_mbr, 512, 0, 512);
+
+    for (const auto& part : hda_mbr.parts) {
+        if (!part.type)
+            continue;
+
+        fs::register_special_block(major, minor++,
+            _ata_read<p_ata_pri>,
+            nullptr,
+            part.lba_start, part.cnt);
+
+        fs::vfs_mknode(dev, "hda1", { .in { .major = 2, .minor = 1 } });
+    }
+}
+
 void hw::init_ata(void* data)
 {
     if (data != nullptr)
         syscall(0x03);
 
-    ata ata_pri(ATA_PRIMARY_BUS_BASE);
-    ata_pri.identify();
-    char buf[512] {};
-    ata_pri.read_sector(buf, 0, 0);
-    tty_print(console, "sector 0 read\n");
-    ata_pri.read_sector(buf, 1, 0);
-    tty_print(console, "sector 1 read\n");
+    ata_pri = types::kernel_allocator_new<ata>(ATA_PRIMARY_BUS_BASE);
+    if (ata_pri->identify())
+        ata_pri->select(true);
+
+    ata_sec = types::kernel_allocator_new<ata>(ATA_SECONDARY_BUS_BASE);
+    if (ata_pri->identify())
+        ata_pri->select(true);
+
+    // data1: offset sectors
+    // data2: limit sectors
+    fs::register_special_block(
+        2, 0,
+        _ata_read<p_ata_pri>,
+        nullptr,
+        0,
+        0xffffffff);
+
+    // data1: offset sectors
+    // data2: limit sectors
+    fs::register_special_block(
+        2, 8,
+        _ata_read<p_ata_sec>,
+        nullptr,
+        0,
+        0xffffffff);
+
+    auto* hda = fs::vfs_open("/dev/hda");
+    mbr_part_probe(hda->ind, 2, 1);
 }

+ 3 - 3
src/kernel/interrupt.cpp

@@ -11,7 +11,7 @@
 #include <kernel/stdio.h>
 #include <kernel/syscall.hpp>
 #include <kernel/tty.h>
-#include <kernel/vfs.h>
+#include <kernel/vfs.hpp>
 #include <kernel/vga.h>
 #include <kernel_main.h>
 #include <types/size.h>
@@ -214,8 +214,8 @@ extern "C" void int14_handler(int14_data* d)
 
         // memory mapped
         if (d->error_code.present == 0) {
-            size_t offset = d->l_addr - mm_area->start;
-            vfs_read(mm_area->mapped_file, new_page_data, 4096, mm_area->file_offset + offset, PAGE_SIZE);
+            size_t offset = (d->l_addr - mm_area->start) & 0xfffff000;
+            vfs_read(mm_area->mapped_file, new_page_data, PAGE_SIZE, mm_area->file_offset + offset, PAGE_SIZE);
         }
     }
 }

+ 3 - 3
src/kernel/mem.cpp

@@ -476,12 +476,12 @@ static inline int _mmap(
     mm_list* mms,
     void* hint,
     size_t len,
-    struct inode* file,
+    fs::inode* file,
     size_t offset,
     int write,
     int priv)
 {
-    if (!file->flags.file) {
+    if (!file->flags.in.file && !file->flags.in.special_node) {
         errno = EINVAL;
         return GB_FAILED;
     }
@@ -508,7 +508,7 @@ static inline int _mmap(
 int mmap(
     void* hint,
     size_t len,
-    struct inode* file,
+    fs::inode* file,
     size_t offset,
     int write,
     int priv)

+ 341 - 274
src/kernel/vfs.cpp

@@ -1,16 +1,18 @@
 #include <kernel/errno.h>
 #include <kernel/mem.h>
 #include <kernel/stdio.h>
+#include <kernel/syscall.hpp>
 #include <kernel/tty.h>
-#include <kernel/vfs.h>
+#include <kernel/vfs.hpp>
 #include <types/allocator.hpp>
 #include <types/list.hpp>
+#include <types/status.h>
+#include <types/stdint.h>
 #include <types/string.hpp>
 #include <types/vector.hpp>
 
 using types::allocator_traits;
 using types::kernel_allocator;
-using types::list;
 using types::string;
 using types::vector;
 
@@ -19,328 +21,369 @@ struct tmpfs_file_entry {
     char filename[128];
 };
 
-class tmpfs {
-private:
-    using inode_list_type = list<struct inode, kernel_allocator>;
-
-private:
-    size_t m_limit;
-    // TODO: hashtable etc.
-    inode_list_type m_inodes;
-    struct fs_info m_fs;
-    size_t m_last_inode_no;
-
-protected:
-    inline vector<struct tmpfs_file_entry>* mk_fe_vector(void)
-    {
-        return allocator_traits<kernel_allocator<vector<struct tmpfs_file_entry>>>::allocate_and_construct();
-    }
-
-    inline vector<char>* mk_data_vector(void)
-    {
-        return allocator_traits<kernel_allocator<vector<char>>>::allocate_and_construct();
-    }
-
-    inline struct inode mk_inode(unsigned int dir, unsigned int file, unsigned int mnt, void* data)
-    {
-        struct inode i { };
-        i.flags.directory = dir;
-        i.flags.file = file;
-        i.flags.mount_point = mnt;
-        i.fs = &m_fs;
-        i.impl = data;
-        i.ino = m_last_inode_no++;
-        i.perm = 0777;
-        return i;
-    }
+fs::vfs::dentry::dentry(dentry* _parent, inode* _ind, const name_type& _name)
+    : parent(_parent)
+    , ind(_ind)
+    , flags { 0 }
+    , name(_name)
+{
+}
+fs::vfs::dentry::dentry(dentry* _parent, inode* _ind, name_type&& _name)
+    : parent(_parent)
+    , ind(_ind)
+    , flags { 0 }
+    , name(types::move(_name))
+{
+}
+fs::vfs::dentry::dentry(dentry&& val)
+    : children(types::move(val.children))
+    , idx_children(types::move(val.idx_children))
+    , parent(val.parent)
+    , ind(val.ind)
+    , flags { val.flags }
+    , name(types::move(val.name))
+{
+    for (auto& item : children)
+        item.parent = this;
+}
+fs::vfs::dentry* fs::vfs::dentry::append(inode* ind, const name_type& name)
+{
+    auto iter = children.emplace_back(this, ind, name);
+    idx_children.insert(iter->name, iter.ptr());
+    return iter.ptr();
+}
+fs::vfs::dentry* fs::vfs::dentry::append(inode* ind, name_type&& name)
+{
+    auto iter = children.emplace_back(this, ind, types::move(name));
+    idx_children.insert(iter->name, iter.ptr());
+    return iter.ptr();
+}
+fs::vfs::dentry* fs::vfs::dentry::find(const name_type& name)
+{
+    if (ind->flags.in.directory && !flags.in.present)
+        ind->fs->load_dentry(this);
 
-public:
-    explicit tmpfs(size_t limit);
-    void mklink(struct inode* dir, struct inode* inode, const char* filename);
-    void mkfile(struct inode* dir, const char* filename);
-    void mkdir(struct inode* dir, const char* dirname);
-    size_t read(struct inode* file, char* buf, size_t buf_size, size_t offset, size_t n);
-    size_t write(struct inode* file, const char* buf, size_t offset, size_t n);
-    int readdir(struct inode* dir, struct dirent* entry, size_t i);
-    struct inode* findinode(struct inode* dir, const char* filename);
-    int stat(struct inode* dir, struct stat* stat, const char* filename);
-
-    struct inode* root_inode(void)
-    {
-        return &*m_inodes.begin();
+    auto iter = idx_children.find(name);
+    if (!iter) {
+        errno = ENOTFOUND;
+        return nullptr;
     }
-};
 
-size_t tmpfs_read(struct inode* file, char* buf, size_t buf_size, size_t offset, size_t n)
+    return iter->value;
+}
+fs::vfs::dentry* fs::vfs::dentry::replace(dentry* val)
 {
-    auto* fs = static_cast<tmpfs*>(file->fs->impl);
-    return fs->read(file, buf, buf_size, offset, n);
+    // TODO: prevent the dirent to be swapped out of memory
+    parent->idx_children.find(this->name)->value = val;
+    return this;
 }
-size_t tmpfs_write(struct inode* file, const char* buf, size_t offset, size_t n)
+void fs::vfs::dentry::invalidate(void)
 {
-    auto* fs = static_cast<tmpfs*>(file->fs->impl);
-    return fs->write(file, buf, offset, n);
+    // TODO: write back
+    flags.in.dirty = 0;
+    children.clear();
+    idx_children.clear();
+    flags.in.present = 0;
 }
-int tmpfs_readdir(struct inode* dir, struct dirent* entry, size_t i)
+fs::vfs::vfs(void)
+    : _last_inode_no(0)
+    , _root(nullptr, nullptr, "/")
 {
-    auto* fs = static_cast<tmpfs*>(dir->fs->impl);
-    return fs->readdir(dir, entry, i);
 }
-struct inode* tmpfs_findinode(struct inode* dir, const char* filename)
+fs::ino_t fs::vfs::_assign_inode_id(void)
 {
-    auto* fs = static_cast<tmpfs*>(dir->fs->impl);
-    return fs->findinode(dir, filename);
+    return ++_last_inode_no;
 }
-int tmpfs_mkfile(struct inode* dir, const char* filename)
+fs::inode* fs::vfs::cache_inode(inode_flags flags, uint32_t perm, size_t size, void* impl_data)
 {
-    auto* fs = static_cast<tmpfs*>(dir->fs->impl);
-    fs->mkfile(dir, filename);
-    return GB_OK;
+    auto iter = _inodes.emplace_back(inode { flags, perm, impl_data, _assign_inode_id(), this, size });
+    _idx_inodes.insert(iter->ino, iter.ptr());
+    return iter.ptr();
 }
-// int tmpfs_rmfile(struct inode* dir, const char* filename)
-// {
-//     auto* fs = static_cast<tmpfs*>(dir->fs->impl);
-//     fs->rmfile(dir, filename);
-//     return GB_OK;
-// }
-int tmpfs_mkdir(struct inode* dir, const char* dirname)
+fs::inode* fs::vfs::get_inode(ino_t ino)
 {
-    auto* fs = static_cast<tmpfs*>(dir->fs->impl);
-    fs->mkdir(dir, dirname);
-    return GB_OK;
+    auto iter = _idx_inodes.find(ino);
+    // TODO: load inode from disk if not found
+    if (!iter)
+        return nullptr;
+    else
+        return iter->value;
 }
-int tmpfs_stat(struct inode* dir, struct stat* stat, const char* filename)
+void fs::vfs::register_root_node(inode* root)
 {
-    auto* fs = static_cast<tmpfs*>(dir->fs->impl);
-    return fs->stat(dir, stat, filename);
+    if (!_root.ind)
+        _root.ind = root;
 }
-
-static const struct inode_ops tmpfs_inode_ops = {
-    .read = tmpfs_read,
-    .write = tmpfs_write,
-    .readdir = tmpfs_readdir,
-    .findinode = tmpfs_findinode,
-    .mkfile = tmpfs_mkfile,
-    .rmfile = 0,
-    .mkdir = tmpfs_mkdir,
-    .stat = tmpfs_stat,
-};
-
-tmpfs::tmpfs(size_t limit)
-    : m_limit(limit)
-    , m_fs { .ops = &tmpfs_inode_ops, .impl = this }
-    , m_last_inode_no(0)
+int fs::vfs::load_dentry(dentry*)
+{
+    syscall(0x03);
+    return GB_FAILED;
+}
+int fs::vfs::mount(dentry* mnt, vfs* new_fs)
 {
-    struct inode in = mk_inode(1, 0, 1, mk_fe_vector());
+    if (!mnt->ind->flags.in.directory) {
+        errno = ENOTDIR;
+        return GB_FAILED;
+    }
 
-    mklink(&in, &in, ".");
-    mklink(&in, &in, "..");
+    auto* new_ent = new_fs->root();
 
-    m_inodes.push_back(in);
-}
+    new_ent->parent = mnt->parent;
+    new_ent->name = mnt->name;
 
-void tmpfs::mklink(struct inode* dir, struct inode* inode, const char* filename)
+    auto* orig_ent = mnt->replace(new_ent);
+    _mount_recover_list.insert(new_ent, orig_ent);
+    return GB_OK;
+}
+size_t fs::vfs::inode_read(inode*, char*, size_t, size_t, size_t)
 {
-    auto* fes = static_cast<vector<struct tmpfs_file_entry>*>(dir->impl);
-    struct tmpfs_file_entry ent = {
-        .ino = inode->ino,
-        .filename = { 0 },
-    };
-    snprintf(ent.filename, sizeof(ent.filename), filename);
-    fes->push_back(ent);
+    syscall(0x03);
+    return 0xffffffff;
 }
-
-void tmpfs::mkfile(struct inode* dir, const char* filename)
+size_t fs::vfs::inode_write(inode*, const char*, size_t, size_t)
 {
-    struct inode file = mk_inode(0, 1, 0, mk_data_vector());
-    m_inodes.push_back(file);
-    mklink(dir, &file, filename);
+    syscall(0x03);
+    return 0xffffffff;
 }
-
-void tmpfs::mkdir(struct inode* dir, const char* dirname)
+int fs::vfs::inode_mkfile(dentry*, const char*)
 {
-    struct inode new_dir = mk_inode(1, 0, 0, mk_fe_vector());
-    m_inodes.push_back(new_dir);
-    mklink(&new_dir, &new_dir, ".");
-
-    mklink(dir, &new_dir, dirname);
-    mklink(&new_dir, dir, "..");
+    syscall(0x03);
+    return GB_FAILED;
 }
-
-size_t tmpfs::read(struct inode* file, char* buf, size_t buf_size, size_t offset, size_t n)
+int fs::vfs::inode_mknode(dentry*, const char*, node_t)
 {
-    if (file->flags.file != 1)
-        return 0;
+    syscall(0x03);
+    return GB_FAILED;
+}
+int fs::vfs::inode_rmfile(dentry*, const char*)
+{
+    syscall(0x03);
+    return GB_FAILED;
+}
+int fs::vfs::inode_mkdir(dentry*, const char*)
+{
+    syscall(0x03);
+    return GB_FAILED;
+}
+int fs::vfs::inode_stat(dentry*, stat*)
+{
+    syscall(0x03);
+    return GB_FAILED;
+}
 
-    auto* data = static_cast<vector<char>*>(file->impl);
-    size_t fsize = data->size();
+class tmpfs : public virtual fs::vfs {
+protected:
+    inline vector<tmpfs_file_entry>* mk_fe_vector(void)
+    {
+        return allocator_traits<kernel_allocator<vector<tmpfs_file_entry>>>::allocate_and_construct();
+    }
 
-    if (offset + n > fsize)
-        n = fsize - offset;
+    inline vector<char>* mk_data_vector(void)
+    {
+        return allocator_traits<kernel_allocator<vector<char>>>::allocate_and_construct();
+    }
 
-    if (buf_size < n) {
-        n = buf_size;
+    void mklink(fs::inode* dir, fs::inode* inode, const char* filename)
+    {
+        auto* fes = static_cast<vector<struct tmpfs_file_entry>*>(dir->impl);
+        struct tmpfs_file_entry ent = {
+            .ino = inode->ino,
+            .filename = { 0 },
+        };
+        snprintf(ent.filename, sizeof(ent.filename), filename);
+        fes->push_back(ent);
+        dir->size += sizeof(tmpfs_file_entry);
     }
 
-    memcpy(buf, data->data() + offset, n);
+    virtual int load_dentry(dentry* ent) override
+    {
+        if (!ent->ind->flags.in.directory) {
+            errno = ENOTDIR;
+            return GB_FAILED;
+        }
 
-    return n;
-}
+        auto& entries = *static_cast<vector<tmpfs_file_entry>*>(ent->ind->impl);
+        for (const auto& entry : entries)
+            ent->append(get_inode(entry.ino), entry.filename);
 
-size_t tmpfs::write(struct inode* file, const char* buf, size_t offset, size_t n)
-{
-    if (file->flags.file != 1)
-        return 0;
+        ent->flags.in.present = 1;
+        return GB_OK;
+    }
 
-    auto* data = static_cast<vector<char>*>(file->impl);
+public:
+    explicit tmpfs(void)
+    {
+        auto& in = *cache_inode({ INODE_DIR | INODE_MNT }, 0777, 0, mk_fe_vector());
 
-    for (size_t i = data->size(); i < offset + n; ++i) {
-        data->push_back(0);
+        mklink(&in, &in, ".");
+        mklink(&in, &in, "..");
+
+        register_root_node(&in);
     }
-    memcpy(data->data() + offset, buf, n);
 
-    return n;
-}
+    virtual int inode_mkfile(dentry* dir, const char* filename) override
+    {
+        auto& file = *cache_inode({ .v = INODE_FILE }, 0777, 0, mk_data_vector());
+        mklink(dir->ind, &file, filename);
+        dir->invalidate();
+        return GB_OK;
+    }
 
-int tmpfs::readdir(struct inode* dir, struct dirent* entry, size_t i)
-{
-    if (dir->flags.directory != 1) {
-        errno = ENOTDIR;
-        return GB_FAILED;
+    virtual int inode_mknode(dentry* dir, const char* filename, fs::node_t sn) override
+    {
+        auto& node = *cache_inode({ .v = INODE_NODE }, 0777, 0, (void*)sn.v);
+        mklink(dir->ind, &node, filename);
+        dir->invalidate();
+        return GB_OK;
     }
 
-    auto* fes = static_cast<vector<struct tmpfs_file_entry>*>(dir->impl);
+    virtual int inode_mkdir(dentry* dir, const char* dirname) override
+    {
+        auto& new_dir = *cache_inode({ .v = INODE_DIR }, 0777, 0, mk_fe_vector());
+        mklink(&new_dir, &new_dir, ".");
 
-    if (i >= fes->size()) {
-        errno = ENOENT;
-        return GB_FAILED;
+        mklink(dir->ind, &new_dir, dirname);
+        mklink(&new_dir, dir->ind, "..");
+
+        dir->invalidate();
+        return GB_OK;
     }
 
-    entry->ino = fes->at(i).ino;
-    snprintf(entry->name, sizeof(entry->name), fes->at(i).filename);
+    virtual size_t inode_read(fs::inode* file, char* buf, size_t buf_size, size_t offset, size_t n) override
+    {
+        if (file->flags.in.file != 1)
+            return 0;
 
-    return GB_OK;
-}
+        auto* data = static_cast<vector<char>*>(file->impl);
+        size_t fsize = data->size();
 
-struct inode* tmpfs::findinode(struct inode* dir, const char* filename)
-{
-    struct dirent ent { };
-    size_t i = 0;
-    while (readdir(dir, &ent, i) == GB_OK) {
-        if (strcmp(ent.name, filename) == 0) {
-            // optimize: use hash table to build an index
-            auto& inodes = static_cast<tmpfs*>(dir->fs->impl)->m_inodes;
-            for (auto iter = inodes.begin(); iter != inodes.end(); ++iter)
-                if (iter->ino == ent.ino)
-                    return iter.ptr();
+        if (offset + n > fsize)
+            n = fsize - offset;
+
+        if (buf_size < n) {
+            n = buf_size;
         }
-        ++i;
+
+        memcpy(buf, data->data() + offset, n);
+
+        return n;
     }
-    return nullptr;
-}
 
-int tmpfs::stat(struct inode* dir, struct stat* stat, const char* filename)
-{
-    // for later use
-    // auto* fes = static_cast<vector<struct tmpfs_file_entry>*>(dir->impl);
+    virtual size_t inode_write(fs::inode* file, const char* buf, size_t offset, size_t n) override
+    {
+        if (file->flags.in.file != 1)
+            return 0;
 
-    auto* file_inode = vfs_findinode(dir, filename);
+        auto* data = static_cast<vector<char>*>(file->impl);
 
-    if (!file_inode) {
-        errno = ENOENT;
-        return GB_FAILED;
-    }
+        for (size_t i = data->size(); i < offset + n; ++i) {
+            data->push_back(0);
+        }
+        memcpy(data->data() + offset, buf, n);
 
-    stat->st_ino = file_inode->ino;
-    if (file_inode->flags.file) {
-        stat->st_blksize = 1;
-        stat->st_blocks = static_cast<vector<char>*>(file_inode->impl)->size();
-    }
-    if (file_inode->flags.directory) {
-        stat->st_blksize = sizeof(struct tmpfs_file_entry);
-        stat->st_blocks = static_cast<vector<struct tmpfs_file_entry>*>(file_inode->impl)->size();
+        return n;
     }
 
-    return GB_OK;
-}
+    virtual int inode_stat(dentry* dir, fs::stat* stat) override
+    {
+        auto* file_inode = dir->ind;
+
+        stat->st_ino = file_inode->ino;
+        stat->st_size = file_inode->size;
+        if (file_inode->flags.in.file) {
+            stat->st_rdev.v = 0;
+            stat->st_blksize = 1;
+            stat->st_blocks = file_inode->size;
+        }
+        if (file_inode->flags.in.directory) {
+            stat->st_rdev.v = 0;
+            stat->st_blksize = sizeof(tmpfs_file_entry);
+            stat->st_blocks = file_inode->size;
+        }
+        if (file_inode->flags.in.special_node) {
+            stat->st_rdev.v = (uint32_t)file_inode->impl;
+            stat->st_blksize = 0;
+            stat->st_blocks = 0;
+        }
 
-size_t vfs_read(struct inode* file, char* buf, size_t buf_size, size_t offset, size_t n)
-{
-    if (file->fs->ops->read) {
-        return file->fs->ops->read(file, buf, buf_size, offset, n);
-    } else {
-        return 0;
+        return GB_OK;
     }
-}
-size_t vfs_write(struct inode* file, const char* buf, size_t offset, size_t n)
+};
+
+// 8 * 8 for now
+static fs::special_node sns[8][8];
+
+size_t fs::vfs_read(fs::inode* file, char* buf, size_t buf_size, size_t offset, size_t n)
 {
-    if (file->fs->ops->write) {
-        return file->fs->ops->write(file, buf, offset, n);
+    if (file->flags.in.special_node) {
+        fs::node_t sn {
+            .v = (uint32_t)file->impl
+        };
+        auto* ptr = &sns[sn.in.major][sn.in.minor];
+        auto* ops = &ptr->ops;
+        if (ops && ops->read)
+            return ops->read(ptr, buf, buf_size, offset, n);
+        else {
+            errno = EINVAL;
+            return 0xffffffff;
+        }
     } else {
-        return 0;
+        return file->fs->inode_read(file, buf, buf_size, offset, n);
     }
 }
-int vfs_readdir(struct inode* dir, struct dirent* entry, size_t i)
+size_t fs::vfs_write(fs::inode* file, const char* buf, size_t offset, size_t n)
 {
-    if (dir->fs->ops->readdir) {
-        return dir->fs->ops->readdir(dir, entry, i);
+    if (file->flags.in.special_node) {
+        fs::node_t sn {
+            .v = (uint32_t)file->impl
+        };
+        auto* ptr = &sns[sn.in.major][sn.in.minor];
+        auto* ops = &ptr->ops;
+        if (ops && ops->write)
+            return ops->write(ptr, buf, offset, n);
+        else {
+            errno = EINVAL;
+            return 0xffffffff;
+        }
     } else {
-        return 0;
+        return file->fs->inode_write(file, buf, offset, n);
     }
 }
-struct inode* vfs_findinode(struct inode* dir, const char* filename)
+int fs::vfs_mkfile(fs::vfs::dentry* dir, const char* filename)
 {
-    if (dir->fs->ops->findinode) {
-        return dir->fs->ops->findinode(dir, filename);
-    } else {
-        return nullptr;
-    }
+    return dir->ind->fs->inode_mkfile(dir, filename);
 }
-int vfs_mkfile(struct inode* dir, const char* filename)
+int fs::vfs_mknode(fs::vfs::dentry* dir, const char* filename, fs::node_t sn)
 {
-    if (dir->fs->ops->mkfile) {
-        return dir->fs->ops->mkfile(dir, filename);
-    } else {
-        return 0;
-    }
+    return dir->ind->fs->inode_mknode(dir, filename, sn);
 }
-int vfs_rmfile(struct inode* dir, const char* filename)
+int fs::vfs_rmfile(fs::vfs::dentry* dir, const char* filename)
 {
-    if (dir->fs->ops->rmfile) {
-        return dir->fs->ops->rmfile(dir, filename);
-    } else {
-        return 0;
-    }
+    return dir->ind->fs->inode_rmfile(dir, filename);
 }
-int vfs_mkdir(struct inode* dir, const char* dirname)
+int fs::vfs_mkdir(fs::vfs::dentry* dir, const char* dirname)
 {
-    if (dir->fs->ops->mkdir) {
-        return dir->fs->ops->mkdir(dir, dirname);
-    } else {
-        return 0;
-    }
+    return dir->ind->fs->inode_mkdir(dir, dirname);
 }
 
-struct inode* vfs_open(const char* path)
+fs::vfs::dentry* fs::vfs_open(const char* path)
 {
     if (path[0] == '/' && path[1] == 0x00) {
-        return fs_root;
+        return fs::fs_root;
     }
 
-    struct inode* cur = fs_root;
+    auto* cur = fs::fs_root;
     size_t n = 0;
     switch (*(path++)) {
     // absolute path
     case '/':
         while (true) {
             if (path[n] == 0x00) {
-                string fname(path, n);
-                cur = vfs_findinode(cur, fname.c_str());
+                cur = cur->find(string(path, n));
                 return cur;
             }
             if (path[n] == '/') {
-                string fname(path, n);
-                cur = vfs_findinode(cur, fname.c_str());
+                cur = cur->find(string(path, n));
                 if (path[n + 1] == 0x00) {
                     return cur;
                 } else {
@@ -363,47 +406,63 @@ struct inode* vfs_open(const char* path)
     }
     return nullptr;
 }
-
-int vfs_stat(struct stat* stat, const char* _path)
+int fs::vfs_stat(const char* filename, stat* stat)
 {
-    if (_path[0] == '/' && _path[1] == 0x00) {
-        if (fs_root->fs->ops->stat) {
-            return fs_root->fs->ops->stat(fs_root, stat, ".");
-        } else {
-            errno = EINVAL;
-            return GB_FAILED;
-        }
-    }
-
-    string path(_path);
-    auto iter = path.back();
-    while (*(iter - 1) != '/')
-        --iter;
-    string filename(&*iter);
-    string parent_path = path.substr(0, &*iter - path.data());
+    auto ent = vfs_open(filename);
+    return vfs_stat(ent, stat);
+}
+int fs::vfs_stat(fs::vfs::dentry* ent, stat* stat)
+{
+    return ent->ind->fs->inode_stat(ent, stat);
+}
 
-    auto* dir_inode = vfs_open(parent_path.c_str());
+fs::vfs::dentry* fs::fs_root;
+static types::list<fs::vfs*>* fs_es;
 
-    if (!dir_inode) {
-        errno = ENOENT;
-        return GB_FAILED;
-    }
+void fs::register_special_block(
+    uint16_t major,
+    uint16_t minor,
+    fs::special_node_read read,
+    fs::special_node_write write,
+    uint32_t data1,
+    uint32_t data2)
+{
+    fs::special_node& sn = sns[major][minor];
+    sn.ops.read = read;
+    sn.ops.write = write;
+    sn.data1 = data1;
+    sn.data2 = data2;
+}
 
-    if (dir_inode->fs->ops->stat) {
-        return dir_inode->fs->ops->stat(dir_inode, stat, filename.c_str());
-    } else {
-        errno = EINVAL;
-        return GB_FAILED;
-    }
+fs::vfs* fs::register_fs(vfs* fs)
+{
+    fs_es->push_back(fs);
+    return fs;
 }
 
-struct inode* fs_root;
-static tmpfs* rootfs;
+size_t b_null_read(fs::special_node*, char* buf, size_t buf_size, size_t, size_t n)
+{
+    if (n >= buf_size)
+        n = buf_size;
+    memset(buf, 0x00, n);
+    return n;
+}
+size_t b_null_write(fs::special_node*, const char*, size_t, size_t n)
+{
+    return n;
+}
 
 void init_vfs(void)
 {
-    rootfs = allocator_traits<kernel_allocator<tmpfs>>::allocate_and_construct(4096 * 1024);
-    fs_root = rootfs->root_inode();
+    using namespace fs;
+    // null
+    register_special_block(0, 0, b_null_read, b_null_write, 0, 0);
+
+    fs_es = types::kernel_allocator_new<types::list<vfs*>>();
+
+    auto* rootfs = types::kernel_allocator_new<tmpfs>();
+    fs_es->push_back(rootfs);
+    fs_root = rootfs->root();
 
     vfs_mkdir(fs_root, "dev");
     vfs_mkdir(fs_root, "root");
@@ -411,11 +470,19 @@ void init_vfs(void)
 
     auto* init = vfs_open("/init");
     const char* str = "#/bin/sh\nexec /bin/sh\n";
-    vfs_write(init, str, 0, strlen(str));
+    vfs_write(init->ind, str, 0, strlen(str));
+
+    auto* dev = vfs_open("/dev");
+    vfs_mknode(dev, "null", { .in { .major = 0, .minor = 0 } });
+    vfs_mknode(dev, "console", { .in { .major = 1, .minor = 0 } });
+    vfs_mknode(dev, "hda", { .in { .major = 2, .minor = 0 } });
 
-    struct stat _stat { };
+    stat _stat {};
 
-    vfs_stat(&_stat, "/init");
-    vfs_stat(&_stat, "/");
-    vfs_stat(&_stat, "/dev");
+    vfs_stat("/init", &_stat);
+    vfs_stat("/", &_stat);
+    vfs_stat("/dev", &_stat);
+    vfs_stat("/dev/null", &_stat);
+    vfs_stat("/dev/console", &_stat);
+    vfs_stat("/dev/hda", &_stat);
 }

+ 2 - 4
src/kernel_main.c

@@ -13,7 +13,6 @@
 #include <kernel/stdio.h>
 #include <kernel/task.h>
 #include <kernel/tty.h>
-#include <kernel/vfs.h>
 #include <kernel/vga.h>
 #include <types/bitmap.h>
 
@@ -144,6 +143,8 @@ void init_bss_section(void)
 
 static struct tty early_console;
 
+extern void init_vfs();
+
 void NORETURN kernel_main(void)
 {
     // MAKE_BREAK_POINT();
@@ -196,9 +197,6 @@ void NORETURN kernel_main(void)
 
     init_vfs();
 
-    struct inode* init = vfs_open("/init");
-    vfs_read(init, buf, 128, 1, 10);
-
     printkf("switching execution to the scheduler...\n");
     init_scheduler(&tss);
 }

+ 61 - 0
src/mbr.S

@@ -0,0 +1,61 @@
+.section .text.bootsect
+.code16
+
+.globl mbr_start
+mbr_start:
+    movw %cs, %ax
+    movw %ax, %ds
+    movw %ax, %es
+    movw %ax, %ss
+
+# perform a temporary stack
+    movw $stack_base, %ax
+    movw %ax, %bp
+    movw %ax, %sp
+
+# read the first 64k
+    call read_data
+
+# read the rest
+    addw $(0x100 * 16), read_data_segment
+    addl $(8 * 16), read_data_lba
+    call read_data
+
+# loader start
+    jmp 0x7e00
+
+read_data:
+    movw $read_data_pack, %si
+    mov $0x42, %ah
+    mov $0x80, %dl
+    int $0x13
+    jc read_data_error
+    ret
+
+read_data_error:
+    hlt
+    jmp read_data_error
+
+.align 4
+read_data_pack:
+    .byte 0x10, 0
+read_data_count:
+    .word 128    # sector count (read 64k)
+read_data_offset:
+    .word 0x0000 # offset address
+read_data_segment:
+    .word 0x07e0 # segment address
+read_data_lba:
+    .long 1      # lower 4 bytes of the LBA to read
+    .long 0      # higher 2 bytes of the LBA to read
+
+__mbr_code_border__:
+    .long 0xffffffff
+
+.align 16
+stack_edge:
+.space 128
+stack_base:
+
+. = 510
+.byte 0x55, 0xaa

+ 15 - 0
src/mbr.ld

@@ -0,0 +1,15 @@
+OUTPUT_FORMAT(binary)
+OUTPUT_ARCH(i386:i386)
+
+SECTIONS
+{
+    .text 0x7c00 :
+    {
+        *(.text.bootsect)
+    }
+
+    /DISCARD/ :
+    {
+        *(.note*)
+    }
+}

+ 1 - 0
user-space-program/Makefile.src

@@ -24,6 +24,7 @@ user.sym: $(OBJS) output_symbols.ld
 
 .PHONY: clean
 clean:
+	-rm -rf build
 	-rm $(OBJS)
 	-rm $(RES)
 	-rm user.sym