ソースを参照

Merge branch 'kernel-thread' into user-space-program

greatbridf 2 年 前
コミット
710e9cf529

+ 1 - 0
.gitignore

@@ -11,3 +11,4 @@ bx_enh_dbg.ini
 compile_commands.json
 
 Makefile
+cross-compile.cmake

+ 1 - 0
CMakeLists.txt

@@ -83,6 +83,7 @@ set(KERNEL_MAIN_SOURCES src/kernel_main.c
                         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

+ 39 - 2
configure

@@ -6,6 +6,27 @@ 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
@@ -37,11 +58,9 @@ OS=`uname`
 case "$OS" in
     "Linux")
         echo "Linux"
-        QEMU_ACCEL='-enable-kvm'
         ;;
     "Darwin")
         echo "macOS"
-        QEMU_ACCEL='-accel hvf'
         ;;
     *)
         echo "unknown"
@@ -49,10 +68,28 @@ case "$OS" in
         ;;
 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=

+ 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)

+ 3 - 3
include/kernel/interrupt.h

@@ -22,13 +22,13 @@ struct regs_32 {
     uint32_t eax;
 };
 
-struct PACKED irq0_data {
+struct PACKED interrupt_stack {
     struct regs_32 s_regs;
     void* v_eip;
     uint32_t cs;
     uint32_t eflags;
-    uint32_t esp;
-    uint32_t ss;
+    const uint32_t esp;
+    const uint32_t ss;
 };
 
 // present: When set, the page fault was caused by a page-protection violation.

+ 4 - 0
include/kernel/mm.hpp

@@ -143,6 +143,10 @@ int k_map(
 // 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);
 

+ 22 - 2
include/kernel/process.hpp

@@ -8,6 +8,8 @@
 #include <kernel/mm.hpp>
 #include <types/list.hpp>
 
+typedef size_t pid_t;
+
 class process;
 struct thread;
 
@@ -15,32 +17,50 @@ 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;
-    uint32_t esp;
+    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 context_switch(irq0_data* intrpt_data);
+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);
 
 #else
 

+ 1 - 10
include/kernel/syscall.hpp

@@ -3,16 +3,7 @@
 #include <types/types.h>
 #include <kernel/interrupt.h>
 
-struct PACKED syscall_stack_data {
-    struct regs_32 s_regs;
-    void* v_eip;
-    uint32_t cs;
-    uint32_t eflags;
-    uint32_t esp;
-    uint32_t ss;
-};
-
 // return value is stored in %eax and %edx
-typedef void (*syscall_handler)(syscall_stack_data* data);
+typedef void (*syscall_handler)(interrupt_stack* data);
 
 void init_syscall(void);

+ 19 - 19
include/types/list.hpp

@@ -33,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);
         }
     };
 
@@ -84,7 +84,7 @@ public:
             return *this;
         }
 
-        explicit iterator(node_base* _n) noexcept
+        explicit iterator(node_type* _n) noexcept
             : n(_n)
         {
         }
@@ -127,17 +127,17 @@ public:
 
         Reference operator*() const noexcept
         {
-            return (static_cast<node_type*>(n))->value;
+            return n->value;
         }
 
         Pointer operator->() const noexcept
         {
-            return &(static_cast<node_type*>(n))->value;
+            return &n->value;
         }
 
         Pointer ptr(void) const noexcept
         {
-            return &(static_cast<node_type*>(n))->value;
+            return &n->value;
         }
 
         node_base_type* _node(void) const noexcept
@@ -146,7 +146,7 @@ public:
         }
 
     protected:
-        node_base_type* n;
+        node_type* n;
     };
 
 private:
@@ -178,8 +178,8 @@ public:
         : 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)
