Эх сурвалжийг харах

Merge branch 'exit_and_wait' into exec

greatbridf 2 жил өмнө
parent
commit
020f3cb5c6

+ 4 - 2
CMakeLists.txt

@@ -32,8 +32,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 -fno-rtti")
+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 -ffreestanding -mstack-protector-guard=global")
+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 -ffreestanding -fno-rtti -mstack-protector-guard=global")
 set(CMAKE_CXX_STANDARD 20)
 
 if (CMAKE_BUILD_TYPE STREQUAL "Debug")
@@ -72,6 +72,7 @@ set(KERNEL_MAIN_SOURCES src/fs/fat.cpp
                         include/asm/sys.h
                         include/fs/fat.hpp
                         include/kernel/event/event.h
+                        include/kernel/event/evtqueue.hpp
                         include/kernel/errno.h
                         include/kernel/tty.h
                         include/kernel/interrupt.h
@@ -89,6 +90,7 @@ set(KERNEL_MAIN_SOURCES src/fs/fat.cpp
                         include/kernel/hw/timer.h
                         include/kernel/input/keycodes.h
                         include/kernel/input/input_event.h
+                        include/types/assert.h
                         include/types/bitmap.h
                         include/types/buffer.h
                         include/types/elf.hpp

+ 1 - 1
Makefile.src

@@ -3,7 +3,7 @@ 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)
+QEMU_ARGS=-cpu SandyBridge,check -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

+ 1 - 0
include/kernel/errno.h

@@ -21,3 +21,4 @@ extern uint32_t* _get_errno(void);
 #define EISDIR (1 << 4)
 #define ENOTDIR (1 << 5)
 #define ENOTFOUND (1 << 6)
+#define ECHILD (1 << 7)

+ 45 - 0
include/kernel/event/evtqueue.hpp

@@ -0,0 +1,45 @@
+#pragma once
+
+#include <types/list.hpp>
+#include <types/lock.hpp>
+
+// declaration in kernel/process.hpp
+struct thread;
+
+namespace kernel {
+
+struct evt {
+    thread* emitter;
+    void* data1;
+    void* data2;
+    void* data3;
+};
+
+class evtqueue {
+public:
+    // TODO: use small object allocator
+    using evt_list_type = types::list<evt>;
+    using subscriber_list_type = types::list<thread*>;
+
+private:
+    types::mutex m_mtx;
+    evt_list_type m_evts;
+    subscriber_list_type m_subscribers;
+
+public:
+    evtqueue(void) = default;
+    evtqueue(const evtqueue&) = delete;
+    evtqueue(evtqueue&&);
+
+    void push(evt&& event);
+    evt&& front();
+    const evt* peek(void) const;
+
+    bool empty(void) const;
+    void notify(void);
+
+    void subscribe(thread* thd);
+    void unsubscribe(thread* thd);
+};
+
+} // namespace kernel

+ 1 - 1
include/kernel/hw/ata.hpp

@@ -57,5 +57,5 @@ public:
     int write_sector(const char* buf, uint32_t lba_low, uint16_t lba_high) const;
 };
 
-void NORETURN init_ata(void* data);
+void init_ata(void);
 } // namespace hw

+ 9 - 9
include/kernel/interrupt.h

@@ -27,8 +27,8 @@ struct PACKED interrupt_stack {
     void* v_eip;
     uint32_t cs;
     uint32_t eflags;
-    const uint32_t esp;
-    const uint32_t ss;
+    uint32_t esp;
+    uint32_t ss;
 };
 
 // present: When set, the page fault was caused by a page-protection violation.
