Sfoglia il codice sorgente

Merge branch 'getdents'

greatbridf 2 anni fa
parent
commit
df785572f8

+ 14 - 11
CMakeLists.txt

@@ -49,24 +49,24 @@ if (NOT DEFINED FDISK_BIN)
 endif()
 
 set(KERNEL_MAIN_SOURCES src/fs/fat.cpp
-                        src/kernel_main.c
+                        src/kernel_main.cpp
                         src/kernel/errno.c
                         src/kernel/interrupt.cpp
                         src/kernel/process.cpp
-                        src/kernel/tty.c
-                        src/kernel/stdio.c
+                        src/kernel/tty.cpp
+                        src/kernel/stdio.cpp
                         src/kernel/syscall.cpp
                         src/kernel/mem.cpp
                         src/kernel/vfs.cpp
-                        src/kernel/vga.c
+                        src/kernel/vga.cpp
                         src/kernel/hw/ata.cpp
                         src/kernel/hw/keyboard.cpp
-                        src/kernel/hw/serial.c
+                        src/kernel/hw/serial.cpp
                         src/kernel/hw/timer.c
                         src/kernel/event/event.cpp
                         src/types/bitmap.c
-                        src/types/buffer.c
                         src/types/elf.cpp
+                        src/types/libstdcpp.cpp
                         include/asm/boot.h
                         include/asm/port_io.h
                         include/asm/sys.h
@@ -74,15 +74,15 @@ set(KERNEL_MAIN_SOURCES src/fs/fat.cpp
                         include/kernel/event/event.h
                         include/kernel/event/evtqueue.hpp
                         include/kernel/errno.h
-                        include/kernel/tty.h
+                        include/kernel/tty.hpp
                         include/kernel/interrupt.h
                         include/kernel/process.hpp
-                        include/kernel/stdio.h
+                        include/kernel/stdio.hpp
                         include/kernel/syscall.hpp
                         include/kernel/mem.h
                         include/kernel/mm.hpp
                         include/kernel/vfs.hpp
-                        include/kernel/vga.h
+                        include/kernel/vga.hpp
                         include/kernel/hw/ata.hpp
                         include/kernel/hw/keyboard.h
                         include/kernel/hw/port.hpp
@@ -92,9 +92,11 @@ set(KERNEL_MAIN_SOURCES src/fs/fat.cpp
                         include/kernel/input/input_event.h
                         include/types/assert.h
                         include/types/bitmap.h
-                        include/types/buffer.h
+                        include/types/buffer.hpp
                         include/types/elf.hpp
                         include/types/hash_map.hpp
+                        include/types/map.hpp
+                        include/types/pair.hpp
                         include/types/types.h
                         include/types/size.h
                         include/types/status.h
@@ -105,7 +107,8 @@ set(KERNEL_MAIN_SOURCES src/fs/fat.cpp
                         include/types/lock.hpp
                         include/types/string.hpp
                         include/types/vector.hpp
-                        include/kernel_main.h
+                        include/types/function.hpp
+                        include/kernel_main.hpp
                         )
 add_library(kernel_main STATIC ${KERNEL_MAIN_SOURCES})
 

+ 0 - 2
include/asm/boot.h

@@ -12,8 +12,6 @@ struct __attribute__((__packed__)) gdt_descriptor {
 
 extern struct gdt_descriptor asm_gdt_descriptor;
 
-extern uint32_t check_a20_on(void);
-
 extern struct mem_size_info asm_mem_size_info;
 
 extern uint8_t asm_e820_mem_map[1024];

+ 18 - 7
include/fs/fat.hpp

@@ -100,6 +100,7 @@ struct PACKED directory_entry {
     uint32_t size;
 };
 
+// TODO: deallocate inodes when dentry is destroyed
 class fat32 : public virtual fs::vfs {
 private:
     constexpr static uint32_t SECTOR_SIZE = 512;
@@ -113,6 +114,7 @@ private:
     uint32_t next_free_cluster_hint;
     cluster_t root_dir;
     cluster_t data_region_offset;
+    // TODO: use block device special node id
     inode* device;
     uint16_t reserved_sectors;
     uint8_t fat_copies;
@@ -120,15 +122,26 @@ private:
     char label[12];
     cluster_t* fat;
 
+    struct buf_object {
+        char* data;
+        int ref;
+        // bool dirty;
+    };
+    types::hash_map<cluster_t, buf_object, types::linux_hasher<cluster_t>> buf;
+
     // buf MUST be larger than 512 bytes
-    inline void read_sector(void* buf, uint32_t sector_no);
+    inline void _raw_read_sector(void* buf, uint32_t sector_no);
 
     // buf MUST be larger than 4096 bytes
-    inline void read_cluster(void* buf, cluster_t no);
+    inline void _raw_read_cluster(void* buf, cluster_t no);
+
+    // buffered version, release_cluster(cluster_no) after used
+    char* read_cluster(cluster_t no);
+    void release_cluster(cluster_t no);
 
-    static inline cluster_t cl(const inode* ind)
+    static constexpr cluster_t cl(const inode* ind)
     {
-        return reinterpret_cast<cluster_t>(ind->impl);
+        return ind->ino;
     }
 
     static inline cluster_t _rearrange(directory_entry* d)
@@ -147,9 +160,6 @@ private:
         }
     }
 
-protected:
-    virtual int load_dentry(dentry* ent) override;
-
 public:
     fat32(const fat32&) = delete;
     explicit fat32(inode* _device);
@@ -157,6 +167,7 @@ public:
 
     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;
+    virtual int inode_readdir(fs::inode* dir, size_t offset, fs::vfs::filldir_func callback) override;
 };
 
 }; // namespace fs::fat

+ 2 - 2
include/kernel/mm.hpp

@@ -124,7 +124,7 @@ public:
     {
         v.m_pd = nullptr;
     }
-    constexpr ~mm_list()
+    ~mm_list()
     {
         if (!m_pd)
             return;
@@ -170,7 +170,7 @@ public:
                 },
             },
             .owner = this,
-            .pgs = ::types::kernel_ident_allocator_new<page_arr>(),
+            .pgs = types::_new<types::kernel_ident_allocator, page_arr>(),
         });
     }
 

+ 212 - 28
include/kernel/process.hpp

@@ -1,12 +1,18 @@
 #pragma once
 
+#include <kernel/errno.h>
 #include <kernel/event/evtqueue.hpp>
 #include <kernel/interrupt.h>
 #include <kernel/mm.hpp>
 #include <kernel/task.h>
+#include <kernel/vfs.hpp>
+#include <types/allocator.hpp>
 #include <types/cplusplus.hpp>
 #include <types/hash_map.hpp>
 #include <types/list.hpp>
+#include <types/map.hpp>
+#include <types/pair.hpp>
+#include <types/status.h>
 #include <types/stdint.h>
 #include <types/types.h>
 
@@ -15,6 +21,14 @@ typedef size_t pid_t;
 class process;
 struct thread;
 
+class proclist;
+class readyqueue;
+
+inline process* volatile current_process;
+inline thread* volatile current_thread;
+inline proclist* procs;
+inline readyqueue* readythds;
+
 struct process_attr {
     uint16_t system : 1;
     uint16_t zombie : 1 = 0;
@@ -72,6 +86,12 @@ public:
         alloc_kstack();
     }
 
+    inline thread(const thread& thd, process* new_parent)
+        : thread { thd }
+    {
+        owner = new_parent;
+    }
+
     constexpr ~thread()
     {
         if (kstack)
@@ -79,25 +99,188 @@ public:
     }
 };
 
+class thdlist {
+public:
+    using list_type = types::list<thread>;
+
+private:
+    list_type thds;
+
+public:
+    constexpr thdlist(const thdlist& obj) = delete;
+    constexpr thdlist(thdlist&& obj) = delete;
+
+    constexpr thdlist& operator=(const thdlist& obj) = delete;
+    constexpr thdlist& operator=(thdlist&& obj) = delete;
+
+    constexpr thdlist(thdlist&& obj, process* new_parent)
+        : thds { types::move(obj.thds) }
+    {
+        for (auto& thd : thds)
+            thd.owner = new_parent;
+    }
+
+    explicit constexpr thdlist(void)
+    {
+    }
+
+    // implementation is below
+    constexpr ~thdlist();
+
+    template <typename... Args>
+    constexpr thread& Emplace(Args&&... args)
+    {
+        return *thds.emplace_back(types::forward<Args>(args)...);
+    }
+
+    constexpr size_t size(void) const
+    {
+        return thds.size();
+    }
+
+    constexpr list_type& underlying_list(void)
+    {
+        return thds;
+    }
+};
+
 class process {
+public:
+    class filearr {
+    public:
+        using container_type = types::list<fs::file>;
+        using array_type = types::hash_map<int, container_type::iterator_type, types::linux_hasher<size_t>>;
+
+    private:
+        inline static container_type* files = nullptr;
+        array_type arr;
+        int next_fd = 0;
+
+    public:
+        constexpr filearr(const filearr&) = delete;
+        constexpr filearr& operator=(const filearr&) = delete;
+        constexpr filearr& operator=(filearr&&) = delete;
+        constexpr filearr(filearr&& val)
+            : arr { types::move(val.arr) }
+            , next_fd { val.next_fd }
+        {
+            val.next_fd = 0;
+        }
+
+        explicit filearr()
+        {
+            if (!files)
+                files = types::pnew<types::kernel_allocator>(files);
+        }
+
+        constexpr void dup(const filearr& orig)
+        {
+            if (this->next_fd)
+                return;
+
+            this->next_fd = orig.next_fd;
+
+            for (int i = 0; i < this->next_fd; ++i) {
+                auto iter = orig.arr.find(i);
+                if (!iter)
+                    continue;
+
+                this->arr.emplace(iter->key, iter->value);
+                ++iter->value->ref;
+            }
+        }
+
+        constexpr fs::file* operator[](int i) const
+        {
+            auto iter = arr.find(i);
+            if (!iter)
+                return nullptr;
+            else
+                return &iter->value;
+        }
+
+        // TODO: file opening flags (permissions etc.)
+        int open(const char* filename, uint32_t)
+        {
+            auto* dentry = fs::vfs_open(filename);
+
+            if (!dentry) {
+                errno = ENOTFOUND;
+                return -1;
+            }
+
+            // TODO: check whether dentry is a file if O_DIRECTORY is set
+            // if (!(dentry->ind->flags.in.file || dentry->ind->flags.in.special_node)) {
+            //     errno = EISDIR;
+            //     return -1;
+            // }
+
+            // TODO: unify file, inode, dentry TYPE
+            fs::file::types type = fs::file::types::regular_file;
+            if (dentry->ind->flags.in.directory)
+                type = fs::file::types::directory;
+            if (dentry->ind->flags.in.special_node)
+                type = fs::file::types::block_dev;
+
+            auto iter = files->emplace_back(fs::file {
+                type,
+                dentry->ind,
+                dentry->parent,
+                0,
+                1 });
+
+            int fd = next_fd++;
+            arr.emplace(fd, iter);
+            return fd;
+        }
+
+        // close file descriptor
+        // where iter is guaranteed not nullptr
+        constexpr void close(array_type::iterator_type iter)
+        {
+            if (iter->value->ref == 1)
+                files->erase(iter->value);
+            else
+                --iter->value->ref;
+        }
+
+        constexpr void close(int fd)
+        {
+            auto iter = arr.find(fd);
+            if (iter)
+                close(iter);
+        }
+
+        constexpr ~filearr()
+        {
+            for (int i = 0; i < next_fd; ++i)
+                close(i);
+        }
+    };
+
 public:
     mutable kernel::mm_list mms;
-    types::list<thread> thds;
+    thdlist thds;
     kernel::evtqueue wait_lst;
     process_attr attr;
     pid_t pid;
     pid_t ppid;
+    filearr files;
 
 public:
     process(process&& val);
-    process(const process&) = delete;
-    process(const process& proc, const thread& main_thread);
+    process(const process&);
 
-    // only used for system initialization
-    explicit process(pid_t ppid);
-    explicit process(void (*func_in_kernel_space)(void), pid_t ppid);
+    explicit process(pid_t ppid, bool system = true);
 
-    ~process();
+    constexpr bool is_system(void) const
+    {
+        return attr.system;
+    }
+    constexpr bool is_zombie(void) const
+    {
+        return attr.zombie;
+    }
 
 private:
     static inline pid_t max_pid;
@@ -110,31 +293,31 @@ private:
 
 class proclist final {
 public:
-    using list_type = types::list<process>;
-    using index_type = types::hash_map<pid_t, types::list<process>::iterator_type, types::linux_hasher<pid_t>>;
+    using list_type = types::map<pid_t, process>;
     using child_index_type = types::hash_map<pid_t, types::list<pid_t>, types::linux_hasher<pid_t>>;
     using iterator_type = list_type::iterator_type;
     using const_iterator_type = list_type::const_iterator_type;
 
 private:
     list_type m_procs;
-    index_type m_idx;
     child_index_type m_child_idx;
 
 public:
     template <typename... Args>
-    constexpr iterator_type emplace(Args&&... args)
+    iterator_type emplace(Args&&... args)
     {
-        auto iter = m_procs.emplace_back(types::forward<Args>(args)...);
-        m_idx.insert(iter->pid, iter);
+        process _proc(types::forward<Args>(args)...);
+        auto pid = _proc.pid;
+        auto ppid = _proc.ppid;
+        auto iter = m_procs.insert(types::make_pair(pid, types::move(_proc)));
 
-        auto children = m_child_idx.find(iter->ppid);
+        auto children = m_child_idx.find(ppid);
         if (!children) {
-            m_child_idx.insert(iter->ppid, {});
-            children = m_child_idx.find(iter->ppid);
+            m_child_idx.emplace(ppid, types::list<pid_t> {});
+            children = m_child_idx.find(ppid);
         }
 
-        children->value.push_back(iter->pid);
+        children->value.push_back(pid);
 
         return iter;
     }
@@ -143,21 +326,20 @@ public:
     {
         make_children_orphans(pid);
 
-        auto proc_iter = m_idx.find(pid);
-        auto ppid = proc_iter->value->ppid;
+        auto proc_iter = m_procs.find(pid);
+        auto ppid = proc_iter->value.ppid;
 
         auto& parent_children = m_child_idx.find(ppid)->value;
 
         auto i = parent_children.find(pid);
         parent_children.erase(i);
 
-        m_procs.erase(proc_iter->value);
-        m_idx.remove(proc_iter);
+        m_procs.erase(proc_iter);
     }
 
     constexpr process* find(pid_t pid)
     {
-        return &m_idx.find(pid)->value;
+        return &m_procs.find(pid)->value;
     }
 
     constexpr bool has_child(pid_t pid)
@@ -232,12 +414,7 @@ public:
     }
 };
 
-inline process* volatile current_process;
-inline thread* volatile current_thread;
-inline proclist* procs;
-inline readyqueue* readythds;
-
-extern "C" void NORETURN init_scheduler();
+void NORETURN init_scheduler(void);
 void schedule(void);
 
 constexpr uint32_t push_stack(uint32_t** stack, uint32_t val)
@@ -247,4 +424,11 @@ constexpr uint32_t push_stack(uint32_t** stack, uint32_t val)
     return val;
 }
 
+// class thdlist
+constexpr thdlist::~thdlist()
+{
+    for (auto iter = thds.begin(); iter != thds.end(); ++iter)
+        readythds->remove_all(&iter);
+}
+
 void k_new_thread(void (*func)(void*), void* data);

+ 2 - 0
include/kernel/stdio.h → include/kernel/stdio.hpp

@@ -33,6 +33,8 @@ snprintf(
     const char* fmt,
     ...);
 
+void kmsg(const char* msg);
+
 #ifdef __cplusplus
 }
 #endif