@@ -249,10 +249,10 @@ public:
     // insert the value v in front of the given iterator
     iterator_type insert(const iterator_type& iter, const value_type& v) noexcept
     {
-        node_base_type* new_node = allocator_traits<allocator_type>::allocate_and_construct(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;
@@ -261,10 +261,10 @@ public:
     // insert the value v in front of the given iterator
     iterator_type insert(const iterator_type& iter, value_type&& v) noexcept
     {
-        node_base_type* new_node = allocator_traits<allocator_type>::allocate_and_construct(move(v));
+        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(iter._node());
+        new_node->connect(static_cast<node_type*>(iter._node()));
 
         ++_size();
         return ret;
@@ -314,7 +314,7 @@ public:
 
     iterator_type end() noexcept
     {
-        return iterator_type(tail);
+        return iterator_type(static_cast<node_type*>(tail));
     }
 
     const_iterator_type begin() const noexcept
@@ -324,17 +324,17 @@ public:
 
     const_iterator_type end() const noexcept
     {
-        return const_iterator_type(tail);
+        return const_iterator_type(static_cast<node_type*>(tail));
     }
 
     const_iterator_type cbegin() const noexcept
     {
-        return const_iterator_type(head->next);
+        return begin();
     }
 
     const_iterator_type cend() const noexcept
     {
-        return const_iterator_type(tail);
+        return end();
     }
 
     bool empty(void) const 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* 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* lock_addr)
+{
+    asm volatile("movl $0, %%eax\nxchgl %%eax, (%0)"
+                 :
+                 : "r"  (lock_addr)
+                 : "eax", "memory"
+                 );
+}
+
+#ifdef __cplusplus
+}
+#endif

+ 65 - 0
src/asm/interrupt.s

@@ -218,3 +218,68 @@ syscall_stub:
     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

+ 6 - 6
src/kernel/interrupt.cpp

@@ -82,9 +82,9 @@ void init_pic(void)
 
 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];
 
@@ -94,11 +94,11 @@ extern "C" void int6_handler(
         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   ----\n");
@@ -219,11 +219,11 @@ kill:
     asm_hlt();
 }
 
-extern "C" void irq0_handler(struct irq0_data* d)
+extern "C" void irq0_handler(struct interrupt_stack* d)
 {
     inc_tick();
-    context_switch(d);
     asm_outb(PORT_PIC1_COMMAND, PIC_EOI);
+    do_scheduling(d);
 }
 // keyboard interrupt
 extern "C" void irq1_handler(void)

+ 24 - 4
src/kernel/mem.cpp

@@ -296,10 +296,30 @@ static inline void free_addr_range(phys_ptr_t start, phys_ptr_t end)
 
 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 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();

+ 208 - 70
src/kernel/process.cpp

@@ -3,21 +3,24 @@
 #include <kernel/mm.hpp>
 #include <kernel/process.hpp>
 #include <kernel/stdio.h>
+#include <kernel/tty.h>
 #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);
-
-static inline void* align_down_to_16byte(void* addr)
-{
-    return (void*)((uint32_t)addr & 0xfffffff0);
-}
+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;
+void (* volatile kthreadd_new_thd_func)(void*);
+void  * volatile kthreadd_new_thd_data;
+uint32_t kthreadd_lock = 0;
 
 thread* current_thread;
 process* current_process;
@@ -25,6 +28,7 @@ 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;
@@ -39,49 +43,155 @@ process::process(process&& val)
     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++ }
 {
-    k_esp = align_down_to_16byte((char*)k_malloc(THREAD_KERNEL_STACK_SIZE) + THREAD_KERNEL_STACK_SIZE);
-    memset((char*)k_esp - THREAD_KERNEL_STACK_SIZE, 0x00, THREAD_KERNEL_STACK_SIZE);
-
-    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: 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,
-        .regs {},
-        .eflags {},
         // TODO: change this
-        .esp = 0x40100000U,
+        .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());
 
-    // 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);
+    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);
 
-    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;
+        // TODO: change this
+        memcpy((void*)0x40000000U, image, image_size);
 
-    current_process = this;
-    current_thread = thd.ptr();
-    asm_switch_pd(pd);
+        current_process = old_proc;
+        current_thread = old_thd;
+        asm_switch_pd(old_pd);
+    }
+}
 