@@ -56,15 +56,15 @@ struct page_fault_error_code {
     SET_IDT_ENTRY(0x20 + (N), (addr_irq##N), (SELECTOR), KERNEL_INTERRUPT_GATE_TYPE);
 
 #define SET_IDT_ENTRY_FN(N, FUNC_NAME, SELECTOR, TYPE) \
-    extern void FUNC_NAME();                     \
-    ptr_t addr_##FUNC_NAME = (ptr_t)FUNC_NAME;   \
+    extern void FUNC_NAME();                           \
+    ptr_t addr_##FUNC_NAME = (ptr_t)FUNC_NAME;         \
     SET_IDT_ENTRY((N), (addr_##FUNC_NAME), (SELECTOR), (TYPE));
 
-#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 = (TYPE); \
+#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 = (TYPE);               \
     IDT[(N)].offset_high = ((ADDR)&0xffff0000) >> 16
 
 struct IDT_entry {

+ 176 - 58
include/kernel/mm.hpp

@@ -29,20 +29,6 @@ struct page {
     } attr;
 };
 
-struct mm;
-
-// map the page to the end of the mm_area in pd
-int k_map(
-    mm* mm_area,
-    page* page,
-    int read,
-    int write,
-    int priv,
-    int cow);
-
-// unmap a whole mem area, making sure that we will never use it again
-int k_unmap(mm* mm_area);
-
 // private memory mapping
 // changes won't be neither written back to file nor shared between processes
 // TODO: shared mapping
@@ -58,7 +44,21 @@ int mmap(
 
 using page_arr = types::vector<page, types::kernel_ident_allocator>;
 
-using mm_list = types::list<mm, types::kernel_ident_allocator>;
+// allocate n raw page(s)
+// @return the id of the first page allocated
+page_t alloc_n_raw_pages(size_t n);
+void free_n_raw_pages(page_t start_pg, size_t n);
+
+pd_t alloc_pd(void);
+pt_t alloc_pt(void);
+
+void dealloc_pd(pd_t pd);
+void dealloc_pt(pt_t pt);
+
+// forward declaration
+namespace kernel {
+class mm_list;
+} // namespace kernel
 
 struct mm {
 public:
@@ -71,52 +71,188 @@ public:
             uint32_t system : 1;
         } in;
     } attr;
-    pd_t pd;
-    page_arr* pgs = types::kernel_ident_allocator_new<page_arr>();
+    kernel::mm_list* owner;
+    page_arr* pgs = nullptr;
     fs::inode* mapped_file = nullptr;
     size_t file_offset = 0;
 
 public:
-    static constexpr int mirror_mm_area(mm_list* dst, const mm* src, pd_t pd)
-    {
-        mm new_nn {
-            .start = src->start,
-            .attr { src->attr.v },
-            .pd = pd,
-            .mapped_file = src->mapped_file,
-            .file_offset = src->file_offset,
-        };
-
-        for (auto iter = src->pgs->begin(); iter != src->pgs->end(); ++iter) {
-            // TODO: preserve dirty flag, clear accessed flag
-            if (k_map(&new_nn, &*iter,
-                    src->attr.in.read, src->attr.in.write, src->attr.in.system, 1)
+    constexpr void* end(void) const
+    {
+        return (char*)this->start + this->pgs->size() * PAGE_SIZE;
+    }
+
+    inline bool is_ident(void) const
+    {
+        return this->end() <= (void*)0x40000000U;
+    }
+
+    constexpr bool is_avail(void* start, void* end) const
+    {
+        void* m_start = this->start;
+        void* m_end = this->end();
+
+        return (start >= m_end || end <= m_start);
+    }
+
+    int append_page(page* pg, bool present, bool write, bool priv, bool cow);
+};
+
+namespace kernel {
+
+class mm_list {
+public:
+    using list_type = ::types::list<mm, types::kernel_ident_allocator>;
+    using iterator_type = list_type::iterator_type;
+    using const_iterator_type = list_type::const_iterator_type;
+
+private:
+    list_type m_areas;
+
+public:
+    pd_t m_pd;
+
+public:
+    explicit constexpr mm_list(pd_t pd)
+        : m_pd(pd)
+    {
+    }
+    mm_list(const mm_list& v);
+    constexpr mm_list(mm_list&& v)
+        : m_areas(::types::move(v.m_areas))
+        , m_pd(v.m_pd)
+    {
+        v.m_pd = nullptr;
+    }
+    constexpr ~mm_list()
+    {
+        if (!m_pd)
+            return;
+
+        this->clear_user();
+        dealloc_pd(m_pd);
+    }
+
+    constexpr iterator_type begin(void)
+    {
+        return m_areas.begin();
+    }
+    constexpr iterator_type end(void)
+    {
+        return m_areas.end();
+    }
+    constexpr const_iterator_type begin(void) const
+    {
+        return m_areas.begin();
+    }
+    constexpr const_iterator_type end(void) const
+    {
+        return m_areas.end();
+    }
+    constexpr const_iterator_type cbegin(void) const
+    {
+        return m_areas.cbegin();
+    }
+    constexpr const_iterator_type cend(void) const
+    {
+        return m_areas.cend();
+    }
+
+    constexpr iterator_type addarea(void* start, bool w, bool system)
+    {
+        return m_areas.emplace_back(mm {
+            .start = start,
+            .attr {
+                .in {
+                    .read = 1,
+                    .write = w,
+                    .system = system,
+                },
+            },
+            .owner = this,
+            .pgs = ::types::kernel_ident_allocator_new<page_arr>(),
+        });
+    }
+
+    constexpr void clear_user()
+    {
+        for (auto iter = this->begin(); iter != this->end();) {
+            if (iter->is_ident()) {
+                ++iter;
+                continue;
+            }
+
+            this->unmap(iter);
+            iter = m_areas.erase(iter);
+        }
+    }
+
+    constexpr int mirror_area(mm& src)
+    {
+        auto area = this->addarea(
+            src.start, src.attr.in.write, src.attr.in.system);
+        if (src.mapped_file) {
+            area->mapped_file = src.mapped_file;
+            area->file_offset = src.file_offset;
+        }
+
+        for (auto& pg : *src.pgs) {
+            if (area->append_page(&pg,
+                    true,
+                    src.attr.in.write,
+                    src.attr.in.system,
+                    true)
                 != GB_OK) {
                 return GB_FAILED;
             }
         }
 
-        dst->emplace_back(types::move(new_nn));
         return GB_OK;
     }
+
+    constexpr void unmap(iterator_type area)
+    {
+        for (auto& pg : *area->pgs) {
+            if (*pg.ref_count == 1) {
+                ki_free(pg.ref_count);
+                free_n_raw_pages(pg.phys_page_id, 1);
+            } else {
+                --*pg.ref_count;
+            }
+
+            pg.phys_page_id = 0;
+            pg.attr.v = 0;
+            pg.pte->v = 0;
+        }
+        area->attr.v = 0;
+        area->start = 0;
+    }
+
+    constexpr iterator_type find(void* lp)
+    {
+        for (auto iter = this->begin(); iter != this->end(); ++iter)
+            if (lp >= iter->start && lp < iter->end())
+                return iter;
+
+        return this->end();
+    }
 };
 
-// in mem.cpp
-extern mm_list* kernel_mms;
-extern page empty_page;
+} // namespace kernel
+
+// global variables
+inline kernel::mm_list* kernel_mms;
+inline page empty_page;
+// --------------------------------
 
 // translate physical address to virtual(mapped) address
 void* ptovp(pptr_t p_ptr);
 
-// @return the pointer to the mm_area containing l_ptr
-//         nullptr if not
-mm* find_mm_area(mm_list* mms, void* l_ptr);
-
 inline constexpr size_t vptrdiff(void* p1, void* p2)
 {
     return (uint8_t*)p1 - (uint8_t*)p2;
 }
-inline constexpr page* lto_page(const mm* mm_area, void* l_ptr)
+inline constexpr page* lto_page(mm* mm_area, void* l_ptr)
 {
     size_t offset = vptrdiff(l_ptr, mm_area->start);
     return &mm_area->pgs->at(offset / PAGE_SIZE);
@@ -149,10 +285,6 @@ inline constexpr pte_t* to_pte(pt_t pt, page_t pg)
 {
     return *pt + to_pti(pg);
 }
-inline pd_t mms_get_pd(const mm_list* mms)
-{
-    return mms->begin()->pd;
-}
 inline void* to_vp(page_t pg)
 {
     return ptovp(to_pp(pg));
@@ -189,14 +321,6 @@ inline pte_t* to_pte(pde_t* pde, page_t pg)
 {
     return to_pte(to_pt(pde), pg);
 }
-inline constexpr void* mmend(const mm* mm_area)
-{
-    return (char*)mm_area->start + mm_area->pgs->size() * PAGE_SIZE;
-}
-
-// allocate n raw page(s)
-// @return the id of the first page allocated
-page_t alloc_n_raw_pages(size_t n);
 
 // allocate a raw page
 inline page_t alloc_raw_page(void)
@@ -206,9 +330,3 @@ inline page_t alloc_raw_page(void)
 
 // allocate a struct page together with the raw page
 struct page allocate_page(void);
-
-pd_t alloc_pd(void);
-pt_t alloc_pt(void);
-
-void dealloc_pd(pd_t pd);
-void dealloc_pt(pt_t pt);

+ 94 - 25
include/kernel/process.hpp

@@ -1,12 +1,13 @@
 #pragma once
 
+#include <kernel/event/evtqueue.hpp>
 #include <kernel/interrupt.h>
-#include <kernel/task.h>
-#include <types/types.h>
-
-#ifdef __cplusplus
 #include <kernel/mm.hpp>
+#include <kernel/task.h>
+#include <types/hash_map.hpp>
 #include <types/list.hpp>
+#include <types/stdint.h>
+#include <types/types.h>
 
 typedef size_t pid_t;
 
@@ -15,6 +16,7 @@ struct thread;
 
 struct process_attr {
     uint16_t system : 1;
+    uint16_t zombie : 1 = 0;
 };
 
 struct thread_attr {
@@ -24,21 +26,66 @@ struct thread_attr {
 };
 
 struct thread {
-    void* eip;
+private:
+    inline void alloc_kstack(void)
+    {
+        // TODO: alloc low mem
+        kstack = to_pp(alloc_n_raw_pages(2));
+        kstack += THREAD_KERNEL_STACK_SIZE;
+        esp = reinterpret_cast<uint32_t*>(kstack);
+    }
+
+public:
+    uint32_t* esp;
+    pptr_t kstack;
     process* owner;
-    regs_32 regs;
-    uint32_t eflags;
     thread_attr attr;
+
+    explicit inline thread(process* _owner, bool system)
+        : owner { _owner }
+        , attr {
+            .system = system,
+            .ready = 1,
+            .wait = 0,
+        }
+    {
+        alloc_kstack();
+    }
+
+    constexpr thread(thread&& val)
+        : esp { val.esp }
+        , kstack { val.kstack }
+        , owner { val.owner }
+        , attr { val.attr }
+    {
+        val.attr = {};
+        val.esp = 0;
+        val.kstack = 0;
+        val.owner = nullptr;
+    }
+
+    inline thread(const thread& val)
+        : owner { val.owner }
+        , attr { val.attr }
+    {
+        alloc_kstack();
+    }
+
+    constexpr ~thread()
+    {
+        if (kstack)
+            free_n_raw_pages(to_page(kstack), 2);
+    }
 };
 
 class process {
 public:
-    mm_list mms;
+    mutable kernel::mm_list mms;
     types::list<thread> thds;
-    // TODO: allocate a kernel stack for EVERY THREAD
-    void* k_esp;
+    kernel::evtqueue wait_lst;
     process_attr attr;
     pid_t pid;
+    pid_t ppid;
 
 public:
     process(process&& val);
@@ -46,28 +93,50 @@ public:
     process(const process& proc, const thread& main_thread);
 
     // only used for system initialization
-    process(void* start_eip);
+    explicit process(void);
+    explicit process(void (*func_in_kernel_space)(void), pid_t ppid);
+
+    ~process();
+
+private:
+    static inline pid_t max_pid;
+
+    static inline pid_t alloc_pid(void)
+    {
+        return ++max_pid;
+    }
 };
 
-// in process.cpp
-extern process* current_process;
-extern thread* current_thread;
+constexpr uint32_t push_stack(uint32_t** stack, uint32_t val)
+{
+    --*stack;
+    **stack = val;
+    return val;
+}
+
+inline process* volatile current_process;
+inline thread* volatile current_thread;
+inline typename types::hash_map<pid_t, types::list<pid_t>, types::linux_hasher<pid_t>>* idx_child_processes;
 
 extern "C" void NORETURN init_scheduler();
-void do_scheduling(interrupt_stack* intrpt_data);
+void schedule(void);
 
-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);
+pid_t add_to_process_list(process&& proc);
+void remove_from_process_list(pid_t pid);
 
-void add_to_process_list(process&& proc);
 void add_to_ready_list(thread* thd);
+void remove_from_ready_list(thread* thd);
+types::list<thread*>::iterator_type query_next_thread(void);
 
-void k_new_thread(void (*func)(void*), void* data);
-
-#else
+// the function call INVALIDATES iterator
+inline void next_task(types::list<thread*>::iterator_type target)
+{
+    auto* ptr = *target;
+    remove_from_ready_list(ptr);
+    if (ptr->attr.ready)
+        add_to_ready_list(ptr);
+}
 
-void NORETURN init_scheduler();
+process* findproc(pid_t pid);
 
-#endif
+void k_new_thread(void (*func)(void*), void* data);

+ 0 - 14
include/kernel/syscall.hpp

@@ -6,18 +6,4 @@
 // return value is stored in %eax and %edx
 typedef void (*syscall_handler)(interrupt_stack* data);
 
-inline uint32_t syscall(uint32_t num, uint32_t arg1 = 0, uint32_t arg2 = 0)
-{
-    asm volatile(
-        "movl %1, %%edi\n"
-        "movl %2, %%esi\n"
-        "movl %3, %%eax\n"
-        "int $0x80\n"
-        "movl %%eax, %0"
-        : "=g"(num)
-        : "g"(arg1), "g"(arg2), "g"(num)
-        : "eax", "edx", "edi", "esi");
-    return num;
-}
-
 void init_syscall(void);

+ 7 - 4
include/kernel_main.h

@@ -1,10 +1,13 @@
 #pragma once
 #include <types/types.h>
 
-static inline void __break_point(void)
-{
-    asm volatile("xchgw %bx, %bx");
-}
+#define __crash() asm volatile("ud2")
+
+#ifdef __BOCHS_SYSTEM__
+#define __break_point() asm volatile("xchgw %bx, %bx")
+#else
+#define __break_point() __crash()
+#endif
 
 #define MAKE_BREAK_POINT() __break_point()
 

+ 19 - 0
include/types/assert.h

@@ -0,0 +1,19 @@
+#pragma once
+
+#include "types.h"
+
+#ifndef NDEBUG
+
+#define assert(_statement) \
+    if (!(_statement))     \
+    asm volatile("ud2")
+
+#define assert_likely(_statement) \
+    if (unlikely(!(_statement)))  \
+    asm volatile("ud2")
+
+#else
+
+#define assert(_statement) ;
+
+#endif

+ 2 - 2
include/types/cplusplus.hpp

@@ -99,12 +99,12 @@ public:
 
 namespace types {
 template <typename T>
-T&& move(T& val)
+constexpr T&& move(T& val)
 {
     return static_cast<T&&>(val);
 }
 template <typename T>
-T&& forward(typename traits::remove_reference<T>::type& val)
+constexpr T&& forward(typename traits::remove_reference<T>::type& val)
 {
     return static_cast<T&&>(val);
 }

+ 11 - 1
include/types/elf.hpp

@@ -109,6 +109,16 @@ struct PACKED elf32_program_header_entry {
     uint32_t align;
 };
 
-int elf32_load(const char* exec, interrupt_stack* intrpt_stack, bool system);
+struct elf32_load_data {
+    const char* exec;
+    const char** argv;
+    int errcode;
+    void* eip;
+    uint32_t* sp;
+    bool system;
+};
+
+// TODO: environment variables
+int elf32_load(elf32_load_data* data);
 
 } // namespace types::elf

+ 1 - 1
include/types/hash_map.hpp

@@ -221,7 +221,7 @@ public:
     {
     }
 
-    ~hash_map()
+    constexpr ~hash_map()
     {
         buckets.clear();
     }

+ 1 - 1
include/types/list.hpp

@@ -217,7 +217,7 @@ public:
         return *this;
     }
 
-    ~list() noexcept
+    constexpr ~list() noexcept
     {
         destroy();
     }

+ 40 - 0
include/types/lock.hpp

@@ -25,3 +25,43 @@ inline void spin_unlock(uint32_t volatile* lock_addr)
         : "r"(lock_addr)
         : "eax", "memory");
 }
+
+namespace types {
+
+struct mutex {
+    using mtx_t = volatile uint32_t;
+
+    mtx_t m_lock = 0;
+
+    inline void lock(void)
+    {
+        spin_lock(&m_lock);
+    }
+
+    inline void unlock(void)
+    {
+        spin_unlock(&m_lock);
+    }
+};
+
+class lock_guard {
+private:
+    mutex& m_mtx;
+
+public:
+    explicit lock_guard(mutex& mtx)
+        : m_mtx(mtx)
+    {
+        mtx.lock();
+    }
+
+    lock_guard(const lock_guard&) = delete;
+    lock_guard(lock_guard&&) = delete;
+
+    ~lock_guard()
+    {
+        m_mtx.unlock();
+    }
+};
+
+} // namespace types

+ 17 - 17
include/types/string.hpp

@@ -17,17 +17,17 @@ public:
     static inline constexpr size_type npos = (-1U);
 
 public:
-    explicit string(size_type capacity = 8)
+    explicit constexpr string(size_type capacity = 8)
         : inner_vector_type(capacity)
     {
         this->push_back(0x00);
     }
-    string(const char* str, size_type n = npos)
+    constexpr string(const char* str, size_type n = npos)
         : string()
     {
         this->append(str, n);
     }
-    string& append(const char* str, size_type n = npos)
+    constexpr string& append(const char* str, size_type n = npos)
     {
         this->pop_back();
 
@@ -39,51 +39,51 @@ public:
         this->push_back(0x00);
         return *this;
     }
-    string& append(const string& str)
+    constexpr string& append(const string& str)
     {
         return this->append(str.data());
     }
-    string& append(string&& str)
+    constexpr string& append(string&& str)
     {
         return this->append(str.data());
     }
-    string& operator+=(const char c)
+    constexpr string& operator+=(const char c)
     {
         this->pop_back();
         this->push_back(c);
         this->push_back(0x00);
         return *this;
     }
-    string& operator+=(const char* str)
+    constexpr string& operator+=(const char* str)
     {
         return this->append(str);
     }
-    string& operator+=(const string& str)
+    constexpr string& operator+=(const string& str)
     {
         return this->append(str);
     }
-    string& operator+=(string&& str)
+    constexpr string& operator+=(string&& str)
     {
         return this->append(move(str));
     }
-    bool operator==(const string& rhs) const
+    constexpr bool operator==(const string& rhs) const
     {
         return strcmp(c_str(), rhs.c_str()) == 0;
     }
-    string substr(size_type pos, size_type n = npos)
+    constexpr string substr(size_type pos, size_type n = npos)
     {
         return string(this->m_arr + pos, n);
     }
-    const char* c_str(void) const noexcept
+    constexpr const char* c_str(void) const noexcept
     {
         return this->data();
     }
-    void clear(void)
+    constexpr void clear(void)
     {
         inner_vector_type::clear();
         this->push_back(0x00);
     }
-    char pop(void)
+    constexpr char pop(void)
     {
         this->pop_back();
         auto iter = inner_vector_type::back();
@@ -91,21 +91,21 @@ public:
         *iter = 0x00;
         return c;
     }
-    typename inner_vector_type::iterator_type back(void)
+    constexpr 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
+    constexpr 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
+    constexpr typename inner_vector_type::const_iterator_type cback(void) const
     {
         // TODO: assert
         if (this->empty())

+ 2 - 2
include/types/vector.hpp

@@ -159,7 +159,7 @@ public:
         return operator=(vector(arr));
     }
 
-    ~vector() noexcept
+    constexpr ~vector() noexcept
     {
         resize(0);
     }
@@ -355,7 +355,7 @@ protected:
     }
 
 protected:
-    value_type* m_arr;
+    T* m_arr;
     size_type m_capacity;
     size_type m_size;
 };

+ 73 - 0
pretty-print.py

@@ -0,0 +1,73 @@
+import gdb.printing
+
+def create_iter(item, end, idx):
+    return vectorPrinter._iterator(item, end, idx)
+
+class vectorPrinter:
+    class _iterator:
+        def __init__(self, item, end, idx):
+            self.item = item
+            self.end = end
+            self.size = self.end - self.item
+            self.idx = idx
+
+        def __iter__(self):
+            return self
+
+        def __next__(self):
+            if self.item >= self.end:
+                raise StopIteration
+            key = '[%d]' % self.idx
+            iter = self.item.dereference()
+            self.item += 1
+            self.idx += 1
+            return key, iter
+
+    def __init__(self, val):
+        self.val = val
+
+    def to_string(self):
+        return "vector of size %d, capacity %d" % (self.val['m_size'], self.val['m_capacity'])
+
+    def display_hint(self):
+        return 'array'
+
+    def children(self):
+        return self._iterator(self.val['m_arr'], self.val['m_arr'] + self.val['m_size'], 0)
+
+class stringPrinter:
+    def __init__(self, val):
+        self.val = val
+    
+    def to_string(self):
+        return self.val['m_arr']
+
+class listPrinter:
+    def __init__(self, val):
+        self.val = val
+    
+    def to_string(self):
+        return "list of size %d" % (self.val['head'].reinterpret_cast(gdb.lookup_type("size_t").pointer()) + 2).dereference()
+
+    def display_hint(self):
+        return 'array'
+
+    def children(self):
+        node = self.val['head']['next']
+        end = self.val['tail']
+        idx = 0
+        while node != end:
+            yield '[%d]' % idx, node['value']
+            idx += 1
+            node = node['next']
+
+def build_pretty_printer():
+    pp = gdb.printing.RegexpCollectionPrettyPrinter("gbos")
+    pp.add_printer("vector", "^types::vector<.*?>$", vectorPrinter)
+    pp.add_printer("string", "^types::string<.*?>$", stringPrinter)
+    pp.add_printer("list", "^types::list<.*?>$", listPrinter)
+    return pp
+
+gdb.printing.register_pretty_printer(
+        gdb.current_objfile(),
+        build_pretty_printer())

+ 68 - 45
src/asm/interrupt.s

@@ -215,15 +215,54 @@ syscall_stub:
     # restore stack
     popl %esp
 
+.globl _syscall_stub_fork_return
+.type  _syscall_stub_fork_return @function
+_syscall_stub_fork_return:
     popal
 syscall_stub_end:
     iret
 
-# parameters:
-# interrupt_stack* ret_stack
-.globl to_kernel
-.type  to_kernel @function
-to_kernel:
+# parameters
+# #1: uint32_t* curr_esp
+# #2: uint32_t next_esp
+.globl asm_ctx_switch
+.type  asm_ctx_switch @function
+asm_ctx_switch:
+    movl 4(%esp), %ecx
+    movl 8(%esp), %eax
+
+    push $_ctx_switch_return
+    push %ebx
+    push %edi
+    push %esi
+    push %ebp
+    pushfl
+
+    movl %esp, (%ecx)
+    movl %eax, %esp
+
+    popfl
+    pop %ebp
+    pop %esi
+    pop %edi
+    pop %ebx
+
+    ret
+
+_ctx_switch_return:
+    ret
+
+# param:
+# #1: uint32_t esp
+# #2: void (*k_init)()
+.globl go_kernel
+.type  go_kernel @function
+go_kernel:
+    movl 4(%esp), %eax
+    movl 8(%esp), %ecx
+    movl %eax, %esp
+    pushl %ecx
+
     movw $0x10, %ax
     movw %ax, %ss
     movw %ax, %ds
@@ -231,55 +270,39 @@ to_kernel:
     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
+    xorl %eax, %eax
+    xorl %ebx, %ebx
+    xorl %ecx, %ecx
+    xorl %edx, %edx
+    xorl %esi, %esi
+    xorl %edi, %edi
+    xorl %ebp, %ebp
 
-    pushl 40(%eax) # %eflags
-    pushl $0x08    # %cs
-    pushl 32(%eax) # %eip
+# eflags: IN
+    pushl $0x200
+    popfl
 
-    movl 28(%eax), %eax
-
-    iret
+    ret
 
-# parameters:
-# interrupt_stack* ret_stack
-.globl to_user
-.type  to_user @function
-to_user:
+# parameters
+# #1: eip
+# #2: esp
+.globl go_user
+.type  go_user @function
+go_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 4(%esp), %eax
+    movl 8(%esp), %ecx
 
-    movl 8(%edi), %ebp
-    movl 4(%edi), %esi
-    movl (%edi), %edi
+    pushl $0x23
+    pushl %ecx
+    pushl $0x200
+    pushl $0x1b
+    pushl %eax
 
     iret

+ 3 - 1
src/asm/port_io.s

@@ -45,9 +45,11 @@ asm_sti:
 .type  asm_enable_sse @function
 asm_enable_sse:
 	movl %cr0, %eax
-	orl $0b10, %eax
+    andl $0xfffffff3, %eax
+	orl $0b100010, %eax
 	movl %eax, %cr0
 	movl %cr4, %eax
 	orl $0b11000000000, %eax
 	movl %eax, %cr4
+    fninit
 	ret

+ 8 - 10
src/fs/fat.cpp

@@ -1,9 +1,9 @@
 #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/assert.h>
 #include <types/hash_map.hpp>
 #include <types/status.h>
 #include <types/stdint.h>
@@ -12,15 +12,13 @@ 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);
-    }
+    assert(vfs_read(
+               device,
+               (char*)buf,
+               SECTOR_SIZE,
+               sector_no * SECTOR_SIZE,
+               SECTOR_SIZE)
+        == SECTOR_SIZE);
 }
 
 // buf MUST be larger than 4096 bytes

+ 3 - 0
src/kernel.ld

@@ -38,6 +38,9 @@ SECTIONS
         KEEP(*(SORT_BY_INIT_PRIORITY(.ctors*)));
         end_ctors = .;
 
+        __stack_chk_guard = .;
+        LONG(0x19198101);
+
         *(.data)
         *(.data*)
         __kernel_text_and_data_end = .;

+ 58 - 0
src/kernel/event/event.cpp

@@ -1,10 +1,15 @@
 #include <asm/port_io.h>
 #include <kernel/event/event.h>
+#include <kernel/event/evtqueue.hpp>
 #include <kernel/input/input_event.h>
+#include <kernel/process.hpp>
 #include <kernel/stdio.h>
 #include <kernel/tty.h>
 #include <types/allocator.hpp>
+#include <types/assert.h>
+#include <types/cplusplus.hpp>
 #include <types/list.hpp>
+#include <types/lock.hpp>
 
 static ::types::list<::input_event>* _input_event_queue;
 
@@ -40,3 +45,56 @@ void dispatch_event(void)
         }
     }
 }