+ 0 - 34
include/kernel/tty.h

@@ -1,34 +0,0 @@
-#pragma once
-#include <asm/port_io.h>
-
-#define STRUCT_TTY_NAME_LEN (32)
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-struct tty;
-
-struct tty_operations
-{
-    void (*put_char)(struct tty* p_tty, char c);
-};
-
-struct tty
-{
-    char name[STRUCT_TTY_NAME_LEN];
-    struct tty_operations* ops;
-    char data[12];
-};
-
-// in kernel_main.c
-extern struct tty* console;
-
-void tty_print(struct tty* p_tty, const char* str);
-
-int make_serial_tty(struct tty* p_tty, int id);
-int make_vga_tty(struct tty* p_tty);
-
-#ifdef __cplusplus
-}
-#endif

+ 45 - 0
include/kernel/tty.hpp

@@ -0,0 +1,45 @@
+#pragma once
+#include <kernel/event/evtqueue.hpp>
+#include <types/allocator.hpp>
+#include <types/buffer.hpp>
+#include <types/cplusplus.hpp>
+#include <types/stdint.h>
+
+class tty : public types::non_copyable {
+public:
+    static constexpr size_t BUFFER_SIZE = 4096;
+    static constexpr size_t NAME_SIZE = 32;
+
+public:
+    tty();
+    virtual void putchar(char c) = 0;
+    virtual void recvchar(char c) = 0;
+    void print(const char* str);
+    size_t read(char* buf, size_t buf_size, size_t n);
+
+    char name[NAME_SIZE];
+    bool echo = true;
+
+protected:
+    types::buffer<types::kernel_ident_allocator> buf;
+    kernel::evtqueue blocklist;
+};
+
+class vga_tty : public virtual tty {
+public:
+    vga_tty();
+    virtual void putchar(char c) override;
+    virtual void recvchar(char c) override;
+};
+
+class serial_tty : public virtual tty {
+public:
+    serial_tty(int id);
+    virtual void putchar(char c) override;
+    virtual void recvchar(char c) override;
+
+public:
+    uint16_t id;
+};
+
+inline tty* console;

+ 64 - 16
include/kernel/vfs.hpp

@@ -1,8 +1,10 @@
 #pragma once
 
 #include <types/allocator.hpp>
+#include <types/function.hpp>
 #include <types/hash_map.hpp>
 #include <types/list.hpp>
+#include <types/map.hpp>
 #include <types/stdint.h>
 #include <types/types.h>
 #include <types/vector.hpp>
@@ -12,6 +14,19 @@
 #define INODE_MNT (1 << 2)
 #define INODE_NODE (1 << 3)
 
