Browse Source

Merge branch 'ata'

greatbridf 2 years ago
parent
commit
3d716d00d5
62 changed files with 4741 additions and 1116 deletions
  1. 4 0
      .gitignore
  2. 55 13
      CMakeLists.txt
  3. 17 9
      Makefile.src
  4. 119 0
      configure
  5. 3 3
      cross-compile.cmake
  6. 12 0
      doc/mem_layout.txt
  7. 1 1
      global_find.sh
  8. 3 0
      include/asm/boot.h
  9. 1 0
      include/asm/sys.h
  10. 162 0
      include/fs/fat.hpp
  11. 14 2
      include/kernel/errno.h
  12. 61 0
      include/kernel/hw/ata.hpp
  13. 67 0
      include/kernel/hw/port.hpp
  14. 10 0
      include/kernel/hw/serial.h
  15. 57 30
      include/kernel/interrupt.h
  16. 4 144
      include/kernel/mem.h
  17. 132 0
      include/kernel/mm.hpp
  18. 0 15
      include/kernel/process.h
  19. 71 0
      include/kernel/process.hpp
  20. 2 1
      include/kernel/stdio.h
  21. 11 0
      include/kernel/syscall.hpp
  22. 2 0
      include/kernel/task.h
  23. 186 0
      include/kernel/vfs.hpp
  24. 10 1
      include/kernel_main.h
  25. 109 0
      include/types/allocator.hpp
  26. 8 0
      include/types/bitmap.h
  27. 115 0
      include/types/cplusplus.hpp
  28. 284 0
      include/types/hash_map.hpp
  29. 0 9
      include/types/list.h
  30. 146 86
      include/types/list.hpp
  31. 35 0
      include/types/lock.h
  32. 6 0
      include/types/size.h
  33. 4 0
      include/types/stdint.h
  34. 118 0
      include/types/string.hpp
  35. 17 0
      include/types/types.h
  36. 363 0
      include/types/vector.hpp
  37. 115 3
      src/asm/interrupt.s
  38. 17 17
      src/asm/sys.s
  39. 9 64
      src/boot.s
  40. 190 0
      src/fs/fat.cpp
  41. 22 28
      src/kernel.ld
  42. 7 3
      src/kernel/event/event.cpp
  43. 232 0
      src/kernel/hw/ata.cpp
  44. 57 22
      src/kernel/hw/serial.c
  45. 110 89
      src/kernel/interrupt.cpp
  46. 0 545
      src/kernel/mem.c
  47. 637 0
      src/kernel/mem.cpp
  48. 0 0
      src/kernel/process.c
  49. 298 0
      src/kernel/process.cpp
  50. 25 1
      src/kernel/stdio.c
  51. 69 0
      src/kernel/syscall.cpp
  52. 2 2
      src/kernel/tty.c
  53. 488 0
      src/kernel/vfs.cpp
  54. 43 28
      src/kernel_main.c
  55. 61 0
      src/mbr.S
  56. 15 0
      src/mbr.ld
  57. 4 0
      user-space-program/.gitignore
  58. 30 0
      user-space-program/Makefile.src
  59. 8 0
      user-space-program/hello-world.s
  60. 26 0
      user-space-program/interrupt-test.s
  61. 20 0
      user-space-program/output_code.ld
  62. 47 0
      user-space-program/output_symbols.ld

+ 4 - 0
.gitignore

@@ -7,5 +7,9 @@ test/
 bx_enh_dbg.ini
 
 .cache/
+*.swp
 
 compile_commands.json
+
+Makefile
+cross-compile.cmake

+ 55 - 13
CMakeLists.txt

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

+ 17 - 9
Makefile → Makefile.src

@@ -1,10 +1,10 @@
 # disable kvm to debug triple faults
-GDB_BIN=gdb
-QEMU_BIN=qemu-system-i386
-QEMU_ACCELERATION_FLAG=-enable-kvm
-QEMU_DEBUG_FLAG=-d cpu_reset,int
-QEMU_ARGS=-drive file=build/boot.img,format=raw -no-reboot -no-shutdown $(QEMU_ACCELERATION_FLAG) #$(QEMU_DEBUG_FLAG)
-CROSS_COMPILE=#--toolchain cross-compile.cmake
+QEMU_BIN=##PLACEHOLDER_1##
+GDB_BIN=##PLACEHOLDER_2##
+QEMU_ACCELERATION_FLAG=##PLACEHOLDER_3##
+QEMU_DEBUG_FLAG=#-d cpu_reset,int
+QEMU_ARGS=-drive file=build/boot.img,format=raw -no-reboot -no-shutdown $(QEMU_ACCELERATION_FLAG) $(QEMU_DEBUG_FLAG)
+CROSS_COMPILE=##PLACEHOLDER_4##
 .PHONY: run
 run: build
 	$(QEMU_BIN) $(QEMU_ARGS) -display curses -S -s
@@ -15,20 +15,28 @@ srun: build
 nativerun: build
 	$(QEMU_BIN) $(QEMU_ARGS) -display none -serial mon:stdio
 
-.PHONY: configure
-configure:
+.PHONY: prepare
+prepare:
 	cmake -Bbuild -DCMAKE_BUILD_TYPE=Debug $(CROSS_COMPILE)
 	cp build/compile_commands.json .
 
+.PHONY: reprepare
+reprepare: clean prepare
+	true
+
 .PHONY: build
 build:
-	cmake --build build --target boot.img
+	cmake --build build -j 6 --target boot.img
 
 .PHONY: clean
 clean:
 	-rm -rf build
 	-rm compile_commands.json
 
+.PHONY: clean-all
+clean-all: clean
+	-rm Makefile
+
 .PHONY: debug
 debug:
 	$(GDB_BIN) --symbols=build/kernel.out --init-eval-command 'set pagination off' --init-eval-command 'target remote:1234' --eval-command 'hbr kernel_main' --eval-command 'c'

+ 119 - 0
configure

@@ -0,0 +1,119 @@
+#!/bin/sh
+QEMU_EXECUTABLES="qemu-system-i386 qemu-system-x86_64"
+GDB_EXECUTABLES="gdb x86_64-elf-gdb"
+
+event() {
+    printf "$1... "
+}
+
+# $1: OS NAME
+# $2: CROSS COMPILE FLAG
+generate_cross_compile_script() {
+cat > cross-compile.cmake <<EOF
+set(CMAKE_SYSTEM_NAME $1)
+
+set(TOOLCHAIN_PATH_AND_PREFIX "$2")
+
+set(CMAKE_C_COMPILER \${TOOLCHAIN_PATH_AND_PREFIX}gcc)
+set(CMAKE_CXX_COMPILER \${TOOLCHAIN_PATH_AND_PREFIX}c++)
+set(CMAKE_AR \${TOOLCHAIN_PATH_AND_PREFIX}ar)
+set(CMAKE_LINKER \${TOOLCHAIN_PATH_AND_PREFIX}ld)
+set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY)
+
+SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
+# for libraries and headers in the target directories
+SET(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
+SET(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
+EOF
+}
+
+event "finding qemu"
+for item in $QEMU_EXECUTABLES; do
+    if $item --version > /dev/null 2>&1; then
+        QEMU="$item"
+        break
+    fi
+done
+if [ "$QEMU" = "" ]; then
+    echo "failed"
+    exit 1
+fi
+echo "$QEMU"
+
+event "finding gdb"
+for item in $GDB_EXECUTABLES; do
+    if $item --version > /dev/null 2>&1; then
+        GDB="$item"
+        break
+    fi
+done
+if [ "$GDB" = "" ]; then
+    echo "failed"
+    exit 1
+fi
+echo "$GDB"
+
+event "checking os type"
+OS=`uname`
+case "$OS" in
+    "Linux")
+        echo "Linux"
+        ;;
+    "Darwin")
+        echo "macOS"
+        ;;
+    *)
+        echo "unknown"
+        exit 1
+        ;;
+esac
+
+event "setting hardware acceleration type"
+case "$OS" in
+    "Linux")
+        echo "kvm"
+        QEMU_ACCEL='-enable-kvm'
+        ;;
+    "Darwin")
+        if [ "$QEMU" = "qemu-system-x86_64" ]; then
+            echo "hvf"
+            QEMU_ACCEL='-accel hvf'
+        else
+            echo "tcg"
+            QEMU_ACCEL='-accel tcg'
+        fi
+        ;;
+esac
+
+event "checking cross compiling"
+if [ "$CROSS_COMPILE" != "" ]; then
+    echo "yes"
+    CROSS_COMPILE_FLAG='--toolchain cross-compile.cmake'
+    generate_cross_compile_script "$OS" "$CROSS_COMPILE"
+else
+    echo "no"
+    CROSS_COMPILE_FLAG=
+fi
+
+event "checking mkfs tool"
+if ! which mkfs.fat > /dev/null 2>&1; then
+    echo "no"
+    exit 1
+else
+    echo `which mkfs.fat`
+fi
+
+event "checking mtools"
+if ! which mcopy > /dev/null 2>&1; then
+    echo "no"
+    exit 1
+else
+    echo `which mcopy`
+fi
+
+cp Makefile.src Makefile
+sed -i '' -e "s/##PLACEHOLDER_1##/$QEMU/" Makefile > /dev/null 2>&1
+sed -i '' -e "s/##PLACEHOLDER_2##/$GDB/" Makefile > /dev/null 2>&1
+sed -i '' -e "s/##PLACEHOLDER_3##/$QEMU_ACCEL/" Makefile > /dev/null 2>&1
+sed -i '' -e "s/##PLACEHOLDER_4##/$CROSS_COMPILE_FLAG/" Makefile > /dev/null 2>&1
+exit 0

+ 3 - 3
cross-compile.cmake

@@ -1,8 +1,8 @@
-set(CMAKE_SYSTEM_NAME Linux)
+set(CMAKE_SYSTEM_NAME Darwin)
 
-set(TOOLCHAIN_PATH_AND_PREFIX "")
+set(TOOLCHAIN_PATH_AND_PREFIX "/usr/local/bin/x86_64-elf-")
 
-set(CMAKE_C_COMPILER ${TOOLCHAIN_PATH_AND_PREFIX}cc)
+set(CMAKE_C_COMPILER ${TOOLCHAIN_PATH_AND_PREFIX}gcc)
 set(CMAKE_CXX_COMPILER ${TOOLCHAIN_PATH_AND_PREFIX}c++)
 set(CMAKE_AR ${TOOLCHAIN_PATH_AND_PREFIX}ar)
 set(CMAKE_LINKER ${TOOLCHAIN_PATH_AND_PREFIX}ld)

+ 12 - 0
doc/mem_layout.txt

@@ -0,0 +1,12 @@
+0x00000000 - 0x00001000 kernel pd
+0x00001000 - 0x00005000 kernel pt
+0x00005000 - 0x00006000 empty page
+
+....
+
+0x00100000 - 0x???????? kernel code, data, bss
+0x???????? - 0x01000000 kernel early stack
+
+....
+
+0x30000000 - 0x40000000 kernel heap

+ 1 - 1
global_find.sh

@@ -5,7 +5,7 @@
 do_find()
 {
     for ext in $2; do
-        find src include -name "*.$ext" -exec grep -n -H -T -i "$1" {} \;
+        find src include -name "*.$ext" -exec grep -n --color=always -H -i "$1" {} \; -exec echo "" \;
     done
 }
 

+ 3 - 0
include/asm/boot.h

@@ -21,3 +21,6 @@ extern uint32_t asm_e820_mem_map_count;
 extern uint32_t asm_e820_mem_map_entry_size;
 
 extern uint32_t asm_kernel_size;
+
+extern uint32_t bss_section_start_addr;
+extern uint32_t bss_section_end_addr;

+ 1 - 0
include/asm/sys.h