+
+kernel::evtqueue::evtqueue(evtqueue&& q)
+    : m_evts { types::move(q.m_evts) }
+    , m_subscribers { types::move(q.m_subscribers) }
+{
+}
+
+void kernel::evtqueue::push(kernel::evt&& event)
+{
+    types::lock_guard lck(m_mtx);
+    m_evts.push_back(types::move(event));
+    this->notify();
+}
+
+kernel::evt&& kernel::evtqueue::front()
+{
+    assert(!this->empty());
+    types::lock_guard lck(m_mtx);
+
+    auto iter = m_evts.begin();
+    evt e = types::move(*iter);
+    m_evts.erase(iter);
+    return types::move(e);
+}
+
+const kernel::evt* kernel::evtqueue::peek(void) const
+{
+    return m_evts.begin().ptr();
+}
+
+bool kernel::evtqueue::empty(void) const
+{
+    return m_evts.empty();
+}
+
+void kernel::evtqueue::notify(void)
+{
+    for (auto* sub : m_subscribers) {
+        sub->attr.ready = 1;
+        sub->attr.wait = 0;
+        add_to_ready_list(sub);
+    }
+}
+
+void kernel::evtqueue::subscribe(thread* thd)
+{
+    m_subscribers.push_back(thd);
+}
+
+void kernel::evtqueue::unsubscribe(thread* thd)
+{
+    m_subscribers.erase(m_subscribers.find(thd));
+}