+// dirent file types
+#define DT_UNKNOWN 0
+#define DT_FIFO 1
+#define DT_CHR 2
+#define DT_DIR 4
+#define DT_BLK 6
+#define DT_REG 8
+#define DT_LNK 10
+#define DT_SOCK 12
+#define DT_WHT 14
+
+#define DT_MAX (S_DT_MASK + 1) /* 16 */
+
 namespace fs {
 using ino_t = size_t;
 using blksize_t = size_t;
@@ -32,12 +47,12 @@ union inode_flags {
 struct inode {
     inode_flags flags;
     uint32_t perm;
-    void* impl;
     ino_t ino;
     vfs* fs;
     size_t size;
 };
 
+#define SN_INVALID (0xffffffff)
 union node_t {
     uint32_t v;
     struct {
@@ -70,20 +85,30 @@ struct stat {
     blkcnt_t st_blocks;
 };
 
+struct PACKED user_dirent {
+    ino_t d_ino; // inode number
+    uint32_t d_off; // ignored
+    uint16_t d_reclen; // length of this struct user_dirent
+    char d_name[1]; // file name with a padding zero
+    // uint8_t d_type; // file type, with offset of (d_reclen - 1)
+};
+
 class vfs {
 public:
     struct dentry {
     public:
         using name_type = types::string<>;
+        template <typename T>
+        using allocator_type = types::kernel_allocator<T>;
 
     private:
-        types::list<dentry> children;
-        types::hash_map<name_type, dentry*, types::string_hasher<const name_type&>> idx_children;
+        types::list<dentry, allocator_type>* children = nullptr;
+        types::hash_map<name_type, dentry*, types::string_hasher<const name_type&>, allocator_type>* idx_children = nullptr;
 
     public:
         dentry* parent;
         inode* ind;
-        // if the entry is not a file, this flag is ignored
+        // if the entry is a file, this flag is ignored
         union {
             uint32_t v;
             struct {
@@ -101,8 +126,10 @@ public:
         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();
+
+        dentry* append(inode* ind, const name_type& name, bool set_dirty);
+        dentry* append(inode* ind, name_type&& name, bool set_dirty);
 
         dentry* find(const name_type& name);
 
@@ -111,29 +138,26 @@ public:
         void invalidate(void);
     };
 
+public:
+    using filldir_func = std::function<int(const char*, size_t, ino_t, uint8_t)>;
+
 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>>;
+    using inode_list = types::map<ino_t, inode>;
 
 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* cache_inode(inode_flags flags, uint32_t perm, size_t size, ino_t ino);
     inode* get_inode(ino_t ino);
     void register_root_node(inode* root);
 
-    virtual int load_dentry(dentry* ent);
+    int load_dentry(dentry* ent);
 
 public:
     explicit vfs(void);
@@ -156,9 +180,33 @@ public:
     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);
+    virtual uint32_t inode_getnode(inode* file);
+
+    // parameter 'length' in callback:
+    // if 0, 'name' should be null terminated
+    // else, 'name' size
+    //
+    // @return
+    // return -1 if an error occurred
+    // return 0 if no more entry available
+    // otherwise, return bytes to be added to the offset
+    virtual int inode_readdir(inode* dir, size_t offset, filldir_func callback) = 0;
+};
+
+struct file {
+    enum class types {
+        regular_file,
+        directory,
+        block_dev,
+        char_dev,
+    } type;
+    inode* ind;
+    vfs::dentry* parent;
+    size_t cursor;
+    size_t ref;
 };
 
-extern fs::vfs::dentry* fs_root;
+inline fs::vfs::dentry* fs_root;
 
 void register_special_block(uint16_t major,
     uint16_t minor,

+ 0 - 8
include/kernel/vga.h → include/kernel/vga.hpp

@@ -4,10 +4,6 @@
 
 #include <types/stdint.h>
 
-#ifdef __cplusplus
-extern "C" {
-#endif
-
 #define VGA_CHAR_COLOR_WHITE (0x0fU)
 
 struct vga_char {
@@ -22,8 +18,4 @@ struct vga_char {
 void vga_put_char(struct vga_char* c);
 void vga_print(const char* str, uint8_t color);
 
-#ifdef __cplusplus
-}
-#endif
-
 #endif // _KERNEL_VGA_H_

+ 1 - 8
include/kernel_main.h → include/kernel_main.hpp

@@ -6,12 +6,5 @@
 
 #define KERNEL_START_ADDR (0x00100000)
 
-void NORETURN kernel_main(void);
-
-#ifdef __cplusplus
-// in kernel_main.c
-extern "C" struct tss32_t tss;
-#else
-// in kernel_main.c
+// in kernel_main.cpp
 extern struct tss32_t tss;
-#endif

+ 20 - 42
include/types/allocator.hpp

@@ -4,7 +4,7 @@
 #include <types/stdint.h>
 #include <types/types.h>
 
-inline void* operator new(size_t, void* ptr)
+constexpr void* operator new(size_t, void* ptr)
 {
     return ptr;
 }
@@ -31,12 +31,12 @@ class kernel_allocator {
 public:
     using value_type = T;
 
-    static value_type* allocate_memory(size_t count)
+    static constexpr value_type* allocate_memory(size_t count)
     {
         return static_cast<value_type*>(::k_malloc(count));
     }
 
-    static void deallocate_memory(value_type* ptr)
+    static constexpr void deallocate_memory(value_type* ptr)
     {
         ::k_free(ptr);
     }
@@ -47,55 +47,33 @@ class kernel_ident_allocator {
 public:
     using value_type = T;
 
-    static value_type* allocate_memory(size_t count)
+    static constexpr value_type* allocate_memory(size_t count)
     {
         return static_cast<value_type*>(::ki_malloc(count));
     }
 
-    static void deallocate_memory(value_type* ptr)
+    static constexpr void deallocate_memory(value_type* ptr)
     {
         ::ki_free(ptr);
     }
 };
 
-template <typename T, typename... Args>
-constexpr T* kernel_allocator_new(Args&&... args)
+template <template <typename _T> class Allocator, typename T, typename... Args>
+constexpr T* _new(Args&&... args)
 {
-    return allocator_traits<kernel_allocator<T>>::allocate_and_construct(forward<Args>(args)...);
+    return allocator_traits<Allocator<T>>::allocate_and_construct(forward<Args>(args)...);
 }
 
-template <PointerType T, typename... Args>
-constexpr auto kernel_allocator_pnew(T, Args&&... args)
+template <template <typename _T> class Allocator, typename T, typename... Args>
+constexpr T* pnew(T* = nullptr, Args&&... args)
 {
-    using value_type = typename traits::remove_pointer<T>::type;
-    return kernel_allocator_new<value_type>(forward<Args>(args)...);
+    return _new<Allocator, T, Args...>(forward<Args>(args)...);
 }
 
-template <typename T, typename... Args>
-constexpr T* kernel_ident_allocator_new(Args&&... args)
+template <template <typename _T> class Allocator, typename T>
+constexpr void pdelete(T* ptr)
 {
-    return allocator_traits<kernel_ident_allocator<T>>::allocate_and_construct(forward<Args>(args)...);
-}
-
-template <PointerType T, typename... Args>
-constexpr auto kernel_ident_allocator_pnew(T, Args&&... args)
-{
-    using value_type = typename traits::remove_pointer<T>::type;
-    return kernel_ident_allocator_new<value_type>(forward<Args>(args)...);
-}
-
-template <PointerType T>
-constexpr void kernel_allocator_delete(T ptr)
-{
-    using value_type = typename traits::remove_pointer<T>::type;
-    return allocator_traits<kernel_allocator<value_type>>::deconstruct_and_deallocate(ptr);
-}
-
-template <PointerType T>
-constexpr void kernel_ident_allocator_delete(T ptr)
-{
-    using value_type = typename traits::remove_pointer<T>::type;
-    return allocator_traits<kernel_ident_allocator<value_type>>::deconstruct_and_deallocate(ptr);
+    allocator_traits<Allocator<T>>::deconstruct_and_deallocate(ptr);
 }
 
 template <Allocator _allocator>
@@ -103,7 +81,7 @@ class allocator_traits {
 public:
     using value_type = typename _allocator::value_type;
 
-    static value_type* allocate(size_t count)
+    static constexpr value_type* allocate(size_t count)
     {
         if (count == 0)
             return nullptr;
@@ -111,35 +89,35 @@ public:
     }
 
     template <typename... Args>
-    static value_type* construct(value_type* ptr, Args&&... args)
+    static constexpr value_type* construct(value_type* ptr, Args&&... args)
     {
         new (ptr) value_type(forward<Args>(args)...);
         return ptr;
     }
 
     template <typename... Args>
-    static value_type* allocate_and_construct(Args&&... args)
+    static constexpr value_type* allocate_and_construct(Args&&... args)
     {
         auto* ptr = allocate(1);
         construct(ptr, forward<Args>(args)...);
         return ptr;
     }
 
-    static void deconstruct(value_type* ptr)
+    static constexpr void deconstruct(value_type* ptr)
     {
         if (!ptr)
             return;
         ptr->~value_type();
     }
 
-    static void deallocate(value_type* ptr)
+    static constexpr void deallocate(value_type* ptr)
     {
         if (!ptr)
             return;
         _allocator::deallocate_memory(ptr);
     }
 
-    static void deconstruct_and_deallocate(value_type* ptr)
+    static constexpr void deconstruct_and_deallocate(value_type* ptr)
     {
         if (!ptr)
             return;

+ 0 - 31
include/types/buffer.h

@@ -1,31 +0,0 @@
-#pragma once
-
-#include "stdint.h"
-
-#define MAKE_RING_BUFFER(BUF_PTR, SIZE)        \
-    {                                          \
-        .buf_start_pos = (BUF_PTR),            \
-        .buf_end_pos = ((BUF_PTR) + (SIZE)-1), \
-        .base = (BUF_PTR),                     \
-        .head = (BUF_PTR),                     \
-        .count = 0,                            \
-    }
-
-struct ring_buffer {
-    char* const buf_start_pos;
-    char* const buf_end_pos;
-    char* base;
-    char* head;
-    size_t count;
-};
-
-int32_t
-ring_buffer_empty(struct ring_buffer* buf);
-
-int32_t
-ring_buffer_full(struct ring_buffer* buf);
-
-char ring_buffer_read(struct ring_buffer* buf);
-
-int32_t
-ring_buffer_write(struct ring_buffer* buf, char c);

+ 137 - 0
include/types/buffer.hpp

@@ -0,0 +1,137 @@
+#pragma once
+
+#include <types/allocator.hpp>
+#include <types/stdint.h>
+
+namespace types {
+
+template <template <typename> class Allocator>
+class buffer {
+public:
+    using allocator_type = Allocator<char>;
+
+private:
+    char* const start;
+    char* const end;
+    char* base;
+    char* head;
+    size_t count;
+
+private:
+    constexpr char _get_char(char* ptr)
+    {
+        --count;
+        return *ptr;
+    }
+
+    constexpr void _put_char(char c)
+    {
+        *head = c;
+        ++count;
+    }
+
+    constexpr char* _forward(char* ptr)
+    {
+        if (ptr == end)
+            return start;
+        else
+            return ptr + 1;
+    }
+
+    constexpr char* _backward(char* ptr)
+    {
+        if (ptr == start)
+            return end;
+        else
+            return ptr - 1;
+    }
+
+public:
+    constexpr buffer(size_t size)
+        : start { types::allocator_traits<allocator_type>::allocate(size) }
+        , end { start + size - 1 }
+        , base { start }
+        , head { start }
+        , count { 0 }
+    {
+    }
+
+    constexpr buffer(const buffer& buf)
+        : start { types::allocator_traits<allocator_type>::allocate(buf.end + 1 - buf.start) }
+        , end { (uint32_t)start + (uint32_t)buf.end - (uint32_t)buf.start }
+        , base { (uint32_t)start + (uint32_t)buf.base - (uint32_t)buf.start }
+        , head { (uint32_t)start + (uint32_t)buf.base - (uint32_t)buf.start }
+        , count { buf.count }
+    {
+    }
+
+    constexpr buffer(buffer&& buf)
+        : start { buf.start }
+        , end { buf.end }
+        , base { buf.base }
+        , head { buf.head }
+        , count { buf.count }
+    {
+    }
+
+    constexpr ~buffer()
+    {
+        if (start)
+            types::allocator_traits<allocator_type>::deallocate(start);
+    }
+
+    constexpr bool empty(void) const
+    {
+        return count == 0;
+    }
+
+    constexpr bool full(void) const
+    {
+        return count == static_cast<size_t>(end - start + 1);
+    }
+
+    constexpr char front(void)
+    {
+        return *base;
+    }
+
+    constexpr char back(void)
+    {
+        return *_backward(head);
+    }
+
+    constexpr char get(void)
+    {
+        // TODO: set error flag
+        if (empty())
+            return 0xff;
+
+        char c = _get_char(base);
+        base = _forward(base);
+        return c;
+    }
+
+    constexpr char pop(void)
+    {
+        // TODO: set error flag
+        if (empty())
+            return 0xff;
+
+        char c = _get_char(_backward(head));
+        head = _backward(head);
+        return c;
+    }
+
+    constexpr char put(char c)
+    {
+        // TODO: set error flag
+        if (full())
+            return 0xff;
+
+        _put_char(c);
+        head = _forward(head);
+        return c;
+    }
+};
+
+} // namespace types

+ 44 - 1
include/types/cplusplus.hpp

@@ -17,6 +17,15 @@ using false_type = constant_value<bool, false>;
 
 namespace types::traits {
 
+template <bool Expression, typename TTrue, typename TFalse>
+struct condition {
+    using type = TFalse;
+};
+template <typename TTrue, typename TFalse>
+struct condition<true, TTrue, TFalse> {
+    using type = TTrue;
+};
+
 template <typename T>
 struct remove_pointer {
     using type = T;
@@ -65,6 +74,19 @@ struct remove_cv<const volatile T> {
     using type = T;
 };
 
+template <typename T>
+struct add_const {
+    using type = const T;
+};
+template <typename T>
+struct add_const<const T> {
+    using type = const T;
+};
+template <>
+struct add_const<void> {
+    using type = void;
+};
+
 template <typename T>
 struct is_pointer : false_type {
 };
@@ -73,10 +95,24 @@ template <typename T>
 struct is_pointer<T*> : true_type {
 };
 
+template <typename T>
+struct is_const : public false_type {
+};
+template <typename T>
+struct is_const<const T> : public true_type {
+};
+
+template <typename U, template <typename...> class T, typename...>
+struct is_template_instance : public false_type {
+};
+template <template <typename...> class T, typename... Ts>
+struct is_template_instance<T<Ts...>, T> : public true_type {
+};
+
 template <typename T>
 struct decay {
 private:
-    using U = remove_reference<T>;
+    using U = typename remove_reference<T>::type;
 
 public:
     using type = typename remove_cv<U>::type;
@@ -139,6 +175,13 @@ concept PointerType = traits::is_pointer<T>::value;
 template <typename A, typename B>
 concept same_as = is_same<A, B>::value;
 
+class non_copyable {
+public:
+    non_copyable() = default;
+    non_copyable(const non_copyable&) = delete;
+    non_copyable& operator=(const non_copyable&) = delete;
+};
+
 } // namespace types
 
 #endif

+ 75 - 0
include/types/function.hpp

@@ -0,0 +1,75 @@
+#pragma once
+#include <types/cplusplus.hpp>
+
+namespace std {
+
+namespace __inner {
+
+    template <typename Ret, typename... Args>
+    class _function_base {
+    public:
+        constexpr _function_base() = default;
+        virtual constexpr ~_function_base() = default;
+        virtual constexpr Ret operator()(Args&&... args) const = 0;
+    };
+
+    template <typename FuncLike, typename Ret, typename... Args>
+    class _function : public _function_base<Ret, Args...> {
+    private:
+        FuncLike func;
+
+    public:
+        constexpr _function(FuncLike&& _func)
+            : func(types::forward<FuncLike>(_func))
+        {
+        }
+        constexpr ~_function() = default;
+
+        constexpr Ret operator()(Args&&... args) const override
+        {
+            return func(types::forward<Args>(args)...);
+        }
+    };
+
+} // namespace __inner
+
+template <typename>
+class function;
+
+template <typename Ret, typename... Args>
+class function<Ret(Args...)> {
+private:
+    char _data[sizeof(void*) * 2];
+    using fb_t = __inner::_function_base<Ret, Args...>;
+    constexpr fb_t* _f(void) const
+    {
+        return (fb_t*)_data;
+    }
+
+public:
+    template <typename FuncLike>
+    constexpr function(FuncLike&& func)
+    {
+        static_assert(sizeof(FuncLike) <= sizeof(_data));
+        new (_f()) __inner::_function<FuncLike, Ret, Args...>(types::forward<FuncLike>(func));
+    }
+
+    template <typename FuncPtr>
+    constexpr function(FuncPtr* funcPtr)
+    {
+        new (_f()) __inner::_function<typename types::traits::decay<FuncPtr>::type, Ret, Args...>(
+            types::forward<typename types::traits::decay<FuncPtr>::type>(funcPtr));
+    }
+
+    constexpr ~function()
+    {
+        _f()->~_function_base();
+    }
+
+    constexpr Ret operator()(Args... args) const
+    {
+        return (*_f())(types::forward<Args>(args)...);
+    }
+};
+
+} // namespace std

+ 12 - 31
include/types/hash_map.hpp

@@ -3,6 +3,7 @@
 #include <types/allocator.hpp>
 #include <types/cplusplus.hpp>
 #include <types/list.hpp>
+#include <types/pair.hpp>
 #include <types/stdint.h>
 #include <types/string.hpp>
 #include <types/types.h>
@@ -89,40 +90,23 @@ concept Hasher = requires(Value&& val, uint32_t bits)
 template <typename Key, typename Value, Hasher<Key> _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 key_type = typename traits::add_const<Key>::type;
     using value_type = Value;
-    using pair_type = pair;
+    using pair_type = pair<key_type, value_type>;
     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_type = list<pair_type, 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;
-
-        constexpr pair(void) = delete;
-        constexpr pair(const key_type _key, value_type _val)
-            : key(_key)
-            , value(_val)
-        {
-        }
-        constexpr bool operator==(const pair& p)
-        {
-            return key == p.key;
-        }
-    };
-
     template <typename Pointer>
     class iterator {
     public:
@@ -226,19 +210,16 @@ public:
         buckets.clear();
     }
 
-    constexpr void insert(const pair& p)
-    {
-        auto hash_value = _Hasher::hash(p.key, hash_length());
-        buckets.at(hash_value).push_back(p);
-    }
-    constexpr void insert(pair&& p)
+    constexpr void emplace(pair_type&& p)
     {
         auto hash_value = _Hasher::hash(p.key, hash_length());
         buckets.at(hash_value).push_back(move(p));
     }
-    constexpr void insert(const key_type& key, const value_type& val)
+
+    template <typename _key_type, typename _value_type>
+    constexpr void emplace(_key_type&& key, _value_type&& value)
     {
-        insert(pair { key, val });
+        emplace(make_pair(forward<_key_type>(key), forward<_value_type>(value)));
     }
 
     constexpr void remove(const key_type& key)
@@ -279,9 +260,9 @@ public:
     {
         auto hash_value = _Hasher::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));
+        for (auto iter = bucket.cbegin(); iter != bucket.cend(); ++iter) {
+            if (key == iter->key)
+                return const_iterator_type(&iter);
         }
         return const_iterator_type(nullptr);
     }

+ 699 - 0
include/types/map.hpp

@@ -0,0 +1,699 @@
+#pragma once
+
+#include <types/allocator.hpp>
+#include <types/cplusplus.hpp>
+#include <types/pair.hpp>
+#include <types/types.h>
+
+namespace types {
+
+template <typename Key, typename Value, template <typename _T> class _Allocator = kernel_allocator>
+class map {
+public:
+    using key_type = typename traits::add_const<Key>::type;
+    using value_type = Value;
+    using pair_type = pair<key_type, value_type>;
+
+    struct node {
+        node* parent = nullptr;
+
+        node* left = nullptr;
+        node* right = nullptr;
+
+        enum class node_color {
+            RED,
+            BLACK,
+        } color
+            = node_color::RED;
+
+        pair_type v;
+
+        constexpr node(pair_type&& pair)
+            : v(move(pair))
+        {
+        }
+        constexpr node(const pair_type& pair)
+            : v(pair)
+        {
+        }
+
+        constexpr node* grandparent(void) const
+        {
+            return this->parent->parent;
+        }
+
+        constexpr node* uncle(void) const
+        {
+            node* pp = this->grandparent();
+            return (this->parent == pp->left) ? pp->right : pp->left;
+        }
+
+        constexpr node* leftmost(void)
+        {
+            node* nd = this;
+            while (nd->left)
+                nd = nd->left;
+            return nd;
+        }
+
+        constexpr const node* leftmost(void) const
+        {
+            const node* nd = this;
+            while (nd->left)
+                nd = nd->left;
+            return nd;
+        }
+
+        constexpr node* rightmost(void)
+        {
+            node* nd = this;
+            while (nd->right)
+                nd = nd->right;
+            return nd;
+        }
+
+        constexpr const node* rightmost(void) const
+        {
+            const node* nd = this;
+            while (nd->right)
+                nd = nd->right;
+            return nd;
+        }
+
+        constexpr node* next(void)
+        {
+            if (this->right) {
+                return this->right->leftmost();
+            } else {
+                if (this->is_root()) {
+                    return nullptr;
+                } else if (this->is_left_child()) {
+                    return this->parent;
+                } else {
+                    node* ret = this;
+                    do {
+                        ret = ret->parent;
+                    } while (!ret->is_root() && !ret->is_left_child());
+                    return ret->parent;
+                }
+            }
+        }
+
+        constexpr const node* next(void) const
+        {
+            if (this->right) {
+                return this->right->leftmost();
+            } else {
+                if (this->is_root()) {
+                    return nullptr;
+                } else if (this->is_left_child()) {
+                    return this->parent;
+                } else {
+                    const node* ret = this;
+                    do {
+                        ret = ret->parent;
+                    } while (!ret->is_root() && !ret->is_left_child());
+                    return ret->parent;
+                }
+            }
+        }
+
+        constexpr node* prev(void)
+        {
+            if (this->left) {
+                return this->left->rightmost();
+            } else {
+                if (this->is_root()) {
+                    return nullptr;
+                } else if (this->is_right_child()) {
+                    return this->parent;
+                } else {
+                    node* ret = this;
+                    do {
+                        ret = ret->parent;
+                    } while (!ret->is_root() && !ret->is_right_child());
+                    return ret->parent;
+                }
+            }
+        }
+
+        static constexpr bool is_red(node* nd)
+        {
+            return nd && nd->color == node_color::RED;
+        }
+        static constexpr bool is_black(node* nd)
+        {
+            return !node::is_red(nd);
+        }
+
+        constexpr const node* prev(void) const
+        {
+            if (this->left) {
+                return this->left->rightmost();
+            } else {
+                if (this->is_root()) {
+                    return nullptr;
+                } else if (this->is_right_child()) {
+                    return this->parent;
+                } else {
+                    const node* ret = this;
+                    do {
+                        ret = ret->parent;
+                    } while (!ret->is_root() && !ret->is_right_child());
+                    return ret->parent;
+                }
+            }
+        }
+
+        constexpr bool is_root(void) const
+        {
+            return this->parent == nullptr;
+        }
+
+        constexpr bool is_full(void) const
+        {
+            return this->left && this->right;
+        }
+
+        constexpr bool has_child(void) const
+        {
+            return this->left || this->right;
+        }
+
+        constexpr bool is_leaf(void) const
+        {
+            return !this->has_child();
+        }
+
+        constexpr bool is_left_child(void) const
+        {
+            return this == this->parent->left;
+        }
+
+        constexpr bool is_right_child(void) const
+        {
+            return this == this->parent->right;
+        }
+
+        constexpr void tored(void)
+        {
+            this->color = node_color::RED;
+        }
+        constexpr void toblack(void)
+        {
+            this->color = node_color::BLACK;
+        }
+
+        static constexpr void swap(node* first, node* second)
+        {
+            node* p = first->parent;
+            node* cl = first->left;
+            node* cr = first->right;
+
+            if (node::is_red(first)) {
+                first->color = second->color;
+                second->color = node_color::RED;
+            } else {
+                first->color = second->color;
+                second->color = node_color::BLACK;
+            }
+
+            if (!first->is_root()) {
+                if (first->is_left_child())
+                    p->left = second;
+                else
+                    p->right = second;
+            }
+            if (cl)
+                cl->parent = second;
+            if (cr)
+                cr->parent = second;
+
+            first->parent = second->parent;
+            first->left = second->left;
+            first->right = second->right;
+
+            if (!second->is_root()) {
+                if (second->is_left_child())
+                    second->parent->left = first;
+                else
+                    second->parent->right = first;
+            }
+            if (second->left)
+                second->left->parent = first;
+            if (second->right)
+                second->right->parent = first;
+
+            second->parent = p;
+            second->left = cl;
+            second->right = cr;
+        }
+    };
+
+    using allocator_type = _Allocator<node>;
+
+    template <bool Const>
+    class iterator {
+    private:
+        static constexpr bool _is_const_iterator = Const;
+
+    public:
+        using node_pointer_type = typename traits::condition<_is_const_iterator, const node*, node*>::type;
+        using value_type = typename traits::condition<_is_const_iterator, const pair_type, pair_type>::type;
+        using pointer_type = typename traits::add_pointer<value_type>::type;
+        using reference_type = typename traits::add_reference<value_type>::type;
+
+        friend class map;
+
+    private:
+        node_pointer_type p;
+
+    public:
+        explicit constexpr iterator(node_pointer_type ptr)
+            : p { ptr }
+        {
+        }
+
+        constexpr iterator(const iterator& iter)
+            : p { iter.p }
+        {
+        }
+
+        constexpr iterator(iterator&& iter)
+            : p { iter.p }
+        {
+            iter.p = nullptr;
+        }
+
+        constexpr ~iterator()
+        {
+#ifndef NDEBUG
+            p = nullptr;
+#endif
+        }
+
+        constexpr iterator& operator=(const iterator& iter)
+        {
+            p = iter.p;
+        }
+
+        constexpr iterator& operator=(iterator&& iter)
+        {
+            p = iter.p;
+            iter.p = nullptr;
+        }
+
+        constexpr bool operator==(const iterator& iter) const
+        {
+            return p == iter.p;
+        }
+
+        constexpr bool operator!=(const iterator& iter) const
+        {
+            return !this->operator==(iter);
+        }
+
+        constexpr reference_type operator*(void) const
+        {
+            return p->v;
+        }
+
+        constexpr pointer_type operator&(void) const
+        {
+            return &p->v;
+        }
+
+        constexpr pointer_type operator->(void) const
+        {
+            return this->operator&();
+        }
+
+        constexpr iterator& operator++(void)
+        {
+            p = p->next();
+            return *this;
+        }
+
+        constexpr iterator operator++(int)
+        {
+            iterator ret(p);
+
+            (void)this->operator++();
+
+            return ret;
+        }
+
+        constexpr iterator& operator--(void)
+        {
+            p = p->prev();
+            return *this;
+        }
+
+        constexpr iterator operator--(int)
+        {
+            iterator ret(p);
+
+            (void)this->operator--();
+
+            return ret;
+        }
+
+        constexpr operator bool(void)
+        {
+            return p;
+        }
+    };
+
+    using iterator_type = iterator<false>;
+    using const_iterator_type = iterator<true>;
+
+private:
+    node* root = nullptr;
+
+private:
+    static constexpr node* newnode(node* parent, const pair_type& val)
+    {
+        auto* ptr = allocator_traits<allocator_type>::allocate_and_construct(val);
+        ptr->parent = parent;
+        return ptr;
+    }
+    static constexpr node* newnode(node* parent, pair_type&& val)
+    {
+        auto* ptr = allocator_traits<allocator_type>::allocate_and_construct(move(val));
+        ptr->parent = parent;
+        return ptr;
+    }
+    static constexpr void delnode(node* nd)
+    {
+        allocator_traits<allocator_type>::deconstruct_and_deallocate(nd);
+    }
+
+    constexpr void rotateleft(node* rt)
+    {
+        node* nrt = rt->right;
+
+        if (!rt->is_root()) {
+            if (rt->is_left_child()) {
+                rt->parent->left = nrt;
+            } else {
+                rt->parent->right = nrt;
+            }
+        } else {
+            this->root = nrt;
+        }
+
+        nrt->parent = rt->parent;
+        rt->parent = nrt;
+
+        rt->right = nrt->left;
+        nrt->left = rt;
+    }
+
+    constexpr void rotateright(node* rt)
+    {
+        node* nrt = rt->left;
+
+        if (!rt->is_root()) {
+            if (rt->is_left_child()) {
+                rt->parent->left = nrt;
+            } else {
+                rt->parent->right = nrt;
+            }
+        } else {
+            this->root = nrt;
+        }
+
+        nrt->parent = rt->parent;
+        rt->parent = nrt;
+
+        rt->left = nrt->right;
+        nrt->right = rt;
+    }
+
+    constexpr void balance(node* nd)
+    {
+        if (nd->is_root()) {
+            nd->toblack();
+            return;
+        }
+
+        if (node::is_black(nd->parent))
+            return;
+
+        node* p = nd->parent;
+        node* pp = nd->grandparent();
+        node* uncle = nd->uncle();
+
+        if (node::is_red(uncle)) {
+            p->toblack();
+            uncle->toblack();
+            pp->tored();
+            this->balance(pp);
+            return;
+        }
+
+        if (p->is_left_child()) {
+            if (nd->is_left_child()) {
+                p->toblack();
+                pp->tored();
+                this->rotateright(pp);
+            } else {
+                this->rotateleft(p);
+                this->balance(p);
+            }
+        } else {
+            if (nd->is_right_child()) {
+                p->toblack();
+                pp->tored();
+                this->rotateleft(pp);
+            } else {
+                this->rotateright(p);
+                this->balance(p);
+            }
+        }
+    }
+
+    constexpr node* _find(const key_type& key) const
+    {
+        node* cur = root;
+
+        for (; cur;) {
+            if (cur->v.key == key)
+                return cur;
+
+            if (key < cur->v.key)
+                cur = cur->left;
+            else
+                cur = cur->right;
+        }
+
+        return nullptr;
+    }
+
+    // this function DOES NOT dellocate the node
+    // caller is responsible for freeing the memory
+    // @param: nd is guaranteed to be a leaf node
+    constexpr void _erase(node* nd)
+    {
+        if (node::is_black(nd)) {
+            node* p = nd->parent;
+            node* s = nullptr;
+            if (nd->is_left_child())
+                s = p->right;
+            else
+                s = p->left;
+
+            if (node::is_red(s)) {
+                p->tored();
+                s->toblack();
+                if (nd->is_right_child()) {
+                    this->rotateright(p);
+                    s = p->left;
+                } else {
+                    this->rotateleft(p);
+                    s = p->right;
+                }
+            }
+
+            node* r = nullptr;
+            if (node::is_red(s->left)) {
+                r = s->left;
+                if (s->is_left_child()) {
+                    r->toblack();
+                    s->color = p->color;
+                    this->rotateright(p);
+                    p->toblack();
+                } else {
+                    r->color = p->color;
+                    this->rotateright(s);
+                    this->rotateleft(p);
+                    p->toblack();
+                }
+            } else if (node::is_red(s->right)) {
+                r = s->right;
+                if (s->is_left_child()) {
+                    r->color = p->color;
+                    this->rotateleft(s);
+                    this->rotateright(p);
+                    p->toblack();
+                } else {
+                    r->toblack();
+                    s->color = p->color;
+                    this->rotateleft(p);
+                    p->toblack();
+                }
+            } else {
+                s->tored();
+                this->_erase(p);
+            }
+        }
+    }
+
+public:
+    constexpr iterator_type end(void)
+    {
+        return iterator_type(nullptr);
+    }
+    constexpr const_iterator_type end(void) const
+    {
+        return const_iterator_type(nullptr);
+    }
+    constexpr const_iterator_type cend(void) const
+    {
+        return const_iterator_type(nullptr);
+    }
+
+    constexpr iterator_type begin(void)
+    {
+        return root ? iterator_type(root->leftmost()) : end();
+    }
+    constexpr const_iterator_type begin(void) const
+    {
+        return root ? const_iterator_type(root->leftmost()) : end();
+    }
+    constexpr const_iterator_type cbegin(void) const
+    {
+        return root ? const_iterator_type(root->leftmost()) : end();
+    }
+
+    constexpr iterator_type find(const key_type& key)
+    {
+        return iterator_type(_find(key));
+    }
+    constexpr const_iterator_type find(const key_type& key) const
+    {
+        return const_iterator_type(_find(key));
+    }
+
+    constexpr iterator_type insert(pair_type&& val)
+    {
+        node* cur = root;
+
+        while (likely(cur)) {
+            if (val.key < cur->v.key) {
+                if (!cur->left) {
+                    node* nd = newnode(cur, move(val));
+                    cur->left = nd;
+                    this->balance(nd);
+                    return iterator_type(nd);
+                } else {
+                    cur = cur->left;
+                }
+            } else {
+                if (!cur->right) {
+                    node* nd = newnode(cur, move(val));
+                    cur->right = nd;
+                    this->balance(nd);
+                    return iterator_type(nd);
+                } else {
+                    cur = cur->right;
+                }
+            }
+        }
+
+        root = newnode(nullptr, move(val));
+        root->toblack();
+        return iterator_type(root);
+    }
+
+    constexpr iterator_type erase(const iterator_type& iter)
+    {
+        node* nd = iter.p;
+        if (!nd)
+            return end();
+
+        if (nd->is_root() && nd->is_leaf()) {
+            delnode(nd);
+            root = nullptr;
+            return end();
+        }
+
+        node* next = nd->next();
+
+        while (!nd->is_leaf()) {
+            if (nd->is_root())
+                this->root = nd->right->leftmost();
+            node::swap(nd, nd->right->leftmost());
+        }
+
+        this->_erase(nd);
+
+        if (nd->is_left_child())
+            nd->parent->left = nullptr;
+        else
+            nd->parent->right = nullptr;
+
+        delnode(nd);
+
+        return iterator_type(next);
+    }
+
+    constexpr void remove(const key_type& key)
+    {
+        auto iter = this->find(key);
+        if (iter != this->end())
+            this->erase(iter);
+    }
+
+    // destroy a subtree without adjusting nodes to maintain binary tree properties
+    constexpr void destroy(node* nd)
+    {
+        if (nd) {
+            this->destroy(nd->left);
+            this->destroy(nd->right);
+            delnode(nd);
+        }
+    }
+
+    explicit constexpr map(void)
+    {
+    }
+    constexpr map(const map& val)
+    {
+        for (const auto& item : val)
+            this->insert(item);
+    }
+    constexpr map(map&& val)
+        : root(val.root)
+    {
+        val.root = nullptr;
+    }
+    constexpr map& operator=(const map& val)
+    {
+        this->destroy(root);
+        for (const auto& item : val)
+            this->insert(item);
+    }
+    constexpr map& operator=(map&& val)
+    {
+        this->destroy(root);
+        root = val.root;
+        val.root = nullptr;
+    }
+    constexpr ~map()
+    {
+        this->destroy(root);
+    }
+};
+
+} // namespace types

+ 92 - 0
include/types/pair.hpp

@@ -0,0 +1,92 @@
+#pragma once
+
+#include <types/cplusplus.hpp>
+
+namespace types {
+
+template <typename Key, typename Value>
+struct pair {
+    using key_type = Key;
+    using value_type = Value;
+
+    key_type key;
+    value_type value;
+
+    constexpr pair(void) = delete;
+    constexpr ~pair()
+    {
+    }
+
+    template <typename _key_type, typename _value_type>
+    constexpr pair(_key_type&& _key, _value_type&& _value)
+        : key(forward<_key_type>(_key))
+        , value(forward<_value_type>(_value))
+    {
+    }
+
+    template <typename _key_type, typename _value_type>
+    constexpr pair(const pair<_key_type, _value_type>& val)
+        : key(val.key)
+        , value(val.value)
+    {
+        static_assert(is_same<typename traits::decay<_key_type>::type, typename traits::decay<key_type>::type>::value);
+        static_assert(is_same<typename traits::decay<_value_type>::type, typename traits::decay<value_type>::type>::value);
+    }
+    template <typename _key_type, typename _value_type>
+    constexpr pair(pair<_key_type, _value_type>&& val)
+        : key(move(val.key))
+        , value(move(val.value))
+    {
+        static_assert(is_same<typename traits::decay<_key_type>::type, typename traits::decay<key_type>::type>::value);
+        static_assert(is_same<typename traits::decay<_value_type>::type, typename traits::decay<value_type>::type>::value);
+    }
+    constexpr pair(const pair& val)
+        : key(val.key)
+        , value(val.value)
+    {
+    }
+    constexpr pair(pair&& val)
+        : key(move(val.key))
+        , value(move(val.value))
+    {
+    }
+    constexpr pair& operator=(const pair& val)
+    {
+        key = val.key;
+        value = val.vaule;
+    }
+    constexpr pair& operator=(pair&& val)
+    {
+        key = move(val.key);
+        value = move(val.value);
+    }
+
+    constexpr bool key_eq(const pair& p)
+    {
+        return key == p.key;
+    }
+
+    constexpr bool value_eq(const pair& p)
+    {
+        return value == p.value;
+    }
+
+    constexpr bool operator==(const pair& p)
+    {
+        return key_eq(p) && value_eq(p);
+    }
+
+    constexpr bool operator!=(const pair& p)
+    {
+        return !this->operator==(p);
+    }
+};
+
+template <typename T1, typename T2>
+constexpr pair<typename traits::decay<T1>::type, typename traits::decay<T2>::type>
+make_pair(T1&& t1, T2&& t2)
+{
+    return pair<typename traits::decay<T1>::type, typename traits::decay<T2>::type> { forward<T1>(t1), forward<T2>(t2) };
+}
+
+} // namespace types

+ 1 - 5
include/types/string.hpp

@@ -1,12 +1,10 @@
 #pragma once
 
-#include <kernel/stdio.h>
+#include <kernel/stdio.hpp>
 #include <types/allocator.hpp>
 #include <types/types.h>
 #include <types/vector.hpp>
 
-#ifdef __cplusplus
-
 namespace types {
 template <template <typename _value_type> class Allocator = kernel_allocator>
 class string : public types::vector<char, Allocator> {
@@ -114,5 +112,3 @@ public:
     }
 };
 } // namespace types
-
-#endif

+ 0 - 1
include/types/types.h

@@ -1,7 +1,6 @@
 #pragma once
 
 #include "bitmap.h"
-#include "buffer.h"
 #include "size.h"
 #include "status.h"
 #include "stdint.h"

+ 1 - 1
src/asm/interrupt.s

@@ -197,7 +197,7 @@ asm_load_idt_skip:
 .globl syscall_stub
 .type  syscall_stub @function
 syscall_stub:
-    cmpl $8, %eax
+    cmpl $16, %eax
     jge syscall_stub_end
     pushal
 

+ 86 - 36
src/fs/fat.cpp

@@ -1,6 +1,7 @@
 #include <fs/fat.hpp>
 #include <kernel/mem.h>
 #include <kernel/mm.hpp>
+#include <kernel/stdio.hpp>
 #include <kernel/vfs.hpp>
 #include <types/allocator.hpp>
 #include <types/assert.h>
@@ -10,7 +11,7 @@
 
 namespace fs::fat {
 // buf MUST be larger than 512 bytes
-inline void fat32::read_sector(void* buf, uint32_t sector_no)
+inline void fat32::_raw_read_sector(void* buf, uint32_t sector_no)
 {
     assert(vfs_read(
                device,
@@ -22,33 +23,71 @@ inline void fat32::read_sector(void* buf, uint32_t sector_no)
 }
 
 // buf MUST be larger than 4096 bytes
-inline void fat32::read_cluster(void* buf, cluster_t no)
+inline void fat32::_raw_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);
+        _raw_read_sector((char*)buf + SECTOR_SIZE * i, data_region_offset + no * sectors_per_cluster + i);
     }
 }
 
-int fat32::load_dentry(dentry* ent)
+char* fat32::read_cluster(cluster_t no)
 {
-    cluster_t next = cl(ent->ind);
-    auto buf = (char*)k_malloc(4096);
+    auto iter = buf.find(no);
+    if (iter) {
+        ++iter->value.ref;
+        return iter->value.data;
+    }
+    auto* data = (char*)k_malloc(sectors_per_cluster * SECTOR_SIZE);
+    _raw_read_cluster(data, no);
+    buf.emplace(no,
+        buf_object {
+            data,
+            1,
+            // false,
+        });
+    return data;
+}
+
+void fat32::release_cluster(cluster_t no)
+{
+    auto iter = buf.find(no);
+    if (iter)
+        --iter->value.ref;
+}
+
+int fat32::inode_readdir(fs::inode* dir, size_t offset, fs::vfs::filldir_func filldir)
+{
+    cluster_t next = cl(dir);
+    for (size_t i = 0; i < (offset / (sectors_per_cluster * SECTOR_SIZE)); ++i) {
+        if (next >= EOC)
+            return 0;
+        next = fat[next];
+    }
+    size_t nread = 0;
     do {
-        read_cluster(buf, next);
-        auto* d = reinterpret_cast<directory_entry*>(buf);
-        for (; d->filename[0]; ++d) {
+        char* buf = read_cluster(next);
+        auto* d = reinterpret_cast<directory_entry*>(buf) + (offset % (sectors_per_cluster * SECTOR_SIZE)) / sizeof(directory_entry);
+        offset = 0;
+        auto* end = d + (sectors_per_cluster * SECTOR_SIZE / sizeof(directory_entry));
+        for (; d < end && 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));
+
+            fs::ino_t ino = _rearrange(d);
+            auto* ind = get_inode(ino);
+            if (!ind) {
+                ind = cache_inode({ .in {
+                                      .file = !d->attributes.subdir,
+                                      .directory = d->attributes.subdir,
+                                      .mount_point = 0,
+                                      .special_node = 0,
+                                  } },
+                    0777, d->size, ino);
+            }
+
             types::string<> fname;
             for (int i = 0; i < 8; ++i) {
                 if (d->filename[i] == ' ')
@@ -64,12 +103,20 @@ int fat32::load_dentry(dentry* ent)
                     break;
                 fname += d->extension[i];
             }
-            ent->append(ind, fname);
+            auto ret = filldir(fname.c_str(), 0, ind->ino,
+                (ind->flags.in.directory || ind->flags.in.mount_point) ? DT_DIR : DT_REG);
+
+            if (ret != GB_OK) {
+                release_cluster(next);
+                return nread;
+            }
+
+            nread += sizeof(directory_entry);
         }
+        release_cluster(next);
         next = fat[next];
     } while (next < EOC);
-    k_free(buf);
-    return GB_OK;
+    return nread;
 }
 
 fat32::fat32(inode* _device)
@@ -77,7 +124,7 @@ fat32::fat32(inode* _device)
     , label { 0 }
 {
     char* buf = (char*)k_malloc(SECTOR_SIZE);
-    read_sector(buf, 0);
+    _raw_read_sector(buf, 0);
 
     auto* info = reinterpret_cast<ext_boot_sector*>(buf);
 
@@ -93,7 +140,7 @@ fat32::fat32(inode* _device)
     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);
+        _raw_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);
 
@@ -104,7 +151,7 @@ fat32::fat32(inode* _device)
     }
     label[i] = 0x00;
 
-    read_sector(buf, info->fs_info_sector);
+    _raw_read_sector(buf, info->fs_info_sector);
 
     auto* fsinfo = reinterpret_cast<fs_info_sector*>(buf);
     free_clusters = fsinfo->free_clusters;
@@ -116,7 +163,11 @@ fat32::fat32(inode* _device)
     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);
+    auto* n = cache_inode(
+        { INODE_MNT | INODE_DIR },
+        0777,
+        _root_dir_clusters * sectors_per_cluster * SECTOR_SIZE,
+        root_dir);
     register_root_node(n);
 }
 
@@ -127,40 +178,43 @@ fat32::~fat32()
 
 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);
+    cluster_t next = cl(file);
     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);
+                auto* data = read_cluster(next);
+                memcpy(buf, data, cluster_size);
+                release_cluster(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);
+                auto* data = read_cluster(next);
+                auto read = _write_buf_n(buf, buf_size, data, n);
+                release_cluster(next);
+
                 return orig_n - n + read;
             }
         } else {
             if (offset > cluster_size) {
                 offset -= cluster_size;
             } else {
-                read_cluster(b, next);
+                auto* data = read_cluster(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);
+                auto read = _write_buf_n(buf, buf_size, data + offset, to_read);
                 buf += read;
                 n -= read;
 
+                release_cluster(next);
                 if (read != to_read) {
-                    k_free(b);
                     return orig_n - n;
                 }
 
@@ -170,7 +224,6 @@ size_t fat32::inode_read(inode* file, char* buf, size_t buf_size, size_t offset,
         next = fat[next];
     } while (n && next < EOC);
 
-    k_free(b);
     return orig_n - n;
 }
 
@@ -180,9 +233,6 @@ int fat32::inode_stat(dentry* ent, stat* st)
     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

+ 3 - 4
src/kernel/event/event.cpp

@@ -3,8 +3,7 @@
 #include <kernel/event/evtqueue.hpp>
 #include <kernel/input/input_event.h>
 #include <kernel/process.hpp>
-#include <kernel/stdio.h>
-#include <kernel/tty.h>
+#include <kernel/stdio.hpp>
 #include <types/allocator.hpp>
 #include <types/assert.h>
 #include <types/cplusplus.hpp>
@@ -17,7 +16,7 @@ namespace event {
 ::types::list<::input_event>& input_event_queue(void)
 {
     if (!_input_event_queue) {
-        _input_event_queue = types::kernel_allocator_new<types::list<input_event>>();
+        _input_event_queue = types::pnew<types::kernel_allocator>(_input_event_queue);
     }
     return *_input_event_queue;
 }
@@ -40,7 +39,7 @@ void dispatch_event(void)
         for (auto iter = input_event_queue.begin(); iter != input_event_queue.end(); ++iter) {
             const auto& item = *iter;
             snprintf(buf, 1024, "\rinput event: type%x, data%x, code%x\r", item.type, item.data, item.code);
-            tty_print(console, buf);
+            kmsg(buf);
             input_event_queue.erase(iter);
         }
     }

+ 5 - 5
src/kernel/hw/ata.cpp

@@ -1,9 +1,8 @@
 #include <asm/port_io.h>
 #include <fs/fat.hpp>
 #include <kernel/hw/ata.hpp>
-#include <kernel/stdio.h>
+#include <kernel/stdio.hpp>
 #include <kernel/syscall.hpp>
-#include <kernel/tty.h>
 #include <kernel/vfs.hpp>
 #include <types/allocator.hpp>
 #include <types/assert.h>
@@ -180,7 +179,8 @@ struct PACKED mbr {
 
 static inline void mbr_part_probe(fs::inode* drive, uint16_t major, uint16_t minor)
 {
-    struct mbr hda_mbr { };
+    struct mbr hda_mbr {
+    };
     auto* dev = fs::vfs_open("/dev");
 
     fs::vfs_read(drive, (char*)&hda_mbr, 512, 0, 512);
@@ -201,11 +201,11 @@ static inline void mbr_part_probe(fs::inode* drive, uint16_t major, uint16_t min
 // data: void (*func_to_call_next)(void)
 void hw::init_ata(void)
 {
-    ata_pri = types::kernel_allocator_new<ata>(ATA_PRIMARY_BUS_BASE);
+    ata_pri = types::pnew<types::kernel_allocator>(ata_pri, ATA_PRIMARY_BUS_BASE);
     if (ata_pri->identify())
         ata_pri->select(true);
 
-    ata_sec = types::kernel_allocator_new<ata>(ATA_SECONDARY_BUS_BASE);
+    ata_sec = types::pnew<types::kernel_allocator>(ata_pri, ATA_SECONDARY_BUS_BASE);
     if (ata_pri->identify())
         ata_pri->select(true);
 

+ 4 - 27
src/kernel/hw/serial.c → src/kernel/hw/serial.cpp

@@ -1,8 +1,8 @@
-#include <types/status.h>
 #include <asm/port_io.h>
 #include <kernel/hw/serial.h>
-#include <kernel/stdio.h>
-#include <kernel/tty.h>
+#include <kernel/stdio.hpp>
+#include <kernel/tty.hpp>
+#include <types/status.h>
 
 int32_t init_serial_port(port_id_t port)
 {
@@ -61,29 +61,6 @@ void serial_receive_data_interrupt(void)
 {
     while (is_serial_has_data(PORT_SERIAL0)) {
         uint8_t data = serial_read_data(PORT_SERIAL0);
-        char buf[64] = { 0 };
-        switch (data) {
-        case '\r':
-            serial_send_data(PORT_SERIAL0, '\r');
-            serial_send_data(PORT_SERIAL0, '\n');
-            break;
-        // ^?
-        case 0x7f:
-            serial_send_data(PORT_SERIAL0, 0x08);
-            serial_send_data(PORT_SERIAL0, '\x1b');
-            serial_send_data(PORT_SERIAL0, '[');
-            serial_send_data(PORT_SERIAL0, 'K');
-            break;
-        // ^U
-        case 0x15:
-            serial_send_data(PORT_SERIAL0, '\r');
-            serial_send_data(PORT_SERIAL0, '\x1b');
-            serial_send_data(PORT_SERIAL0, '[');
-            serial_send_data(PORT_SERIAL0, '2');
-            serial_send_data(PORT_SERIAL0, 'K');
-        default:
-            serial_send_data(PORT_SERIAL0, data);
-            break;
-        }
+        console->recvchar(data);
     }
 }

+ 0 - 1
src/kernel/hw/timer.c

@@ -1,5 +1,4 @@
 #include <asm/port_io.h>
-#include <kernel/tty.h>
 #include <kernel/hw/timer.h>
 
 static time_t _current_ticks = 0;

+ 11 - 11
src/kernel/interrupt.cpp

@@ -8,12 +8,11 @@
 #include <kernel/mem.h>
 #include <kernel/mm.hpp>
 #include <kernel/process.hpp>
-#include <kernel/stdio.h>
+#include <kernel/stdio.hpp>
 #include <kernel/syscall.hpp>
-#include <kernel/tty.h>
 #include <kernel/vfs.hpp>
-#include <kernel/vga.h>
-#include <kernel_main.h>
+#include <kernel/vga.hpp>
+#include <kernel_main.hpp>
 #include <types/assert.h>
 #include <types/size.h>
 #include <types/stdint.h>
@@ -91,7 +90,7 @@ extern "C" void int6_handler(
 {
     char buf[512];
 
-    tty_print(console, "\n---- INVALID OPCODE ----\n");
+    kmsg("\n---- INVALID OPCODE ----\n");
 
     snprintf(
         buf, 512,
@@ -102,9 +101,9 @@ extern "C" void int6_handler(
         s_regs.edx, s_regs.esp, s_regs.ebp,
         s_regs.esi, s_regs.edi, eip,
         cs, eflags);
-    tty_print(console, buf);
+    kmsg(buf);
 
-    tty_print(console, "----   HALTING SYSTEM   ----\n");
+    kmsg("----   HALTING SYSTEM   ----\n");
 
     asm_cli();
     asm_hlt();
@@ -120,7 +119,7 @@ extern "C" void int13_handler(
 {
     char buf[512];
 
-    tty_print(console, "\n---- SEGMENTATION FAULT ----\n");
+    kmsg("\n---- SEGMENTATION FAULT ----\n");
 
     snprintf(
         buf, 512,
@@ -132,9 +131,9 @@ extern "C" void int13_handler(
         s_regs.edx, s_regs.esp, s_regs.ebp,
         s_regs.esi, s_regs.edi, eip,
         cs, error_code, eflags);
-    tty_print(console, buf);
+    kmsg(buf);
 
-    tty_print(console, "----   HALTING SYSTEM   ----\n");
+    kmsg("----   HALTING SYSTEM   ----\n");
 
     asm_cli();
     asm_hlt();
@@ -155,7 +154,7 @@ static inline void _int14_panic(void* eip, void* cr2, struct page_fault_error_co
     snprintf(
         buf, 256,
         "\nkilled: segmentation fault (eip: %x, cr2: %x, error_code: %x)\n", eip, cr2, error_code);
-    tty_print(console, buf);
+    kmsg(buf);
     assert(false);
 }
 
@@ -238,6 +237,7 @@ extern "C" void irq3_handler(void)
 }
 extern "C" void irq4_handler(void)
 {
+    // TODO: register interrupt handler in serial port driver
     serial_receive_data_interrupt();
     asm_outb(PORT_PIC1_COMMAND, PIC_EOI);
 }

+ 7 - 7
src/kernel/mem.cpp

@@ -5,10 +5,10 @@
 #include <kernel/mem.h>
 #include <kernel/mm.hpp>
 #include <kernel/process.hpp>
-#include <kernel/stdio.h>
+#include <kernel/stdio.hpp>
 #include <kernel/task.h>
-#include <kernel/vga.h>
-#include <kernel_main.h>
+#include <kernel/vga.hpp>
+#include <kernel_main.hpp>
 #include <types/allocator.hpp>
 #include <types/assert.h>
 #include <types/bitmap.h>
@@ -305,7 +305,7 @@ struct page allocate_page(void)
     return page {
         .phys_page_id = alloc_raw_page(),
         .pte = nullptr,
-        .ref_count = types::kernel_ident_allocator_new<size_t>(0),
+        .ref_count = types::_new<types::kernel_ident_allocator, size_t>(0),
         .attr { 0 },
     };
 }
@@ -523,13 +523,13 @@ void init_mem(void)
     // map the 16MiB-768MiB identically
     init_paging_map_low_mem_identically();
 
-    kernel_mms = types::kernel_ident_allocator_pnew(kernel_mms, KERNEL_PAGE_DIRECTORY_ADDR);
+    kernel_mms = types::pnew<types::kernel_ident_allocator>(kernel_mms, KERNEL_PAGE_DIRECTORY_ADDR);
     auto heap_mm = kernel_mms->addarea(KERNEL_HEAP_START, true, true);
 
     // create empty_page struct
     empty_page.attr.in.cow = 0;
     empty_page.phys_page_id = to_page(EMPTY_PAGE_ADDR);
-    empty_page.ref_count = types::kernel_ident_allocator_new<size_t>(1);
+    empty_page.ref_count = types::_new<types::kernel_ident_allocator, size_t>(1);
     empty_page.pte = to_pte(*KERNEL_PAGE_DIRECTORY_ADDR, empty_page.phys_page_id);
     empty_page.pte->in.rw = 0;
     invalidate_tlb(0x00000000);
@@ -538,7 +538,7 @@ void init_mem(void)
     while (heap_mm->pgs->size() < 256 * 1024 * 1024 / PAGE_SIZE)
         heap_mm->append_page(&empty_page, true, true, true, true);
 
-    kernel_heap_allocator = types::kernel_ident_allocator_pnew(kernel_heap_allocator,
+    kernel_heap_allocator = types::pnew<types::kernel_ident_allocator>(kernel_heap_allocator,
         KERNEL_HEAP_START, vptrdiff(KERNEL_HEAP_LIMIT, KERNEL_HEAP_START));
 }
 

+ 49 - 56
src/kernel/process.cpp

@@ -6,12 +6,12 @@
 #include <kernel/mem.h>
 #include <kernel/mm.hpp>
 #include <kernel/process.hpp>
-#include <kernel/stdio.h>
-#include <kernel/tty.h>
+#include <kernel/stdio.hpp>
 #include <kernel/vfs.hpp>
-#include <kernel_main.h>
+#include <kernel_main.hpp>
 #include <types/allocator.hpp>
 #include <types/assert.h>
+#include <types/cplusplus.hpp>
 #include <types/elf.hpp>
 #include <types/hash_map.hpp>
 #include <types/list.hpp>
@@ -46,74 +46,41 @@ struct no_irq_guard {
 
 process::process(process&& val)
     : mms(types::move(val.mms))
-    , thds(types::move(val.thds))
+    , thds { types::move(val.thds), this }
     , wait_lst(types::move(val.wait_lst))
+    , attr { val.attr }
     , pid(val.pid)
     , ppid(val.ppid)
+    , files(types::move(val.files))
 {
     if (current_process == &val)
         current_process = this;
 
-    attr.system = val.attr.system;
-
-    for (auto& item : thds)
-        item.owner = this;
-
+    val.pid = 0;
+    val.ppid = 0;
     val.attr.system = 0;
+    val.attr.zombie = 0;
 }
 
-process::process(const process& val, const thread& main_thd)
-    : mms(*kernel_mms)
-    , attr { .system = val.attr.system }
-    , pid { process::alloc_pid() }
-    , ppid { val.pid }
+process::process(const process& parent)
+    : process { parent.pid, parent.is_system() }
 {
-    auto* thd = &thds.emplace_back(main_thd);
-    thd->owner = this;
-
-    for (auto& area : val.mms) {
+    for (auto& area : parent.mms) {
         if (area.is_ident())
             continue;
 
         mms.mirror_area(area);
     }
 
-    readythds->push(thd);
+    this->files.dup(parent.files);
 }
 
-process::process(pid_t _ppid)
+process::process(pid_t _ppid, bool _system)
     : mms(*kernel_mms)
-    , attr { .system = 1 }
+    , attr { .system = _system }
     , pid { process::alloc_pid() }
     , ppid { _ppid }
 {
-    auto thd = thds.emplace_back(this, true);
-    readythds->push(&thd);
-}
-
-process::process(void (*func)(void), pid_t _ppid)
-    : process { _ppid }
-{
-    auto* esp = &thds.begin()->esp;
-
-    // return(start) address
-    push_stack(esp, (uint32_t)func);
-    // ebx
-    push_stack(esp, 0);
-    // edi
-    push_stack(esp, 0);
-    // esi
-    push_stack(esp, 0);
-    // ebp
-    push_stack(esp, 0);
-    // eflags
-    push_stack(esp, 0x200);
-}
-
-process::~process()
-{
-    for (auto iter = thds.begin(); iter != thds.end(); ++iter)
-        readythds->remove_all(&iter);
 }
 
 inline void NORETURN _noreturn_crash(void)
@@ -124,7 +91,7 @@ inline void NORETURN _noreturn_crash(void)
 
 void kernel_threadd_main(void)
 {
-    tty_print(console, "kernel thread daemon started\n");
+    kmsg("kernel thread daemon started\n");
 
     for (;;) {
         if (kthreadd_new_thd_func) {
@@ -161,14 +128,37 @@ void kernel_threadd_main(void)
 
 void NORETURN _kernel_init(void)
 {
-    procs->emplace(kernel_threadd_main, 1);
+    // pid 2 is kernel thread daemon
+    auto* proc = &procs->emplace(1)->value;
+
+    // create thread
+    thread thd(proc, true);
+
+    auto* esp = &thd.esp;
+
+    // return(start) address
+    push_stack(esp, (uint32_t)kernel_threadd_main);
+    // ebx
+    push_stack(esp, 0);
+    // edi
+    push_stack(esp, 0);
+    // esi
+    push_stack(esp, 0);
+    // ebp
+    push_stack(esp, 0);
+    // eflags
+    push_stack(esp, 0x200);
+
+    readythds->push(&proc->thds.Emplace(types::move(thd)));
+
+    // ------------------------------------------
 
     asm_sti();
 
     hw::init_ata();
 
     // TODO: parse kernel parameters
-    auto* _new_fs = fs::register_fs(types::kernel_allocator_new<fs::fat::fat32>(fs::vfs_open("/dev/hda1")->ind));
+    auto* _new_fs = fs::register_fs(types::_new<types::kernel_allocator, fs::fat::fat32>(fs::vfs_open("/dev/hda1")->ind));
     int ret = fs::fs_root->ind->fs->mount(fs::vfs_open("/mnt"), _new_fs);
     assert_likely(ret == GB_OK);
 
@@ -212,19 +202,22 @@ void k_new_thread(void (*func)(void*), void* data)
     kthreadd_new_thd_data = data;
 }
 
-void NORETURN init_scheduler()
+void NORETURN init_scheduler(void)
 {
-    procs = types::kernel_allocator_pnew(procs);
-    readythds = types::kernel_ident_allocator_pnew(readythds);
+    procs = types::pnew<types::kernel_allocator>(procs);
+    readythds = types::pnew<types::kernel_allocator>(readythds);
 
-    auto* init = &procs->emplace(1);
+    // init process has no parent
+    auto* init = &procs->emplace(0)->value;
+    init->files.open("/dev/console", 0);
 
     // we need interrupts enabled for cow mapping so now we disable it
     // in case timer interrupt mess things up
     asm_cli();
 
     current_process = init;
-    current_thread = &init->thds.begin();
+    current_thread = &init->thds.Emplace(init, true);
+    readythds->push(current_thread);
 
     tss.ss0 = KERNEL_DATA_SEGMENT;
     tss.esp0 = current_thread->kstack;

+ 16 - 7
src/kernel/stdio.c → src/kernel/stdio.cpp

@@ -1,4 +1,5 @@
-#include <kernel/stdio.h>
+#include <kernel/stdio.hpp>
+#include <kernel/tty.hpp>
 
 #include <types/size.h>
 #include <types/stdint.h>
@@ -46,12 +47,12 @@ int64_t do_div_s(int64_t a, int64_t b, uint64_t* remainder)
     return quotient;
 }
 
-int64_t __divdi3(int64_t a, int64_t b)
+extern "C" int64_t __divdi3(int64_t a, int64_t b)
 {
     return do_div_s(a, b, (uint64_t*)0);
 }
 
-int64_t __moddi3(int64_t a, int64_t b)
+extern "C" int64_t __moddi3(int64_t a, int64_t b)
 {
     uint64_t remainder = 0;
     do_div_s(a, b, &remainder);
@@ -301,7 +302,7 @@ snprintf(
     ...)
 {
     ssize_t n_write = 0;
-    void* arg_ptr = ((void*)&buf) + sizeof(char*) + sizeof(size_t) + sizeof(const char*);
+    uint8_t* arg_ptr = ((uint8_t*)&buf) + sizeof(char*) + sizeof(size_t) + sizeof(const char*);
 
     for (char c; (c = *fmt) != 0x00; ++fmt) {
         if (c == '%') {
@@ -413,9 +414,11 @@ snprintf(
 }
 
 #define BYTES_PER_MAX_COPY_UNIT (sizeof(uint32_t) / sizeof(uint8_t))
-void* memcpy(void* dst, const void* src, size_t n)
+void* memcpy(void* _dst, const void* _src, size_t n)
 {
-    void* orig_dst = dst;
+    void* orig_dst = _dst;
+    uint8_t* dst = (uint8_t*)_dst;
+    const uint8_t* src = (const uint8_t*)_src;
     for (size_t i = 0; i < n / BYTES_PER_MAX_COPY_UNIT; ++i) {
         *(uint32_t*)dst = *(uint32_t*)src;
         dst += BYTES_PER_MAX_COPY_UNIT;
@@ -427,8 +430,9 @@ void* memcpy(void* dst, const void* src, size_t n)
     return orig_dst;
 }
 
-void* memset(void* dst, int c, size_t n)
+void* memset(void* _dst, int c, size_t n)
 {
+    uint8_t* dst = (uint8_t*)_dst;
     c &= 0xff;
     int cc = (c + (c << 8) + (c << 16) + (c << 24));
     for (size_t i = 0; i < n / BYTES_PER_MAX_COPY_UNIT; ++i) {
@@ -472,3 +476,8 @@ int strcmp(const char* s1, const char* s2)
     }
     return c;
 }
+
+void kmsg(const char* msg)
+{
+    console->print(msg);
+}

+ 98 - 12
src/kernel/syscall.cpp

@@ -5,9 +5,10 @@
 #include <kernel/mem.h>
 #include <kernel/mm.hpp>
 #include <kernel/process.hpp>
+#include <kernel/stdio.hpp>
 #include <kernel/syscall.hpp>
-#include <kernel/tty.h>
-#include <kernel_main.h>
+#include <kernel/vfs.hpp>
+#include <kernel_main.hpp>
 #include <types/allocator.hpp>
 #include <types/assert.h>
 #include <types/elf.hpp>
@@ -24,7 +25,8 @@
     SYSCALL_SET_RETURN_VAL_EAX(_eax);      \
     SYSCALL_SET_RETURN_VAL_EDX(_edx)
 
-syscall_handler syscall_handlers[8];
+#define SYSCALL_HANDLERS_SIZE (16)
+syscall_handler syscall_handlers[SYSCALL_HANDLERS_SIZE];
 
 void _syscall_not_impl(interrupt_stack* data)
 {
@@ -34,8 +36,9 @@ void _syscall_not_impl(interrupt_stack* data)
 extern "C" void _syscall_stub_fork_return(void);
 void _syscall_fork(interrupt_stack* data)
 {
-    auto* newproc = &procs->emplace(*current_process, *current_thread);
-    thread* newthd = &newproc->thds.begin();
+    auto* newproc = &procs->emplace(*current_process)->value;
+    auto* newthd = &newproc->thds.Emplace(*current_thread, newproc);
+    readythds->push(newthd);
 
     // create fake interrupt stack
     push_stack(&newthd->esp, data->ss);
@@ -74,9 +77,37 @@ void _syscall_fork(interrupt_stack* data)
 
 void _syscall_write(interrupt_stack* data)
 {
-    tty_print(console, reinterpret_cast<const char*>(data->s_regs.edi));
+    int fd = data->s_regs.edi;
+    const char* buf = reinterpret_cast<const char*>(data->s_regs.esi);
+    size_t n = data->s_regs.edx;
 
-    SYSCALL_SET_RETURN_VAL(0, 0);
+    auto* file = current_process->files[fd];
+    if (file->type == fs::file::types::directory) {
+        SYSCALL_SET_RETURN_VAL(GB_FAILED, EINVAL);
+        return;
+    }
+
+    int n_wrote = fs::vfs_write(file->ind, buf, file->cursor, n);
+    file->cursor += n_wrote;
+    SYSCALL_SET_RETURN_VAL(n_wrote, 0);
+}
+
+void _syscall_read(interrupt_stack* data)
+{
+    int fd = data->s_regs.edi;
+    char* buf = reinterpret_cast<char*>(data->s_regs.esi);
+    size_t n = data->s_regs.edx;
+
+    auto* file = current_process->files[fd];
+    if (file->type == fs::file::types::directory) {
+        SYSCALL_SET_RETURN_VAL(GB_FAILED, EINVAL);
+        return;
+    }
+
+    // TODO: copy to user function !IMPORTANT
+    int n_wrote = fs::vfs_read(file->ind, buf, n, file->cursor, n);
+    file->cursor += n_wrote;
+    SYSCALL_SET_RETURN_VAL(n_wrote, 0);
 }
 
 void _syscall_sleep(interrupt_stack* data)
@@ -91,7 +122,7 @@ void _syscall_sleep(interrupt_stack* data)
 
 void _syscall_crash(interrupt_stack*)
 {
-    tty_print(console, "\nan error occurred while executing command\n");
+    kmsg("\nan error occurred while executing command\n");
     asm_cli();
     asm_hlt();
 }
@@ -131,9 +162,11 @@ void _syscall_exit(interrupt_stack* data)
 
     // terminating a whole process:
 
-    // remove this thread from ready list
-    current_thread->attr.ready = 0;
-    readythds->remove_all(current_thread);
+    // remove threads from ready list
+    for (auto& thd : current_process->thds.underlying_list()) {
+        thd.attr.ready = 0;
+        readythds->remove_all(&thd);
+    }
 
     // TODO: write back mmap'ped files and close them
 
@@ -201,6 +234,57 @@ void _syscall_wait(interrupt_stack* data)
     SYSCALL_SET_RETURN_VAL(pid, 0);
 }
 
+void _syscall_getdents(interrupt_stack* data)
+{
+    int fd = data->s_regs.edi;
+    auto* buf = (char*)(data->s_regs.esi);
+    size_t cnt = data->s_regs.edx;
+
+    auto* dir = current_process->files[fd];
+    if (dir->type != fs::file::types::directory) {
+        data->s_regs.eax = -1;
+        return;
+    }
+
+    size_t orig_cnt = cnt;
+    int nread = dir->ind->fs->inode_readdir(dir->ind, dir->cursor,
+        [&buf, &cnt](const char* fn, size_t len, fs::ino_t ino, uint8_t type) -> int {
+            if (!len)
+                len = strlen(fn);
+
+            size_t reclen = sizeof(fs::user_dirent) + 1 + len;
+            if (cnt < reclen)
+                return GB_FAILED;
+
+            auto* dirp = (fs::user_dirent*)buf;
+            dirp->d_ino = ino;
+            dirp->d_reclen = reclen;
+            // TODO: show offset
+            // dirp->d_off = 0;
+            // TODO: use copy_to_user
+            memcpy(dirp->d_name, fn, len);
+            buf[reclen - 2] = 0;
+            buf[reclen - 1] = type;
+
+            buf += reclen;
+            cnt -= reclen;
+            return GB_OK;
+        });
+
+    if (nread > 0)
+        dir->cursor += nread;
+
+    data->s_regs.eax = orig_cnt - cnt;
+}
+
+void _syscall_open(interrupt_stack* data)
+{
+    auto* path = (const char*)data->s_regs.edi;
+    // flags are ignored for now
+    uint32_t flags = data->s_regs.esi;
+    data->s_regs.eax = current_process->files.open(path, flags);
+}
+
 void init_syscall(void)
 {
     syscall_handlers[0] = _syscall_fork;
@@ -210,5 +294,7 @@ void init_syscall(void)
     syscall_handlers[4] = _syscall_exec;
     syscall_handlers[5] = _syscall_exit;
     syscall_handlers[6] = _syscall_wait;
-    syscall_handlers[7] = _syscall_not_impl;
+    syscall_handlers[7] = _syscall_read;
+    syscall_handlers[8] = _syscall_getdents;
+    syscall_handlers[9] = _syscall_open;
 }

+ 0 - 49
src/kernel/tty.c

@@ -1,49 +0,0 @@
-#include <asm/port_io.h>
-#include <kernel/hw/serial.h>
-#include <kernel/mem.h>
-#include <kernel/stdio.h>
-#include <kernel/tty.h>
-#include <kernel/vga.h>
-
-static void serial_tty_put_char(struct tty* p_tty, char c)
-{
-    serial_send_data(*(port_id_t*)&p_tty->data, c);
-}
-
-static void vga_tty_put_char(struct tty* _unused, char c)
-{
-    static struct vga_char vc = { .c = '\0', .color = VGA_CHAR_COLOR_WHITE };
-    vc.c = c;
-    vga_put_char(&vc);
-}
-
-static struct tty_operations serial_tty_ops = {
-    .put_char = serial_tty_put_char,
-};
-
-static struct tty_operations vga_tty_ops = {
-    .put_char = vga_tty_put_char,
-};
-
-void tty_print(struct tty* p_tty, const char* str)
-{
-    while (*str != '\0') {
-        p_tty->ops->put_char(p_tty, *str);
-        ++str;
-    }
-}
-
-int make_serial_tty(struct tty* p_tty, int id)
-{
-    *(port_id_t*)&p_tty->data = id;
-    snprintf(p_tty->name, sizeof(p_tty->name), "ttyS%x", id);
-    p_tty->ops = &serial_tty_ops;
-    return GB_OK;
-}
-
-int make_vga_tty(struct tty* p_tty)
-{
-    snprintf(p_tty->name, sizeof(p_tty->name), "ttyVGA");
-    p_tty->ops = &vga_tty_ops;
-    return GB_OK;
-}

+ 114 - 0
src/kernel/tty.cpp

@@ -0,0 +1,114 @@
+#include <kernel/hw/serial.h>
+#include <kernel/process.hpp>
+#include <kernel/stdio.hpp>
+#include <kernel/tty.hpp>
+#include <kernel/vga.hpp>
+#include <types/stdint.h>
+
+tty::tty()
+    : buf(BUFFER_SIZE)
+{
+}
+
+void tty::print(const char* str)
+{
+    while (*str != '\0')
+        this->putchar(*(str++));
+}
+
+size_t tty::read(char* buf, size_t buf_size, size_t n)
+{
+    size_t orig_n = n;
+
+    while (buf_size && n) {
+        while (this->buf.empty()) {
+            current_thread->attr.ready = 0;
+            current_thread->attr.wait = 1;
+            this->blocklist.subscribe(current_thread);
+            schedule();
+            this->blocklist.unsubscribe(current_thread);
+        }
+
+        *buf = this->buf.get();
+        --buf_size;
+        --n;
+
+        if (*(buf++) == '\n')
+            break;
+    }
+
+    return orig_n - n;
+}
+
+vga_tty::vga_tty()
+{
+    snprintf(this->name, sizeof(this->name), "ttyVGA");
+}
+
+serial_tty::serial_tty(int id)
+    : id(id)
+{
+    snprintf(this->name, sizeof(this->name), "ttyS%x", (int)id);
+}
+
+void serial_tty::putchar(char c)
+{
+    serial_send_data(id, c);
+}
+
+void vga_tty::putchar(char c)
+{
+    static struct vga_char vc = { .c = '\0', .color = VGA_CHAR_COLOR_WHITE };
+    vc.c = c;
+    vga_put_char(&vc);
+}
+
+void vga_tty::recvchar(char c)
+{
+    // TODO: keyboard scan code
+    buf.put(c);
+}
+
+void serial_tty::recvchar(char c)
+{
+    switch (c) {
+    case '\r':
+        buf.put('\n');
+        if (echo) {
+            serial_send_data(PORT_SERIAL0, '\r');
+            serial_send_data(PORT_SERIAL0, '\n');
+        }
+        this->blocklist.notify();
+        break;
+    // ^?: backspace
+    case 0x7f:
+        if (!buf.empty() && buf.back() != '\n')
+            buf.pop();
+
+        if (echo) {
+            serial_send_data(PORT_SERIAL0, 0x08);
+            serial_send_data(PORT_SERIAL0, '\x1b');
+            serial_send_data(PORT_SERIAL0, '[');
+            serial_send_data(PORT_SERIAL0, 'K');
+        }
+        break;
+    // ^U: clear the line
+    case 0x15:
+        while (!buf.empty() && buf.back() != '\n')
+            buf.pop();
+
+        if (echo) {
+            serial_send_data(PORT_SERIAL0, '\r');
+            serial_send_data(PORT_SERIAL0, '\x1b');
+            serial_send_data(PORT_SERIAL0, '[');
+            serial_send_data(PORT_SERIAL0, '2');
+            serial_send_data(PORT_SERIAL0, 'K');
+        }
+        break;
+    default:
+        buf.put(c);
+        if (echo)
+            serial_send_data(PORT_SERIAL0, c);
+        break;
+    }
+}

+ 203 - 72
src/kernel/vfs.cpp

@@ -1,11 +1,13 @@
 #include <kernel/errno.h>
 #include <kernel/mem.h>
-#include <kernel/stdio.h>
-#include <kernel/tty.h>
+#include <kernel/stdio.hpp>
+#include <kernel/tty.hpp>
 #include <kernel/vfs.hpp>
 #include <types/allocator.hpp>
 #include <types/assert.h>
 #include <types/list.hpp>
+#include <types/map.hpp>
+#include <types/pair.hpp>
 #include <types/status.h>
 #include <types/stdint.h>
 #include <types/string.hpp>
@@ -27,6 +29,10 @@ fs::vfs::dentry::dentry(dentry* _parent, inode* _ind, const name_type& _name)
     , flags { 0 }
     , name(_name)
 {
+    if (!_ind || _ind->flags.in.directory) {
+        children = types::pnew<allocator_type>(children);
+        idx_children = types::pnew<allocator_type>(idx_children);
+    }
 }
 fs::vfs::dentry::dentry(dentry* _parent, inode* _ind, name_type&& _name)
     : parent(_parent)
@@ -34,28 +40,44 @@ fs::vfs::dentry::dentry(dentry* _parent, inode* _ind, name_type&& _name)
     , flags { 0 }
     , name(types::move(_name))
 {
+    if (!_ind || _ind->flags.in.directory) {
+        children = types::pnew<allocator_type>(children);
+        idx_children = types::pnew<allocator_type>(idx_children);
+    }
 }
 fs::vfs::dentry::dentry(dentry&& val)
-    : children(types::move(val.children))
-    , idx_children(types::move(val.idx_children))
+    : children(val.children)
+    , idx_children(val.idx_children)
     , parent(val.parent)
     , ind(val.ind)
     , flags { val.flags }
     , name(types::move(val.name))
 {
-    for (auto& item : children)
+    for (auto& item : *children)
         item.parent = this;
+    memset(&val, 0x00, sizeof(dentry));
+}
+fs::vfs::dentry::~dentry()
+{
+    if (children) {
+        types::pdelete<allocator_type>(children);
+        types::pdelete<allocator_type>(idx_children);
+    }
 }
-fs::vfs::dentry* fs::vfs::dentry::append(inode* ind, const name_type& name)
+fs::vfs::dentry* fs::vfs::dentry::append(inode* ind, const name_type& name, bool set_dirty)
 {
-    auto iter = children.emplace_back(this, ind, name);
-    idx_children.insert(iter->name, &iter);
+    auto iter = children->emplace_back(this, ind, name);
+    idx_children->emplace(iter->name, &iter);
+    if (set_dirty)
+        this->flags.in.dirty = 1;
     return &iter;
 }
-fs::vfs::dentry* fs::vfs::dentry::append(inode* ind, name_type&& name)
+fs::vfs::dentry* fs::vfs::dentry::append(inode* ind, name_type&& name, bool set_dirty)
 {
-    auto iter = children.emplace_back(this, ind, types::move(name));
-    idx_children.insert(iter->name, &iter);
+    auto iter = children->emplace_back(this, ind, types::move(name));
+    idx_children->emplace(iter->name, &iter);
+    if (set_dirty)
+        this->flags.in.dirty = 1;
     return &iter;
 }
 fs::vfs::dentry* fs::vfs::dentry::find(const name_type& name)
@@ -63,7 +85,7 @@ fs::vfs::dentry* fs::vfs::dentry::find(const name_type& name)
     if (ind->flags.in.directory && !flags.in.present)
         ind->fs->load_dentry(this);
 
-    auto iter = idx_children.find(name);
+    auto iter = idx_children->find(name);
     if (!iter) {
         errno = ENOTFOUND;
         return nullptr;
@@ -74,50 +96,66 @@ fs::vfs::dentry* fs::vfs::dentry::find(const name_type& name)
 fs::vfs::dentry* fs::vfs::dentry::replace(dentry* val)
 {
     // TODO: prevent the dirent to be swapped out of memory
-    parent->idx_children.find(this->name)->value = val;
+    parent->idx_children->find(this->name)->value = val;
     return this;
 }
 void fs::vfs::dentry::invalidate(void)
 {
     // TODO: write back
     flags.in.dirty = 0;
-    children.clear();
-    idx_children.clear();
+    children->clear();
+    idx_children->clear();
     flags.in.present = 0;
 }
 fs::vfs::vfs(void)
-    : _last_inode_no(0)
-    , _root(nullptr, nullptr, "/")
+    : _root(nullptr, nullptr, "/")
 {
 }
-fs::ino_t fs::vfs::_assign_inode_id(void)
+fs::inode* fs::vfs::cache_inode(inode_flags flags, uint32_t perm, size_t size, ino_t ino)
 {
-    return ++_last_inode_no;
-}
-fs::inode* fs::vfs::cache_inode(inode_flags flags, uint32_t perm, size_t size, void* impl_data)
-{
-    auto iter = _inodes.emplace_back(inode { flags, perm, impl_data, _assign_inode_id(), this, size });
-    _idx_inodes.insert(iter->ino, &iter);
-    return &iter;
+    auto iter = _inodes.insert(types::make_pair(ino, inode { flags, perm, ino, this, size }));
+    return &iter->value;
 }
 fs::inode* fs::vfs::get_inode(ino_t ino)
 {
-    auto iter = _idx_inodes.find(ino);
+    auto iter = _inodes.find(ino);
     // TODO: load inode from disk if not found
-    if (!iter)
-        return nullptr;
+    if (iter)
+        return &iter->value;
     else
-        return iter->value;
+        return nullptr;
 }
 void fs::vfs::register_root_node(inode* root)
 {
     if (!_root.ind)
         _root.ind = root;
 }
-int fs::vfs::load_dentry(dentry*)
+int fs::vfs::load_dentry(dentry* ent)
 {
-    assert(false);
-    return GB_FAILED;
+    auto* ind = ent->ind;
+
+    if (!ind->flags.in.directory) {
+        errno = ENOTDIR;
+        return GB_FAILED;
+    }
+
+    size_t offset = 0;
+
+    for (int ret = 1; ret > 0; offset += ret) {
+        ret = this->inode_readdir(ind, offset,
+            [&, this](const char* name, size_t len, ino_t ino, uint8_t) -> int {
+                if (!len)
+                    ent->append(get_inode(ino), name, false);
+                else
+                    ent->append(get_inode(ino), dentry::name_type(name, len), false);
+
+                return GB_OK;
+            });
+    }
+
+    ent->flags.in.present = 1;
+
+    return GB_OK;
 }
 int fs::vfs::mount(dentry* mnt, vfs* new_fs)
 {
@@ -132,7 +170,10 @@ int fs::vfs::mount(dentry* mnt, vfs* new_fs)
     new_ent->name = mnt->name;
 
     auto* orig_ent = mnt->replace(new_ent);
-    _mount_recover_list.insert(new_ent, orig_ent);
+    _mount_recover_list.emplace(new_ent, orig_ent);
+
+    new_ent->ind->flags.in.mount_point = 1;
+
     return GB_OK;
 }
 size_t fs::vfs::inode_read(inode*, char*, size_t, size_t, size_t)
@@ -170,50 +211,111 @@ int fs::vfs::inode_stat(dentry*, stat*)
     assert(false);
     return GB_FAILED;
 }
+uint32_t fs::vfs::inode_getnode(fs::inode*)
+{
+    assert(false);
+    return 0xffffffff;
+}
 
 class tmpfs : public virtual fs::vfs {
+private:
+    using fe_t = tmpfs_file_entry;
+    using vfe_t = vector<fe_t>;
+    using fdata_t = vector<char>;
+
+private:
+    fs::ino_t _next_ino;
+    types::map<fs::ino_t, void*> inode_data;
+
+private:
+    fs::ino_t _assign_ino(void)
+    {
+        return _next_ino++;
+    }
+
+    static constexpr vfe_t* as_vfe(void* data)
+    {
+        return static_cast<vfe_t*>(data);
+    }
+    static constexpr fdata_t* as_fdata(void* data)
+    {
+        return static_cast<fdata_t*>(data);
+    }
+    static inline ptr_t as_val(void* data)
+    {
+        return reinterpret_cast<ptr_t>(data);
+    }
+    inline void* _getdata(fs::ino_t ino) const
+    {
+        return inode_data.find(ino)->value;
+    }
+    inline fs::ino_t _savedata(void* data)
+    {
+        fs::ino_t ino = _assign_ino();
+        inode_data.insert(types::make_pair(ino, data));
+        return ino;
+    }
+    inline fs::ino_t _savedata(ptr_t data)
+    {
+        return _savedata((void*)data);
+    }
+
 protected:
-    inline vector<tmpfs_file_entry>* mk_fe_vector(void)
+    inline vfe_t* mk_fe_vector(void)
     {
-        return allocator_traits<kernel_allocator<vector<tmpfs_file_entry>>>::allocate_and_construct();
+        return allocator_traits<kernel_allocator<vfe_t>>::allocate_and_construct();
     }
 
-    inline vector<char>* mk_data_vector(void)
+    inline fdata_t* mk_data_vector(void)
     {
-        return allocator_traits<kernel_allocator<vector<char>>>::allocate_and_construct();
+        return allocator_traits<kernel_allocator<fdata_t>>::allocate_and_construct();
     }
 
     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 = {
+        auto* fes = as_vfe(_getdata(dir->ino));
+        auto iter = fes->emplace_back(fe_t {
             .ino = inode->ino,
-            .filename = { 0 },
-        };
-        snprintf(ent.filename, sizeof(ent.filename), filename);
-        fes->push_back(ent);
-        dir->size += sizeof(tmpfs_file_entry);
+            .filename = {} });
+        strncpy(iter->filename, filename, sizeof(iter->filename));
+        iter->filename[sizeof(iter->filename) - 1] = 0;
+        dir->size += sizeof(fe_t);
     }
 
-    virtual int load_dentry(dentry* ent) override
+    virtual int inode_readdir(fs::inode* dir, size_t offset, fs::vfs::filldir_func filldir) override
     {
-        if (!ent->ind->flags.in.directory) {
-            errno = ENOTDIR;
-            return GB_FAILED;
+        if (!dir->flags.in.directory) {
+            return -1;
         }
 
-        auto& entries = *static_cast<vector<tmpfs_file_entry>*>(ent->ind->impl);
-        for (const auto& entry : entries)
-            ent->append(get_inode(entry.ino), entry.filename);
+        auto& entries = *as_vfe(_getdata(dir->ino));
+        size_t off = offset / sizeof(fe_t);
 
-        ent->flags.in.present = 1;
-        return GB_OK;
+        size_t nread = 0;
+
+        for (; (off + 1) <= entries.size(); ++off, nread += sizeof(fe_t)) {
+            const auto& entry = entries[off];
+            auto* ind = get_inode(entry.ino);
+
+            auto type = DT_REG;
+            if (ind->flags.in.directory)
+                type = DT_DIR;
+            if (ind->flags.in.special_node)
+                type = DT_BLK;
+
+            auto ret = filldir(entry.filename, 0, entry.ino, type);
+            if (ret != GB_OK)
+                break;
+        }
+
+        return nread;
     }
 
 public:
     explicit tmpfs(void)
+        : _next_ino(1)
     {
-        auto& in = *cache_inode({ INODE_DIR | INODE_MNT }, 0777, 0, mk_fe_vector());
+        auto& in = *cache_inode({ INODE_DIR | INODE_MNT }, 0777, 0, _savedata(mk_fe_vector()));
 
         mklink(&in, &in, ".");
         mklink(&in, &in, "..");
@@ -223,29 +325,29 @@ public:
 
     virtual int inode_mkfile(dentry* dir, const char* filename) override
     {
-        auto& file = *cache_inode({ .v = INODE_FILE }, 0777, 0, mk_data_vector());
+        auto& file = *cache_inode({ .v = INODE_FILE }, 0777, 0, _savedata(mk_data_vector()));
         mklink(dir->ind, &file, filename);
-        dir->invalidate();
+        dir->append(get_inode(file.ino), filename, true);
         return GB_OK;
     }
 
     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);
+        auto& node = *cache_inode({ .v = INODE_NODE }, 0777, 0, _savedata(sn.v));
         mklink(dir->ind, &node, filename);
-        dir->invalidate();
+        dir->append(get_inode(node.ino), filename, true);
         return GB_OK;
     }
 
     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, ".");
+        auto new_dir = cache_inode({ .v = INODE_DIR }, 0777, 0, _savedata(mk_fe_vector()));
+        mklink(new_dir, new_dir, ".");
 
-        mklink(dir->ind, &new_dir, dirname);
-        mklink(&new_dir, dir->ind, "..");
+        mklink(dir->ind, new_dir, dirname);
+        mklink(new_dir, dir->ind, "..");
 
-        dir->invalidate();
+        dir->append(new_dir, dirname, true);
         return GB_OK;
     }
 
@@ -254,7 +356,7 @@ public:
         if (file->flags.in.file != 1)
             return 0;
 
-        auto* data = static_cast<vector<char>*>(file->impl);
+        auto* data = as_fdata(_getdata(file->ino));
         size_t fsize = data->size();
 
         if (offset + n > fsize)
@@ -274,7 +376,7 @@ public:
         if (file->flags.in.file != 1)
             return 0;
 
-        auto* data = static_cast<vector<char>*>(file->impl);
+        auto* data = as_fdata(_getdata(file->ino));
 
         for (size_t i = data->size(); i < offset + n; ++i) {
             data->push_back(0);
@@ -297,17 +399,22 @@ public:
         }
         if (file_inode->flags.in.directory) {
             stat->st_rdev.v = 0;
-            stat->st_blksize = sizeof(tmpfs_file_entry);
+            stat->st_blksize = sizeof(fe_t);
             stat->st_blocks = file_inode->size;
         }
         if (file_inode->flags.in.special_node) {
-            stat->st_rdev.v = (uint32_t)file_inode->impl;
+            stat->st_rdev.v = as_val(_getdata(file_inode->ino));
             stat->st_blksize = 0;
             stat->st_blocks = 0;
         }
 
         return GB_OK;
     }
+
+    virtual uint32_t inode_getnode(fs::inode* file) override
+    {
+        return as_val(_getdata(file->ino));
+    }
 };
 
 // 8 * 8 for now
@@ -316,8 +423,13 @@ 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->flags.in.special_node) {
+        uint32_t ret = file->fs->inode_getnode(file);
+        if (ret == SN_INVALID) {
+            errno = EINVAL;
+            return 0xffffffff;
+        }
         fs::node_t sn {
-            .v = (uint32_t)file->impl
+            .v = ret
         };
         auto* ptr = &sns[sn.in.major][sn.in.minor];
         auto* ops = &ptr->ops;
@@ -334,8 +446,13 @@ size_t fs::vfs_read(fs::inode* file, char* buf, size_t buf_size, size_t offset,
 size_t fs::vfs_write(fs::inode* file, const char* buf, size_t offset, size_t n)
 {
     if (file->flags.in.special_node) {
+        uint32_t ret = file->fs->inode_getnode(file);
+        if (ret == SN_INVALID) {
+            errno = EINVAL;
+            return 0xffffffff;
+        }
         fs::node_t sn {
-            .v = (uint32_t)file->impl
+            .v = ret
         };
         auto* ptr = &sns[sn.in.major][sn.in.minor];
         auto* ops = &ptr->ops;
@@ -420,7 +537,6 @@ int fs::vfs_stat(fs::vfs::dentry* ent, stat* stat)
     return ent->ind->fs->inode_stat(ent, stat);
 }
 
-fs::vfs::dentry* fs::fs_root;
 static types::list<fs::vfs*>* fs_es;
 
 void fs::register_special_block(
@@ -455,16 +571,31 @@ size_t b_null_write(fs::special_node*, const char*, size_t, size_t n)
 {
     return n;
 }
+static size_t console_read(fs::special_node*, char* buf, size_t buf_size, size_t, size_t n)
+{
+    return console->read(buf, buf_size, n);
+}
+static size_t console_write(fs::special_node*, const char* buf, size_t, size_t n)
+{
+    size_t orig_n = n;
+    while (n--)
+        console->putchar(*(buf++));
+
+    return orig_n;
+}
 
 void init_vfs(void)
 {
     using namespace fs;
     // null
     register_special_block(0, 0, b_null_read, b_null_write, 0, 0);
+    // console (supports serial console only for now)
+    // TODO: add interface to bind console device to other devices
+    register_special_block(1, 0, console_read, console_write, 0, 0);
 
-    fs_es = types::kernel_allocator_new<types::list<vfs*>>();
+    fs_es = types::pnew<types::kernel_ident_allocator>(fs_es);
 
-    auto* rootfs = types::kernel_allocator_new<tmpfs>();
+    auto* rootfs = types::_new<types::kernel_allocator, tmpfs>();
     fs_es->push_back(rootfs);
     fs_root = rootfs->root();
 

+ 2 - 2
src/kernel/vga.c → src/kernel/vga.cpp

@@ -1,8 +1,8 @@
 #define _KERNEL_VGA_C_
 #include <types/stdint.h>
 
-#include <kernel/stdio.h>
-#include <kernel/vga.h>
+#include <kernel/stdio.hpp>
+#include <kernel/vga.hpp>
 
 static struct vga_char* p_vga_head = VGA_MEM;
 

+ 47 - 41
src/kernel_main.c → src/kernel_main.cpp

@@ -1,4 +1,4 @@
-#include "kernel_main.h"
+#include "kernel_main.hpp"
 
 #include <asm/boot.h>
 #include <asm/port_io.h>
@@ -9,26 +9,22 @@
 #include <kernel/hw/timer.h>
 #include <kernel/interrupt.h>
 #include <kernel/mem.h>
-#include <kernel/stdio.h>
+#include <kernel/process.hpp>
+#include <kernel/stdio.hpp>
 #include <kernel/task.h>
-#include <kernel/tty.h>
-#include <kernel/vga.h>
+#include <kernel/tty.hpp>
+#include <kernel/vga.hpp>
 #include <types/assert.h>
 #include <types/bitmap.h>
 #include <types/status.h>
+#include <types/stdint.h>
 #include <types/types.h>
 
 #define KERNEL_MAIN_BUF_SIZE (128)
 
-struct tty* console = NULL;
 #define printkf(x...)                       \
     snprintf(buf, KERNEL_MAIN_BUF_SIZE, x); \
-    tty_print(console, buf)
-
-#define EVE_START(x) printkf(x "... ")
-#define INIT_START(x) EVE_START("initializing " x)
-#define INIT_OK() printkf("ok\n")
-#define INIT_FAILED() printkf("failed\n")
+    console->print(buf)
 
 typedef void (*constructor)(void);
 extern constructor start_ctors;
@@ -123,12 +119,32 @@ void init_bss_section(void)
     memset(bss_addr, 0x00, bss_size);
 }
 
-static struct tty early_console;
+int init_console(const char* name)
+{
+    if (name[0] == 't' && name[1] == 't' && name[2] == 'y') {
+        if (name[3] == 'S' || name[3] == 's') {
+            if (name[4] == '0') {
+                console = types::_new<types::kernel_ident_allocator, serial_tty>(PORT_SERIAL0);
+                return GB_OK;
+            }
+            if (name[4] == '1') {
+                console = types::_new<types::kernel_ident_allocator, serial_tty>(PORT_SERIAL1);
+                return GB_OK;
+            }
+        }
+        if (name[3] == 'V' && name[3] == 'G' && name[3] == 'A') {
+            console = types::_new<types::kernel_ident_allocator, vga_tty>();
+            return GB_OK;
+        }
+    }
+    crash();
+    return GB_FAILED;
+}
 
 extern void init_vfs();
-extern void NORETURN init_scheduler();
+extern "C" uint32_t check_a20_on(void);
 
-void NORETURN kernel_main(void)
+extern "C" void NORETURN kernel_main(void)
 {
     assert(check_a20_on());
 
@@ -140,49 +156,39 @@ void NORETURN kernel_main(void)
 
     load_new_gdt();
 
-    char buf[KERNEL_MAIN_BUF_SIZE] = { 0 };
-
-    assert(init_serial_port(PORT_SERIAL0) == GB_OK);
-    assert(make_serial_tty(&early_console, PORT_SERIAL0) == GB_OK);
-    console = &early_console;
-
-    show_mem_info(buf);
-
-    INIT_START("exception handlers");
-    init_idt();
-    INIT_OK();
-
     // NOTE:
     // the initializer of c++ global objects MUST NOT contain
     // all kinds of memory allocations
-    INIT_START("C++ global objects");
     call_constructors_for_cpp();
-    INIT_OK();
 
-    INIT_START("memory allocation");
-    init_mem();
-    INIT_OK();
+    char buf[KERNEL_MAIN_BUF_SIZE] = { 0 };
+
+    assert(init_serial_port(PORT_SERIAL0) == GB_OK);
 
-    INIT_START("programmable interrupt controller and timer");
+    init_idt();
+    init_mem();
     init_pic();
     init_pit();
-    INIT_OK();
 
-    printkf("Testing k_malloc...\n");
-    char* k_malloc_buf = (char*)k_malloc(sizeof(char) * 4097);
-    snprintf(k_malloc_buf, 4097, "This text is printed on the heap!\n");
-    tty_print(console, k_malloc_buf);
-    k_free(k_malloc_buf);
+    assert(init_console("ttyS0") == GB_OK);
+
+    show_mem_info(buf);
 
     init_vfs();
 
-    printkf("switching execution to the scheduler...\n");
-    init_scheduler(&tss);
+    kmsg("switching execution to the scheduler...\n");
+    init_scheduler();
 }
 
 void NORETURN __stack_chk_fail(void)
 {
-    tty_print(console, "***** stack smashing detected! *****\n");
+    kmsg("***** stack smashing detected! *****\n");
+    for (;;)
+        assert(0);
+}
+
+extern "C" void NORETURN __cxa_pure_virtual(void)
+{
     for (;;)
         assert(0);
 }

+ 0 - 77
src/types/buffer.c

@@ -1,77 +0,0 @@
-#include <types/buffer.h>
-#include <types/stdint.h>
-
-int32_t
-ring_buffer_empty(struct ring_buffer* buf)
-{
-    return (buf->count == 0);
-}
-
-int32_t
-ring_buffer_full(struct ring_buffer* buf)
-{
-    return (buf->count == (size_t)(buf->buf_end_pos - buf->buf_start_pos + 1));
-}
-
-static inline void
-ring_buffer_move_base_ptr_forward(struct ring_buffer* buf)
-{
-    if (buf->base == buf->buf_end_pos) {
-        buf->base = buf->buf_start_pos;
-    } else {
-        ++buf->base;
-    }
-}
-
-static inline void
-ring_buffer_move_head_ptr_forward(struct ring_buffer* buf)
-{
-    if (buf->head == buf->buf_end_pos) {
-        buf->head = buf->buf_start_pos;
-    } else {
-        ++buf->head;
-    }
-}
-
-static inline char
-ring_buffer_get_data(struct ring_buffer* buf)
-{
-    --buf->count;
-    return *buf->base;
-}
-
-static inline void
-ring_buffer_put_data(struct ring_buffer* buf, char c)
-{
-    *buf->head = c;
-    ++buf->count;
-}
-
-char ring_buffer_read(struct ring_buffer* buf)
-{
-    if (ring_buffer_empty(buf)) {
-        // TODO: set error flag
-        return 0xff;
-    }
-
-    char c = ring_buffer_get_data(buf);
-
-    ring_buffer_move_base_ptr_forward(buf);
-
-    return c;
-}
-
-int32_t
-ring_buffer_write(struct ring_buffer* buf, char c)
-{
-    if (ring_buffer_full(buf)) {
-        // TODO: set error flag
-        return 1;
-    }
-
-    ring_buffer_put_data(buf, c);
-
-    ring_buffer_move_head_ptr_forward(buf);
-
-    return 0;
-}

+ 1 - 1
src/types/elf.cpp

@@ -1,5 +1,5 @@
 #include <kernel/errno.h>
-#include <kernel/stdio.h>
+#include <kernel/stdio.hpp>
 #include <types/assert.h>
 #include <types/elf.hpp>
 #include <types/stdint.h>

+ 10 - 0
src/types/libstdcpp.cpp

@@ -0,0 +1,10 @@
+#include <types/assert.h>
+
+void operator delete(void*)
+{
+    crash();
+}
+void operator delete(void*, unsigned int)
+{
+    crash();
+}

+ 56 - 14
user-space-program/basic-lib.h

@@ -3,60 +3,102 @@ typedef __UINT16_TYPE__ uint16_t;
 typedef __UINT8_TYPE__ uint8_t;
 
 typedef uint32_t pid_t;
+typedef uint32_t size_t;
+typedef size_t ino_t;
 
 #define GNU_ATTRIBUTE(attr) __attribute__((attr))
 #define NORETURN GNU_ATTRIBUTE(noreturn)
 
-static inline uint32_t syscall(uint32_t num, uint32_t arg1, uint32_t arg2)
+#define O_RDONLY (0)
+#define O_DIRECTORY (0x4)
+
+// dirent file types
+#define DT_UNKNOWN 0
+#define DT_FIFO 1
+#define DT_CHR 2
+#define DT_DIR 4
+#define DT_BLK 6
+#define DT_REG 8
+#define DT_LNK 10
+#define DT_SOCK 12
+#define DT_WHT 14
+
+#define DT_MAX (S_DT_MASK + 1) /* 16 */
+
+static inline uint32_t syscall(uint32_t num, uint32_t arg1, uint32_t arg2, uint32_t arg3)
 {
     asm volatile(
         "movl %1, %%edi\n"
         "movl %2, %%esi\n"
-        "movl %3, %%eax\n"
+        "movl %3, %%edx\n"
+        "movl %4, %%eax\n"
         "int $0x80\n"
         "movl %%eax, %0"
         : "=g"(num)
-        : "g"(arg1), "g"(arg2), "g"(num)
+        : "g"(arg1), "g"(arg2), "g"(arg3), "g"(num)
         : "eax", "edx", "edi", "esi");
     return num;
 }
 
-static inline void NORETURN syscall_noreturn(uint32_t num, uint32_t arg1, uint32_t arg2)
+static inline void NORETURN syscall_noreturn(uint32_t num, uint32_t arg1, uint32_t arg2, uint32_t arg3)
 {
     asm volatile(
         "movl %1, %%edi\n"
         "movl %2, %%esi\n"
-        "movl %3, %%eax\n"
+        "movl %3, %%edx\n"
+        "movl %4, %%eax\n"
         "int $0x80\n"
         "movl %%eax, %0"
         : "=g"(num)
-        : "g"(arg1), "g"(arg2), "g"(num)
+        : "g"(arg1), "g"(arg2), "g"(arg3), "g"(num)
         : "eax", "edx", "edi", "esi");
     // crash
-    syscall_noreturn(0x05, 0, 0);
+    syscall_noreturn(0x05, 0, 0, 0);
 }
 
 static inline int fork(void)
 {
-    return syscall(0x00, 0, 0);
+    return syscall(0x00, 0, 0, 0);
 }
-static inline uint32_t write(const char* buf)
+static inline uint32_t write(int fd, const char* buf, size_t count)
 {
-    return syscall(0x01, (uint32_t)buf, 0);
+    return syscall(0x01, fd, (uint32_t)buf, count);
+}
+static inline uint32_t read(int fd, char* buf, size_t count)
+{
+    return syscall(0x07, fd, (uint32_t)buf, count);
 }
 static inline void sleep(void)
 {
-    syscall(0x02, 0, 0);
+    syscall(0x02, 0, 0, 0);
 }
 static inline void NORETURN exec(const char* bin, const char** argv)
 {
-    syscall_noreturn(0x04, (uint32_t)bin, (uint32_t)argv);
+    syscall_noreturn(0x04, (uint32_t)bin, (uint32_t)argv, 0);
 }
 static inline void NORETURN exit(int exit_code)
 {
-    syscall_noreturn(0x05, exit_code, 0);
+    syscall_noreturn(0x05, exit_code, 0, 0);
 }
 static inline uint32_t wait(int* return_value)
 {
-    return syscall(0x06, (uint32_t)return_value, 0);
+    return syscall(0x06, (uint32_t)return_value, 0, 0);
+}
+
+struct __attribute__((__packed__)) user_dirent {
+    ino_t d_ino; // inode number
+    uint32_t d_off; // ignored
+    uint16_t d_reclen; // length of this struct user_dirent
+    char d_name[1]; // file name with a padding zero
+    // uint8_t d_type; // file type, with offset of (d_reclen - 1)
+};
+
+static inline size_t getdents(int fd, struct user_dirent* buf, size_t cnt)
+{
+    return syscall(0x08, fd, (uint32_t)buf, cnt);
+}
+
+static inline int open(const char* path, uint32_t flags)
+{
+    return syscall(0x09, (uint32_t)path, flags, 0);
 }

+ 96 - 7
user-space-program/init.c

@@ -1,25 +1,114 @@
 #include "basic-lib.h"
 
+static inline char tc(int n) {
+    return '0' + n;
+}
+
+void print_int(int n) {
+    char buf[12];
+    int len = 0;
+
+    while (n) {
+        int dig = n % 10;
+        buf[len++] = tc(dig);
+        n /= 10;
+    }
+
+    write(0, buf, len);
+}
+
+size_t strlen(const char* str)
+{
+    const char* p = str;
+    while (*p)
+        ++p;
+    return p - str;
+}
+
+static inline void print(const char* str)
+{
+    write(0, str, strlen(str));
+}
+
 int main(int argc, char** argv)
 {
     for (int i = 0; i < argc; ++i)
-        write(argv[i]);
+        write(0, argv[i], 0);
 
-    const char* data = "Hello World from user space init\n";
-    write(data);
+    print("Hello World from user space init\n");
     int ret = fork();
     if (ret == 0) {
-        write("child\n");
+        print("child\n");
         exit(255);
     } else {
-        write("parent\n");
+        print("parent\n");
+    }
+
+    char buf[128] = {};
+
+    const char* path = "/dev";
+    print("content in ");
+    print(path);
+    print(":\n");
+
+    int dir = open(path, O_RDONLY | O_DIRECTORY);
+    if (dir >= 0) {
+        for (;;) {
+            int n = getdents(dir, (struct user_dirent*)buf, 128);
+            if (n < 0)
+                print("error\n");
+            if (n <= 0)
+                break;
+
+            int bpos = 0;
+            for (; bpos < n;) {
+                struct user_dirent* dirp = (struct user_dirent*)(buf + bpos);
+                print("ino: ");
+                print_int(dirp->d_ino);
+                print(", filename: \"");
+                print(dirp->d_name);
+                print("\", filetype: ");
+                switch (buf[bpos + dirp->d_reclen - 1]) {
+                    case DT_REG:
+                        print("regular file");
+                        break;
+                    case DT_DIR:
+                        print("directory");
+                        break;
+                    case DT_BLK:
+                        print("block device");
+                        break;
+                    default:
+                        print("unknown");
+                        break;
+                }
+                print("\n");
+                bpos += dirp->d_reclen;
+            }
+        }
+    }
+
+    for (;;) {
+        int n = read(0, buf, 128);
+        if (n)
+            write(0, buf, n);
+        else
+            print("fuck!\n");
+        
+        if (buf[0] == 'e' && buf[1] == 'x' && buf[2] == 'i' && buf[3] == 't') {
+            print("\nexited echo mode!\n");
+            break;
+        }
     }
 
     for (;;) {
         int ret;
         pid_t pid = wait(&ret);
-        (void)pid;
-        pid += 1000;
+        print("ret: ");
+        print_int(ret);
+        print(", pid: ");
+        print_int(pid);
+        print("\n");
     }
     return 0;
 }