@@ -7,6 +7,7 @@
 extern "C" {
 #endif
 
+void asm_switch_pd(page_directory_entry* pd_addr);
 void asm_enable_paging(page_directory_entry* pd_addr);
 
 phys_ptr_t current_pd(void);

+ 162 - 0
include/fs/fat.hpp

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

+ 14 - 2
include/kernel/errno.h

@@ -2,10 +2,22 @@
 
 #include <types/types.h>
 
+#ifdef __cplusplus
+extern "C" {
+#endif
+
 extern uint32_t* _get_errno(void);
 
 #define errno (*_get_errno())
 
-#define ENOMEM 0
-#define ENOTFOUND 1
+#ifdef __cplusplus
+}
+#endif
+
+#define ENOMEM (1 << 0)
 #define EEXIST (1 << 1)
+#define ENOENT (1 << 2)
+#define EINVAL (1 << 3)
+#define EISDIR (1 << 4)
+#define ENOTDIR (1 << 5)
+#define ENOTFOUND (1 << 6)

+ 61 - 0
include/kernel/hw/ata.hpp

@@ -0,0 +1,61 @@
+#pragma once
+
+#include <asm/port_io.h>
+#include <kernel/hw/port.hpp>
+#include <kernel/syscall.hpp>
+#include <types/cplusplus.hpp>
+
+constexpr port_id_t ATA_PRIMARY_BUS_BASE = 0x1f0;
+constexpr port_id_t ATA_PRIMARY_BUS_DEV_CONTROL_OR_ALTER_STATUS = 0x1f0;
+
+constexpr port_id_t ATA_SECONDARY_BUS_BASE = 0x170;
+constexpr port_id_t ATA_SECONDARY_BUS_DEV_CONTROL_OR_ALTER_STATUS = 0x1f0;
+
+namespace hw {
+class ata {
+public:
+    union stat_t {
+        uint8_t v;
+        struct {
+            uint8_t err : 1;
+            uint8_t idx : 1;
+            uint8_t corr : 1;
+            uint8_t drq : 1;
+            uint8_t srv : 1;
+            uint8_t df : 1;
+            uint8_t rdy : 1;
+            uint8_t bsy : 1;
+        } in;
+    };
+
+private:
+    p16 data;
+    p16r error;
+    p16w feats;
+    p8 count;
+    p8 lbalo;
+    p8 lbami;
+    p8 lbahi;
+    p8 drive;
+    p8r stats;
+    p8w comms;
+
+    uint8_t slave_flag;
+
+public:
+    ata(port_id_t port_base);
+
+    stat_t status(void) const;
+
+    bool identify(void) const;
+    int select(bool master);
+
+    size_t read_data(char* buf, size_t n) const;
+    size_t write_data(const char* buf, size_t n) const;
+
+    int read_sector(char* buf, uint32_t lba_low, uint16_t lba_high) const;
+    int write_sector(const char* buf, uint32_t lba_low, uint16_t lba_high) const;
+};
+
+void init_ata(void* data);
+} // namespace hw

+ 67 - 0
include/kernel/hw/port.hpp

@@ -0,0 +1,67 @@
+#pragma once
+
+#include <types/cplusplus.hpp>
+#include <types/stdint.h>
+
+namespace hw {
+template <typename port_size_t, bool r = true, bool w = true>
+class port {
+private:
+    uint16_t mp;
+
+public:
+    explicit port(uint16_t p)
+        : mp(p)
+    {
+    }
+
+    port_size_t operator*(void) const
+    {
+        static_assert(
+            types::is_same<port_size_t, uint8_t>::value || types::is_same<port_size_t, uint16_t>::value,
+            "this type is not implemented yet.");
+        port_size_t ret;
+        if constexpr (types::is_same<port_size_t, uint8_t>::value)
+            asm("inb %1, %0"
+                : "=a"(ret)
+                : "d"(mp));
+        if constexpr (types::is_same<port_size_t, uint16_t>::value)
+            asm("inw %1, %0"
+                : "=a"(ret)
+                : "d"(mp));
+        return ret;
+    }
+
+    port_size_t operator=(port_size_t n) const
+    {
+        static_assert(
+            types::is_same<port_size_t, uint8_t>::value || types::is_same<port_size_t, uint16_t>::value,
+            "this type is not implemented yet.");
+        if constexpr (types::is_same<port_size_t, uint8_t>::value)
+            asm("outb %1, %0"
+                :
+                : "d"(mp), "a"(n));
+        if constexpr (types::is_same<port_size_t, uint16_t>::value)
+            asm("outw %1, %0"
+                :
+                : "d"(mp), "a"(n));
+        return n;
+    }
+};
+
+using p8 = port<uint8_t>;
+using p8r = port<uint8_t, true, false>;
+using p8w = port<uint8_t, false, true>;
+using p16 = port<uint16_t>;
+using p16r = port<uint16_t, true, false>;
+using p16w = port<uint16_t, false, true>;
+
+template <>
+uint8_t p8r::operator=(uint8_t n) const = delete;
+template <>
+uint8_t p8w::operator*(void) const = delete;
+template <>
+uint16_t p16r::operator=(uint16_t n) const = delete;
+template <>
+uint16_t p16w::operator*(void) const = delete;
+} // namespace hw

+ 10 - 0
include/kernel/hw/serial.h

@@ -1,6 +1,10 @@
 #pragma once
 #include <asm/port_io.h>
 
+#ifdef __cplusplus
+extern "C" {
+#endif
+
 #define PORT_SERIAL0 (0x3f8)
 #define PORT_SERIAL1 (0x2f8)
 
@@ -11,3 +15,9 @@ uint8_t serial_read_data(port_id_t port);
 
 int32_t is_serial_ready_for_transmition(port_id_t port);
 void serial_send_data(port_id_t port, uint8_t data);
+
+void serial_receive_data_interrupt(void);
+
+#ifdef __cplusplus
+}
+#endif

+ 57 - 30
include/kernel/interrupt.h

@@ -2,7 +2,12 @@
 
 #include <types/types.h>
 
-#define INTERRUPT_GATE_TYPE (0x8e)
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define KERNEL_INTERRUPT_GATE_TYPE (0x8e)
+#define USER_INTERRUPT_GATE_TYPE (0xee)
 
 #define PIC_EOI (0x20)
 
@@ -17,6 +22,15 @@ struct regs_32 {
     uint32_t eax;
 };
 
+struct PACKED interrupt_stack {
+    struct regs_32 s_regs;
+    void* v_eip;
+    uint32_t cs;
+    uint32_t eflags;
+    const uint32_t esp;
+    const uint32_t ss;
+};
+
 // present: When set, the page fault was caused by a page-protection violation.
 //          When not set, it was caused by a non-present page.
 // write:   When set, the page fault was caused by a write access.
@@ -38,20 +52,19 @@ struct page_fault_error_code {
 // external interrupt handler function
 // stub in assembly MUST be called irqN
 #define SET_UP_IRQ(N, SELECTOR)        \
-    extern void irq##N();              \
     ptr_t addr_irq##N = (ptr_t)irq##N; \
-    SET_IDT_ENTRY(0x20 + (N), (addr_irq##N), (SELECTOR));
+    SET_IDT_ENTRY(0x20 + (N), (addr_irq##N), (SELECTOR), KERNEL_INTERRUPT_GATE_TYPE);
 
-#define SET_IDT_ENTRY_FN(N, FUNC_NAME, SELECTOR) \
+#define SET_IDT_ENTRY_FN(N, FUNC_NAME, SELECTOR, TYPE) \
     extern void FUNC_NAME();                     \
     ptr_t addr_##FUNC_NAME = (ptr_t)FUNC_NAME;   \
-    SET_IDT_ENTRY((N), (addr_##FUNC_NAME), (SELECTOR));
+    SET_IDT_ENTRY((N), (addr_##FUNC_NAME), (SELECTOR), (TYPE));
 
-#define SET_IDT_ENTRY(N, ADDR, SELECTOR)      \
+#define SET_IDT_ENTRY(N, ADDR, SELECTOR, TYPE)      \
     IDT[(N)].offset_low = (ADDR)&0x0000ffff;  \
     IDT[(N)].selector = (SELECTOR);           \
     IDT[(N)].zero = 0;                        \
-    IDT[(N)].type_attr = INTERRUPT_GATE_TYPE; \
+    IDT[(N)].type_attr = (TYPE); \
     IDT[(N)].offset_high = ((ADDR)&0xffff0000) >> 16
 
 struct IDT_entry {
@@ -81,27 +94,41 @@ void int13_handler(
     uint16_t cs,
     uint32_t eflags);
 
-void int14_handler(
-    linr_ptr_t l_addr,
-    struct regs_32 s_regs,
-    struct page_fault_error_code error_code,
-    void* v_eip,
-    uint16_t cs,
-    uint32_t eflags);
+void int0(void);
+void int1(void);
+void int2(void);
+void int3(void);
+void int4(void);
+void int5(void);
+void int6(void);
+void int7(void);
+void int8(void);
+void int9(void);
+void int10(void);
+void int11(void);
+void int12(void);
+void int13(void);
+void int14(void);
+
+void irq0(void);
+void irq1(void);
+void irq2(void);
+void irq3(void);
+void irq4(void);
+void irq5(void);
+void irq6(void);
+void irq7(void);
+void irq8(void);
+void irq9(void);
+void irq10(void);
+void irq11(void);
+void irq12(void);
+void irq13(void);
+void irq14(void);
+void irq15(void);
 
-void irq0_handler(void);
-void irq1_handler(void);
-void irq2_handler(void);
-void irq3_handler(void);
-void irq4_handler(void);
-void irq5_handler(void);
-void irq6_handler(void);
-void irq7_handler(void);
-void irq8_handler(void);
-void irq9_handler(void);
-void irq10_handler(void);
-void irq11_handler(void);
-void irq12_handler(void);
-void irq13_handler(void);
-void irq14_handler(void);
-void irq15_handler(void);
+void syscall_stub(void);
+
+#ifdef __cplusplus
+}
+#endif

+ 4 - 144
include/kernel/mem.h

@@ -10,9 +10,6 @@
 extern "C" {
 #endif
 
-// in mem.c
-extern struct mm* kernel_mm_head;
-
 // don't forget to add the initial 1m to the total
 struct mem_size_info {
     uint16_t n_1k_blks; // memory between 1m and 16m in 1k blocks
@@ -94,35 +91,6 @@ typedef union page_table_entry {
     struct page_table_entry_in in;
 } page_table_entry;
 
-struct page_attr {
-    uint32_t read : 1;
-    uint32_t write : 1;
-    uint32_t system : 1;
-    uint32_t cow : 1;
-};
-
-struct page {
-    page_t phys_page_id;
-    size_t* ref_count;
-    struct page_attr attr;
-    struct page* next;
-};
-
-struct mm_attr {
-    uint32_t read : 1;
-    uint32_t write : 1;
-    uint32_t system : 1;
-};
-
-struct mm {
-    linr_ptr_t start;
-    size_t len;
-    struct mm_attr attr;
-    struct page* pgs;
-    struct mm* next;
-    page_directory_entry* pd;
-};
-
 // in kernel_main.c
 extern uint8_t e820_mem_map[1024];
 extern uint32_t e820_mem_map_count;
@@ -133,123 +101,13 @@ extern struct mem_size_info mem_size_info;
 #define KERNEL_HEAP_START ((void*)0x30000000)
 #define KERNEL_HEAP_LIMIT ((void*)0x40000000)
 
-struct mem_blk_flags {
-    uint8_t is_free;
-    uint8_t has_next;
-    uint8_t _unused2;
-    uint8_t _unused3;
-};
-
-struct mem_blk {
-    size_t size;
-    struct mem_blk_flags flags;
-    // the first byte of the memory space
-    // the minimal allocated space is 4 bytes
-    uint8_t data[4];
-};
-
-int init_heap(void);
-
 void* k_malloc(size_t size);
 
 void k_free(void* ptr);
 
-// translate physical address to virtual(mapped) address
-void* p_ptr_to_v_ptr(phys_ptr_t p_ptr);
-
-// translate linear address to physical address
-phys_ptr_t l_ptr_to_p_ptr(struct mm* mm_area, linr_ptr_t v_ptr);
-
-// translate virtual(mapped) address to physical address
-phys_ptr_t v_ptr_to_p_ptr(void* v_ptr);
-
-// check if the l_ptr is contained in the area
-// @return GB_OK if l_ptr is in the area
-//         GB_FAILED if not
-int is_l_ptr_valid(struct mm* mm_area, linr_ptr_t l_ptr);
-
-// find the corresponding page the l_ptr pointing to
-// @return the pointer to the struct if found, NULL if not found
-struct page* find_page_by_l_ptr(struct mm* mm_area, linr_ptr_t l_ptr);
-
-static inline page_t phys_addr_to_page(phys_ptr_t ptr)
-{
-    return ptr >> 12;
-}
-
-static inline pd_i_t page_to_pd_i(page_t p)
-{
-    return p >> 10;
-}
-
-static inline pt_i_t page_to_pt_i(page_t p)
-{
-    return p & (1024 - 1);
-}
-
-static inline phys_ptr_t page_to_phys_addr(page_t p)
-{
-    return p << 12;
-}
-
-static inline pd_i_t linr_addr_to_pd_i(linr_ptr_t ptr)
-{
-    return page_to_pd_i(phys_addr_to_page(ptr));
-}
-
-static inline pd_i_t linr_addr_to_pt_i(linr_ptr_t ptr)
-{
-    return page_to_pt_i(phys_addr_to_page(ptr));
-}
-
-static inline page_directory_entry* lptr_to_pde(struct mm* mm, linr_ptr_t l_ptr)
-{
-    return mm->pd + linr_addr_to_pd_i((phys_ptr_t)l_ptr);
-}
-
-static inline page_table_entry* lptr_to_pte(struct mm* mm, linr_ptr_t l_ptr)
-{
-    page_directory_entry* pde = lptr_to_pde(mm, l_ptr);
-    page_table_entry* pte = (page_table_entry*)p_ptr_to_v_ptr(page_to_phys_addr(pde->in.pt_page));
-    return pte + linr_addr_to_pt_i((phys_ptr_t)l_ptr);
-}
-
-static inline page_directory_entry* lp_to_pde(struct mm* mm, linr_ptr_t l_ptr)
-{
-    phys_ptr_t p_ptr = l_ptr_to_p_ptr(mm, l_ptr);
-    page_directory_entry* pde = mm->pd + linr_addr_to_pd_i(p_ptr);
-    return pde;
-}
-
-// get the corresponding pte for the linear address
-// for example: l_ptr = 0x30001000 will return the pte including the page it is mapped to
-static inline page_table_entry* lp_to_pte(struct mm* mm, linr_ptr_t l_ptr)
-{
-    phys_ptr_t p_ptr = l_ptr_to_p_ptr(mm, l_ptr);
-
-    page_directory_entry* pde = lp_to_pde(mm, l_ptr);
-    phys_ptr_t p_pt = page_to_phys_addr(pde->in.pt_page);
-
-    page_table_entry* pte = (page_table_entry*)p_ptr_to_v_ptr(p_pt);
-    pte += linr_addr_to_pt_i(p_ptr);
-
-    return pte;
-}
-
-// map the page to the end of the mm_area in pd
-int k_map(
-    struct mm* mm_area,
-    struct page* page,
-    int read,
-    int write,
-    int priv,
-    int cow);
-
-// allocate a raw page
-page_t alloc_raw_page(void);
+void* ki_malloc(size_t size);
 
-// allocate a struct page together with the raw page
-struct page* allocate_page(void);
+void ki_free(void* ptr);
 
 #define KERNEL_PAGE_DIRECTORY_ADDR ((page_directory_entry*)0x00000000)
 
@@ -259,6 +117,8 @@ void init_mem(void);
 #define KERNEL_DATA_SEGMENT (0x10)
 #define USER_CODE_SEGMENT (0x18)
 #define USER_DATA_SEGMENT (0x20)
+#define USER_CODE_SELECTOR (USER_CODE_SEGMENT | 3)
+#define USER_DATA_SELECTOR (USER_DATA_SEGMENT | 3)
 
 #define SD_TYPE_CODE_SYSTEM (0x9a)
 #define SD_TYPE_DATA_SYSTEM (0x92)

+ 132 - 0
include/kernel/mm.hpp

@@ -0,0 +1,132 @@
+#pragma once
+
+#include <kernel/mem.h>
+#include <kernel/vfs.hpp>
+#include <types/allocator.hpp>
+#include <types/list.hpp>
+#include <types/types.h>
+#include <types/vector.hpp>
+
+constexpr size_t THREAD_KERNEL_STACK_SIZE = 2 * PAGE_SIZE;
+
+struct page_attr {
+    uint32_t cow : 1;
+};
+
+struct page {
+    page_t phys_page_id;
+    size_t* ref_count;
+    struct page_attr attr;
+};
+
+using page_arr = types::vector<page, types::kernel_ident_allocator>;
+
+struct mm_attr {
+    uint32_t read : 1;
+    uint32_t write : 1;
+    uint32_t system : 1;
+};
+
+class mm {
+public:
+    linr_ptr_t start;
+    struct mm_attr attr;
+    page_directory_entry* pd;
+    page_arr* pgs;
+    fs::inode* mapped_file;
+    size_t file_offset;
+
+public:
+    mm(const mm& val);
+    mm(linr_ptr_t start, page_directory_entry* pd, bool write, bool system);
+};
+
+using mm_list = types::list<mm, types::kernel_ident_allocator>;
+
+// in mem.cpp
+extern mm_list* kernel_mms;
+extern page empty_page;
+
+// translate physical address to virtual(mapped) address
+void* p_ptr_to_v_ptr(phys_ptr_t p_ptr);
+
+// translate linear address to physical address
+phys_ptr_t l_ptr_to_p_ptr(const mm_list* mms, linr_ptr_t v_ptr);
+
+// translate virtual(mapped) address to physical address
+phys_ptr_t v_ptr_to_p_ptr(void* v_ptr);
+
+// @return the pointer to the mm_area containing l_ptr
+//         nullptr if not
+mm* find_mm_area(mm_list* mms, linr_ptr_t l_ptr);
+
+// find the corresponding page the l_ptr pointing to
+// @return the pointer to the struct if found, NULL if not found
+struct page* find_page_by_l_ptr(const mm_list* mms, linr_ptr_t l_ptr);
+
+static inline page_t phys_addr_to_page(phys_ptr_t ptr)
+{
+    return ptr >> 12;
+}
+
+static inline pd_i_t page_to_pd_i(page_t p)
+{
+    return p >> 10;
+}
+
+static inline pt_i_t page_to_pt_i(page_t p)
+{
+    return p & (1024 - 1);
+}
+
+static inline phys_ptr_t page_to_phys_addr(page_t p)
+{
+    return p << 12;
+}
+
+static inline pd_i_t linr_addr_to_pd_i(linr_ptr_t ptr)
+{
+    return page_to_pd_i(phys_addr_to_page(ptr));
+}
+
+static inline pd_i_t linr_addr_to_pt_i(linr_ptr_t ptr)
+{
+    return page_to_pt_i(phys_addr_to_page(ptr));
+}
+
+static inline page_directory_entry* mms_get_pd(const mm_list* mms)
+{
+    return mms->begin()->pd;
+}
+
+// map the page to the end of the mm_area in pd
+int k_map(
+    mm* mm_area,
+    const struct page* page,
+    int read,
+    int write,
+    int priv,
+    int cow);
+
+// @param len is aligned to 4kb boundary automatically, exceeding part will
+// be filled with '0's and not written back to the file
+int mmap(
+    void* hint,
+    size_t len,
+    fs::inode* file,
+    size_t offset,
+    int write,
+    int priv);
+
+// allocate a raw page
+page_t alloc_raw_page(void);
+
+// allocate n raw page(s)
+// @return the id of the first page allocated
+page_t alloc_n_raw_pages(size_t n);
+
+// allocate a struct page together with the raw page
+struct page allocate_page(void);
+
+page_directory_entry* alloc_pd(void);
+page_table_entry* alloc_pt(void);

+ 0 - 15
include/kernel/process.h

@@ -1,15 +0,0 @@
-#pragma once
-
-#include <kernel/mem.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-struct process {
-    struct mm* mm;
-};
-
-#ifdef __cplusplus
-}
-#endif

+ 71 - 0
include/kernel/process.hpp

@@ -0,0 +1,71 @@
+#pragma once
+
+#include <kernel/interrupt.h>
+#include <kernel/task.h>
+#include <types/types.h>
+
+#ifdef __cplusplus
+#include <kernel/mm.hpp>
+#include <types/list.hpp>
+
+typedef size_t pid_t;
+
+class process;
+struct thread;
+
+struct process_attr {
+    uint16_t system : 1;
+};
+
+struct thread_attr {
+    uint32_t system : 1;
+    uint32_t ready : 1;
+    uint32_t wait : 1;
+};
+
+struct thread {
+    void* eip;
+    process* owner;
+    regs_32 regs;
+    uint32_t eflags;
+    thread_attr attr;
+};
+
+class process {
+public:
+    mm_list mms;
+    types::list<thread> thds;
+    // TODO: allocate a kernel stack for EVERY THREAD
+    void* k_esp;
+    process_attr attr;
+    pid_t pid;
+
+public:
+    process(process&& val);
+    process(const process&) = delete;
+    process(const process& proc, const thread& main_thread);
+    process(void* start_eip, uint8_t* image, size_t image_size, bool system);
+};
+
+// in process.cpp
+extern process* current_process;
+extern thread* current_thread;
+
+extern "C" void NORETURN init_scheduler();
+void do_scheduling(interrupt_stack* intrpt_data);
+
+void thread_context_save(interrupt_stack* int_stack, thread* thd);
+void thread_context_load(interrupt_stack* int_stack, thread* thd);
+void process_context_save(interrupt_stack*, process*);
+void process_context_load(interrupt_stack*, process* proc);
+
+void add_to_process_list(process&& proc);
+void add_to_ready_list(thread* thd);
+
+void k_new_thread(void(*func)(void*), void* data);
+
+#else
+
+void NORETURN init_scheduler();
+
+#endif

+ 2 - 1
include/kernel/stdio.h

@@ -17,7 +17,8 @@ extern "C" {
 void* memcpy(void* dst, const void* src, size_t n);
 void* memset(void* dst, int c, size_t n);
 size_t strlen(const char* str);
-char* strncpy(char* dst, const char* src, size_t max_n);
+char* strncpy(char* dst, const char* src, size_t n);
+int strcmp(const char* s1, const char* s2);
 
 ssize_t
 snprint_decimal(

+ 11 - 0
include/kernel/syscall.hpp

@@ -0,0 +1,11 @@
+#pragma once
+
+#include <types/types.h>
+#include <kernel/interrupt.h>
+
+// return value is stored in %eax and %edx
+typedef void (*syscall_handler)(interrupt_stack* data);
+
+#define syscall(eax) asm volatile("movl %0, %%eax\n\tint $0x80"::"r"(eax):"eax","edx")
+
+void init_syscall(void);

+ 2 - 0
include/kernel/task.h

@@ -1,5 +1,7 @@
 #pragma once
 
+#include <types/types.h>
+
 #ifdef __cplusplus
 extern "C" {
 #endif

+ 186 - 0
include/kernel/vfs.hpp

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

+ 10 - 1
include/kernel_main.h

@@ -1,4 +1,5 @@
 #pragma once
+#include <types/types.h>
 
 static inline void __break_point(void)
 {
@@ -12,4 +13,12 @@ static inline void __break_point(void)
 
 #define KERNEL_START_ADDR (0x00100000)
 
-void kernel_main(void);
+void NORETURN kernel_main(void);
+
+#ifdef __cplusplus
+// in kernel_main.c
+extern "C" struct tss32_t tss;
+#else
+// in kernel_main.c
+extern struct tss32_t tss;
+#endif

+ 109 - 0
include/types/allocator.hpp

@@ -0,0 +1,109 @@
+#pragma once
+#include <kernel/mem.h>
+#include <types/cplusplus.hpp>
+#include <types/types.h>
+
+inline void* operator new(size_t, void* ptr)
+{
+    return ptr;
+}
+
+namespace types {
+
+template <typename Allocator>
+class allocator_traits;
+
+template <typename T>
+class kernel_allocator {
+public:
+    using value_type = T;
+
+    static value_type* allocate_memory(size_t count)
+    {
+        return static_cast<value_type*>(::k_malloc(count));
+    }
+
+    static void deallocate_memory(value_type* ptr)
+    {
+        ::k_free(ptr);
+    }
+};
+
+template <typename T>
+class kernel_ident_allocator {
+public:
+    using value_type = T;
+
+    static value_type* allocate_memory(size_t count)
+    {
+        return static_cast<value_type*>(::ki_malloc(count));
+    }
+
+    static void deallocate_memory(value_type* ptr)
+    {
+        ::ki_free(ptr);
+    }
+};
+
+template <typename T, typename... Args>
+T* kernel_allocator_new(Args&&... args)
+{
+    return allocator_traits<kernel_allocator<T>>::allocate_and_construct(forward<Args>(args)...);
+}
+
+template <typename T, typename... Args>
+T* kernel_ident_allocator_new(Args&&... args)
+{
+    return allocator_traits<kernel_ident_allocator<T>>::allocate_and_construct(forward<Args>(args)...);
+}
+
+template <typename Allocator>
+class allocator_traits {
+public:
+    using value_type = typename Allocator::value_type;
+
+    static value_type* allocate(size_t count)
+    {
+        if (count == 0)
+            return nullptr;
+        return Allocator::allocate_memory(sizeof(value_type) * count);
+    }
+
+    template <typename... Args>
+    static 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)
+    {
+        auto* ptr = allocate(1);
+        construct(ptr, forward<Args>(args)...);
+        return ptr;
+    }
+
+    static void deconstruct(value_type* ptr)
+    {
+        if (!ptr)
+            return;
+        ptr->~value_type();
+    }
+
+    static void deallocate(value_type* ptr)
+    {
+        if (!ptr)
+            return;
+        Allocator::deallocate_memory(ptr);
+    }
+
+    static void deconstruct_and_deallocate(value_type* ptr)
+    {
+        if (!ptr)
+            return;
+        deconstruct(ptr);
+        deallocate(ptr);
+    }
+};
+} // namespace types

+ 8 - 0
include/types/bitmap.h

@@ -2,6 +2,14 @@
 
 #include <types/stdint.h>
 
+#ifdef __cplusplus
+extern "C" {
+#endif
+
 int bm_test(char* bm, size_t n);
 void bm_set(char* bm, size_t n);
 void bm_clear(char* bm, size_t n);
+
+#ifdef __cplusplus
+}
+#endif

+ 115 - 0
include/types/cplusplus.hpp

@@ -0,0 +1,115 @@
+#pragma once
+
+#ifdef __cplusplus
+
+namespace types::traits::inner {
+
+template <typename Tp, typename>
+struct remove_pointer {
+    using type = Tp;
+};
+
+template <typename Tp, typename T>
+struct remove_pointer<Tp, T*> {
+    using type = T;
+};
+
+template <typename Tr, typename>
+struct remove_reference {
+    using type = Tr;
+};
+
+template <typename Tr, typename T>
+struct remove_reference<Tr, T&> {
+    using type = T;
+};
+
+} // namespace types::traits::inner
+
+namespace types::traits {
+
+template <typename Tp>
+struct remove_pointer
+    : inner::remove_pointer<Tp, Tp> {
+};
+
+template <typename Tr>
+struct remove_reference
+    : inner::remove_reference<Tr, Tr> {
+};
+
+template <typename T>
+struct add_pointer {
+    using type = T*;
+};
+
+template <typename T>
+struct add_reference {
+    using type = T&;
+};
+
+template <typename T>
+struct remove_cv {
+    using type = T;
+};
+template <typename T>
+struct remove_cv<const T> {
+    using type = T;
+};
+template <typename T>
+struct remove_cv<volatile T> {
+    using type = T;
+};
+template <typename T>
+struct remove_cv<const volatile T> {
+    using type = T;
+};
+
+template <typename T>
+struct decay {
+private:
+    using U = remove_reference<T>;
+
+public:
+    using type = typename remove_cv<U>::type;
+};
+
+} // namespace types::traits
+
+namespace types {
+template <typename T>
+T&& move(T& val)
+{
+    return static_cast<T&&>(val);
+}
+template <typename T>
+T&& forward(typename traits::remove_reference<T>::type& val)
+{
+    return static_cast<T&&>(val);
+}
+
+template <typename T, T _value>
+struct constant_value {
+    static constexpr T value = _value;
+};
+using true_type = constant_value<bool, true>;
+using false_type = constant_value<bool, false>;
+
+template <typename>
+struct template_true_type : public true_type {
+};
+template <typename>
+struct template_false_type : public false_type {
+};
+
+template <typename, typename>
+struct is_same : false_type {
+};
+
+template <typename T>
+struct is_same<T, T> : true_type {
+};
+
+} // namespace types
+
+#endif

+ 284 - 0
include/types/hash_map.hpp

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

+ 0 - 9
include/types/list.h

@@ -1,9 +0,0 @@
-#pragma once
-
-#define LIST_LIKE_AT(type, list_like, pos, result_name) \
-    type* result_name = list_like; \
-    {                   \
-        size_t _tmp_pos = (pos); \
-        while (_tmp_pos--) \
-            result_name = result_name->next; \
-    }

+ 146 - 86
include/types/list.hpp

@@ -1,56 +1,11 @@
 #pragma once
 
-#include <kernel/mem.h>
+#include <types/allocator.hpp>
+#include <types/cplusplus.hpp>
 #include <types/types.h>
 
-inline void* operator new(size_t, void* ptr)
-{
-    return ptr;
-}
-
 namespace types {
 
-template <typename T>
-class kernel_allocator {
-public:
-    using value_type = T;
-
-    static value_type* allocate_memory(size_t count)
-    {
-        return static_cast<value_type*>(::k_malloc(sizeof(value_type) * count));
-    }
-
-    static void deallocate_memory(value_type* ptr)
-    {
-        ::k_free(ptr);
-    }
-};
-
-template <typename Allocator>
-class allocator_traits {
-public:
-    using value_type = typename Allocator::value_type;
-
-    static value_type* allocate_memory(size_t count)
-    {
-        return Allocator::allocate_memory(count);
-    }
-
-    template <typename... Args>
-    static value_type* allocate(Args... args)
-    {
-        value_type* ptr = allocate_memory(sizeof(value_type));
-        new (ptr) value_type(args...);
-        return ptr;
-    }
-
-    static void deallocate(value_type* ptr)
-    {
-        ptr->~value_type();
-        Allocator::deallocate_memory(ptr);
-    }
-};
-
 template <typename T, template <typename _value_type> class Allocator = kernel_allocator>
 class list {
 private:
@@ -59,12 +14,14 @@ private:
     class node;
 
 public:
+    template <typename Pointer>
     class iterator;
 
     using value_type = T;
     using pointer_type = value_type*;
     using reference_type = value_type&;
-    using iterator_type = iterator;
+    using iterator_type = iterator<value_type*>;
+    using const_iterator_type = iterator<const value_type*>;
     using size_type = size_t;
     using difference_type = ssize_t;
     using node_base_type = node_base;
@@ -76,13 +33,13 @@ public:
 private:
     class node_base {
     public:
-        node_base* prev = 0;
-        node_base* next = 0;
+        node_type* prev = 0;
+        node_type* next = 0;
 
-        void connect(node_base* _next) noexcept
+        void connect(node_type* _next) noexcept
         {
             this->next = _next;
-            _next->prev = this;
+            _next->prev = static_cast<node_type*>(this);
         }
     };
 
@@ -95,7 +52,7 @@ private:
         }
 
         explicit node(NodeValue&& v) noexcept
-            : value(v)
+            : value(move(v))
         {
         }
 
@@ -103,19 +60,31 @@ private:
     };
 
 public:
+    template <typename Pointer>
     class iterator {
     public:
-        explicit iterator(const iterator& iter) noexcept
+        using Value = typename types::traits::remove_pointer<Pointer>::type;
+        using Reference = typename types::traits::add_reference<Value>::type;
+
+    public:
+        iterator(const iterator& iter) noexcept
             : n(iter.n)
         {
         }
 
-        explicit iterator(iterator&& iter) noexcept
+        iterator(iterator&& iter) noexcept
             : n(iter.n)
         {
+            iter.n = nullptr;
+        }
+
+        iterator& operator=(const iterator& iter)
+        {
+            n = iter.n;
+            return *this;
         }
 
-        explicit iterator(node_base* _n) noexcept
+        explicit iterator(node_type* _n) noexcept
             : n(_n)
         {
         }
@@ -156,14 +125,19 @@ public:
             return iter;
         }
 
-        reference_type operator*() const noexcept
+        Reference operator*() const noexcept
         {
-            return (static_cast<node_type*>(n))->value;
+            return n->value;
         }
 
-        pointer_type operator->() const noexcept
+        Pointer operator->() const noexcept
         {
-            return (static_cast<node_type*>(n))->value;
+            return &n->value;
+        }
+
+        Pointer ptr(void) const noexcept
+        {
+            return &n->value;
         }
 
         node_base_type* _node(void) const noexcept
@@ -172,7 +146,7 @@ public:
         }
 
     protected:
-        node_base_type* n;
+        node_type* n;
     };
 
 private:
@@ -189,54 +163,111 @@ private:
         return (static_cast<sentry_node_type*>(head))->value;
     }
 
+    void destroy(void)
+    {
+        if (!head || !tail)
+            return;
+        clear();
+        allocator_traits<sentry_allocator_type>::deconstruct_and_deallocate(static_cast<sentry_node_type*>(head));
+        allocator_traits<sentry_allocator_type>::deconstruct_and_deallocate(static_cast<sentry_node_type*>(tail));
+    }
+
 public:
     list() noexcept
         // size is stored in the 'head' node
-        : head(allocator_traits<sentry_allocator_type>::allocate(0))
-        , tail(allocator_traits<sentry_allocator_type>::allocate(0))
+        : head(allocator_traits<sentry_allocator_type>::allocate_and_construct(0))
+        , tail(allocator_traits<sentry_allocator_type>::allocate_and_construct(0))
     {
-        head->connect(tail);
-        tail->connect(head);
+        head->connect(static_cast<node_type*>(tail));
+        tail->connect(static_cast<node_type*>(head));
+    }
+
+    list(const list& v)
+        : list()
+    {
+        for (const auto& item : v)
+            push_back(item);
+    }
+
+    list(list&& v)
+        : head(v.head)
+        , tail(v.tail)
+    {
+        v.head = nullptr;
+        v.tail = nullptr;
+    }
+
+    list& operator=(const list& v)
+    {
+        clear();
+        for (const auto& item : v)
+            push_back(item);
+        return *this;
+    }
+
+    list& operator=(list&& v)
+    {
+        destroy();
+
+        head = v.head;
+        tail = v.tail;
+        v.head = nullptr;
+        v.tail = nullptr;
+
+        return *this;
     }
 
     ~list() noexcept
     {
-        for (auto iter = begin(); iter != end(); ++iter) {
-            erase(iter);
-        }
-        allocator_traits<sentry_allocator_type>::deallocate(static_cast<sentry_node_type*>(head));
-        allocator_traits<sentry_allocator_type>::deallocate(static_cast<sentry_node_type*>(tail));
+        destroy();
     }
 
-    // TODO: find
+    iterator_type find(const value_type& v) noexcept
+    {
+        for (iterator_type iter = begin(); iter != end(); ++iter)
+            if (*iter == v)
+                return iter;
+    }
 
     // erase the node which iter points to
-    void erase(const iterator_type& iter) noexcept
+    iterator_type erase(const iterator_type& iter) noexcept
     {
         node_base_type* current_node = iter._node();
+        iterator_type ret(current_node->next);
         current_node->prev->connect(current_node->next);
-        allocator_traits<allocator_type>::deallocate(static_cast<node_type*>(current_node));
+        allocator_traits<allocator_type>::deconstruct_and_deallocate(static_cast<node_type*>(current_node));
         --_size();
+        return ret;
+    }
+
+    void clear(void)
+    {
+        for (auto iter = begin(); iter != end();)
+            iter = erase(iter);
     }
 
     // insert the value v in front of the given iterator
-    void insert(const iterator_type& iter, const value_type& v) noexcept
+    iterator_type insert(const iterator_type& iter, const value_type& v) noexcept
     {
-        node_base_type* new_node = allocator_traits<allocator_type>::allocate(v);
+        node_type* new_node = allocator_traits<allocator_type>::allocate_and_construct(v);
+        iterator_type ret(new_node);
         iter._node()->prev->connect(new_node);
-        new_node->connect(iter._node());
+        new_node->connect(static_cast<node_type*>(iter._node()));
 
         ++_size();
+        return ret;
     }
 
     // insert the value v in front of the given iterator
-    void insert(const iterator_type& iter, value_type&& v) noexcept
+    iterator_type insert(const iterator_type& iter, value_type&& v) noexcept
     {
-        node_base_type* new_node = allocator_traits<allocator_type>::allocate(v);
-        iter._node().prev->connect(new_node);
-        new_node->connect(iter._node());
+        node_type* new_node = allocator_traits<allocator_type>::allocate_and_construct(move(v));
+        iterator_type ret(new_node);
+        iter._node()->prev->connect(new_node);
+        new_node->connect(static_cast<node_type*>(iter._node()));
 
         ++_size();
+        return ret;
     }
 
     void push_back(const value_type& v) noexcept
@@ -246,7 +277,13 @@ public:
 
     void push_back(value_type&& v) noexcept
     {
-        insert(end(), v);
+        insert(end(), move(v));
+    }
+
+    template <typename... Args>
+    iterator_type emplace_back(Args&&... args)
+    {
+        return insert(end(), value_type(forward<Args>(args)...));
     }
 
     void push_front(const value_type& v) noexcept
@@ -256,7 +293,13 @@ public:
 
     void push_front(value_type&& v) noexcept
     {
-        insert(begin(), v);
+        insert(begin(), move(v));
+    }
+
+    template <typename... Args>
+    iterator_type emplace_front(Args&&... args)
+    {
+        return insert(begin(), value_type(forward<Args>(args)...));
     }
 
     size_t size(void) const noexcept
@@ -271,7 +314,27 @@ public:
 
     iterator_type end() noexcept
     {
-        return iterator_type(tail);
+        return iterator_type(static_cast<node_type*>(tail));
+    }
+
+    const_iterator_type begin() const noexcept
+    {
+        return const_iterator_type(head->next);
+    }
+
+    const_iterator_type end() const noexcept
+    {
+        return const_iterator_type(static_cast<node_type*>(tail));
+    }
+
+    const_iterator_type cbegin() const noexcept
+    {
+        return begin();
+    }
+
+    const_iterator_type cend() const noexcept
+    {
+        return end();
     }
 
     bool empty(void) const noexcept
@@ -280,9 +343,6 @@ public:
     }
 
     // TODO
-    // iterator_type cstart() noexcept;
-    // iterator_type cend() noexcept;
-
     // iterator_type r_start() noexcept;
     // iterator_type r_end() noexcept;
 

+ 35 - 0
include/types/lock.h

@@ -0,0 +1,35 @@
+#pragma once
+
+#include <types/stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+static inline void spin_lock(uint32_t volatile* lock_addr)
+{
+    asm volatile(
+            "_spin:\n\t\
+             movl $1, %%eax\n\t\
+             xchgl %%eax, (%0)\n\t\
+             test $0, %%eax\n\t\
+             jne _spin\n\t\
+            "
+            : "=r" (lock_addr)
+            : "0"  (lock_addr)
+            : "eax", "memory"
+            );
+}
+
+static inline void spin_unlock(uint32_t volatile* lock_addr)
+{
+    asm volatile("movl $0, %%eax\nxchgl %%eax, (%0)"
+                 :
+                 : "r"  (lock_addr)
+                 : "eax", "memory"
+                 );
+}
+
+#ifdef __cplusplus
+}
+#endif

+ 6 - 0
include/types/size.h

@@ -2,6 +2,12 @@
 
 #include "stdint.h"
 
+#ifdef __GNUC__
+#define PACKED __attribute__((__packed__))
+#else
+#error "no definition for ((PACKED))"
+#endif
+
 #define __32bit_system
 
 #ifdef __32bit_system

+ 4 - 0
include/types/stdint.h

@@ -1,6 +1,10 @@
 #pragma once
 
+#ifdef __cplusplus
+#define NULL (nullptr)
+#else
 #define NULL ((void*)0)
+#endif
 
 typedef __INT8_TYPE__ int8_t;
 typedef __INT16_TYPE__ int16_t;

+ 118 - 0
include/types/string.hpp

@@ -0,0 +1,118 @@
+#pragma once
+
+#include <kernel/stdio.h>
+#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> {
+public:
+    using inner_vector_type = types::vector<char, Allocator>;
+    using size_type = typename inner_vector_type::size_type;
+
+    static inline constexpr size_type npos = (-1U);
+
+public:
+    explicit string(size_type capacity = 8)
+        : inner_vector_type(capacity)
+    {
+        this->push_back(0x00);
+    }
+    string(const char* str, size_type n = npos)
+        : string()
+    {
+        this->append(str, n);
+    }
+    string& append(const char* str, size_type n = npos)
+    {
+        this->pop_back();
+
+        while (n-- && *str != 0x00) {
+            this->push_back(*str);
+            ++str;
+        }
+
+        this->push_back(0x00);
+        return *this;
+    }
+    string& append(const string& str)
+    {
+        return this->append(str.data());
+    }
+    string& append(string&& str)
+    {
+        return this->append(str.data());
+    }
+    string& operator+=(const char c)
+    {
+        this->pop_back();
+        this->push_back(c);
+        this->push_back(0x00);
+        return *this;
+    }
+    string& operator+=(const char* str)
+    {
+        return this->append(str);
+    }
+    string& operator+=(const string& str)
+    {
+        return this->append(str);
+    }
+    string& operator+=(string&& str)
+    {
+        return this->append(move(str));
+    }
+    bool operator==(const string& rhs) const
+    {
+        return strcmp(c_str(), rhs.c_str()) == 0;
+    }
+    string substr(size_type pos, size_type n = npos)
+    {
+        return string(this->m_arr + pos, n);
+    }
+    const char* c_str(void) const noexcept
+    {
+        return this->data();
+    }
+    void clear(void)
+    {
+        inner_vector_type::clear();
+        this->push_back(0x00);
+    }
+    char pop(void)
+    {
+        this->pop_back();
+        auto iter = inner_vector_type::back();
+        char c = *iter;
+        *iter = 0x00;
+        return c;
+    }
+    typename inner_vector_type::iterator_type back(void)
+    {
+        // TODO: assert
+        if (this->empty())
+            return typename inner_vector_type::iterator_type((void*)0xffffffff);
+        return --inner_vector_type::back();
+    }
+    typename inner_vector_type::const_iterator_type back(void) const
+    {
+        // TODO: assert
+        if (this->empty())
+            return typename inner_vector_type::iterator_type((void*)0xffffffff);
+        return --inner_vector_type::back();
+    }
+    typename inner_vector_type::const_iterator_type cback(void) const
+    {
+        // TODO: assert
+        if (this->empty())
+            return typename inner_vector_type::iterator_type((void*)0xffffffff);
+        return --inner_vector_type::cback();
+    }
+};
+} // namespace types
+
+#endif

+ 17 - 0
include/types/types.h

@@ -5,3 +5,20 @@
 #include "size.h"
 #include "status.h"
 #include "stdint.h"
+
+#ifdef __GNUC__
+#define NORETURN __attribute__((noreturn))
+#else
+#error "no definition for ((NORETURN))"
+#endif
+
+#ifdef __GNUC__
+#define SECTION(x) __attribute__((section(x)))
+#else
+#error "no definition for ((SECTION))"
+#endif
+
+#ifdef __cplusplus
+#include <types/allocator.hpp>
+#include <types/cplusplus.hpp>
+#endif

+ 363 - 0
include/types/vector.hpp

@@ -0,0 +1,363 @@
+#pragma once
+
+#include <types/allocator.hpp>
+#include <types/cplusplus.hpp>
+#include <types/types.h>
+
+namespace types {
+
+template <typename T, template <typename _value_type> class Allocator = kernel_allocator>
+class vector {
+public:
+    template <typename Pointer>
+    class iterator;
+
+    using value_type = T;
+    using pointer_type = value_type*;
+    using reference_type = value_type&;
+    using iterator_type = iterator<value_type*>;
+    using const_iterator_type = iterator<const value_type*>;
+    using size_type = size_t;
+    using difference_type = ssize_t;
+    using index_type = size_type;
+    using allocator_type = Allocator<value_type>;
+
+public:
+    template <typename Pointer>
+    class iterator {
+    public:
+        using Value = typename types::traits::remove_pointer<Pointer>::type;
+        using Reference = typename types::traits::add_reference<Value>::type;
+
+    public:
+        iterator(const iterator& iter) noexcept
+            : p(iter.p)
+        {
+        }
+
+        iterator(iterator&& iter) noexcept
+            : p(iter.p)
+        {
+            iter.p = nullptr;
+        }
+
+        iterator& operator=(const iterator& iter)
+        {
+            p = iter.p;
+            return *this;
+        }
+
+        explicit iterator(Pointer p) noexcept
+            : p(p)
+        {
+        }
+
+        bool operator==(const iterator& iter) noexcept
+        {
+            return this->p == iter.p;
+        }
+
+        bool operator!=(const iterator& iter) noexcept
+        {
+            return !(*this == iter);
+        }
+
+        iterator& operator++() noexcept
+        {
+            ++p;
+            return *this;
+        }
+
+        iterator operator++(int) noexcept
+        {
+            iterator iter(*this);
+            ++p;
+            return iter;
+        }
+
+        iterator& operator--() noexcept
+        {
+            --p;
+            return *this;
+        }
+
+        iterator operator--(int) noexcept
+        {
+            iterator iter(*this);
+            --p;
+            return iter;
+        }
+
+        iterator operator+(size_type n) noexcept
+        {
+            iterator iter(p + n);
+            return iter;
+        }
+
+        iterator operator-(size_type n) noexcept
+        {
+            iterator iter(p - n);
+            return iter;
+        }
+
+        Reference operator*() const noexcept
+        {
+            return *p;
+        }
+
+        Pointer operator->() const noexcept
+        {
+            return p;
+        }
+
+    protected:
+        Pointer p;
+    };
+
+public:
+    explicit vector(size_type capacity = 1) noexcept
+        : m_arr(nullptr)
+        , m_size(0)
+    {
+        resize(capacity);
+    }
+
+    vector(const vector& arr) noexcept
+        : vector(arr.capacity())
+    {
+        for (const auto& item : arr)
+            push_back(item);
+    }
+
+    vector(vector&& arr) noexcept
+    {
+        m_arr = arr.m_arr;
+        m_capacity = arr.m_capacity;
+        m_size = arr.m_size;
+
+        arr.m_arr = nullptr;
+        arr.m_capacity = 0;
+        arr.m_size = 0;
+    }
+
+    vector& operator=(vector&& arr)
+    {
+        resize(0);
+        m_arr = arr.m_arr;
+        m_capacity = arr.m_capacity;
+        m_size = arr.m_size;
+
+        arr.m_arr = nullptr;
+        arr.m_capacity = 0;
+        arr.m_size = 0;
+
+        return *this;
+    }
+
+    vector& operator=(const vector& arr)
+    {
+        return operator=(vector(arr));
+    }
+
+    ~vector() noexcept
+    {
+        resize(0);
+    }
+
+    void resize(size_type n)
+    {
+        value_type* new_ptr = allocator_traits<allocator_type>::allocate(n);
+
+        m_capacity = n;
+        size_t orig_size = m_size;
+        if (m_size > m_capacity)
+            m_size = m_capacity;
+
+        for (size_t i = 0; i < m_size; ++i)
+            allocator_traits<allocator_type>::construct(new_ptr + i, _at(i));
+
+        for (size_t i = 0; i < orig_size; ++i)
+            allocator_traits<allocator_type>::deconstruct(m_arr + i);
+
+        if (m_arr)
+            allocator_traits<allocator_type>::deallocate(m_arr);
+        m_arr = new_ptr;
+    }
+
+    // TODO: find
+
+    // erase the node which iter points to
+    // void erase(const iterator_type& iter) noexcept
+    // {
+    //     allocator_traits<allocator_type>::deconstruct(iter.p);
+    //     --m_size;
+    // }
+
+    // insert the value v in front of the given iterator
+    // void insert(const iterator_type& iter, const value_type& v) noexcept
+    // {
+    //     node_base_type* new_node = allocator_traits<allocator_type>::allocate(v);
+    //     iter._node()->prev->connect(new_node);
+    //     new_node->connect(iter._node());
+
+    //     ++_size();
+    // }
+
+    // insert the value v in front of the given iterator
+    // void insert(const iterator_type& iter, value_type&& v) noexcept
+    // {
+    //     node_base_type* new_node = allocator_traits<allocator_type>::allocate(v);
+    //     iter._node().prev->connect(new_node);
+    //     new_node->connect(iter._node());
+
+    //     ++_size();
+    // }
+
+    value_type* data(void) noexcept
+    {
+        return m_arr;
+    }
+
+    const value_type* data(void) const noexcept
+    {
+        return m_arr;
+    }
+
+    value_type& at(index_type i) noexcept
+    {
+        // TODO: boundary check
+        return _at(i);
+    }
+
+    const value_type& at(index_type i) const noexcept
+    {
+        // TODO: boundary check
+        return _at(i);
+    }
+
+    value_type& operator[](index_type i) noexcept
+    {
+        return at(i);
+    }
+
+    const value_type& operator[](index_type i) const noexcept
+    {
+        return at(i);
+    }
+
+    void push_back(const value_type& v) noexcept
+    {
+        if (m_size == m_capacity)
+            resize(m_capacity * 2);
+        allocator_traits<allocator_type>::construct(m_arr + m_size, v);
+        ++m_size;
+    }
+
+    void push_back(value_type&& v) noexcept
+    {
+        if (m_size == m_capacity)
+            resize(m_capacity * 2);
+        allocator_traits<allocator_type>::construct(m_arr + m_size, move(v));
+        ++m_size;
+    }
+
+    template <typename... Args>
+    iterator_type emplace_back(Args&&... args)
+    {
+        push_back(value_type(forward<Args>(args)...));
+        return back();
+    }
+
+    void pop_back(void) noexcept
+    {
+        allocator_traits<allocator_type>::deconstruct(&*back());
+        --m_size;
+    }
+
+    size_type size(void) const noexcept
+    {
+        return m_size;
+    }
+
+    size_type capacity(void) const noexcept
+    {
+        return m_capacity;
+    }
+
+    const_iterator_type cbegin() const noexcept
+    {
+        return const_iterator_type(m_arr);
+    }
+
+    const_iterator_type cend() const noexcept
+    {
+        return const_iterator_type(m_arr + m_size);
+    }
+
+    iterator_type begin() noexcept
+    {
+        return iterator_type(m_arr);
+    }
+
+    const_iterator_type begin() const noexcept
+    {
+        return cbegin();
+    }
+
+    iterator_type end() noexcept
+    {
+        return iterator_type(m_arr + m_size);
+    }
+
+    const_iterator_type end() const noexcept
+    {
+        return cend();
+    }
+
+    iterator_type back() noexcept
+    {
+        return iterator_type(m_arr + m_size - 1);
+    }
+
+    const_iterator_type back() const noexcept
+    {
+        return const_iterator_type(m_arr + m_size - 1);
+    }
+
+    bool empty(void) const noexcept
+    {
+        return size() == 0;
+    }
+
+    void clear(void)
+    {
+        for (size_t i = 0; i < size(); ++i)
+            allocator_traits<allocator_type>::deconstruct(m_arr + i);
+        m_size = 0;
+    }
+
+    // TODO
+
+    // iterator_type r_start() noexcept;
+    // iterator_type r_end() noexcept;
+
+    // iterator_type cr_start() noexcept;
+    // iterator_type cr_end() noexcept;
+
+protected:
+    inline const value_type& _at(index_type i) const
+    {
+        return m_arr[i];
+    }
+    inline value_type& _at(index_type i)
+    {
+        return m_arr[i];
+    }
+
+protected:
+    value_type* m_arr;
+    size_type m_capacity;
+    size_type m_size;
+};
+
+} // namespace types

+ 115 - 3
src/asm/interrupt.s

@@ -2,26 +2,27 @@
 
 .text
 
+# TODO: stack alignment
 .globl int6
 .type  int6 @function
 int6:
-    xchgw %bx, %bx
     pushal
     call int6_handler
     popal
 
     iret
 
+# TODO: stack alignment
 .globl int8
 .type  int8 @function
 int8:
     nop
     iret
 
+# TODO: stack alignment
 .globl int13
 .type  int13 @function
 int13:
-    xchgw %bx, %bx
     pushal
     call int13_handler
     popal
@@ -33,11 +34,21 @@ int13:
 .globl int14
 .type  int14 @function
 int14:
-    xchgw %bx, %bx
     pushal
     movl %cr2, %eax
     pushl %eax
+
+    # stack alignment and push *data
+    movl %esp, %eax
+    subl $0x4, %esp
+    andl $0xfffffff0, %esp
+    movl %eax, (%esp)
+
     call int14_handler
+
+    # restore stack
+    popl %esp
+
     popl %eax
     popal
 
@@ -49,7 +60,18 @@ int14:
 .type  irq0 @function
 irq0:
     pushal
+
+    # stack alignment and push *data
+    movl %esp, %eax
+    subl $0x4, %esp
+    andl $0xfffffff0, %esp
+    movl %eax, (%esp)
+
     call irq0_handler
+
+    # restore stack
+    popl %esp
+
     popal
     iret
 
@@ -171,3 +193,93 @@ asm_load_idt:
     sti
 asm_load_idt_skip:
     ret
+
+.globl syscall_stub
+.type  syscall_stub @function
+syscall_stub:
+    cmpl $8, %eax
+    jge syscall_stub_end
+    pushal
+
+    # syscall function no. is in %eax
+    # store it in %ebx
+    movl %eax, %ebx
+    # stack alignment and push *data
+    movl %esp, %eax
+    subl $0x4, %esp
+    andl $0xfffffff0, %esp
+    movl %eax, (%esp)
+
+    call *syscall_handlers(,%ebx,4)
+
+    # restore stack
+    popl %esp
+
+    popal
+syscall_stub_end:
+    iret
+
+# parameters:
+# interrupt_stack* ret_stack
+.globl to_kernel
+.type  to_kernel @function
+to_kernel:
+    movw $0x10, %ax
+    movw %ax, %ss
+    movw %ax, %ds
+    movw %ax, %es
+    movw %ax, %fs
+    movw %ax, %gs
+
+    movl 4(%esp), %eax
+
+    movl (%eax), %edi
+    movl 4(%eax), %esi
+    movl 8(%eax), %ebp
+    movl 12(%eax), %esp # %esp is the dst stack
+    movl 16(%eax), %ebx
+    movl 20(%eax), %edx
+    movl 24(%eax), %ecx
+#   TODO: optimize for caching
+#   movl 28(%eax), %eax # we deal with %eax later
+
+    pushl 40(%eax) # %eflags
+    pushl $0x08    # %cs
+    pushl 32(%eax) # %eip
+
+    movl 28(%eax), %eax
+
+    iret
+
+# parameters:
+# interrupt_stack* ret_stack
+.globl to_user
+.type  to_user @function
+to_user:
+    movw $0x23, %ax
+    movw %ax, %ds
+    movw %ax, %es
+    movw %ax, %fs
+    movw %ax, %gs
+
+    movl 4(%esp), %edi
+
+    movl 40(%edi), %ebp # save eflags
+    movl 32(%edi), %esi # save eip
+
+    movl 28(%edi), %eax
+    movl 24(%edi), %ecx
+    movl 20(%edi), %edx
+    movl 16(%edi), %ebx
+
+    pushl $0x23    # %ss
+    pushl 12(%edi) # %esp
+    pushl %ebp     # %eflags
+    pushl $0x1b    # %cs
+    pushl %esi     # %eip
+
+    movl 8(%edi), %ebp
+    movl 4(%edi), %esi
+    movl (%edi), %edi
+
+    iret

+ 17 - 17
src/asm/sys.s

@@ -2,6 +2,13 @@
 
 .text
 
+.global asm_switch_pd
+.type   asm_switch_pd @function
+asm_switch_pd:
+    movl 4(%esp), %eax
+    movl %eax, %cr3
+    ret
+
 .global asm_enable_paging
 .type   asm_enable_paging @function
 asm_enable_paging:
@@ -34,7 +41,6 @@ _asm_load_gdt_fin:
 	movw 4(%esp), %ax
 	cmpw $0, %ax
 	je _asm_load_gdt_fin_ret
-    sti
 _asm_load_gdt_fin_ret:
     ret
 
@@ -45,32 +51,26 @@ asm_load_tr:
     movl 4(%esp), %eax
     orl $0, %eax
     ltr %ax
-    sti
     ret
 
-
-# examples for going ring 3
-_test_user_space_program:
-    movl $0x1919810, %eax
-    movl $0xc48c, %ecx
-_reap:
-    cmpl $1000, (%ecx)
-    jl _reap
-_fault:
-    cli
-
-go_user_space_example:
+.globl go_user_space
+.type  go_user_space @function
+go_user_space:
     movl $((4 * 8) | 3), %eax
     movw %ax, %ds
     movw %ax, %es
     movw %ax, %fs
     movw %ax, %gs
 
-    movl %esp, %eax
+    movl 4(%esp), %ebx
     pushl $((4 * 8) | 3)
-    pushl %eax
+    pushl $0x40100000
     pushf
+    # allow interrupts in user mode
+    movl (%esp), %eax
+    orl $0x200, %eax
+    movl %eax, (%esp)
     pushl $((3 * 8) | 3)
-    pushl $_test_user_space_program
+    pushl %ebx
 
     iret

+ 9 - 64
src/boot.s

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

+ 190 - 0
src/fs/fat.cpp

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

+ 22 - 28
ldscript.ld → src/kernel.ld

@@ -3,61 +3,58 @@ OUTPUT_ARCH(i386:i386)
 
 MEMORY
 {
-    WHOLE : org = 0x00000000, l = 4M
+    MEM : org = 0x00000000, l = 4096M
 }
 
 SECTIONS
 {
-    .text.bootsect : AT(0x00)
-    {
-        *(.text.bootsect)
-    } > WHOLE
-
-    .magicnumber : AT(0x1fe)
-    {
-        BYTE(0x55);
-        BYTE(0xaa);
-    } > WHOLE
-
-    .text.loader 0x7e00 : AT(0x200)
+    .text.loader 0x7e00 : AT(0x00000000)
     {
         *(.text.loader)
-        __kernel_size_offset = .;
-        LONG(LOADADDR(.data) - LOADADDR(.text));
+        __p_kernel_text_and_data_size_addr = .;
+        LONG(__loader_end + kernel_text_and_data_size - __real_kernel_start);
         __loader_end = .;
-    } > WHOLE
+    } > MEM
 
     .text 0x100000 : AT(LOADADDR(.text.loader) + SIZEOF(.text.loader))
     {
         __real_kernel_start = .;
         *(.text*)
         *(.rodata*)
-    } > WHOLE
 
-    .data : AT(LOADADDR(.text) + ADDR(.data) - ADDR(.text))
-    {
+        kernel_text_and_data_size = .;
+        LONG(__kernel_text_and_data_end - ADDR(.text));
         asm_kernel_size = .;
         LONG(__real_kernel_end - ADDR(.text));
+        bss_section_start_addr = .;
+        LONG(ABSOLUTE(__bss_start));
+        bss_section_end_addr = .;
+        LONG(ABSOLUTE(__bss_end));
 
         start_ctors = .;
         KEEP(*(.init_array));
         KEEP(*(SORT_BY_INIT_PRIORITY(.init_array*)));
+        KEEP(*(.ctors));
+        KEEP(*(SORT_BY_INIT_PRIORITY(.ctors*)));
         end_ctors = .;
 
         *(.data)
         *(.data*)
-    } > WHOLE
+        __kernel_text_and_data_end = .;
+    } > MEM
 
-    .bss :
+    .bss ALIGN(0x1000) :
     {
+        __bss_start = .;
         *(.bss)
         *(.bss*)
-    } > WHOLE
+        __bss_end = ALIGN(0x1000);
+    } > MEM
 
     .kernel_end :
     {
         __real_kernel_end = .;
-    } > WHOLE
+    } > MEM
 
     /* Stabs debugging sections.  */
     .stab          0 : { *(.stab) }
@@ -93,14 +90,11 @@ SECTIONS
     .debug_typenames 0 : { *(.debug_typenames) }
     .debug_varnames  0 : { *(.debug_varnames) }
 
-    .end : AT(0x0ffffff)
-    {
-        BYTE(0x00);
-    } > WHOLE
-
     /DISCARD/ :
     {
         *(.fini_array*)
         *(.eh_frame*)
+        *(.text.bootsect)
+        *(.note*)
     }
 }

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

@@ -1,16 +1,20 @@
-#include <kernel/tty.h>
 #include <asm/port_io.h>
 #include <kernel/event/event.h>
 #include <kernel/input/input_event.h>
 #include <kernel/stdio.h>
+#include <kernel/tty.h>
+#include <types/allocator.hpp>
 #include <types/list.hpp>
 
-static ::types::list<::input_event> _input_event_queue {};
+static ::types::list<::input_event>* _input_event_queue;
 
 namespace event {
 ::types::list<::input_event>& input_event_queue(void)
 {
-    return _input_event_queue;
+    if (!_input_event_queue) {
+        _input_event_queue = types::kernel_allocator_new<types::list<input_event>>();
+    }
+    return *_input_event_queue;
 }
 } // namespace event
 

+ 232 - 0
src/kernel/hw/ata.cpp

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

+ 57 - 22
src/kernel/hw/serial.c

@@ -1,31 +1,35 @@
-#include <kernel/hw/serial.h>
 #include <asm/port_io.h>
+#include <kernel/hw/serial.h>
+#include <kernel/stdio.h>
+#include <kernel/tty.h>
 
 int32_t init_serial_port(port_id_t port)
 {
     // taken from osdev.org
 
-   asm_outb(port + 1, 0x00);    // Disable all interrupts
-   asm_outb(port + 3, 0x80);    // Enable DLAB (set baud rate divisor)
-   // TODO: set baud rate
-   asm_outb(port + 0, 0x00);    // Set divisor to 0 -3- (lo byte) 115200 -38400- baud
-   asm_outb(port + 1, 0x00);    //                  (hi byte)
-   asm_outb(port + 3, 0x03);    // 8 bits, no parity, one stop bit
-   asm_outb(port + 2, 0xC7);    // Enable FIFO, clear them, with 14-byte threshold
-   // TODO: IRQ disabled
-   // asm_outb(port + 4, 0x0B);    // IRQs enabled, RTS/DSR set
-   asm_outb(port + 4, 0x1E);    // Set in loopback mode, test the serial chip
-   asm_outb(port + 0, 0xAE);    // Test serial chip (send byte 0xAE and check if serial returns same byte)
-
-   // Check if serial is faulty (i.e: not same byte as sent)
-   if(asm_inb(port + 0) != 0xAE) {
-      return 1;
-   }
-
-   // If serial is not faulty set it in normal operation mode
-   // (not-loopback with IRQs enabled and OUT#1 and OUT#2 bits enabled)
-   asm_outb(port + 4, 0x0F);
-   return 0;
+    asm_outb(port + 1, 0x00); // Disable all interrupts
+    asm_outb(port + 3, 0x80); // Enable DLAB (set baud rate divisor)
+    // TODO: set baud rate
+    asm_outb(port + 0, 0x00); // Set divisor to 0 -3- (lo byte) 115200 -38400- baud
+    asm_outb(port + 1, 0x00); //                  (hi byte)
+    asm_outb(port + 3, 0x03); // 8 bits, no parity, one stop bit
+    asm_outb(port + 2, 0xC7); // Enable FIFO, clear them, with 14-byte threshold
+    // TODO: IRQ disabled
+    asm_outb(port + 4, 0x0B); // IRQs enabled, RTS/DSR set
+    asm_outb(port + 4, 0x1E); // Set in loopback mode, test the serial chip
+    asm_outb(port + 0, 0xAE); // Test serial chip (send byte 0xAE and check if serial returns same byte)
+
+    // Check if serial is faulty (i.e: not same byte as sent)
+    if (asm_inb(port + 0) != 0xAE) {
+        return 1;
+    }
+
+    // If serial is not faulty set it in normal operation mode
+    // (not-loopback with IRQs enabled and OUT#1 and OUT#2 bits enabled)
+    asm_outb(port + 4, 0x0F);
+
+    asm_outb(port + 1, 0x01); // Enable interrupts #0: Received Data Available
+    return 0;
 }
 
 int32_t is_serial_has_data(port_id_t port)
@@ -51,3 +55,34 @@ void serial_send_data(port_id_t port, uint8_t data)
         ;
     return asm_outb(port, data);
 }
+
+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;
+        }
+    }
+}