+ 1 - 9
src/kernel/hw/ata.cpp

@@ -197,11 +197,8 @@ static inline void mbr_part_probe(fs::inode* drive, uint16_t major, uint16_t min
 }
 
 // data: void (*func_to_call_next)(void)
-void NORETURN hw::init_ata(void* data)
+void hw::init_ata(void)
 {
-    if (!data)
-        syscall(0x03);
-
     ata_pri = types::kernel_allocator_new<ata>(ATA_PRIMARY_BUS_BASE);
     if (ata_pri->identify())
         ata_pri->select(true);
@@ -230,9 +227,4 @@ void NORETURN hw::init_ata(void* data)
 
     auto* hda = fs::vfs_open("/dev/hda");
     mbr_part_probe(hda->ind, 2, 1);
-
-    // noreturn
-    ((void (*)(void))data)();
-    for (;;)
-        syscall(0x03);
 }

+ 7 - 7
src/kernel/interrupt.cpp

@@ -165,18 +165,18 @@ static inline void _int14_panic(void* eip, void* cr2, struct page_fault_error_co
 // page fault
 extern "C" void int14_handler(int14_data* d)
 {
-    mm_list* mms = nullptr;
+    kernel::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 (unlikely(!mm_area))
+    auto mm_area = mms->find(d->l_addr);
+    if (unlikely(mm_area == mms->end()))
         _int14_panic(d->v_eip, d->l_addr, d->error_code);
 
-    pte_t* pte = to_pte(mms_get_pd(mms), d->l_addr);
-    page* page = lto_page(mm_area, d->l_addr);
+    pte_t* pte = to_pte(mms->m_pd, d->l_addr);
+    page* page = lto_page(mm_area.ptr(), d->l_addr);
 
     if (unlikely(d->error_code.present == 0 && !mm_area->mapped_file))
         _int14_panic(d->v_eip, d->l_addr, d->error_code);
@@ -219,11 +219,11 @@ extern "C" void int14_handler(int14_data* d)
     }
 }
 
-extern "C" void irq0_handler(struct interrupt_stack* d)
+extern "C" void irq0_handler(interrupt_stack*)
 {
     inc_tick();
     asm_outb(PORT_PIC1_COMMAND, PIC_EOI);
-    do_scheduling(d);
+    schedule();
 }
 // keyboard interrupt
 extern "C" void irq1_handler(void)

+ 48 - 114
src/kernel/mem.cpp

@@ -9,16 +9,12 @@
 #include <kernel/task.h>
 #include <kernel/vga.h>
 #include <kernel_main.h>
+#include <types/allocator.hpp>
+#include <types/assert.h>
 #include <types/bitmap.h>
 #include <types/size.h>
 #include <types/status.h>
 
-// global objects
-
-mm_list* kernel_mms;
-
-// ---------------------
-
 // constant values
 
 #define EMPTY_PAGE_ADDR ((pptr_t)0x0000)
@@ -202,8 +198,7 @@ static brk_memory_allocator
 void* k_malloc(size_t size)
 {
     void* ptr = kernel_heap_allocator->alloc(size);
-    if unlikely (!ptr)
-        MAKE_BREAK_POINT();
+    assert_likely(ptr);
     return ptr;
 }
 
@@ -215,8 +210,7 @@ void k_free(void* ptr)
 void* ki_malloc(size_t size)
 {
     void* ptr = kernel_ident_mapped_allocator.alloc(size);
-    if (unlikely(!ptr))
-        MAKE_BREAK_POINT();
+    assert_likely(ptr);
     return ptr;
 }
 
@@ -296,14 +290,20 @@ page_t alloc_n_raw_pages(size_t n)
             first += (max + 1);
         } else {
             for (page_t i = first; i < first + n; ++i)
-                bm_set(mem_bitmap, i);
+                mark_page(i);
             return first;
         }
     }
-    MAKE_BREAK_POINT();
+    assert(false);
     return 0xffffffff;
 }
 
+void free_n_raw_pages(page_t start_pg, size_t n)
+{
+    while (n--)
+        free_page(start_pg++);
+}
+
 struct page allocate_page(void)
 {
     return page {
@@ -385,46 +385,40 @@ static inline void init_mem_layout(void)
     }
 }
 
-mm* find_mm_area(mm_list* mms, void* l_ptr)
+using kernel::mm_list;
+mm_list::mm_list(const mm_list& v)
+    : m_areas(v.m_areas)
 {
-    for (auto iter = mms->begin(); iter != mms->end(); ++iter)
-        if (l_ptr >= iter->start && l_ptr < mmend(iter.ptr()))
-            return iter.ptr();
-    return nullptr;
+    pd_t pd = alloc_pd();
+    memcpy(pd, v.m_pd, PAGE_SIZE);
+    m_pd = pd;
 }
 
-static inline void map_raw_page_to_pte(
+inline void map_raw_page_to_pte(
     pte_t* pte,
     page_t page,
-    int present,
-    int rw,
-    int priv)
+    bool present,
+    bool write,
+    bool priv)
 {
     // set P bit
     pte->v = 0;
     pte->in.p = present;
-    pte->in.rw = (rw == 1);
-    pte->in.us = (priv == 0);
+    pte->in.rw = write;
+    pte->in.us = !priv;
     pte->in.page = page;
 }
 