-    // TODO: change this
-    memcpy((void*)0x40000000U, image, image_size);
+void _example_io_thread(void* _d)
+{
+    const char* data = reinterpret_cast<const char*>(_d);
+    tty_print(console, data);
+    // syscall_sleep
+    asm volatile("movl $0x02, %%eax\nint $0x80":::"eax");
+}
 
-    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");
+    kthreadd_new_thd_func = _example_io_thread;
+    kthreadd_new_thd_data = (void*)"data in io thread\n";
+    for (;;) {
+        spin_lock(&kthreadd_lock);
+
+        if (kthreadd_new_thd_func) {
+            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()
+            }
+        } else {
+            spin_unlock(&kthreadd_lock);
+        }
+        asm_hlt();
+    }
 }
 
 void NORETURN init_scheduler()
@@ -93,6 +203,7 @@ void NORETURN init_scheduler()
 
     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();
@@ -108,55 +219,82 @@ void NORETURN init_scheduler()
     go_user_space(user_space_start);
 }
 
-void context_switch(irq0_data* intrpt_data)
+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;
 
-    thread* thd = *ready_thds->begin();
+    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) {
-        ready_thds->erase(ready_thds->begin());
-        // check if the thread is ready
-        ready_thds->push_back(thd);
+        next_task(iter_thd, thd);
         return;
     }
 
-    process* pro = thd->owner;
-    if (current_process != pro) {
-        if (!pro->attr.system) {
-            tss.esp0 = (uint32_t)pro->k_esp;
-        }
-
-        current_process = pro;
-        asm_switch_pd(pro->mms.begin()->pd);
+    process* proc = thd->owner;
+    if (current_process != proc) {
+        process_context_save(intrpt_data, current_process);
+        process_context_load(intrpt_data, proc);
     }
 
-    // save current thread info
-    current_thread->eflags = intrpt_data->eflags;
-    current_thread->eip = intrpt_data->v_eip;
-    memcpy(&current_thread->regs, &intrpt_data->s_regs, sizeof(regs_32));
-
-    // load ready thread info
-    intrpt_data->eflags = thd->eflags;
-    intrpt_data->eflags |= 0x200; // sti
-    intrpt_data->v_eip = thd->eip;
-    memcpy(&intrpt_data->s_regs, &thd->regs, sizeof(regs_32));
-
-    if (!pro->attr.system) {
-        // user mode
-        current_thread->esp = intrpt_data->esp;
-
-        intrpt_data->cs = USER_CODE_SELECTOR;
-        intrpt_data->ss = USER_DATA_SELECTOR;
-        intrpt_data->esp = thd->esp;
-    } else {
-        // supervisor mode
-        intrpt_data->cs = KERNEL_CODE_SEGMENT;
-    }
+    thread_context_save(intrpt_data, current_thread);
+    thread_context_load(intrpt_data, thd);
 
-    ready_thds->erase(ready_thds->begin());
-    // check if the thread is ready
-    ready_thds->push_back(thd);
+    next_task(iter_thd, thd);
 
-    current_thread = thd;
+    if (thd->attr.system)
+        to_kernel(intrpt_data);
+    else
+        to_user(intrpt_data);
 }

+ 28 - 7
src/kernel/syscall.cpp

@@ -1,31 +1,52 @@
 #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(syscall_stack_data* data)
+void _syscall_not_impl(interrupt_stack* data)
 {
     data->s_regs.eax = 0xffffffff;
     data->s_regs.edx = 0xffffffff;
 }
 
-void _syscall_fork(syscall_stack_data* data)
+void _syscall_fork(interrupt_stack* data)
 {
-    data->s_regs.eax = 0xfafafafa;
-    data->s_regs.edx = 0xfefefefe;
+    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(syscall_stack_data* data)
+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(syscall_stack_data* data)
+void _syscall_sleep(interrupt_stack* data)
 {
-    ++data->s_regs.ecx;
+    current_thread->attr.ready = 0;
+    current_thread->attr.wait = 1;
+
+    data->s_regs.eax = 0;
+    data->s_regs.edx = 0;
+
+    do_scheduling(data);
 }
 
 void init_syscall(void)

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

@@ -3,6 +3,14 @@
 
 .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