+ 110 - 89
src/kernel/interrupt.c → src/kernel/interrupt.cpp

@@ -2,13 +2,20 @@
 
 #include <asm/port_io.h>
 #include <kernel/hw/keyboard.h>
+#include <kernel/hw/serial.h>
 #include <kernel/hw/timer.h>
 #include <kernel/interrupt.h>
 #include <kernel/mem.h>
+#include <kernel/mm.hpp>
+#include <kernel/process.hpp>
 #include <kernel/stdio.h>
+#include <kernel/syscall.hpp>
 #include <kernel/tty.h>
+#include <kernel/vfs.hpp>
 #include <kernel/vga.h>
 #include <kernel_main.h>
+#include <types/size.h>
+#include <types/stdint.h>
 
 static struct IDT_entry IDT[256];
 
@@ -19,13 +26,16 @@ void init_idt()
     memset(IDT, 0x00, sizeof(IDT));
 
     // invalid opcode
-    SET_IDT_ENTRY_FN(6, int6, 0x08);
+    SET_IDT_ENTRY_FN(6, int6, 0x08, KERNEL_INTERRUPT_GATE_TYPE);
     // double fault
-    SET_IDT_ENTRY_FN(8, int8, 0x08);
+    SET_IDT_ENTRY_FN(8, int8, 0x08, KERNEL_INTERRUPT_GATE_TYPE);
     // general protection