-// map page to the end of mm_area in pd
-int k_map(
-    mm* mm_area,
-    page* page,
-    int read,
-    int write,
-    int priv,
-    int cow)
+int mm::append_page(page* pg, bool present, bool write, bool priv, bool cow)
 {
-    void* addr = mmend(mm_area);
-    pde_t* pde = to_pde(mm_area->pd, addr);
+    void* addr = this->end();
+    pde_t* pde = to_pde(this->owner->m_pd, addr);
     // page table not exist
     if (unlikely(!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.us = 1;
         pde->in.pt_page = alloc_raw_page();
 
         memset(to_pt(pde), 0x00, PAGE_SIZE);
@@ -432,50 +426,21 @@ int k_map(
 
     // map the page in the page table
     pte_t* pte = to_pte(pde, addr);
-    map_raw_page_to_pte(pte, page->phys_page_id, read, (write && !cow), priv);
+    map_raw_page_to_pte(pte, pg->phys_page_id, present, (write && !cow), priv);
 
-    if (unlikely(cow && !page->attr.in.cow)) {
-        page->attr.in.cow = 1;
-        page->pte->in.rw = 0;
+    if (unlikely(cow && !pg->attr.in.cow)) {
+        pg->attr.in.cow = 1;
+        pg->pte->in.rw = 0;
+        pg->pte->in.a = 0;
         invalidate_tlb(addr);
     }
-    ++*page->ref_count;
+    ++*pg->ref_count;
 
-    auto iter = mm_area->pgs->emplace_back(*page);
+    auto iter = this->pgs->emplace_back(*pg);
     iter->pte = pte;
     return GB_OK;
 }
 
-int k_unmap(mm* mm_area)
-{
-    for (auto iter = mm_area->pgs->begin(); iter != mm_area->pgs->end(); ++iter) {
-        if (*iter->ref_count == 1) {
-            ki_free(iter->ref_count);
-            free_page(iter->phys_page_id);
-        } else {
-            --*iter->ref_count;
-        }
-
-        iter->phys_page_id = 0;
-        iter->attr.v = 0;
-        iter->pte->v = 0;
-    }
-    mm_area->attr.v = 0;
-    mm_area->start = 0;
-    return GB_OK;
-}
-
-bool check_addr_range_avail(const mm* mm_area, void* start, void* end)
-{
-    void* m_start = mm_area->start;
-    void* m_end = mmend(mm_area);
-
-    if (start >= m_end || end <= m_start)
-        return true;
-    else
-        return false;
-}
-
 static inline int _mmap(
     mm_list* mms,
     void* hint,
@@ -494,25 +459,17 @@ static inline int _mmap(
     size_t n_pgs = len >> 12;
 
     for (const auto& mm_area : *mms)
-        if (!check_addr_range_avail(&mm_area, hint, (char*)hint + len)) {
+        if (!mm_area.is_avail(hint, (char*)hint + len)) {
             errno = EEXIST;
             return GB_FAILED;
         }
 
-    auto iter_mm = mms->emplace_back(mm {
-        .start = hint,
-        .attr { .in {
-            .read = 1,
-            .write = static_cast<uint32_t>(write),
-            .system = static_cast<uint32_t>(priv),
-        } },
-        .pd = mms_get_pd(&current_process->mms),
-    });
-    iter_mm->mapped_file = file;
-    iter_mm->file_offset = offset;
+    auto mm = mms->addarea(hint, write, priv);
+    mm->mapped_file = file;
+    mm->file_offset = offset;
 
     for (size_t i = 0; i < n_pgs; ++i)
-        k_map(iter_mm.ptr(), &empty_page, 0, write, priv, 1);
+        mm->append_page(&empty_page, false, write, priv, true);
 
     return GB_OK;
 }
@@ -563,8 +520,6 @@ static inline void init_paging_map_low_mem_identically(void)
     }
 }
 
-page empty_page;
-
 void init_mem(void)
 {
     init_mem_layout();
@@ -572,28 +527,8 @@ void init_mem(void)
     // 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(mm {
-        .start = KERNEL_HEAP_START,
-        .attr { .in {
-            .read = 1,
-            .write = 1,
-            .system = 1,
-        } },
-        .pd = KERNEL_PAGE_DIRECTORY_ADDR,
-    });
-
-    page heap_first_page {
-        .phys_page_id = alloc_raw_page(),
-        .pte = nullptr,
-        .ref_count = types::kernel_ident_allocator_new<size_t>(0),
-        .attr = { 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,
-        vptrdiff(KERNEL_HEAP_LIMIT, KERNEL_HEAP_START));
+    kernel_mms = types::kernel_ident_allocator_pnew(kernel_mms, KERNEL_PAGE_DIRECTORY_ADDR);
+    auto heap_mm = kernel_mms->addarea(KERNEL_HEAP_START, true, true);
 
     // create empty_page struct
     empty_page.attr.in.cow = 0;
@@ -603,13 +538,12 @@ void init_mem(void)
     empty_page.pte->in.rw = 0;
     invalidate_tlb(0x00000000);
 
-    // 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);
-    }
+    // 0x30000000 to 0x40000000 or 768MiB to 1GiB
+    while (heap_mm->pgs->size() < 256 * 1024 * 1024 / PAGE_SIZE)
+        heap_mm->append_page(&empty_page, true, true, true, true);
+
+    kernel_heap_allocator = types::kernel_ident_allocator_pnew(kernel_heap_allocator,
+        KERNEL_HEAP_START, vptrdiff(KERNEL_HEAP_LIMIT, KERNEL_HEAP_START));
 }
 
 void create_segment_descriptor(

+ 191 - 164
src/kernel/process.cpp

@@ -7,289 +7,316 @@
 #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_main.h>
 #include <types/allocator.hpp>
+#include <types/assert.h>
 #include <types/elf.hpp>
-#include <types/lock.h>
+#include <types/hash_map.hpp>
+#include <types/list.hpp>
+#include <types/lock.hpp>
+#include <types/size.h>
 #include <types/status.h>
+#include <types/stdint.h>
 #include <types/types.h>
 
-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 typename types::hash_map<pid_t, types::list<process>::iterator_type, types::linux_hasher<pid_t>>* idx_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;
+static types::mutex kthreadd_mtx;
+
+namespace kernel {
+
+struct no_irq_guard {
+    explicit no_irq_guard()
+    {
+        asm_cli();
+    }
+
+    no_irq_guard(const no_irq_guard&) = delete;
+    no_irq_guard& operator=(const no_irq_guard&) = delete;
 
-thread* current_thread;
-process* current_process;
+    ~no_irq_guard()
+    {
+        asm_sti();
+    }
+};
+
+} // namespace kernel
 
 process::process(process&& val)
     : mms(types::move(val.mms))
     , thds(types::move(val.thds))
+    , wait_lst(types::move(val.wait_lst))
     , pid(val.pid)
+    , ppid(val.ppid)
 {
     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++ }
+    , pid { process::alloc_pid() }
+    , ppid { val.pid }
 {
     auto iter_thd = thds.emplace_back(main_thd);
     iter_thd->owner = this;
 
-    // TODO: allocate low mem
-    k_esp = (void*)to_pp(alloc_n_raw_pages(2));
-    memcpy(k_esp, (char*)main_thd.owner->k_esp - THREAD_KERNEL_STACK_SIZE, THREAD_KERNEL_STACK_SIZE);
-    k_esp = (char*)k_esp + THREAD_KERNEL_STACK_SIZE;
-
-    if (val.attr.system) {
-        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;
-    } else {
-        pd_t pd = alloc_pd();
-        memcpy(pd, mms_get_pd(kernel_mms), PAGE_SIZE);
+    for (auto& area : val.mms) {
+        if (area.is_ident())
+            continue;
 
-        mms.begin()->pd = pd;
-
-        // skip kernel heap since it's already copied above
-        for (auto iter_src = ++val.mms.cbegin(); iter_src != val.mms.cend(); ++iter_src)
-            mm::mirror_mm_area(&mms, iter_src.ptr(), pd);
+        mms.mirror_area(area);
     }
 }
 
-process::process(void* start_eip)
+process::process(void)
     : mms(*kernel_mms)
-    , thds {}
     , attr { .system = 1 }
-    , pid { max_pid++ }
+    , pid { process::alloc_pid() }
+    , ppid { 1 }
 {
-    // TODO: allocate low mem
-    k_esp = (void*)to_pp(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,
-        .regs {
-            .edi {},
-            .esi {},
-            .ebp = reinterpret_cast<uint32_t>(k_esp),
-            .esp = reinterpret_cast<uint32_t>(k_esp),
-            .ebx {},
-            .edx {},
-            .ecx {},
-            .eax {},
-        },
-        .eflags {},
-        .attr {
-            .system = 1,
-            .ready = 1,
-            .wait = 0,
-        },
-    });
-    ready_thds->push_back(thd.ptr());
+    auto thd = thds.emplace_back(this, true);
+
+    add_to_ready_list(thd.ptr());
 }
 
-void NORETURN _kernel_init(void)
+process::process(void (*func)(void), pid_t _ppid)
+    : mms(*kernel_mms)
+    , attr { .system = 1 }
+    , pid { process::alloc_pid() }
+    , ppid { _ppid }
 {
-    // TODO: parse kernel parameters
-    auto* _new_fs = fs::register_fs(types::kernel_allocator_new<fs::fat::fat32>(fs::vfs_open("/dev/hda1")->ind));
-    int ret = fs::fs_root->ind->fs->mount(fs::vfs_open("/mnt"), _new_fs);
-    if (unlikely(ret != GB_OK))
-        syscall(0x03);
-
-    pd_t new_pd = alloc_pd();
-    memcpy(new_pd, mms_get_pd(kernel_mms), PAGE_SIZE);
-
-    asm_cli();
-
-    current_process->mms.begin()->pd = new_pd;
-
-    asm_sti();
+    auto thd = thds.emplace_back(this, true);
+
+    add_to_ready_list(thd.ptr());
+
+    auto* esp = &thd->esp;
+
+    // return(start) address
+    push_stack(esp, (uint32_t)func);
+    // ebx
+    push_stack(esp, 0);
+    // edi
+    push_stack(esp, 0);
+    // esi
+    push_stack(esp, 0);
+    // ebp
+    push_stack(esp, 0);
+    // eflags
+    push_stack(esp, 0x200);
+}
 
-    interrupt_stack intrpt_stack {};
-    intrpt_stack.eflags = 0x200; // STI
-    types::elf::elf32_load("/mnt/INIT.ELF", &intrpt_stack, 0);
-    // map stack area
-    ret = mmap((void*)types::elf::ELF_STACK_TOP, types::elf::ELF_STACK_SIZE, fs::vfs_open("/dev/null")->ind, 0, 1, 0);
-    if (unlikely(ret != GB_OK))
-        syscall(0x03);
+process::~process()
+{
+    for (auto iter = thds.begin(); iter != thds.end(); ++iter)
+        remove_from_ready_list(iter.ptr());
+}
 
-    asm_cli();
-    current_process->attr.system = 0;
-    current_thread->attr.system = 0;
-    to_user(&intrpt_stack);
+inline void NORETURN _noreturn_crash(void)
+{
+    for (;;)
+        assert(false);
 }
 
+extern "C" void NORETURN go_kernel(uint32_t* kstack, void (*k_main)(void));
+extern "C" void NORETURN go_user(void* eip, uint32_t* esp);
+
 void kernel_threadd_main(void)
 {
     tty_print(console, "kernel thread daemon started\n");
-    k_new_thread(hw::init_ata, (void*)_kernel_init);
+
     for (;;) {
         if (kthreadd_new_thd_func) {
-            spin_lock(&kthreadd_lock);
-            int return_value = 0;
+            void (*func)(void*) = nullptr;
+            void* data = nullptr;
 
-            void (*func)(void*) = kthreadd_new_thd_func;
-            void* data = kthreadd_new_thd_data;
-            kthreadd_new_thd_func = nullptr;
-            kthreadd_new_thd_data = nullptr;
+            {
+                types::lock_guard lck(kthreadd_mtx);
+                func = kthreadd_new_thd_func;
+                data = kthreadd_new_thd_data;
 
-            spin_unlock(&kthreadd_lock);
+                kthreadd_new_thd_func = nullptr;
+                kthreadd_new_thd_data = nullptr;
+            }
+
+            // TODO
+            (void)func, (void)data;
+            assert(false);
 
             // syscall_fork
-            return_value = syscall(0x00);
-
-            if (return_value != 0) {
-                // child
-                func(data);
-                for (;;)
-                    syscall(0x03);
-                // TODO: syscall_exit()
-            }
-            spin_unlock(&kthreadd_lock);
+            // int ret = syscall(0x00);
+
+            // if (ret == 0) {
+            //     // child process
+            //     func(data);
+            //     // the function shouldn't return here
+            //     assert(false);
+            // }
         }
+        // TODO: sleep here to wait for new_kernel_thread event
         asm_hlt();
     }
 }
 
+void NORETURN _kernel_init(void)
+{
+    {
+        kernel::no_irq_guard grd;
+
+        add_to_process_list(process { kernel_threadd_main, 1 });
+    }
+    hw::init_ata();
+
+    // TODO: parse kernel parameters
+    auto* _new_fs = fs::register_fs(types::kernel_allocator_new<fs::fat::fat32>(fs::vfs_open("/dev/hda1")->ind));
+    int ret = fs::fs_root->ind->fs->mount(fs::vfs_open("/mnt"), _new_fs);
+    assert_likely(ret == GB_OK);
+
+    current_process->attr.system = 0;
+    current_thread->attr.system = 0;
+
+    const char* argv[] = { "/mnt/INIT.ELF", nullptr };
+
+    types::elf::elf32_load_data d;
+    d.exec = "/mnt/INIT.ELF";
+    d.argv = argv;
+    d.system = false;
+
+    assert(types::elf::elf32_load(&d) == GB_OK);
+
+    is_scheduler_ready = true;
+
+    go_user(d.eip, d.sp);
+}
+
 void k_new_thread(void (*func)(void*), void* data)
 {
-    spin_lock(&kthreadd_lock);
+    types::lock_guard lck(kthreadd_mtx);
     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*>>();
+    processes = types::kernel_allocator_pnew(processes);
+    ready_thds = types::kernel_allocator_pnew(ready_thds);
+    idx_processes = types::kernel_allocator_pnew(idx_processes);
+    idx_child_processes = types::kernel_allocator_pnew(idx_child_processes);
 
-    auto iter = processes->emplace_back((void*)kernel_threadd_main);
+    auto pid = add_to_process_list(process {});
+    auto init = findproc(pid);
 
     // we need interrupts enabled for cow mapping so now we disable it
     // in case timer interrupt mess things up
     asm_cli();
 
-    current_process = iter.ptr();
-    current_thread = iter->thds.begin().ptr();
+    current_process = init;
+    current_thread = init->thds.begin().ptr();
 
     tss.ss0 = KERNEL_DATA_SEGMENT;
-    tss.esp0 = (uint32_t)iter->k_esp;
+    tss.esp0 = current_thread->kstack;
 
-    asm_switch_pd(mms_get_pd(&current_process->mms));
+    asm_switch_pd(current_process->mms.m_pd);
 
-    is_scheduler_ready = true;
-
-    interrupt_stack intrpt_stack {};
-    process_context_load(&intrpt_stack, current_process);
-    thread_context_load(&intrpt_stack, current_thread);
-    to_kernel(&intrpt_stack);
+    go_kernel(current_thread->esp, _kernel_init);
 }
 
-void thread_context_save(interrupt_stack* int_stack, thread* thd)
+pid_t add_to_process_list(process&& proc)
 {
-    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;
-}
+    auto iter = processes->emplace_back(types::move(proc));
+    idx_processes->insert(iter->pid, iter);
 
-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;
+    auto children = idx_child_processes->find(iter->ppid);
+    if (!children) {
+        idx_child_processes->insert(iter->ppid, {});
+        children = idx_child_processes->find(iter->ppid);
+    }
+
+    children->value.push_back(iter->pid);
+
+    return iter->pid;
 }
 
-void process_context_save(interrupt_stack*, process*)
+void remove_from_process_list(pid_t pid)
 {
+    auto proc_iter = idx_processes->find(pid);
+    auto ppid = proc_iter->value->ppid;
+
+    auto& parent_children = idx_child_processes->find(ppid)->value;
+
+    auto i = parent_children.find(pid);
+    parent_children.erase(i);
+
+    processes->erase(proc_iter->value);
+    idx_processes->remove(proc_iter);
 }
 
-void process_context_load(interrupt_stack*, process* proc)
+void add_to_ready_list(thread* thd)
 {
-    if (!proc->attr.system)
-        tss.esp0 = (uint32_t)proc->k_esp;
-    asm_switch_pd(mms_get_pd(&proc->mms));
-    current_process = proc;
+    ready_thds->push_back(thd);
 }
 
-void add_to_process_list(process&& proc)
+void remove_from_ready_list(thread* thd)
 {
-    processes->push_back(types::move(proc));
+    auto iter = ready_thds->find(thd);
+    while (iter != ready_thds->end()) {
+        ready_thds->erase(iter);
+        iter = ready_thds->find(thd);
+    }
 }
 
-void add_to_ready_list(thread* thd)
+types::list<thread*>::iterator_type query_next_thread(void)
 {
-    ready_thds->push_back(thd);
+    auto iter_thd = ready_thds->begin();
+    while (!((*iter_thd)->attr.ready))
+        iter_thd = ready_thds->erase(iter_thd);
+    return iter_thd;
 }
 
-static inline void next_task(const types::list<thread*>::iterator_type& iter_to_remove, thread* cur_thd)
+process* findproc(pid_t pid)
 {
-    ready_thds->erase(iter_to_remove);
-    if (cur_thd->attr.ready)
-        ready_thds->push_back(cur_thd);
+    return idx_processes->find(pid)->value.ptr();
 }
 
-void do_scheduling(interrupt_stack* intrpt_data)
+extern "C" void asm_ctx_switch(uint32_t** curr_esp, uint32_t* next_esp);
+void schedule()
 {
     if (unlikely(!is_scheduler_ready))
         return;
 
-    auto iter_thd = ready_thds->begin();
-    while (!((*iter_thd)->attr.ready))
-        iter_thd = ready_thds->erase(iter_thd);
+    auto iter_thd = query_next_thread();
     auto thd = *iter_thd;
 
     if (current_thread == thd) {
-        next_task(iter_thd, thd);
+        next_task(iter_thd);
         return;
     }
 
     process* proc = thd->owner;
     if (current_process != proc) {
-        process_context_save(intrpt_data, current_process);
-        process_context_load(intrpt_data, proc);
+        asm_switch_pd(proc->mms.m_pd);
+        current_process = proc;
     }
 
-    thread_context_save(intrpt_data, current_thread);
-    thread_context_load(intrpt_data, thd);
+    auto* curr_thd = current_thread;
 
-    next_task(iter_thd, thd);
+    current_thread = thd;
+    tss.esp0 = current_thread->kstack;
+    next_task(iter_thd);
 
-    if (thd->attr.system)
-        to_kernel(intrpt_data);
-    else
-        to_user(intrpt_data);
+    asm_ctx_switch(&curr_thd->esp, thd->esp);
 }

+ 162 - 28
src/kernel/syscall.cpp

@@ -1,43 +1,84 @@
 #include <asm/port_io.h>
+#include <asm/sys.h>
+#include <kernel/errno.h>
 #include <kernel/interrupt.h>
+#include <kernel/mem.h>
+#include <kernel/mm.hpp>
 #include <kernel/process.hpp>
 #include <kernel/syscall.hpp>
 #include <kernel/tty.h>
+#include <kernel_main.h>
+#include <types/allocator.hpp>
+#include <types/assert.h>
 #include <types/elf.hpp>
+#include <types/status.h>
+#include <types/stdint.h>
+
+#define SYSCALL_SET_RETURN_VAL_EAX(_eax) \
+    data->s_regs.eax = ((decltype(data->s_regs.eax))(_eax))
+
+#define SYSCALL_SET_RETURN_VAL_EDX(_edx) \
+    data->s_regs.edx = ((decltype(data->s_regs.edx))(_edx))
+
+#define SYSCALL_SET_RETURN_VAL(_eax, _edx) \
+    SYSCALL_SET_RETURN_VAL_EAX(_eax);      \
+    SYSCALL_SET_RETURN_VAL_EDX(_edx)
 
 syscall_handler syscall_handlers[8];
 
 void _syscall_not_impl(interrupt_stack* data)
 {
-    data->s_regs.eax = 0xffffffff;
-    data->s_regs.edx = 0xffffffff;
+    SYSCALL_SET_RETURN_VAL(0xffffffff, 0xffffffff);
 }
 
+extern "C" void _syscall_stub_fork_return(void);
 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);
+    auto newpid = add_to_process_list(process { *current_process, *current_thread });
+    auto* newproc = findproc(newpid);
+    thread* newthd = newproc->thds.begin().ptr();
+    add_to_ready_list(newthd);
+
+    // create fake interrupt stack
+    push_stack(&newthd->esp, data->ss);
+    push_stack(&newthd->esp, data->esp);
+    push_stack(&newthd->esp, data->eflags);
+    push_stack(&newthd->esp, data->cs);
+    push_stack(&newthd->esp, (uint32_t)data->v_eip);
+
+    // eax
+    push_stack(&newthd->esp, 0);
+    push_stack(&newthd->esp, data->s_regs.ecx);
+    // edx
+    push_stack(&newthd->esp, 0);
+    push_stack(&newthd->esp, data->s_regs.ebx);
+    push_stack(&newthd->esp, data->s_regs.esp);
+    push_stack(&newthd->esp, data->s_regs.ebp);
+    push_stack(&newthd->esp, data->s_regs.esi);
+    push_stack(&newthd->esp, data->s_regs.edi);
+
+    // ctx_switch stack
+    // return address
+    push_stack(&newthd->esp, (uint32_t)_syscall_stub_fork_return);
+    // ebx
+    push_stack(&newthd->esp, 0);
+    // edi
+    push_stack(&newthd->esp, 0);
+    // esi
+    push_stack(&newthd->esp, 0);
+    // ebp
+    push_stack(&newthd->esp, 0);
+    // eflags
+    push_stack(&newthd->esp, 0);
+
+    SYSCALL_SET_RETURN_VAL(newpid, 0);
 }
 
 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;
+    SYSCALL_SET_RETURN_VAL(0, 0);
 }
 
 void _syscall_sleep(interrupt_stack* data)
@@ -45,10 +86,9 @@ 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;
+    SYSCALL_SET_RETURN_VAL(0, 0);
 
-    do_scheduling(data);
+    schedule();
 }
 
 void _syscall_crash(interrupt_stack*)
@@ -64,17 +104,111 @@ void _syscall_crash(interrupt_stack*)
 void _syscall_exec(interrupt_stack* data)
 {
     const char* exec = reinterpret_cast<const char*>(data->s_regs.edi);
-
-    // TODO: load argv
     const char** argv = reinterpret_cast<const char**>(data->s_regs.esi);
-    (void)argv;
 
-    types::elf::elf32_load(exec, data, current_process->attr.system);
+    current_process->mms.clear_user();
+
+    types::elf::elf32_load_data d;
+    d.argv = argv;
+    d.exec = exec;
+    d.system = false;
+
+    assert(types::elf::elf32_load(&d) == GB_OK);
+
+    data->v_eip = d.eip;
+    data->esp = (uint32_t)d.sp;
 }
 
+// @param exit_code
 void _syscall_exit(interrupt_stack* data)
 {
-    _syscall_crash(data);
+    uint32_t exit_code = data->s_regs.edi;
+    pid_t pid = current_process->pid;
+    pid_t ppid = current_process->ppid;
+
+    // TODO: terminating a thread only
+    if (current_thread->owner->thds.size() != 1) {
+        _syscall_crash(data);
+    }
+
+    // terminating a whole process:
+
+    // remove this thread from ready list
+    current_thread->attr.ready = 0;
+    remove_from_ready_list(current_thread);
+
+    // TODO: write back mmap'ped files and close them
+
+    // unmap all user memory areas
+    current_process->mms.clear_user();
+
+    // make child processes orphans (children of init)
+    auto children = idx_child_processes->find(pid);
+    if (children) {
+        auto init_children = idx_child_processes->find(1);
+        for (auto iter = children->value.begin(); iter != children->value.end(); ++iter) {
+            init_children->value.push_back(*iter);
+            findproc(*iter)->ppid = 1;
+        }
+        idx_child_processes->remove(children);
+    }
+
+    current_process->attr.zombie = 1;
+
+    // notify parent process and init
+    auto* proc = findproc(pid);
+    auto* parent = findproc(ppid);
+    auto* init = findproc(1);
+    while (!proc->wait_lst.empty()) {
+        init->wait_lst.push(proc->wait_lst.front());
+    }
+    parent->wait_lst.push({ current_thread, (void*)pid, (void*)exit_code, nullptr });
+
+    // switch to new process and continue
+    schedule();
+
+    // we should not return to here
+    assert(false);
+}
+
+// @param address of exit code: int*
+// @return pid of the exited process
+void _syscall_wait(interrupt_stack* data)
+{
+    auto* arg1 = reinterpret_cast<int*>(data->s_regs.edi);
+
+    if (arg1 < (int*)0x40000000) {
+        SYSCALL_SET_RETURN_VAL(-1, EINVAL);
+        return;
+    }
+
+    auto& waitlst = current_process->wait_lst;
+    if (waitlst.empty() && !idx_child_processes->find(current_process->pid)) {
+        SYSCALL_SET_RETURN_VAL(-1, ECHILD);
+        return;
+    }
+
+    while (waitlst.empty()) {
+        current_thread->attr.ready = 0;
+        current_thread->attr.wait = 1;
+        waitlst.subscribe(current_thread);
+
+        schedule();
+
+        if (!waitlst.empty()) {
+            waitlst.unsubscribe(current_thread);
+            break;
+        }
+    }
+
+    auto evt = waitlst.front();
+
+    pid_t pid = (pid_t)evt.data1;
+    // TODO: copy_to_user check privilege
+    *arg1 = (int)evt.data2;
+
+    remove_from_process_list(pid);
+    SYSCALL_SET_RETURN_VAL(pid, 0);
 }
 
 void init_syscall(void)