-    SET_IDT_ENTRY_FN(13, int13, 0x08);
+    SET_IDT_ENTRY_FN(13, int13, 0x08, KERNEL_INTERRUPT_GATE_TYPE);
     // page fault
-    SET_IDT_ENTRY_FN(14, int14, 0x08);
+    SET_IDT_ENTRY_FN(14, int14, 0x08, KERNEL_INTERRUPT_GATE_TYPE);
+    // system call
+    SET_IDT_ENTRY_FN(0x80, syscall_stub, 0x08, USER_INTERRUPT_GATE_TYPE);
+    init_syscall();
 
     uint16_t idt_descriptor[3];
     idt_descriptor[0] = sizeof(struct IDT_entry) * 256;
@@ -73,35 +83,35 @@ void init_pic(void)
     asm_sti();
 }
 
-void int6_handler(
+extern "C" void int6_handler(
     struct regs_32 s_regs,
-    uint32_t error_code,
     ptr_t eip,
-    uint16_t cs)
+    uint16_t cs,
+    uint32_t eflags)
 {
     char buf[512];
 
-    tty_print(console, "---- INVALID OPCODE ----\n");
+    tty_print(console, "\n---- INVALID OPCODE ----\n");
 
     snprintf(
         buf, 512,
         "eax: %x, ebx: %x, ecx: %x, edx: %x\n"
         "esp: %x, ebp: %x, esi: %x, edi: %x\n"
-        "eip: %x, cs: %x, error_code: %x   \n",
+        "eip: %x, cs: %x, eflags: %x       \n",
         s_regs.eax, s_regs.ebx, s_regs.ecx,
         s_regs.edx, s_regs.esp, s_regs.ebp,
         s_regs.esi, s_regs.edi, eip,
-        cs, error_code);
+        cs, eflags);
     tty_print(console, buf);
 
-    tty_print(console, "----   HALTING SYSTEM   ----");
+    tty_print(console, "----   HALTING SYSTEM   ----\n");
 
     asm_cli();
     asm_hlt();
 }
 
 // general protection