@@ -85,6 +219,6 @@ void init_syscall(void)
     syscall_handlers[3] = _syscall_crash;
     syscall_handlers[4] = _syscall_exec;
     syscall_handlers[5] = _syscall_exit;
-    syscall_handlers[6] = _syscall_not_impl;
+    syscall_handlers[6] = _syscall_wait;
     syscall_handlers[7] = _syscall_not_impl;
 }

+ 13 - 9
src/kernel/vfs.cpp

@@ -1,10 +1,10 @@
 #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/assert.h>
 #include <types/list.hpp>
 #include <types/status.h>
 #include <types/stdint.h>
@@ -116,7 +116,7 @@ void fs::vfs::register_root_node(inode* root)
 }
 int fs::vfs::load_dentry(dentry*)
 {
-    syscall(0x03);
+    assert(false);
     return GB_FAILED;
 }
 int fs::vfs::mount(dentry* mnt, vfs* new_fs)
@@ -137,37 +137,37 @@ int fs::vfs::mount(dentry* mnt, vfs* new_fs)
 }
 size_t fs::vfs::inode_read(inode*, char*, size_t, size_t, size_t)
 {
-    syscall(0x03);
+    assert(false);
     return 0xffffffff;
 }
 size_t fs::vfs::inode_write(inode*, const char*, size_t, size_t)
 {
-    syscall(0x03);
+    assert(false);
     return 0xffffffff;
 }
 int fs::vfs::inode_mkfile(dentry*, const char*)
 {
-    syscall(0x03);
+    assert(false);
     return GB_FAILED;
 }
 int fs::vfs::inode_mknode(dentry*, const char*, node_t)
 {
-    syscall(0x03);
+    assert(false);
     return GB_FAILED;
 }
 int fs::vfs::inode_rmfile(dentry*, const char*)
 {
-    syscall(0x03);
+    assert(false);
     return GB_FAILED;
 }
 int fs::vfs::inode_mkdir(dentry*, const char*)
 {
-    syscall(0x03);
+    assert(false);
     return GB_FAILED;
 }
 int fs::vfs::inode_stat(dentry*, stat*)
 {
-    syscall(0x03);
+    assert(false);
     return GB_FAILED;
 }
 
@@ -384,6 +384,10 @@ fs::vfs::dentry* fs::vfs_open(const char* path)
             }
             if (path[n] == '/') {
                 cur = cur->find(string(path, n));
+
+                if (!cur)
+                    return cur;
+
                 if (path[n + 1] == 0x00) {
                     return cur;
                 } else {

+ 11 - 1
src/kernel_main.c

@@ -1,5 +1,6 @@
 #include "kernel_main.h"
 
+#include <types/types.h>
 #include <asm/boot.h>
 #include <asm/port_io.h>
 #include <asm/sys.h>
@@ -9,7 +10,6 @@
 #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>
@@ -144,6 +144,7 @@ void init_bss_section(void)
 static struct tty early_console;
 
 extern void init_vfs();
+extern void NORETURN init_scheduler();
 
 void NORETURN kernel_main(void)
 {
@@ -200,3 +201,12 @@ void NORETURN kernel_main(void)
     printkf("switching execution to the scheduler...\n");
     init_scheduler(&tss);
 }
+
+void NORETURN __stack_chk_fail(void)
+{
+    tty_print(console, "***** stack smashing detected! *****\nhalting\n");
+    for (;;) {
+        asm_cli();
+        asm_hlt();
+    }
+}

+ 45 - 15
src/types/elf.cpp

@@ -1,11 +1,21 @@
+#include <kernel/errno.h>
+#include <kernel/stdio.h>
+#include <types/assert.h>
 #include <types/elf.hpp>
+#include <types/stdint.h>
 
-int types::elf::elf32_load(const char* exec, interrupt_stack* intrpt_stack, bool system)
+template <typename T>
+constexpr void _user_push(uint32_t** sp, T d)
 {
-    auto* ent_exec = fs::vfs_open(exec);
+    *sp -= sizeof(T);
+    *(T*)*sp = d;
+}
+
+int types::elf::elf32_load(types::elf::elf32_load_data* d)
+{
+    auto* ent_exec = fs::vfs_open(d->exec);
     if (!ent_exec) {
-        intrpt_stack->s_regs.eax = ENOENT;
-        intrpt_stack->s_regs.edx = 0;
+        d->errcode = ENOENT;
         return GB_FAILED;
     }
 
@@ -18,8 +28,7 @@ int types::elf::elf32_load(const char* exec, interrupt_stack* intrpt_stack, bool
         0, sizeof(types::elf::elf32_header));
 
     if (n_read != sizeof(types::elf::elf32_header)) {
-        intrpt_stack->s_regs.eax = EINVAL;
-        intrpt_stack->s_regs.edx = 0;
+        d->errcode = EINVAL;
         return GB_FAILED;
     }
 
@@ -33,8 +42,7 @@ int types::elf::elf32_load(const char* exec, interrupt_stack* intrpt_stack, bool
 
     // broken file or I/O error
     if (n_read != phents_size) {
-        intrpt_stack->s_regs.eax = EINVAL;
-        intrpt_stack->s_regs.edx = 0;
+        d->errcode = EINVAL;
         return GB_FAILED;
     }
 
@@ -42,19 +50,41 @@ int types::elf::elf32_load(const char* exec, interrupt_stack* intrpt_stack, bool
         if (phents->type != types::elf::elf32_program_header_entry::PT_LOAD)
             continue;
 
-        auto ret = mmap((void*)phents->vaddr, phents->memsz, ent_exec->ind, phents->offset, 1, system);
+        auto ret = mmap((void*)phents->vaddr, phents->memsz, ent_exec->ind, phents->offset, 1, d->system);
         if (ret != GB_OK) {
-            intrpt_stack->s_regs.eax = ret;
-            intrpt_stack->s_regs.edx = 0;
+            d->errcode = ret;
             return GB_FAILED;
         }
 
         ++phents;
     }
 
-    intrpt_stack->v_eip = (void*)hdr.entry;
-    memset((void*)&intrpt_stack->s_regs, 0x00, sizeof(regs_32));
-    intrpt_stack->s_regs.esp = types::elf::ELF_STACK_BOTTOM;
-    intrpt_stack->s_regs.ebp = types::elf::ELF_STACK_BOTTOM;
+    // map stack area
+    auto ret = mmap((void*)types::elf::ELF_STACK_TOP, types::elf::ELF_STACK_SIZE, fs::vfs_open("/dev/null")->ind, 0, 1, 0);
+    assert_likely(ret == GB_OK);
+
+    d->eip = (void*)hdr.entry;
+    d->sp = reinterpret_cast<uint32_t*>(types::elf::ELF_STACK_BOTTOM);
+
+    auto* sp = &d->sp;
+
+    types::vector<const char*> arr;
+    for (const char** ptr = d->argv; *ptr != nullptr; ++ptr) {
+        auto len = strlen(*ptr);
+        *sp -= (len + 1);
+        *sp = (uint32_t*)((uint32_t)*sp & 0xfffffff0);
+        memcpy((char*)*sp, *ptr, len + 1);
+        arr.push_back((const char*)*sp);
+    }
+
+    *sp -= sizeof(const char*) * arr.size();
+    *sp = (uint32_t*)((uint32_t)*sp & 0xfffffff0);
+    memcpy((char*)*sp, arr.data(), sizeof(const char*) * arr.size());
+
+    _user_push(sp, 0);
+    _user_push(sp, 0);
+    _user_push(sp, *sp + 8);
+    _user_push(sp, arr.size());
+
     return GB_OK;
 }

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

@@ -4,7 +4,7 @@ LD=$(CROSS_COMPILE)ld
 OBJCOPY=$(CROSS_COMPILE)objcopy
 XXD=xxd
 
-CFLAGS=-nostdinc -nostdlib -static -g -m32 -W -Wall -Wextra -Werror
+CFLAGS=-nostdinc -nostdlib -static -g -m32 -W -Wall -Wextra -Werror -mstack-protector-guard=global
 
 OBJS=hello-world.out interrupt-test.out stack-test.out init.out
 SYMS=init.sym

+ 45 - 0
user-space-program/basic-lib.h

@@ -2,6 +2,11 @@ typedef __UINT32_TYPE__ uint32_t;
 typedef __UINT16_TYPE__ uint16_t;
 typedef __UINT8_TYPE__ uint8_t;
 
+typedef uint32_t pid_t;
+
+#define GNU_ATTRIBUTE(attr) __attribute__((attr))
+#define NORETURN GNU_ATTRIBUTE(noreturn)
+
 static inline uint32_t syscall(uint32_t num, uint32_t arg1, uint32_t arg2)
 {
     asm volatile(
@@ -15,3 +20,43 @@ static inline uint32_t syscall(uint32_t num, uint32_t arg1, uint32_t arg2)
         : "eax", "edx", "edi", "esi");
     return num;
 }
+
+static inline void NORETURN syscall_noreturn(uint32_t num, uint32_t arg1, uint32_t arg2)
+{
+    asm volatile(
+        "movl %1, %%edi\n"
+        "movl %2, %%esi\n"
+        "movl %3, %%eax\n"
+        "int $0x80\n"
+        "movl %%eax, %0"
+        : "=g"(num)
+        : "g"(arg1), "g"(arg2), "g"(num)
+        : "eax", "edx", "edi", "esi");
+    // crash
+    syscall_noreturn(0x05, 0, 0);
+}
+
+static inline int fork(void)
+{
+    return syscall(0x00, 0, 0);
+}
+static inline uint32_t write(const char* buf)
+{
+    return syscall(0x01, (uint32_t)buf, 0);
+}
+static inline void sleep(void)
+{
+    syscall(0x02, 0, 0);
+}
+static inline void NORETURN exec(const char* bin, const char** argv)
+{
+    syscall_noreturn(0x04, (uint32_t)bin, (uint32_t)argv);
+}
+static inline void NORETURN exit(int exit_code)
+{
+    syscall_noreturn(0x05, exit_code, 0);
+}
+static inline uint32_t wait(int* return_value)
+{
+    return syscall(0x06, (uint32_t)return_value, 0);
+}

+ 19 - 3
user-space-program/init.c

@@ -1,9 +1,25 @@
 #include "basic-lib.h"
 
-int main(void)
+int main(int argc, char** argv)
 {
+    for (int i = 0; i < argc; ++i)
+        write(argv[i]);
+
     const char* data = "Hello World from user space init\n";
-    syscall(0x01, (uint32_t)data, 0);
-    for (;;) ;
+    write(data);
+    int ret = fork();
+    if (ret == 0) {
+        write("child\n");
+        exit(255);
+    } else {
+        write("parent\n");
+    }
+
+    for (;;) {
+        int ret;
+        pid_t pid = wait(&ret);
+        (void)pid;
+        pid += 1000;
+    }
     return 0;
 }

+ 3 - 0
user-space-program/script.ld

@@ -13,6 +13,9 @@ SECTIONS
     {
         *(.text)
         *(.text*)
+
+        __stack_chk_guard = .;
+        LONG(0x11451419);
     } > MEM
 
     /DISCARD/ :