-void int13_handler(
+extern "C" void int13_handler(
     struct regs_32 s_regs,
     uint32_t error_code,
     ptr_t eip,
@@ -110,7 +120,7 @@ void int13_handler(
 {
     char buf[512];
 
-    tty_print(console, "---- SEGMENTATION FAULT ----\n");
+    tty_print(console, "\n---- SEGMENTATION FAULT ----\n");
 
     snprintf(
         buf, 512,
@@ -124,154 +134,165 @@ void int13_handler(
         cs, error_code, eflags);
     tty_print(console, buf);
 
-    tty_print(console, "----   HALTING SYSTEM   ----");
+    tty_print(console, "----   HALTING SYSTEM   ----\n");
 
     asm_cli();
     asm_hlt();
 }
 
-static size_t page_fault_times;
+struct PACKED int14_data {
+    linr_ptr_t l_addr;
+    struct regs_32 s_regs;
+    struct page_fault_error_code error_code;
+    void* v_eip;
+    uint32_t cs;
+    uint32_t eflags;
+};
 
-// page fault
-void int14_handler(
-    linr_ptr_t l_addr,
-    struct regs_32 s_regs,
-    struct page_fault_error_code error_code,
-    void* v_eip,
-    uint16_t cs,
-    uint32_t eflags)
+static inline void _int14_panic(void* eip, linr_ptr_t cr2, struct page_fault_error_code error_code)
 {
+    char buf[256] {};
+    snprintf(
+        buf, 256,
+        "\nkilled: segmentation fault (eip: %x, cr2: %x, error_code: %x)\n", eip, cr2, error_code);
+    tty_print(console, buf);
     MAKE_BREAK_POINT();
-    char buf[512];
+    asm_cli();
+    asm_hlt();
+}
+
+// page fault
+extern "C" void int14_handler(int14_data* d)
+{
+    mm_list* mms = nullptr;
+    if (current_process)
+        mms = &current_process->mms;
+    else
+        mms = kernel_mms;
+
+    mm* mm_area = find_mm_area(mms, d->l_addr);
+    if (!mm_area)
+        _int14_panic(d->v_eip, d->l_addr, d->error_code);
+
+    page_directory_entry* pde = mms_get_pd(mms) + linr_addr_to_pd_i(d->l_addr);
+    page_table_entry* pte = (page_table_entry*)p_ptr_to_v_ptr(page_to_phys_addr(pde->in.pt_page));
+    pte += linr_addr_to_pt_i(d->l_addr);
+    struct page* page = find_page_by_l_ptr(mms, d->l_addr);
+
+    if (d->error_code.present == 0 && !mm_area->mapped_file)
+        _int14_panic(d->v_eip, d->l_addr, d->error_code);
+
+    // copy on write
+    if (page->attr.cow == 1) {
+        // if it is a dying page
+        if (*page->ref_count == 1) {
+            page->attr.cow = 0;
+            pte->in.a = 0;
+            pte->in.rw = mm_area->attr.write;
+            return;
+        }
+        // duplicate the page
+        page_t new_page = alloc_raw_page();
 
-    ++page_fault_times;
+        // memory mapped
+        if (d->error_code.present == 0)
+            pte->in.p = 1;
 
-    // not present page, possibly mapped but not loaded
-    // or invalid address or just invalid address
-    // TODO: mmapping and swapping
-    if (error_code.present == 0) {
-        goto kill;
-    }
+        char* new_page_data = (char*)p_ptr_to_v_ptr(page_to_phys_addr(new_page));
+        memcpy(new_page_data, p_ptr_to_v_ptr(page_to_phys_addr(page->phys_page_id)), PAGE_SIZE);
 
-    // kernel code
-    if (cs == KERNEL_CODE_SEGMENT) {
-        if (is_l_ptr_valid(kernel_mm_head, l_addr) != GB_OK) {
-            goto kill;
-        }
-        struct page* page = find_page_by_l_ptr(kernel_mm_head, l_addr);
-
-        // copy on write
-        if (error_code.write == 1 && page->attr.cow == 1) {
-            page_directory_entry* pde = kernel_mm_head->pd + linr_addr_to_pd_i(l_addr);
-            page_table_entry* pte = p_ptr_to_v_ptr(page_to_phys_addr(pde->in.pt_page));
-            pte += linr_addr_to_pt_i(l_addr);
-
-            // if it is a dying page
-            if (*page->ref_count == 1) {
-                page->attr.cow = 0;
-                pte->in.a = 0;
-                pte->in.rw = 1;
-                return;
-            }
-            // duplicate the page
-            page_t new_page = alloc_raw_page();
-            void* new_page_data = p_ptr_to_v_ptr(page_to_phys_addr(new_page));
-            memcpy(new_page_data, p_ptr_to_v_ptr(page_to_phys_addr(page->phys_page_id)), PAGE_SIZE);
-
-            pte->in.page = new_page;
-            pte->in.rw = 1;
-            pte->in.a = 0;
+        pte->in.page = new_page;
+        pte->in.rw = mm_area->attr.write;
+        pte->in.a = 0;
 
-            --*page->ref_count;
+        --*page->ref_count;
 
-            page->ref_count = (size_t*)k_malloc(sizeof(size_t));
-            *page->ref_count = 1;
-            page->attr.cow = 0;
-            page->phys_page_id = new_page;
-            return;
+        page->ref_count = (size_t*)k_malloc(sizeof(size_t));
+        *page->ref_count = 1;
+        page->attr.cow = 0;
+        page->phys_page_id = new_page;
+
+        // memory mapped
+        if (d->error_code.present == 0) {
+            size_t offset = (d->l_addr - mm_area->start) & 0xfffff000;
+            vfs_read(mm_area->mapped_file, new_page_data, PAGE_SIZE, mm_area->file_offset + offset, PAGE_SIZE);
         }
     }
-
-kill:
-    snprintf(
-        buf, 512,
-        "killed: segmentation fault (eip: %x, cr2: %x, error_code: %x)", v_eip, l_addr, error_code);
-    tty_print(console, buf);
-    asm_cli();
-    asm_hlt();
 }
 
-void irq0_handler(void)
+extern "C" void irq0_handler(struct interrupt_stack* d)
 {
     inc_tick();
     asm_outb(PORT_PIC1_COMMAND, PIC_EOI);
+    do_scheduling(d);
 }
 // keyboard interrupt
-void irq1_handler(void)
+extern "C" void irq1_handler(void)
 {
     asm_outb(PORT_PIC1_COMMAND, PIC_EOI);
     handle_keyboard_interrupt();
 }
-void irq2_handler(void)
+extern "C" void irq2_handler(void)
 {
     asm_outb(PORT_PIC1_COMMAND, PIC_EOI);
 }
-void irq3_handler(void)
+extern "C" void irq3_handler(void)
 {
     asm_outb(PORT_PIC1_COMMAND, PIC_EOI);
 }
-void irq4_handler(void)
+extern "C" void irq4_handler(void)
 {
+    serial_receive_data_interrupt();
     asm_outb(PORT_PIC1_COMMAND, PIC_EOI);
 }
-void irq5_handler(void)
+extern "C" void irq5_handler(void)
 {
     asm_outb(PORT_PIC1_COMMAND, PIC_EOI);
 }
-void irq6_handler(void)
+extern "C" void irq6_handler(void)
 {
     asm_outb(PORT_PIC1_COMMAND, PIC_EOI);
 }
-void irq7_handler(void)
+extern "C" void irq7_handler(void)
 {
     asm_outb(PORT_PIC1_COMMAND, PIC_EOI);
 }
-void irq8_handler(void)
+extern "C" void irq8_handler(void)
 {
     asm_outb(PORT_PIC2_COMMAND, PIC_EOI);
     asm_outb(PORT_PIC1_COMMAND, PIC_EOI);
 }
-void irq9_handler(void)
+extern "C" void irq9_handler(void)
 {
     asm_outb(PORT_PIC2_COMMAND, PIC_EOI);
     asm_outb(PORT_PIC1_COMMAND, PIC_EOI);
 }
-void irq10_handler(void)
+extern "C" void irq10_handler(void)
 {
     asm_outb(PORT_PIC2_COMMAND, PIC_EOI);
     asm_outb(PORT_PIC1_COMMAND, PIC_EOI);
 }
-void irq11_handler(void)
+extern "C" void irq11_handler(void)
 {
     asm_outb(PORT_PIC2_COMMAND, PIC_EOI);
     asm_outb(PORT_PIC1_COMMAND, PIC_EOI);
 }
-void irq12_handler(void)
+extern "C" void irq12_handler(void)
 {
     asm_outb(PORT_PIC2_COMMAND, PIC_EOI);
     asm_outb(PORT_PIC1_COMMAND, PIC_EOI);
 }
-void irq13_handler(void)
+extern "C" void irq13_handler(void)
 {
     asm_outb(PORT_PIC2_COMMAND, PIC_EOI);
     asm_outb(PORT_PIC1_COMMAND, PIC_EOI);
 }
-void irq14_handler(void)
+extern "C" void irq14_handler(void)
 {
     asm_outb(PORT_PIC2_COMMAND, PIC_EOI);
     asm_outb(PORT_PIC1_COMMAND, PIC_EOI);
 }
-void irq15_handler(void)
+extern "C" void irq15_handler(void)
 {
     asm_outb(PORT_PIC2_COMMAND, PIC_EOI);
     asm_outb(PORT_PIC1_COMMAND, PIC_EOI);

+ 0 - 545
src/kernel/mem.c

@@ -1,545 +0,0 @@
-#include <asm/boot.h>
-#include <asm/port_io.h>
-#include <asm/sys.h>
-#include <kernel/errno.h>
-#include <kernel/mem.h>
-#include <kernel/stdio.h>
-#include <kernel/task.h>
-#include <kernel/vga.h>
-#include <kernel_main.h>
-#include <types/bitmap.h>
-#include <types/list.h>
-
-// static variables
-
-struct mm kernel_mm;
-struct mm* kernel_mm_head;
-
-// ---------------------
-
-// constant values
-
-#define EMPTY_PAGE_ADDR ((phys_ptr_t)0x5000)
-#define EMPTY_PAGE_END ((phys_ptr_t)0x6000)
-
-// ---------------------
-
-static void* p_start;
-static void* p_break;
-
-static size_t mem_size;
-static char mem_bitmap[1024 * 1024 / 8];
-
-static int32_t set_heap_start(void* start_addr)
-{
-    p_start = start_addr;
-    return 0;
-}
-
-static int32_t brk(void* addr)
-{
-    if (addr >= KERNEL_HEAP_LIMIT) {
-        return GB_FAILED;
-    }
-    p_break = addr;
-    return 0;
-}
-
-// sets errno when failed to increase heap pointer
-static void* sbrk(size_t increment)
-{
-    if (brk(p_break + increment) != 0) {
-        errno = ENOMEM;
-        return 0;
-    } else {
-        errno = 0;
-        return p_break;
-    }
-}
-
-int init_heap(void)
-{
-    set_heap_start(KERNEL_HEAP_START);
-
-    if (brk(KERNEL_HEAP_START) != 0) {
-        return GB_FAILED;
-    }
-    struct mem_blk* p_blk = sbrk(0);
-    p_blk->size = 4;
-    p_blk->flags.has_next = 0;
-    p_blk->flags.is_free = 1;
-    return GB_OK;
-}
-
-// @param start_pos position where to start finding
-// @param size the size of the block we're looking for
-// @return found block if suitable block exists, if not, the last block
-static struct mem_blk*
-find_blk(
-    struct mem_blk* start_pos,
-    size_t size)
-{
-    while (1) {
-        if (start_pos->flags.is_free && start_pos->size >= size) {
-            errno = 0;
-            return start_pos;
-        } else {
-            if (!start_pos->flags.has_next) {
-                errno = ENOTFOUND;
-                return start_pos;
-            }
-            start_pos = ((void*)start_pos)
-                + sizeof(struct mem_blk)
-                + start_pos->size
-                - 4 * sizeof(uint8_t);
-        }
-    }
-}
-
-static struct mem_blk*
-allocate_new_block(
-    struct mem_blk* blk_before,
-    size_t size)
-{
-    sbrk(sizeof(struct mem_blk) + size - 4 * sizeof(uint8_t));
-    if (errno) {
-        return 0;
-    }
-
-    struct mem_blk* blk = ((void*)blk_before)
-        + sizeof(struct mem_blk)
-        + blk_before->size
-        - 4 * sizeof(uint8_t);
-
-    blk_before->flags.has_next = 1;
-
-    blk->flags.has_next = 0;
-    blk->flags.is_free = 1;
-    blk->size = size;
-
-    errno = 0;
-    return blk;
-}
-
-static void split_block(
-    struct mem_blk* blk,
-    size_t this_size)
-{
-    // block is too small to get split
-    if (blk->size < sizeof(struct mem_blk) + this_size) {
-        return;
-    }
-
-    struct mem_blk* blk_next = ((void*)blk)
-        + sizeof(struct mem_blk)
-        + this_size
-        - 4 * sizeof(uint8_t);
-
-    blk_next->size = blk->size
-        - this_size
-        - sizeof(struct mem_blk)
-        + 4 * sizeof(uint8_t);
-
-    blk_next->flags.has_next = blk->flags.has_next;
-    blk_next->flags.is_free = 1;
-
-    blk->flags.has_next = 1;
-    blk->size = this_size;
-}
-
-void* k_malloc(size_t size)
-{
-    struct mem_blk* block_allocated;
-
-    block_allocated = find_blk(p_start, size);
-    if (errno == ENOTFOUND) {
-        // 'block_allocated' in the argument list is the pointer
-        // pointing to the last block
-        block_allocated = allocate_new_block(block_allocated, size);
-        // no need to check errno and return value
-        // preserve these for the caller
-    } else {
-        split_block(block_allocated, size);
-    }
-
-    block_allocated->flags.is_free = 0;
-    return block_allocated->data;
-}
-
-void k_free(void* ptr)
-{
-    ptr -= (sizeof(struct mem_blk_flags) + sizeof(size_t));
-    struct mem_blk* blk = (struct mem_blk*)ptr;
-    blk->flags.is_free = 1;
-    // TODO: fusion free blocks nearby
-}
-
-void* p_ptr_to_v_ptr(phys_ptr_t p_ptr)
-{
-    if (p_ptr <= 0x30000000) {
-        // memory below 768MiB is identically mapped
-        return (void*)p_ptr;
-    } else {
-        // TODO: address translation
-        MAKE_BREAK_POINT();
-        return (void*)0xffffffff;
-    }
-}
-
-phys_ptr_t l_ptr_to_p_ptr(struct mm* mm, linr_ptr_t v_ptr)
-{
-    if (mm == kernel_mm_head && v_ptr < (linr_ptr_t)KERNEL_IDENTICALLY_MAPPED_AREA_LIMIT) {
-        return (phys_ptr_t)v_ptr;
-    }
-    while (mm != NULL) {
-        if (v_ptr < mm->start || v_ptr >= mm->start + mm->len * 4096) {
-            goto next;
-        }
-        size_t offset = (size_t)(v_ptr - mm->start);
-        LIST_LIKE_AT(struct page, mm->pgs, offset / PAGE_SIZE, result);
-        return page_to_phys_addr(result->phys_page_id) + (offset % 4096);
-    next:
-        mm = mm->next;
-    }
-
-    // TODO: handle error
-    return 0xffffffff;
-}
-
-phys_ptr_t v_ptr_to_p_ptr(void* v_ptr)
-{
-    return l_ptr_to_p_ptr(kernel_mm_head, (linr_ptr_t)v_ptr);
-}
-
-static inline void mark_page(page_t n)
-{
-    bm_set(mem_bitmap, n);
-}
-
-static inline void free_page(page_t n)
-{
-    bm_clear(mem_bitmap, n);
-}
-
-static void mark_addr_len(phys_ptr_t start, size_t n)
-{
-    if (n == 0)
-        return;
-    page_t start_page = phys_addr_to_page(start);
-    page_t end_page = phys_addr_to_page(start + n + 4095);
-    for (page_t i = start_page; i < end_page; ++i)
-        mark_page(i);
-}
-
-static void free_addr_len(phys_ptr_t start, size_t n)
-{
-    if (n == 0)
-        return;
-    page_t start_page = phys_addr_to_page(start);
-    page_t end_page = phys_addr_to_page(start + n + 4095);
-    for (page_t i = start_page; i < end_page; ++i)
-        free_page(i);
-}
-
-static inline void mark_addr_range(phys_ptr_t start, phys_ptr_t end)
-{
-    mark_addr_len(start, end - start);
-}
-
-static inline void free_addr_range(phys_ptr_t start, phys_ptr_t end)
-{
-    free_addr_len(start, end - start);
-}
-
-page_t alloc_raw_page(void)
-{
-    for (page_t i = 0; i < 1024 * 1024; ++i) {
-        if (bm_test(mem_bitmap, i) == 0) {
-            mark_page(i);
-            return i;
-        }
-    }
-    return GB_FAILED;
-}
-
-struct page* allocate_page(void)
-{
-    // TODO: allocate memory on identically mapped area
-    struct page* p = (struct page*)k_malloc(sizeof(struct page));
-    memset(p, 0x00, sizeof(struct page));
-    p->phys_page_id = alloc_raw_page();
-    p->ref_count = (size_t*)k_malloc(sizeof(size_t));
-    return p;
-}
-
-static inline void make_page_table(page_table_entry* pt)
-{
-    memset(pt, 0x00, sizeof(page_table_entry) * 1024);
-}
-
-static inline void init_mem_layout(void)
-{
-    mem_size = 1024 * mem_size_info.n_1k_blks;
-    mem_size += 64 * 1024 * mem_size_info.n_64k_blks;
-
-    // mark kernel page directory
-    mark_addr_range(0x00000000, 0x00005000);
-    // mark empty page
-    mark_addr_range(EMPTY_PAGE_ADDR, EMPTY_PAGE_END);
-    // mark EBDA and upper memory as allocated
-    mark_addr_range(0x80000, 0xfffff);
-    // mark kernel
-    mark_addr_len(0x00100000, kernel_size);
-
-    if (e820_mem_map_entry_size == 20) {
-        struct e820_mem_map_entry_20* entry = (struct e820_mem_map_entry_20*)e820_mem_map;
-        for (uint32_t i = 0; i < e820_mem_map_count; ++i, ++entry) {
-            if (entry->type != 1) {
-                mark_addr_len(entry->base, entry->len);
-            }
-        }
-    } else {
-        struct e820_mem_map_entry_24* entry = (struct e820_mem_map_entry_24*)e820_mem_map;
-        for (uint32_t i = 0; i < e820_mem_map_count; ++i, ++entry) {
-            if (entry->in.type != 1) {
-                mark_addr_len(entry->in.base, entry->in.len);
-            }
-        }
-    }
-}
-
-int is_l_ptr_valid(struct mm* mm_area, linr_ptr_t l_ptr)
-{
-    while (mm_area != NULL) {
-        if (l_ptr >= mm_area->start && l_ptr < mm_area->start + mm_area->len * PAGE_SIZE) {
-            return GB_OK;
-        }
-        mm_area = mm_area->next;
-    }
-    return GB_FAILED;
-}
-
-struct page* find_page_by_l_ptr(struct mm* mm, linr_ptr_t l_ptr)
-{
-    if (mm == kernel_mm_head && l_ptr < (linr_ptr_t)KERNEL_IDENTICALLY_MAPPED_AREA_LIMIT) {
-        // TODO: make mm for identically mapped area
-        MAKE_BREAK_POINT();
-        return (struct page*)0xffffffff;
-    }
-    while (mm != NULL) {
-        if (l_ptr >= mm->start && l_ptr < mm->start + mm->len * 4096) {
-            size_t offset = (size_t)(l_ptr - mm->start);
-            LIST_LIKE_AT(struct page, mm->pgs, offset / PAGE_SIZE, result);
-            return result;
-        }
-        mm = mm->next;
-    }
-
-    // TODO: error handling
-    return NULL;
-}
-
-void map_raw_page_to_pte(
-    page_table_entry* pte,
-    page_t page,
-    int rw,
-    int priv)
-{
-    // set P bit
-    pte->v = 0x00000001;
-    pte->in.rw = (rw == 1);
-    pte->in.us = (priv == 1);
-    pte->in.page = page;
-}
-
-static void _map_raw_page_to_addr(
-    struct mm* mm_area,
-    page_t page,
-    int rw,
-    int priv)
-{
-    linr_ptr_t addr = (linr_ptr_t)mm_area->start + mm_area->len * 4096;
-    page_directory_entry* pde = mm_area->pd + linr_addr_to_pd_i(addr);
-    // page table not exist
-    if (!pde->in.p) {
-        // allocate a page for the page table
-        pde->in.p = 1;
-        pde->in.rw = 1;
-        pde->in.us = 0;
-        pde->in.pt_page = alloc_raw_page();
-
-        make_page_table((page_table_entry*)p_ptr_to_v_ptr(page_to_phys_addr(pde->in.pt_page)));
-    }
-
-    // map the page in the page table
-    page_table_entry* pte = (page_table_entry*)p_ptr_to_v_ptr(page_to_phys_addr(pde->in.pt_page));
-    pte += linr_addr_to_pt_i(addr);
-    map_raw_page_to_pte(pte, page, rw, priv);
-}
-
-// map page to the end of mm_area in pd
-int k_map(
-    struct mm* mm_area,
-    struct page* page,
-    int read,
-    int write,
-    int priv,
-    int cow)
-{
-    struct page* p_page_end = mm_area->pgs;
-    while (p_page_end != NULL && p_page_end->next != NULL)
-        p_page_end = p_page_end->next;
-
-    if (cow) {
-        // find its ancestor
-        while (page->attr.cow)
-            page = page->next;
-
-        // create a new page node
-        struct page* new_page = k_malloc(sizeof(struct page));
-
-        new_page->attr.read = (read == 1);
-        new_page->attr.write = (write == 1);
-        new_page->attr.system = (priv == 1);
-        new_page->attr.cow = 1;
-        // TODO: move *next out of struct page
-        new_page->next = NULL;
-
-        new_page->phys_page_id = page->phys_page_id;
-        new_page->ref_count = page->ref_count;
-
-        if (p_page_end != NULL)
-            p_page_end->next = new_page;
-        else
-            mm_area->pgs = new_page;
-    } else {
-        page->attr.read = (read == 1);
-        page->attr.write = (write == 1);
-        page->attr.system = (priv == 1);
-        page->attr.cow = 0;
-        // TODO: move *next out of struct page
-        page->next = NULL;
-
-        if (p_page_end != NULL)
-            p_page_end->next = page;
-        else
-            mm_area->pgs = page;
-    }
-    _map_raw_page_to_addr(
-        mm_area,
-        page->phys_page_id,
-        (write && !cow),
-        priv);
-
-    ++mm_area->len;
-    ++*page->ref_count;
-    return GB_OK;
-}
-
-// map a page identically
-// this function is only meant to be used in the initialization process
-// it checks the pde's P bit so you need to make sure it's already set
-// to avoid dead loops
-static inline void _init_map_page_identically(page_t page)
-{
-    page_directory_entry* pde = KERNEL_PAGE_DIRECTORY_ADDR + page_to_pd_i(page);
-    // page table not exist
-    if (!pde->in.p) {
-        // allocate a page for the page table
-        // set the P bit of the pde in advance
-        pde->in.p = 1;
-        pde->in.rw = 1;
-        pde->in.us = 0;
-        pde->in.pt_page = alloc_raw_page();
-        _init_map_page_identically(pde->in.pt_page);
-
-        make_page_table((page_table_entry*)p_ptr_to_v_ptr(page_to_phys_addr(pde->in.pt_page)));
-    }
-
-    // map the page in the page table
-    page_table_entry* pt = (page_table_entry*)p_ptr_to_v_ptr(page_to_phys_addr(pde->in.pt_page));
-    pt += page_to_pt_i(page);
-    pt->v = 0x00000003;
-    pt->in.page = page;
-}
-
-static inline void init_paging_map_low_mem_identically(void)
-{
-    for (phys_ptr_t addr = 0x01000000; addr < 0x30000000; addr += 0x1000) {
-        // check if the address is valid and not mapped
-        if (bm_test(mem_bitmap, phys_addr_to_page(addr)))
-            continue;
-        _init_map_page_identically(phys_addr_to_page(addr));
-    }
-}
-
-static struct page empty_page;
-static struct page heap_first_page;
-static size_t heap_first_page_ref_count;
-
-void init_mem(void)
-{
-    init_mem_layout();
-
-    // map the 16MiB-768MiB identically
-    init_paging_map_low_mem_identically();
-
-    kernel_mm_head = &kernel_mm;
-
-    kernel_mm.attr.read = 1;
-    kernel_mm.attr.write = 1;
-    kernel_mm.attr.system = 1;
-    kernel_mm.len = 0;
-    kernel_mm.next = NULL;
-    kernel_mm.pd = KERNEL_PAGE_DIRECTORY_ADDR;
-    kernel_mm.pgs = NULL;
-    kernel_mm.start = (linr_ptr_t)KERNEL_HEAP_START;
-
-    heap_first_page.attr.cow = 0;
-    heap_first_page.attr.read = 1;
-    heap_first_page.attr.write = 1;
-    heap_first_page.attr.system = 1;
-    heap_first_page.next = NULL;
-    heap_first_page.phys_page_id = alloc_raw_page();
-    heap_first_page.ref_count = &heap_first_page_ref_count;
-
-    *heap_first_page.ref_count = 0;
-
-    k_map(kernel_mm_head, &heap_first_page, 1, 1, 1, 0);
-
-    init_heap();
-
-    // create empty_page struct
-    empty_page.attr.cow = 0;
-    empty_page.attr.read = 1;
-    empty_page.attr.write = 0;
-    empty_page.attr.system = 0;
-    empty_page.next = NULL;
-    empty_page.phys_page_id = phys_addr_to_page(EMPTY_PAGE_ADDR);
-    empty_page.ref_count = (size_t*)k_malloc(sizeof(size_t));
-    *empty_page.ref_count = 1;
-
-    // TODO: improve the algorithm SO FREAKING SLOW
-    // while (kernel_mm_head->len < 256 * 1024 * 1024 / PAGE_SIZE) {
-    while (kernel_mm_head->len < 16 * 1024 * 1024 / PAGE_SIZE) {
-        k_map(
-            kernel_mm_head, &empty_page,
-            1, 1, 1, 1);
-    }
-}
-
-void create_segment_descriptor(
-    segment_descriptor* sd,
-    uint32_t base,
-    uint32_t limit,
-    uint32_t flags,
-    uint32_t access)
-{
-    sd->base_low = base & 0x0000ffff;
-    sd->base_mid = ((base & 0x00ff0000) >> 16);
-    sd->base_high = ((base & 0xff000000) >> 24);
-    sd->limit_low = limit & 0x0000ffff;
-    sd->limit_high = ((limit & 0x000f0000) >> 16);
-    sd->access = access;
-    sd->flags = flags;
-}

+ 637 - 0
src/kernel/mem.cpp

@@ -0,0 +1,637 @@
+#include <asm/boot.h>
+#include <asm/port_io.h>
+#include <asm/sys.h>
+#include <kernel/errno.h>
+#include <kernel/mem.h>
+#include <kernel/mm.hpp>
+#include <kernel/process.hpp>
+#include <kernel/stdio.h>
+#include <kernel/task.h>
+#include <kernel/vga.h>
+#include <kernel_main.h>
+#include <types/bitmap.h>
+#include <types/status.h>
+
+// global objects
+
+mm_list* kernel_mms;
+
+// ---------------------
+
+// constant values
+
+#define EMPTY_PAGE_ADDR ((phys_ptr_t)0x5000)
+#define EMPTY_PAGE_END ((phys_ptr_t)0x6000)
+
+#define IDENTICALLY_MAPPED_HEAP_SIZE ((size_t)0x400000)
+
+// ---------------------
+
+static size_t mem_size;
+static char mem_bitmap[1024 * 1024 / 8];
+
+class brk_memory_allocator {
+public:
+    using byte = uint8_t;
+    using size_type = size_t;
+
+    struct mem_blk_flags {
+        uint8_t is_free;
+        uint8_t has_next;
+        uint8_t _unused2;
+        uint8_t _unused3;
+    };
+
+    struct mem_blk {
+        size_t size;
+        struct mem_blk_flags flags;
+        // the first byte of the memory space
+        // the minimal allocated space is 4 bytes
+        uint8_t data[4];
+    };
+
+private:
+    byte* p_start;
+    byte* p_break;
+    byte* p_limit;
+
+    brk_memory_allocator(void) = delete;
+    brk_memory_allocator(const brk_memory_allocator&) = delete;
+    brk_memory_allocator(brk_memory_allocator&&) = delete;
+
+    inline int brk(byte* addr)
+    {
+        if (addr >= p_limit)
+            return GB_FAILED;
+        p_break = addr;
+        return GB_OK;
+    }
+
+    // sets errno
+    inline byte* sbrk(size_type increment)
+    {
+        if (brk(p_break + increment) != GB_OK) {
+            errno = ENOMEM;
+            return nullptr;
+        } else {
+            errno = 0;
+            return p_break;
+        }
+    }
+
+    inline mem_blk* _find_next_mem_blk(mem_blk* blk, size_type blk_size)
+    {
+        byte* p = (byte*)blk;
+        p += sizeof(mem_blk);
+        p += blk_size;
+        p -= (4 * sizeof(byte));
+        return (mem_blk*)p;
+    }
+
+    // sets errno
+    // @param start_pos position where to start finding
+    // @param size the size of the block we're looking for
+    // @return found block if suitable block exists, if not, the last block
+    mem_blk* find_blk(mem_blk* start_pos, size_type size)
+    {
+        while (1) {
+            if (start_pos->flags.is_free && start_pos->size >= size) {
+                errno = 0;
+                return start_pos;
+            } else {
+                if (!start_pos->flags.has_next) {
+                    errno = ENOTFOUND;
+                    return start_pos;
+                }
+                start_pos = _find_next_mem_blk(start_pos, start_pos->size);
+            }
+        }
+    }
+
+    // sets errno
+    mem_blk* allocate_new_block(mem_blk* blk_before, size_type size)
+    {
+        sbrk(sizeof(mem_blk) + size - 4 * sizeof(byte));
+        // preserves errno
+        if (errno) {
+            return nullptr;
+        }
+
+        mem_blk* blk = _find_next_mem_blk(blk_before, blk_before->size);
+
+        blk_before->flags.has_next = 1;
+
+        blk->flags.has_next = 0;
+        blk->flags.is_free = 1;
+        blk->size = size;
+
+        errno = 0;
+        return blk;
+    }
+
+    void split_block(mem_blk* blk, size_type this_size)
+    {
+        // block is too small to get split
+        if (blk->size < sizeof(mem_blk) + this_size) {
+            return;
+        }
+
+        mem_blk* blk_next = _find_next_mem_blk(blk, this_size);
+
+        blk_next->size = blk->size
+            - this_size
+            - sizeof(mem_blk)
+            + 4 * sizeof(byte);
+
+        blk_next->flags.has_next = blk->flags.has_next;
+        blk_next->flags.is_free = 1;
+
+        blk->flags.has_next = 1;
+        blk->size = this_size;
+    }
+
+public:
+    brk_memory_allocator(void* start, size_type limit)
+        : p_start((byte*)start)
+        , p_limit(p_start + limit)
+    {
+        brk(p_start);
+        mem_blk* p_blk = (mem_blk*)sbrk(0);
+        p_blk->size = 4;
+        p_blk->flags.has_next = 0;
+        p_blk->flags.is_free = 1;
+    }
+
+    // sets errno
+    void* alloc(size_type size)
+    {
+        struct mem_blk* block_allocated;
+
+        block_allocated = find_blk((mem_blk*)p_start, size);
+        if (errno == ENOTFOUND) {
+            // 'block_allocated' in the argument list is the pointer
+            // pointing to the last block
+            block_allocated = allocate_new_block(block_allocated, size);
+            if (errno) {
+                // preserves errno
+                return nullptr;
+            }
+        } else {
+            split_block(block_allocated, size);
+        }
+
+        errno = 0;
+        block_allocated->flags.is_free = 0;
+        return block_allocated->data;
+    }
+
+    void free(void* ptr)
+    {
+        mem_blk* blk = (mem_blk*)((byte*)ptr - (sizeof(mem_blk_flags) + sizeof(size_t)));
+        blk->flags.is_free = 1;
+        // TODO: fusion free blocks nearby
+    }
+};
+
+static brk_memory_allocator* kernel_heap_allocator;
+static brk_memory_allocator
+    kernel_ident_mapped_allocator((void*)bss_section_end_addr,
+        IDENTICALLY_MAPPED_HEAP_SIZE);
+
+void* k_malloc(size_t size)
+{
+    return kernel_heap_allocator->alloc(size);
+}
+
+void k_free(void* ptr)
+{
+    kernel_heap_allocator->free(ptr);
+}
+
+void* ki_malloc(size_t size)
+{
+    void* ptr = kernel_ident_mapped_allocator.alloc(size);
+    if (!ptr) {
+        MAKE_BREAK_POINT();
+    }
+    return ptr;
+}
+
+void ki_free(void* ptr)
+{
+    kernel_ident_mapped_allocator.free(ptr);
+}
+
+void* p_ptr_to_v_ptr(phys_ptr_t p_ptr)
+{
+    if (p_ptr <= 0x30000000) {
+        // memory below 768MiB is identically mapped
+        return (void*)p_ptr;
+    } else {
+        // TODO: address translation
+        MAKE_BREAK_POINT();
+        return (void*)0xffffffff;
+    }
+}
+
+phys_ptr_t l_ptr_to_p_ptr(const mm_list* mms, linr_ptr_t v_ptr)
+{
+    for (const mm& item : *mms) {
+        if (v_ptr < item.start || v_ptr >= item.start + item.pgs->size() * PAGE_SIZE)
+            continue;
+        size_t offset = (size_t)(v_ptr - item.start);
+        const page& p = item.pgs->at(offset / PAGE_SIZE);
+        return page_to_phys_addr(p.phys_page_id) + (offset % PAGE_SIZE);
+    }
+
+    // TODO: handle error
+    return 0xffffffff;
+}
+
+phys_ptr_t v_ptr_to_p_ptr(const void* v_ptr)
+{
+    if (v_ptr < KERNEL_IDENTICALLY_MAPPED_AREA_LIMIT) {
+        return (phys_ptr_t)v_ptr;
+    }
+    return l_ptr_to_p_ptr(kernel_mms, (linr_ptr_t)v_ptr);
+}
+
+static inline void mark_page(page_t n)
+{
+    bm_set(mem_bitmap, n);
+}
+
+static inline void free_page(page_t n)
+{
+    bm_clear(mem_bitmap, n);
+}
+
+static void mark_addr_len(phys_ptr_t start, size_t n)
+{
+    if (n == 0)
+        return;
+    page_t start_page = phys_addr_to_page(start);
+    page_t end_page = phys_addr_to_page(start + n + 4095);
+    for (page_t i = start_page; i < end_page; ++i)
+        mark_page(i);
+}
+
+static void free_addr_len(phys_ptr_t start, size_t n)
+{
+    if (n == 0)
+        return;
+    page_t start_page = phys_addr_to_page(start);
+    page_t end_page = phys_addr_to_page(start + n + 4095);
+    for (page_t i = start_page; i < end_page; ++i)
+        free_page(i);
+}
+
+static inline void mark_addr_range(phys_ptr_t start, phys_ptr_t end)
+{
+    mark_addr_len(start, end - start);
+}
+
+static inline void free_addr_range(phys_ptr_t start, phys_ptr_t end)
+{
+    free_addr_len(start, end - start);
+}
+
+page_t alloc_raw_page(void)
+{
+    return alloc_n_raw_pages(1);
+}
+
+// @return the max count (but less than n) of the pages continuously available
+static inline size_t _test_n_raw_pages(page_t start, size_t n)
+{
+    // *start is already allocated
+    if (bm_test(mem_bitmap, start))
+        return 0;
+
+    return 1 + ((n > 1) ? _test_n_raw_pages(start + 1, n - 1) : 0);
+}
+
+page_t alloc_n_raw_pages(size_t n)
+{
+    page_t first = 0;
+    while (first <= 1024 * 1024 - n) {
+        size_t max = _test_n_raw_pages(first, n);
+        if (max != n) {
+            first += (max + 1);
+        } else {
+            for (page_t i = first; i < first + n; ++i)
+                bm_set(mem_bitmap, i);
+            return first;
+        }
+    }
+    MAKE_BREAK_POINT();
+    return 0xffffffff;
+}
+
+struct page allocate_page(void)
+{
+    struct page p { };
+    p.phys_page_id = alloc_raw_page();
+    p.ref_count = types::kernel_ident_allocator_new<size_t>(0);
+    return p;
+}
+
+static inline void make_page_table(page_table_entry* pt)
+{
+    memset(pt, 0x00, sizeof(page_table_entry) * 1024);
+}
+
+page_directory_entry* alloc_pd(void)
+{
+    // TODO: alloc page in low mem and gen struct page for it
+    page_t pd_page = alloc_raw_page();
+    page_directory_entry* pd = (page_directory_entry*)p_ptr_to_v_ptr(page_to_phys_addr(pd_page));
+    memset(pd, 0x00, PAGE_SIZE);
+    return pd;
+}
+
+page_table_entry* alloc_pt(void)
+{
+    // TODO: alloc page in low mem and gen struct page for it
+    page_t pt_page = alloc_raw_page();
+    page_table_entry* pt = (page_table_entry*)p_ptr_to_v_ptr(page_to_phys_addr(pt_page));
+    make_page_table(pt);
+    return pt;
+}
+
+static inline void init_mem_layout(void)
+{
+    mem_size = 1024 * mem_size_info.n_1k_blks;
+    mem_size += 64 * 1024 * mem_size_info.n_64k_blks;
+
+    // mark kernel page directory
+    mark_addr_range(0x00000000, 0x00005000);
+    // mark empty page
+    mark_addr_range(EMPTY_PAGE_ADDR, EMPTY_PAGE_END);
+    // mark EBDA and upper memory as allocated
+    mark_addr_range(0x80000, 0xfffff);
+    // mark kernel
+    mark_addr_len(0x00100000, kernel_size);
+    // mark identically mapped heap
+    mark_addr_len(bss_section_end_addr, IDENTICALLY_MAPPED_HEAP_SIZE);
+
+    if (e820_mem_map_entry_size == 20) {
+        struct e820_mem_map_entry_20* entry = (struct e820_mem_map_entry_20*)e820_mem_map;
+        for (uint32_t i = 0; i < e820_mem_map_count; ++i, ++entry) {
+            if (entry->type != 1) {
+                mark_addr_len(entry->base, entry->len);
+            }
+        }
+    } else {
+        struct e820_mem_map_entry_24* entry = (struct e820_mem_map_entry_24*)e820_mem_map;
+        for (uint32_t i = 0; i < e820_mem_map_count; ++i, ++entry) {
+            if (entry->in.type != 1) {
+                mark_addr_len(entry->in.base, entry->in.len);
+            }
+        }
+    }
+}
+
+mm* find_mm_area(mm_list* mms, linr_ptr_t l_ptr)
+{
+    for (auto iter = mms->begin(); iter != mms->end(); ++iter)
+        if (l_ptr >= iter->start && l_ptr < iter->start + iter->pgs->size() * PAGE_SIZE)
+            return iter.ptr();
+    return nullptr;
+}
+
+struct page* find_page_by_l_ptr(const mm_list* mms, linr_ptr_t l_ptr)
+{
+    for (const mm& item : *mms) {
+        if (l_ptr >= item.start && l_ptr < item.start + item.pgs->size() * PAGE_SIZE) {
+            size_t offset = (size_t)(l_ptr - item.start);
+            return &item.pgs->at(offset / PAGE_SIZE);
+        }
+    }
+
+    // TODO: error handling
+    return nullptr;
+}
+
+static inline void map_raw_page_to_pte(
+    page_table_entry* pte,
+    page_t page,
+    int present,
+    int rw,
+    int priv)
+{
+    // set P bit
+    pte->v = 0;
+    pte->in.p = present;
+    pte->in.rw = (rw == 1);
+    pte->in.us = (priv == 0);
+    pte->in.page = page;
+}
+
+// map page to the end of mm_area in pd
+int k_map(
+    mm* mm_area,
+    const struct page* page,
+    int read,
+    int write,
+    int priv,
+    int cow)
+{
+    linr_ptr_t addr = (linr_ptr_t)mm_area->start + mm_area->pgs->size() * PAGE_SIZE;
+    page_directory_entry* pde = mm_area->pd + linr_addr_to_pd_i(addr);
+    // page table not exist
+    if (!pde->in.p) {
+        // allocate a page for the page table
+        pde->in.p = 1;
+        pde->in.rw = 1;
+        pde->in.us = (priv == 0);
+        pde->in.pt_page = alloc_raw_page();
+
+        make_page_table((page_table_entry*)p_ptr_to_v_ptr(page_to_phys_addr(pde->in.pt_page)));
+    }
+
+    // map the page in the page table
+    page_table_entry* pte = (page_table_entry*)p_ptr_to_v_ptr(page_to_phys_addr(pde->in.pt_page));
+    pte += linr_addr_to_pt_i(addr);
+    map_raw_page_to_pte(pte, page->phys_page_id, read, (write && !cow), priv);
+
+    mm_area->pgs->push_back(*page);
+    mm_area->pgs->back()->attr.cow = cow;
+    ++*page->ref_count;
+    return GB_OK;
+}
+
+bool check_addr_range_avail(const mm* mm_area, void* start, void* end)
+{
+    void* m_start = (void*)mm_area->start;
+    void* m_end = (void*)(mm_area->start + PAGE_SIZE * mm_area->pgs->size());
+
+    if (start >= m_end || end <= m_start)
+        return true;
+    else
+        return false;
+}
+
+static inline int _mmap(
+    mm_list* mms,
+    void* hint,
+    size_t len,
+    fs::inode* file,
+    size_t offset,
+    int write,
+    int priv)
+{
+    if (!file->flags.in.file && !file->flags.in.special_node) {
+        errno = EINVAL;
+        return GB_FAILED;
+    }
+
+    len = (len + PAGE_SIZE - 1) & 0xfffff000;
+    size_t n_pgs = len >> 12;
+
+    for (const auto& mm_area : *mms)
+        if (!check_addr_range_avail(&mm_area, hint, (char*)hint + len)) {
+            errno = EEXIST;
+            return GB_FAILED;
+        }
+
+    auto iter_mm = mms->emplace_back((linr_ptr_t)hint, mms_get_pd(&current_process->mms), write, priv);
+    iter_mm->mapped_file = file;
+    iter_mm->file_offset = offset;
+
+    for (size_t i = 0; i < n_pgs; ++i)
+        k_map(iter_mm.ptr(), &empty_page, 0, write, priv, 1);
+
+    return GB_OK;
+}
+
+int mmap(
+    void* hint,
+    size_t len,
+    fs::inode* file,
+    size_t offset,
+    int write,
+    int priv)
+{
+    return _mmap(&current_process->mms, hint, len, file, offset, write, priv);
+}
+
+// map a page identically
+// this function is only meant to be used in the initialization process
+// it checks the pde's P bit so you need to make sure it's already set
+// to avoid dead loops
+static inline void _init_map_page_identically(page_t page)
+{
+    page_directory_entry* pde = KERNEL_PAGE_DIRECTORY_ADDR + page_to_pd_i(page);
+    // page table not exist
+    if (!pde->in.p) {
+        // allocate a page for the page table
+        // set the P bit of the pde in advance
+        pde->in.p = 1;
+        pde->in.rw = 1;
+        pde->in.us = 0;
+        pde->in.pt_page = alloc_raw_page();
+        _init_map_page_identically(pde->in.pt_page);
+
+        make_page_table((page_table_entry*)p_ptr_to_v_ptr(page_to_phys_addr(pde->in.pt_page)));
+    }
+
+    // map the page in the page table
+    page_table_entry* pt = (page_table_entry*)p_ptr_to_v_ptr(page_to_phys_addr(pde->in.pt_page));
+    pt += page_to_pt_i(page);
+    pt->v = 0x00000003;
+    pt->in.page = page;
+}
+
+static inline void init_paging_map_low_mem_identically(void)
+{
+    for (phys_ptr_t addr = 0x01000000; addr < 0x30000000; addr += 0x1000) {
+        // check if the address is valid and not mapped
+        if (bm_test(mem_bitmap, phys_addr_to_page(addr)))
+            continue;
+        _init_map_page_identically(phys_addr_to_page(addr));
+    }
+}
+
+page empty_page;
+
+void init_mem(void)
+{
+    init_mem_layout();
+
+    // map the 16MiB-768MiB identically
+    init_paging_map_low_mem_identically();
+
+    kernel_mms = types::kernel_ident_allocator_new<mm_list>();
+    auto heap_mm = kernel_mms->emplace_back((linr_ptr_t)KERNEL_HEAP_START, KERNEL_PAGE_DIRECTORY_ADDR, 1, 1);
+
+    page heap_first_page {
+        .phys_page_id = alloc_raw_page(),
+        .ref_count = types::kernel_ident_allocator_new<size_t>(0),
+        .attr = {
+            .cow = 0,
+        },
+    };
+
+    k_map(heap_mm.ptr(), &heap_first_page, 1, 1, 1, 0);
+    memset(KERNEL_HEAP_START, 0x00, PAGE_SIZE);
+    kernel_heap_allocator = types::kernel_ident_allocator_new<brk_memory_allocator>(KERNEL_HEAP_START,
+        (uint32_t)KERNEL_HEAP_LIMIT - (uint32_t)KERNEL_HEAP_START);
+
+    // create empty_page struct
+    empty_page.attr.cow = 0;
+    empty_page.phys_page_id = phys_addr_to_page(EMPTY_PAGE_ADDR);
+    empty_page.ref_count = types::kernel_ident_allocator_new<size_t>(1);
+
+    // TODO: improve the algorithm SO FREAKING SLOW
+    // while (kernel_mm_head->len < 256 * 1024 * 1024 / PAGE_SIZE) {
+    while (heap_mm->pgs->size() < 256 * 1024 * 1024 / PAGE_SIZE) {
+        k_map(
+            heap_mm.ptr(), &empty_page,
+            1, 1, 1, 1);
+    }
+}
+
+void create_segment_descriptor(
+    segment_descriptor* sd,
+    uint32_t base,
+    uint32_t limit,
+    uint32_t flags,
+    uint32_t access)
+{
+    sd->base_low = base & 0x0000ffff;
+    sd->base_mid = ((base & 0x00ff0000) >> 16);
+    sd->base_high = ((base & 0xff000000) >> 24);
+    sd->limit_low = limit & 0x0000ffff;
+    sd->limit_high = ((limit & 0x000f0000) >> 16);
+    sd->access = access;
+    sd->flags = flags;
+}
+
+mm::mm(linr_ptr_t start, page_directory_entry* pd, bool write, bool system)
+    : start(start)
+    , attr({
+          .read { 1 },
+          .write { write },
+          .system { system },
+      })
+    , pd(pd)
+    , pgs(types::kernel_ident_allocator_new<page_arr>())
+    , mapped_file(nullptr)
+    , file_offset(0)
+{
+}
+
+mm::mm(const mm& val)
+    : start(val.start)
+    , attr({
+          .read { val.attr.read },
+          .write { val.attr.write },
+          .system { val.attr.system },
+      })
+    , pd(val.pd)
+    , pgs(val.pgs)
+    , mapped_file(nullptr)
+    , file_offset(0)
+{
+}

+ 0 - 0
src/kernel/process.c


+ 298 - 0
src/kernel/process.cpp

@@ -0,0 +1,298 @@
+#include <asm/port_io.h>
+#include <asm/sys.h>
+#include <kernel/mm.hpp>
+#include <kernel/process.hpp>
+#include <kernel/stdio.h>
+#include <kernel/tty.h>
+#include <kernel/hw/ata.hpp>
+#include <kernel_main.h>
+#include <types/types.h>
+#include <types/lock.h>
+#include <hello-world.res>
+#include <interrupt-test.res>
+
+extern "C" void NORETURN go_user_space(void* eip);
+extern "C" void NORETURN to_kernel(interrupt_stack* ret_stack);
+extern "C" void NORETURN to_user(interrupt_stack* ret_stack);
+
+static bool is_scheduler_ready;
+static types::list<process>* processes;
+static types::list<thread*>* ready_thds;
+static pid_t max_pid = 1;
+static void (* volatile kthreadd_new_thd_func)(void*);
+static void  * volatile kthreadd_new_thd_data;
+static uint32_t volatile kthreadd_lock = 0;
+
+thread* current_thread;
+process* current_process;
+
+process::process(process&& val)
+    : mms(types::move(val.mms))
+    , thds(types::move(val.thds))
+    , pid(val.pid)
+{
+    if (current_process == &val)
+        current_process = this;
+
+    attr.system = val.attr.system;
+    k_esp = val.k_esp;
+
+    for (auto& item : thds)
+        item.owner = this;
+
+    val.k_esp = nullptr;
+    val.attr.system = 0;
+}
+
+process::process(const process& val, const thread& main_thd)
+    : mms(*kernel_mms)
+    , attr { .system = val.attr.system }
+    , pid { max_pid++ }
+{
+    auto iter_thd = thds.emplace_back(main_thd);
+    iter_thd->owner = this;
+
+    if (!val.attr.system) {
+        // TODO: allocate low mem
+        k_esp = (void*)page_to_phys_addr(alloc_n_raw_pages(2));
+        memset((char*)k_esp, 0x00, THREAD_KERNEL_STACK_SIZE);
+        k_esp = (char*)k_esp + THREAD_KERNEL_STACK_SIZE;
+
+        page_directory_entry* pd = alloc_pd();
+        memcpy(pd, mms_get_pd(kernel_mms), PAGE_SIZE);
+
+        mms.begin()->pd = pd;
+        // skip kernel heap
+        for (auto iter_src = ++val.mms.cbegin(); iter_src != val.mms.cend(); ++iter_src) {
+            auto iter_dst = mms.emplace_back(iter_src->start, pd, iter_src->attr.write, iter_src->attr.system);
+            iter_dst->pd = pd;
+            for (auto pg = iter_src->pgs->begin(); pg != iter_src->pgs->end(); ++pg)
+                k_map(iter_dst.ptr(),
+                        &*pg,
+                        iter_src->attr.read,
+                        iter_src->attr.write,
+                        iter_src->attr.system,
+                        1);
+        }
+    } else {
+        // TODO: allocate low mem
+        k_esp = (void*)page_to_phys_addr(alloc_n_raw_pages(2));
+        memcpy(k_esp, main_thd.owner->k_esp, THREAD_KERNEL_STACK_SIZE);
+        k_esp = (char*)k_esp + THREAD_KERNEL_STACK_SIZE;
+
+        auto orig_k_esp = (uint32_t)main_thd.owner->k_esp;
+
+        iter_thd->regs.ebp -= orig_k_esp;
+        iter_thd->regs.ebp += (uint32_t)k_esp;
+
+        iter_thd->regs.esp -= orig_k_esp;
+        iter_thd->regs.esp += (uint32_t)k_esp;
+    }
+}
+
+process::process(void* start_eip, uint8_t* image, size_t image_size, bool system)
+    : mms(*kernel_mms)
+    , thds {}
+    , attr { .system = system }
+    , pid { max_pid++ }
+{
+    // TODO: allocate low mem
+    k_esp = (void*)page_to_phys_addr(alloc_n_raw_pages(2));
+    memset((char*)k_esp, 0x00, THREAD_KERNEL_STACK_SIZE);
+    k_esp = (char*)k_esp + THREAD_KERNEL_STACK_SIZE;
+
+    auto thd = thds.emplace_back(thread {
+        .eip = start_eip,
+        .owner = this,
+        // TODO: change this
+        .regs {
+            .edi {},
+            .esi {},
+            .ebp = system ? (uint32_t)k_esp : 0x40100000U,
+            .esp = system ? (uint32_t)k_esp : 0x40100000U,
+            .ebx {},
+            .edx {},
+            .ecx {},
+            .eax {},
+        },
+        .eflags {},
+        .attr {
+            .system = system,
+            .ready = 1,
+            .wait = 0,
+        },
+    });
+    ready_thds->push_back(thd.ptr());
+
+    if (!system) {
+        page_directory_entry* pd = alloc_pd();
+        memcpy(pd, mms_get_pd(kernel_mms), PAGE_SIZE);
+        for (auto& item : mms)
+            item.pd = pd;
+
+        auto user_mm = mms.emplace_back(0x40000000U, pd, 1, system);
+
+        // TODO: change this
+        for (int i = 0; i < 1 * 1024 * 1024 / PAGE_SIZE; ++i)
+            k_map(user_mm.ptr(), &empty_page, 1, 1, 0, 1);
+
+        auto* old_pd = reinterpret_cast<page_directory_entry*>(p_ptr_to_v_ptr(current_pd()));
+        auto* old_proc = current_process;
+        auto* old_thd = current_thread;
+
+        current_process = this;
+        current_thread = thd.ptr();
+        asm_switch_pd(pd);
+
+        // TODO: change this
+        memcpy((void*)0x40000000U, image, image_size);
+
+        current_process = old_proc;
+        current_thread = old_thd;
+        asm_switch_pd(old_pd);
+    }
+}
+
+void kernel_threadd_main(void)
+{
+    tty_print(console, "kernel thread daemon started\n");
+    k_new_thread(hw::init_ata, nullptr);
+    for (;;) {
+        if (kthreadd_new_thd_func) {
+            spin_lock(&kthreadd_lock);
+            int return_value = 0;
+
+            void (*func)(void*) = kthreadd_new_thd_func;
+            void* data = kthreadd_new_thd_data;
+            kthreadd_new_thd_func = nullptr;
+            kthreadd_new_thd_data = nullptr;
+
+            spin_unlock(&kthreadd_lock);
+
+            // syscall_fork
+            asm volatile("movl $0x00, %%eax\nint $0x80\nmovl %%eax, %0": "=a" (return_value): :);
+
+            if (return_value != 0) {
+                // child
+                func(data);
+                for (;;) asm_hlt();
+                // TODO: syscall_exit()
+            }
+            spin_unlock(&kthreadd_lock);
+        }
+        asm_hlt();
+    }
+}
+
+void k_new_thread(void(*func)(void*), void* data)
+{
+    spin_lock(&kthreadd_lock);
+    kthreadd_new_thd_func = func;
+    kthreadd_new_thd_data = data;
+    spin_unlock(&kthreadd_lock);
+}
+
+void NORETURN init_scheduler()
+{
+    processes = types::kernel_allocator_new<types::list<process>>();
+    ready_thds = types::kernel_allocator_new<types::list<thread*>>();
+
+    void* user_space_start = reinterpret_cast<void*>(0x40000000U);
+
+    processes->emplace_back(user_space_start, hello_world_bin, hello_world_bin_len, false);
+    processes->emplace_back(user_space_start, interrupt_test_bin, interrupt_test_bin_len, false);
+    processes->emplace_back((void*)kernel_threadd_main, nullptr, 0, true);
+
+    // we need interrupts enabled for cow mapping
+    asm_cli();
+
+    auto init_process = processes->begin();
+    current_process = init_process.ptr();
+    current_thread = init_process->thds.begin().ptr();
+    tss.ss0 = KERNEL_DATA_SEGMENT;
+    tss.esp0 = (uint32_t)init_process->k_esp;
+    asm_switch_pd(mms_get_pd(&current_process->mms));
+
+    is_scheduler_ready = true;
+    go_user_space(user_space_start);
+}
+
+void thread_context_save(interrupt_stack* int_stack, thread* thd)
+{
+    thd->eflags = int_stack->eflags;
+    thd->eip = int_stack->v_eip;
+    memcpy(&thd->regs, &int_stack->s_regs, sizeof(regs_32));
+    if (thd->attr.system)
+        thd->regs.esp = int_stack->s_regs.esp + 0x0c;
+    else
+        thd->regs.esp = int_stack->esp;
+}
+
+void thread_context_load(interrupt_stack* int_stack, thread* thd)
+{
+    int_stack->eflags = (thd->eflags | 0x200); // OR $STI
+    int_stack->v_eip = thd->eip;
+    memcpy(&int_stack->s_regs, &thd->regs, sizeof(regs_32));
+    current_thread = thd;
+}
+
+void process_context_save(interrupt_stack*, process*)
+{
+}
+
+void process_context_load(interrupt_stack*, process* proc)
+{
+    if (!proc->attr.system)
+        tss.esp0 = (uint32_t)proc->k_esp;
+    asm_switch_pd(mms_get_pd(&proc->mms));
+    current_process = proc;
+}
+
+void add_to_process_list(process&& proc)
+{
+    processes->push_back(types::move(proc));
+}
+
+void add_to_ready_list(thread* thd)
+{
+    ready_thds->push_back(thd);
+}
+
+static inline void next_task(const types::list<thread*>::iterator_type& iter_to_remove, thread* cur_thd)
+{
+    ready_thds->erase(iter_to_remove);
+    if (cur_thd->attr.ready)
+        ready_thds->push_back(cur_thd);
+}
+
+void do_scheduling(interrupt_stack* intrpt_data)
+{
+    if (!is_scheduler_ready)
+        return;
+
+    auto iter_thd = ready_thds->begin();
+    while (!((*iter_thd)->attr.ready))
+        iter_thd = ready_thds->erase(iter_thd);
+    auto thd = *iter_thd;
+
+    if (current_thread == thd) {
+        next_task(iter_thd, thd);
+        return;
+    }
+
+    process* proc = thd->owner;
+    if (current_process != proc) {
+        process_context_save(intrpt_data, current_process);
+        process_context_load(intrpt_data, proc);
+    }
+
+    thread_context_save(intrpt_data, current_thread);
+    thread_context_load(intrpt_data, thd);
+
+    next_task(iter_thd, thd);
+
+    if (thd->attr.system)
+        to_kernel(intrpt_data);
+    else
+        to_user(intrpt_data);
+}

+ 25 - 1
src/kernel/stdio.c

@@ -412,7 +412,7 @@ snprintf(
     return n_write;
 }
 
-#define BYTES_PER_MAX_COPY_UNIT (sizeof(uint32_t)/sizeof(uint8_t))
+#define BYTES_PER_MAX_COPY_UNIT (sizeof(uint32_t) / sizeof(uint8_t))
 void* memcpy(void* dst, const void* src, size_t n)
 {
     void* orig_dst = dst;
@@ -448,3 +448,27 @@ size_t strlen(const char* str)
         ++n;
     return n;
 }
+
+char* strncpy(char* dst, const char* src, size_t n)
+{
+    size_t len = strlen(src);
+
+    if (len < n) {
+        memset(dst + len, 0x00, n - len);
+        memcpy(dst, src, len);
+    } else {
+        memcpy(dst, src, n);
+    }
+
+    return dst;
+}
+
+int strcmp(const char* s1, const char* s2)
+{
+    int c;
+    while ((c = *s1 - *s2) == 0 && *s1 != 0) {
+        ++s1;
+        ++s2;
+    }
+    return c;
+}

+ 69 - 0
src/kernel/syscall.cpp

@@ -0,0 +1,69 @@
+#include <asm/port_io.h>
+#include <kernel/syscall.hpp>
+#include <kernel/process.hpp>
+#include <kernel/tty.h>
+
+syscall_handler syscall_handlers[8];
+
+void _syscall_not_impl(interrupt_stack* data)
+{
+    data->s_regs.eax = 0xffffffff;
+    data->s_regs.edx = 0xffffffff;
+}
+
+void _syscall_fork(interrupt_stack* data)
+{
+    thread_context_save(data, current_thread);
+    process_context_save(data, current_process);
+
+    process new_proc(*current_process, *current_thread);
+    thread* new_thd = new_proc.thds.begin().ptr();
+
+    // return value
+    new_thd->regs.eax = 0;
+    data->s_regs.eax = new_proc.pid;
+
+    new_thd->regs.edx = 0;
+    data->s_regs.edx = 0;
+
+    add_to_process_list(types::move(new_proc));
+    add_to_ready_list(new_thd);
+}
+
+void _syscall_write(interrupt_stack* data)
+{
+    tty_print(console, reinterpret_cast<const char*>(data->s_regs.edi));
+
+    data->s_regs.eax = 0;
+    data->s_regs.edx = 0;
+}
+
+void _syscall_sleep(interrupt_stack* data)
+{
+    current_thread->attr.ready = 0;
+    current_thread->attr.wait = 1;
+
+    data->s_regs.eax = 0;
+    data->s_regs.edx = 0;
+
+    do_scheduling(data);
+}
+
+void _syscall_crash(interrupt_stack*)
+{
+    tty_print(console, "\nan error occurred while executing command\n");
+    asm_cli();
+    asm_hlt();
+}
+
+void init_syscall(void)
+{
+    syscall_handlers[0] = _syscall_fork;
+    syscall_handlers[1] = _syscall_write;
+    syscall_handlers[2] = _syscall_sleep;
+    syscall_handlers[3] = _syscall_crash;
+    syscall_handlers[4] = _syscall_not_impl;
+    syscall_handlers[5] = _syscall_not_impl;
+    syscall_handlers[6] = _syscall_not_impl;
+    syscall_handlers[7] = _syscall_not_impl;
+}

+ 2 - 2
src/kernel/tty.c

@@ -1,9 +1,9 @@
 #include <asm/port_io.h>
+#include <kernel/hw/serial.h>
+#include <kernel/mem.h>
 #include <kernel/stdio.h>
 #include <kernel/tty.h>
-#include <kernel/mem.h>
 #include <kernel/vga.h>
-#include <kernel/hw/serial.h>
 
 static void serial_tty_put_char(struct tty* p_tty, char c)
 {

+ 488 - 0
src/kernel/vfs.cpp

@@ -0,0 +1,488 @@
+#include <kernel/errno.h>
+#include <kernel/mem.h>
+#include <kernel/stdio.h>
+#include <kernel/syscall.hpp>
+#include <kernel/tty.h>
+#include <kernel/vfs.hpp>
+#include <types/allocator.hpp>
+#include <types/list.hpp>
+#include <types/status.h>
+#include <types/stdint.h>
+#include <types/string.hpp>
+#include <types/vector.hpp>
+
+using types::allocator_traits;
+using types::kernel_allocator;
+using types::string;
+using types::vector;
+
+struct tmpfs_file_entry {
+    size_t ino;
+    char filename[128];
+};
+
+fs::vfs::dentry::dentry(dentry* _parent, inode* _ind, const name_type& _name)
+    : parent(_parent)
+    , ind(_ind)
+    , flags { 0 }
+    , name(_name)
+{
+}
+fs::vfs::dentry::dentry(dentry* _parent, inode* _ind, name_type&& _name)
+    : parent(_parent)
+    , ind(_ind)
+    , flags { 0 }
+    , name(types::move(_name))
+{
+}
+fs::vfs::dentry::dentry(dentry&& val)
+    : children(types::move(val.children))
+    , idx_children(types::move(val.idx_children))
+    , parent(val.parent)
+    , ind(val.ind)
+    , flags { val.flags }
+    , name(types::move(val.name))
+{
+    for (auto& item : children)
+        item.parent = this;
+}
+fs::vfs::dentry* fs::vfs::dentry::append(inode* ind, const name_type& name)
+{
+    auto iter = children.emplace_back(this, ind, name);
+    idx_children.insert(iter->name, iter.ptr());
+    return iter.ptr();
+}
+fs::vfs::dentry* fs::vfs::dentry::append(inode* ind, name_type&& name)
+{
+    auto iter = children.emplace_back(this, ind, types::move(name));
+    idx_children.insert(iter->name, iter.ptr());
+    return iter.ptr();
+}
+fs::vfs::dentry* fs::vfs::dentry::find(const name_type& name)
+{
+    if (ind->flags.in.directory && !flags.in.present)
+        ind->fs->load_dentry(this);
+
+    auto iter = idx_children.find(name);
+    if (!iter) {
+        errno = ENOTFOUND;
+        return nullptr;
+    }
+
+    return iter->value;
+}
+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;
+    return this;
+}
+void fs::vfs::dentry::invalidate(void)
+{
+    // TODO: write back
+    flags.in.dirty = 0;
+    children.clear();
+    idx_children.clear();
+    flags.in.present = 0;
+}
+fs::vfs::vfs(void)
+    : _last_inode_no(0)
+    , _root(nullptr, nullptr, "/")
+{
+}
+fs::ino_t fs::vfs::_assign_inode_id(void)
+{
+    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.ptr());
+    return iter.ptr();
+}
+fs::inode* fs::vfs::get_inode(ino_t ino)
+{
+    auto iter = _idx_inodes.find(ino);
+    // TODO: load inode from disk if not found
+    if (!iter)
+        return nullptr;
+    else
+        return iter->value;
+}
+void fs::vfs::register_root_node(inode* root)
+{
+    if (!_root.ind)
+        _root.ind = root;
+}
+int fs::vfs::load_dentry(dentry*)
+{
+    syscall(0x03);
+    return GB_FAILED;
+}
+int fs::vfs::mount(dentry* mnt, vfs* new_fs)
+{
+    if (!mnt->ind->flags.in.directory) {
+        errno = ENOTDIR;
+        return GB_FAILED;
+    }
+
+    auto* new_ent = new_fs->root();
+
+    new_ent->parent = mnt->parent;
+    new_ent->name = mnt->name;
+
+    auto* orig_ent = mnt->replace(new_ent);
+    _mount_recover_list.insert(new_ent, orig_ent);
+    return GB_OK;
+}
+size_t fs::vfs::inode_read(inode*, char*, size_t, size_t, size_t)
+{
+    syscall(0x03);
+    return 0xffffffff;
+}
+size_t fs::vfs::inode_write(inode*, const char*, size_t, size_t)
+{
+    syscall(0x03);
+    return 0xffffffff;
+}
+int fs::vfs::inode_mkfile(dentry*, const char*)
+{
+    syscall(0x03);
+    return GB_FAILED;
+}
+int fs::vfs::inode_mknode(dentry*, const char*, node_t)
+{
+    syscall(0x03);
+    return GB_FAILED;
+}
+int fs::vfs::inode_rmfile(dentry*, const char*)
+{
+    syscall(0x03);
+    return GB_FAILED;
+}
+int fs::vfs::inode_mkdir(dentry*, const char*)
+{
+    syscall(0x03);
+    return GB_FAILED;
+}
+int fs::vfs::inode_stat(dentry*, stat*)
+{
+    syscall(0x03);
+    return GB_FAILED;
+}
+
+class tmpfs : public virtual fs::vfs {
+protected:
+    inline vector<tmpfs_file_entry>* mk_fe_vector(void)
+    {
+        return allocator_traits<kernel_allocator<vector<tmpfs_file_entry>>>::allocate_and_construct();
+    }
+
+    inline vector<char>* mk_data_vector(void)
+    {
+        return allocator_traits<kernel_allocator<vector<char>>>::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 = {
+            .ino = inode->ino,
+            .filename = { 0 },
+        };
+        snprintf(ent.filename, sizeof(ent.filename), filename);
+        fes->push_back(ent);
+        dir->size += sizeof(tmpfs_file_entry);
+    }
+
+    virtual int load_dentry(dentry* ent) override
+    {
+        if (!ent->ind->flags.in.directory) {
+            errno = ENOTDIR;
+            return GB_FAILED;
+        }
+
+        auto& entries = *static_cast<vector<tmpfs_file_entry>*>(ent->ind->impl);
+        for (const auto& entry : entries)
+            ent->append(get_inode(entry.ino), entry.filename);
+
+        ent->flags.in.present = 1;
+        return GB_OK;
+    }
+
+public:
+    explicit tmpfs(void)
+    {
+        auto& in = *cache_inode({ INODE_DIR | INODE_MNT }, 0777, 0, mk_fe_vector());
+
+        mklink(&in, &in, ".");
+        mklink(&in, &in, "..");
+
+        register_root_node(&in);
+    }
+
+    virtual int inode_mkfile(dentry* dir, const char* filename) override
+    {
+        auto& file = *cache_inode({ .v = INODE_FILE }, 0777, 0, mk_data_vector());
+        mklink(dir->ind, &file, filename);
+        dir->invalidate();
+        return GB_OK;
+    }
+
+    virtual int inode_mknode(dentry* dir, const char* filename, fs::node_t sn) override
+    {
+        auto& node = *cache_inode({ .v = INODE_NODE }, 0777, 0, (void*)sn.v);
+        mklink(dir->ind, &node, filename);
+        dir->invalidate();
+        return GB_OK;
+    }
+
+    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, ".");
+
+        mklink(dir->ind, &new_dir, dirname);
+        mklink(&new_dir, dir->ind, "..");
+
+        dir->invalidate();
+        return GB_OK;
+    }
+
+    virtual size_t inode_read(fs::inode* file, char* buf, size_t buf_size, size_t offset, size_t n) override
+    {
+        if (file->flags.in.file != 1)
+            return 0;
+
+        auto* data = static_cast<vector<char>*>(file->impl);
+        size_t fsize = data->size();
+
+        if (offset + n > fsize)
+            n = fsize - offset;
+
+        if (buf_size < n) {
+            n = buf_size;
+        }
+
+        memcpy(buf, data->data() + offset, n);
+
+        return n;
+    }
+
+    virtual size_t inode_write(fs::inode* file, const char* buf, size_t offset, size_t n) override
+    {
+        if (file->flags.in.file != 1)
+            return 0;
+
+        auto* data = static_cast<vector<char>*>(file->impl);
+
+        for (size_t i = data->size(); i < offset + n; ++i) {
+            data->push_back(0);
+        }
+        memcpy(data->data() + offset, buf, n);
+
+        return n;
+    }
+
+    virtual int inode_stat(dentry* dir, fs::stat* stat) override
+    {
+        auto* file_inode = dir->ind;
+
+        stat->st_ino = file_inode->ino;
+        stat->st_size = file_inode->size;
+        if (file_inode->flags.in.file) {
+            stat->st_rdev.v = 0;
+            stat->st_blksize = 1;
+            stat->st_blocks = file_inode->size;
+        }
+        if (file_inode->flags.in.directory) {
+            stat->st_rdev.v = 0;
+            stat->st_blksize = sizeof(tmpfs_file_entry);
+            stat->st_blocks = file_inode->size;
+        }
+        if (file_inode->flags.in.special_node) {
+            stat->st_rdev.v = (uint32_t)file_inode->impl;
+            stat->st_blksize = 0;
+            stat->st_blocks = 0;
+        }
+
+        return GB_OK;
+    }
+};
+
+// 8 * 8 for now
+static fs::special_node sns[8][8];
+
+size_t fs::vfs_read(fs::inode* file, char* buf, size_t buf_size, size_t offset, size_t n)
+{
+    if (file->flags.in.special_node) {
+        fs::node_t sn {
+            .v = (uint32_t)file->impl
+        };
+        auto* ptr = &sns[sn.in.major][sn.in.minor];
+        auto* ops = &ptr->ops;
+        if (ops && ops->read)
+            return ops->read(ptr, buf, buf_size, offset, n);
+        else {
+            errno = EINVAL;
+            return 0xffffffff;
+        }
+    } else {
+        return file->fs->inode_read(file, buf, buf_size, offset, n);
+    }
+}
+size_t fs::vfs_write(fs::inode* file, const char* buf, size_t offset, size_t n)
+{
+    if (file->flags.in.special_node) {
+        fs::node_t sn {
+            .v = (uint32_t)file->impl
+        };
+        auto* ptr = &sns[sn.in.major][sn.in.minor];
+        auto* ops = &ptr->ops;
+        if (ops && ops->write)
+            return ops->write(ptr, buf, offset, n);
+        else {
+            errno = EINVAL;
+            return 0xffffffff;
+        }
+    } else {
+        return file->fs->inode_write(file, buf, offset, n);
+    }
+}
+int fs::vfs_mkfile(fs::vfs::dentry* dir, const char* filename)
+{
+    return dir->ind->fs->inode_mkfile(dir, filename);
+}
+int fs::vfs_mknode(fs::vfs::dentry* dir, const char* filename, fs::node_t sn)
+{
+    return dir->ind->fs->inode_mknode(dir, filename, sn);
+}
+int fs::vfs_rmfile(fs::vfs::dentry* dir, const char* filename)
+{
+    return dir->ind->fs->inode_rmfile(dir, filename);
+}
+int fs::vfs_mkdir(fs::vfs::dentry* dir, const char* dirname)
+{
+    return dir->ind->fs->inode_mkdir(dir, dirname);
+}
+
+fs::vfs::dentry* fs::vfs_open(const char* path)
+{
+    if (path[0] == '/' && path[1] == 0x00) {
+        return fs::fs_root;
+    }
+
+    auto* cur = fs::fs_root;
+    size_t n = 0;
+    switch (*(path++)) {
+    // absolute path
+    case '/':
+        while (true) {
+            if (path[n] == 0x00) {
+                cur = cur->find(string(path, n));
+                return cur;
+            }
+            if (path[n] == '/') {
+                cur = cur->find(string(path, n));
+                if (path[n + 1] == 0x00) {
+                    return cur;
+                } else {
+                    path += (n + 1);
+                    n = 0;
+                    continue;
+                }
+            }
+            ++n;
+        }
+        break;
+    // empty string
+    case 0x00:
+        return nullptr;
+        break;
+    // relative path
+    default:
+        return nullptr;
+        break;
+    }
+    return nullptr;
+}
+int fs::vfs_stat(const char* filename, stat* stat)
+{
+    auto ent = vfs_open(filename);
+    return vfs_stat(ent, stat);
+}
+int fs::vfs_stat(fs::vfs::dentry* ent, stat* stat)
+{
+    return ent->ind->fs->inode_stat(ent, stat);
+}
+
+fs::vfs::dentry* fs::fs_root;
+static types::list<fs::vfs*>* fs_es;
+
+void fs::register_special_block(
+    uint16_t major,
+    uint16_t minor,
+    fs::special_node_read read,
+    fs::special_node_write write,
+    uint32_t data1,
+    uint32_t data2)
+{
+    fs::special_node& sn = sns[major][minor];
+    sn.ops.read = read;
+    sn.ops.write = write;
+    sn.data1 = data1;
+    sn.data2 = data2;
+}
+
+fs::vfs* fs::register_fs(vfs* fs)
+{
+    fs_es->push_back(fs);
+    return fs;
+}
+
+size_t b_null_read(fs::special_node*, char* buf, size_t buf_size, size_t, size_t n)
+{
+    if (n >= buf_size)
+        n = buf_size;
+    memset(buf, 0x00, n);
+    return n;
+}
+size_t b_null_write(fs::special_node*, const char*, size_t, size_t n)
+{
+    return n;
+}
+
+void init_vfs(void)
+{
+    using namespace fs;
+    // null
+    register_special_block(0, 0, b_null_read, b_null_write, 0, 0);
+
+    fs_es = types::kernel_allocator_new<types::list<vfs*>>();
+
+    auto* rootfs = types::kernel_allocator_new<tmpfs>();
+    fs_es->push_back(rootfs);
+    fs_root = rootfs->root();
+
+    vfs_mkdir(fs_root, "dev");
+    vfs_mkdir(fs_root, "root");
+    vfs_mkfile(fs_root, "init");
+
+    auto* init = vfs_open("/init");
+    const char* str = "#/bin/sh\nexec /bin/sh\n";
+    vfs_write(init->ind, str, 0, strlen(str));
+
+    auto* dev = vfs_open("/dev");
+    vfs_mknode(dev, "null", { .in { .major = 0, .minor = 0 } });
+    vfs_mknode(dev, "console", { .in { .major = 1, .minor = 0 } });
+    vfs_mknode(dev, "hda", { .in { .major = 2, .minor = 0 } });
+
+    stat _stat {};
+
+    vfs_stat("/init", &_stat);
+    vfs_stat("/", &_stat);
+    vfs_stat("/dev", &_stat);
+    vfs_stat("/dev/null", &_stat);
+    vfs_stat("/dev/console", &_stat);
+    vfs_stat("/dev/hda", &_stat);
+}

+ 43 - 28
src/kernel_main.c

@@ -9,21 +9,13 @@
 #include <kernel/hw/timer.h>
 #include <kernel/interrupt.h>
 #include <kernel/mem.h>
+#include <kernel/process.hpp>
 #include <kernel/stdio.h>
+#include <kernel/task.h>
 #include <kernel/tty.h>
 #include <kernel/vga.h>
 #include <types/bitmap.h>
 
-typedef void (*constructor)(void);
-extern constructor start_ctors;
-extern constructor end_ctors;
-void call_constructors_for_cpp(void)
-{
-    for (constructor* ctor = &start_ctors; ctor != &end_ctors; ++ctor) {
-        (*ctor)();
-    }
-}
-
 #define KERNEL_MAIN_BUF_SIZE (128)
 
 struct tty* console = NULL;
@@ -56,6 +48,16 @@ static inline void halt_on_init_error(void)
         asm_hlt();
 }
 
+typedef void (*constructor)(void);
+extern constructor start_ctors;
+extern constructor end_ctors;
+void call_constructors_for_cpp(void)
+{
+    for (constructor* ctor = &start_ctors; ctor != &end_ctors; ++ctor) {
+        (*ctor)();
+    }
+}
+
 uint8_t e820_mem_map[1024];
 uint32_t e820_mem_map_count;
 uint32_t e820_mem_map_entry_size;
@@ -114,7 +116,8 @@ static inline void show_mem_info(char* buf)
     printkf("kernel size: %x\n", kernel_size);
 }
 
-static segment_descriptor new_gdt[5];
+static segment_descriptor new_gdt[6];
+struct tss32_t tss;
 
 void load_new_gdt(void)
 {
@@ -123,15 +126,32 @@ void load_new_gdt(void)
     create_segment_descriptor(new_gdt + 2, 0, ~0, 0b1100, SD_TYPE_DATA_SYSTEM);
     create_segment_descriptor(new_gdt + 3, 0, ~0, 0b1100, SD_TYPE_CODE_USER);
     create_segment_descriptor(new_gdt + 4, 0, ~0, 0b1100, SD_TYPE_DATA_USER);
-    asm_load_gdt((5 * 8 - 1) << 16, (phys_ptr_t)new_gdt);
+    create_segment_descriptor(new_gdt + 5, (uint32_t)&tss, sizeof(tss), 0b0000, SD_TYPE_TSS);
+
+    asm_load_gdt((6 * 8 - 1) << 16, (phys_ptr_t)new_gdt);
+    asm_load_tr((6 - 1) * 8);
+
     asm_cli();
 }
 
-void kernel_main(void)
+void init_bss_section(void)
+{
+    void* bss_addr = (void*)bss_section_start_addr;
+    size_t bss_size = bss_section_end_addr - bss_section_start_addr;
+    memset(bss_addr, 0x00, bss_size);
+}
+
+static struct tty early_console;
+
+extern void init_vfs();
+
+void NORETURN kernel_main(void)
 {
     // MAKE_BREAK_POINT();
     asm_enable_sse();
 
+    init_bss_section();
+
     save_loader_data();
 
     load_new_gdt();
@@ -140,7 +160,6 @@ void kernel_main(void)
 
     init_serial_port(PORT_SERIAL0);
 
-    struct tty early_console;
     if (make_serial_tty(&early_console, PORT_SERIAL0) != GB_OK) {
         halt_on_init_error();
     }
@@ -152,14 +171,17 @@ void kernel_main(void)
     init_idt();
     INIT_OK();
 
-    INIT_START("memory allocation");
-    init_mem();
-    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();
+
     INIT_START("programmable interrupt controller and timer");
     init_pic();
     init_pit();
@@ -173,15 +195,8 @@ void kernel_main(void)
 
     k_malloc_buf[4096] = '\x89';
 
-    printkf("No work to do, halting...\n");
-
-    while (1) {
-        // disable interrupt
-        asm_cli();
-
-        dispatch_event();
+    init_vfs();
 
-        asm_sti();
-        asm_hlt();
-    }
+    printkf("switching execution to the scheduler...\n");
+    init_scheduler(&tss);
 }

+ 61 - 0
src/mbr.S

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

+ 15 - 0
src/mbr.ld

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

+ 4 - 0
user-space-program/.gitignore

@@ -0,0 +1,4 @@
+*.o
+*.bin
+*.res
+user.sym

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

@@ -0,0 +1,30 @@
+CROSS_COMPILE=
+CC=$(CROSS_COMPILE)gcc
+LD=$(CROSS_COMPILE)ld
+XXD=xxd
+
+RES=hello-world.res interrupt-test.res
+OBJS=hello-world.o interrupt-test.o
+
+all: user.sym $(RES)
+	mkdir -p build
+	cp $(RES) build
+
+user.sym: $(OBJS) output_symbols.ld
+	$(LD) -r -T output_symbols.ld $(OBJS) -o user.sym
+
+%.o: %.s
+	$(CC) -c -g -m32 -o $@ $<
+
+%.bin: %.o output_code.ld
+	$(LD) -T output_code.ld $< -o $@
+
+%.res: %.bin
+	$(XXD) -i $< $@
+
+.PHONY: clean
+clean:
+	-rm -rf build
+	-rm $(OBJS)
+	-rm $(RES)
+	-rm user.sym

+ 8 - 0
user-space-program/hello-world.s

@@ -0,0 +1,8 @@
+.code32
+.section .text.user-space
+
+.globl user_hello_world
+user_hello_world:
+	movl $0xcbcbcbcb, %eax
+	movl $0xacacacac, %ebx
+	jmp .

+ 26 - 0
user-space-program/interrupt-test.s

@@ -0,0 +1,26 @@
+.code32
+.section .text.user-space
+
+.globl user_interrupt_test
+user_interrupt_test:
+# fork 1 -> 2
+	xorl %eax, %eax
+	int $0x80
+	movl %eax, %esi
+# fork 2 -> 4
+	xorl %eax, %eax
+	int $0x80
+	movl %eax, %ecx
+# write
+	movl $1, %eax
+	movl $__user_interrupt_test_string, %edi
+	int $0x80
+# sleep
+	movl $2, %eax
+	movl $0xffffffff, %edi
+	int $0x80
+# noreturn
+	jmp .
+
+__user_interrupt_test_string:
+	.ascii "syscall 0x01 write: hello from user space\n\0"

+ 20 - 0
user-space-program/output_code.ld

@@ -0,0 +1,20 @@
+OUTPUT_FORMAT(binary)
+OUTPUT_ARCH(i386:i386)
+
+SECTIONS
+{
+    .text 0x40000000 : AT(0x00)
+    {
+        *(.text*)
+        *(.rodata*)
+        *(.data)
+        *(.data*)
+        *(.bss)
+        *(.bss*)
+    }
+
+    /DISCARD/ :
+    {
+        *(.note.gnu.property)
+    }
+}

+ 47 - 0
user-space-program/output_symbols.ld

@@ -0,0 +1,47 @@
+OUTPUT_FORMAT(elf32-i386)
+OUTPUT_ARCH(i386:i386)
+
+SECTIONS
+{
+    /DISCARD/ :
+    {
+        *(.text*)
+        *(.data)
+        *(.data*)
+        *(.note.gnu.property)
+    }
+
+    /* Stabs debugging sections.  */
+    .stab          0 : { *(.stab) }
+    .stabstr       0 : { *(.stabstr) }
+    .stab.excl     0 : { *(.stab.excl) }
+    .stab.exclstr  0 : { *(.stab.exclstr) }
+    .stab.index    0 : { *(.stab.index) }
+    .stab.indexstr 0 : { *(.stab.indexstr) }
+    .comment       0 : { *(.comment) }
+    /* DWARF debug sections.
+       Symbols in the DWARF debugging sections are relative to the beginning
+       of the section so we begin them at 0.  */
+    /* DWARF 1 */
+    .debug          0 : { *(.debug) }
+    .line           0 : { *(.line) }
+    /* GNU DWARF 1 extensions */
+    .debug_srcinfo  0 : { *(.debug_srcinfo) }
+    .debug_sfnames  0 : { *(.debug_sfnames) }
+    /* DWARF 1.1 and DWARF 2 */
+    .debug_aranges  0 : { *(.debug_aranges) }
+    .debug_pubnames 0 : { *(.debug_pubnames) }
+    /* DWARF 2 */
+    .debug_info     0 : { *(.debug_info .gnu.linkonce.wi.*) }
+    .debug_abbrev   0 : { *(.debug_abbrev) }
+    .debug_line     0 : { *(.debug_line) }
+    .debug_frame    0 : { *(.debug_frame) }
+    .debug_str      0 : { *(.debug_str) }
+    .debug_loc      0 : { *(.debug_loc) }
+    .debug_macinfo  0 : { *(.debug_macinfo) }
+    /* SGI/MIPS DWARF 2 extensions */
+    .debug_weaknames 0 : { *(.debug_weaknames) }
+    .debug_funcnames 0 : { *(.debug_funcnames) }
+    .debug_typenames 0 : { *(.debug_typenames) }
+    .debug_varnames  0 : { *(.debug_varnames) }
+}