Browse Source

feat(thead_local): save and load thread local area

greatbridf 1 year ago
parent
commit
1cacb91f2b

+ 23 - 13
include/kernel/process.hpp

@@ -13,6 +13,8 @@
 #include <kernel/event/evtqueue.hpp>
 #include <kernel/interrupt.h>
 #include <kernel/mm.hpp>
+#include <kernel/mem.h>
+#include <kernel/user/thread_local.hpp>
 #include <kernel/signal.hpp>
 #include <kernel/task.h>
 #include <kernel/tty.hpp>
@@ -61,12 +63,18 @@ using tid_t = uint32_t;
 
 struct thread {
 private:
-    void alloc_kstack(void);
-    void free_kstack(uint32_t p);
+    struct kernel_stack {
+        std::byte* stack_base;
+        uint32_t* esp;
+
+        kernel_stack();
+        kernel_stack(const kernel_stack& other);
+        kernel_stack(kernel_stack&& other);
+        ~kernel_stack();
+    };
 
 public:
-    uint32_t* esp;
-    uint32_t pkstack;
+    kernel_stack kstack;
     pid_t owner;
     thread_attr attr;
     signal_list signals;
@@ -76,20 +84,23 @@ public:
 
     types::string<> name {};
 
+    segment_descriptor tls_desc {};
+
     explicit inline thread(types::string<> name, pid_t owner)
         : owner { owner }
         , attr { .system = 1, .ready = 1, }
         , name { name }
     {
-        alloc_kstack();
     }
 
     inline thread(const thread& val, pid_t owner)
         : owner { owner }, attr { val.attr }, name { val.name }
     {
-        alloc_kstack();
     }
 
+    int set_thread_area(user::user_desc* ptr);
+    int load_thread_area() const;
+
     void sleep();
     void wakeup();
     constexpr bool is_ready() const
@@ -97,15 +108,14 @@ public:
 
     void send_signal(kernel::signal_list::signo_type signal);
 
-    constexpr thread(thread&& val) = default;
-    inline ~thread() { free_kstack(pkstack); }
+    thread(thread&& val) = default;
 
-    constexpr tid_t tid() const { return pkstack; }
+    inline tid_t tid() const { return (tid_t)kstack.stack_base; }
 
-    constexpr bool operator==(const thread& rhs) const
-    { return pkstack == rhs.pkstack; }
-    constexpr bool operator<(const thread& rhs) const
-    { return pkstack < rhs.pkstack; }
+    inline bool operator==(const thread& rhs) const
+    { return tid() == rhs.tid(); }
+    inline bool operator<(const thread& rhs) const
+    { return tid() < rhs.tid(); }
 };
 
 }

+ 3 - 1
include/kernel/user/thread_local.hpp

@@ -1,5 +1,7 @@
 #pragma once
 
+#include <kernel/mem.h>
+
 #include <stdint.h>
 
 namespace kernel::user {
@@ -16,6 +18,6 @@ struct user_desc {
     uint32_t useable : 1;
 };
 
-int set_thread_area(user_desc* ptr);
+void load_thread_area(const segment_descriptor& desc);
 
 } // namespace kernel::user

+ 98 - 55
src/kernel/process.cpp

@@ -1,6 +1,9 @@
 #include <memory>
+#include <queue>
 #include <utility>
 
+#include <stdint.h>
+#include <stdio.h>
 #include <bits/alltypes.h>
 
 #include <asm/port_io.h>
@@ -15,8 +18,8 @@
 #include <kernel/process.hpp>
 #include <kernel/signal.hpp>
 #include <kernel/vfs.hpp>
-#include <stdint.h>
-#include <stdio.h>
+#include <kernel/user/thread_local.hpp>
+
 #include <types/allocator.hpp>
 #include <types/bitmap.hpp>
 #include <types/cplusplus.hpp>
@@ -50,52 +53,6 @@ struct no_irq_guard {
 
 } // namespace kernel
 
-static types::bitmap* pkstack_bmp;
-
-void kernel::tasks::thread::alloc_kstack(void)
-{
-    static int __allocated;
-    if (!pkstack_bmp)
-        pkstack_bmp = new types::bitmap((0x1000000 - 0xc00000) / THREAD_KERNEL_STACK_SIZE);
-
-    for (int i = 0; i < __allocated; ++i) {
-        if (pkstack_bmp->test(i) == 0) {
-            pkstack = 0xffc00000 + THREAD_KERNEL_STACK_SIZE * (i + 1);
-            esp = reinterpret_cast<uint32_t*>(pkstack);
-
-            pkstack_bmp->set(i);
-            return;
-        }
-    }
-
-    // kernel stack pt is at page#0x00005
-    kernel::paccess pa(0x00005);
-    auto pt = (pt_t)pa.ptr();
-    assert(pt);
-
-    auto cnt = THREAD_KERNEL_STACK_SIZE / PAGE_SIZE;
-    pte_t* pte = *pt + __allocated * cnt;
-
-    for (uint32_t i = 0; i < cnt; ++i) {
-        pte[i].v = 0x3;
-        pte[i].in.page = __alloc_raw_page();
-    }
-
-    pkstack = 0xffc00000 + THREAD_KERNEL_STACK_SIZE * (__allocated + 1);
-    esp = reinterpret_cast<uint32_t*>(pkstack);
-
-    pkstack_bmp->set(__allocated);
-    ++__allocated;
-}
-
-void kernel::tasks::thread::free_kstack(uint32_t p)
-{
-    p -= 0xffc00000;
-    p /= THREAD_KERNEL_STACK_SIZE;
-    p -= 1;
-    pkstack_bmp->clear(p);
-}
-
 int filearr::allocate_fd(int from)
 {
     if (from < min_avail)
@@ -252,6 +209,56 @@ void process::send_signal(signo_type signal)
         thd.send_signal(signal);
 }
 
+static std::priority_queue<std::byte*> s_kstacks;
+thread::kernel_stack::kernel_stack()
+{
+    static int allocated;
+    static types::mutex mtx;
+    types::lock_guard lck(mtx);
+
+    if (!s_kstacks.empty()) {
+        stack_base = s_kstacks.top();
+        esp = (uint32_t*)stack_base;
+        s_kstacks.pop();
+        return;
+    }
+
+    // kernel stack pt is at page#0x00005
+    kernel::paccess pa(0x00005);
+    auto pt = (pt_t)pa.ptr();
+    assert(pt);
+
+    int cnt = THREAD_KERNEL_STACK_SIZE / PAGE_SIZE;
+    pte_t* pte = *pt + allocated * cnt;
+
+    for (int i = 0; i < cnt; ++i) {
+        pte[i].v = 0x3;
+        pte[i].in.page = __alloc_raw_page();
+    }
+
+    stack_base = (std::byte*)(0xffc00000 + THREAD_KERNEL_STACK_SIZE * (allocated + 1));
+    esp = (uint32_t*)stack_base;
+
+    ++allocated;
+}
+
+thread::kernel_stack::kernel_stack(const kernel_stack& other)
+    : kernel_stack()
+{
+    auto offset = vptrdiff(other.stack_base, other.esp);
+    esp = (uint32_t*)(stack_base - offset);
+    memcpy(esp, other.esp, offset);
+}
+
+thread::kernel_stack::kernel_stack(kernel_stack&& other)
+    : stack_base(std::exchange(other.stack_base, nullptr))
+    , esp(std::exchange(other.esp, nullptr)) { }
+
+thread::kernel_stack::~kernel_stack()
+{
+    s_kstacks.push(stack_base);
+}
+
 void thread::sleep()
 {
     attr.ready = 0;
@@ -270,6 +277,40 @@ void thread::send_signal(signo_type signal)
         this->wakeup();
 }
 
+int thread::set_thread_area(kernel::user::user_desc* ptr)
+{
+    if (ptr->read_exec_only && ptr->seg_not_present) {
+        void* dst = (void*)ptr->base_addr;
+        std::size_t len = ptr->limit;
+        if (len > 0 && dst)
+            memset(dst, 0x00, len);
+        return 0;
+    }
+
+    if (ptr->entry_number == -1U)
+        ptr->entry_number = 6;
+    else
+        return -1;
+
+    tls_desc.limit_low = ptr->limit & 0xFFFF;
+    tls_desc.base_low = ptr->base_addr & 0xFFFF;
+    tls_desc.base_mid = (ptr->base_addr >> 16) & 0xFF;
+    tls_desc.access = SD_TYPE_DATA_USER;
+    tls_desc.limit_high = (ptr->limit >> 16) & 0xF;
+    tls_desc.flags = (ptr->limit_in_pages << 3) | (ptr->seg_32bit << 2);
+    tls_desc.base_high = (ptr->base_addr >> 24) & 0xFF;
+
+    return 0;
+}
+
+int thread::load_thread_area() const
+{
+    if (tls_desc.flags == 0)
+        return -1;
+    kernel::user::load_thread_area(tls_desc);
+    return 0;
+}
+
 void proclist::kill(pid_t pid, int exit_code)
 {
     auto& proc = this->find(pid);
@@ -400,8 +441,8 @@ static void create_kthreadd_process()
     assert(inserted);
     auto& thd = *iter_thd;
 
-    auto* esp = &thd.esp;
-    auto old_esp = (uint32_t)thd.esp;
+    auto* esp = &thd.kstack.esp;
+    auto old_esp = (uint32_t)thd.kstack.esp;
 
     // return(start) address
     push_stack(esp, (uint32_t)kernel_threadd_main);
@@ -529,7 +570,7 @@ void NORETURN init_scheduler(void)
     readythds->push(current_thread);
 
     tss.ss0 = KERNEL_DATA_SEGMENT;
-    tss.esp0 = current_thread->pkstack;
+    tss.esp0 = (uint32_t)current_thread->kstack.esp;
 
     current_process->mms.switch_pd();
 
@@ -556,7 +597,7 @@ void NORETURN init_scheduler(void)
         "%=:\n"
         "ud2"
         :
-        : "a"(current_thread->esp), "c"(_kernel_init)
+        : "a"(current_thread->kstack.esp), "c"(_kernel_init)
         : "memory");
 
     freeze();
@@ -581,10 +622,12 @@ bool schedule()
     curr_thd = current_thread;
 
     current_thread = next_thd;
-    tss.esp0 = (uint32_t)next_thd->esp;
+    tss.esp0 = (uint32_t)next_thd->kstack.esp;
+
+    next_thd->load_thread_area();
 
-    asm_ctx_switch(&curr_thd->esp, &next_thd->esp);
-    tss.esp0 = (uint32_t)curr_thd->esp;
+    asm_ctx_switch(&curr_thd->kstack.esp, &next_thd->kstack.esp);
+    tss.esp0 = (uint32_t)curr_thd->kstack.esp;
 
 _end:
 

+ 30 - 23
src/kernel/syscall.cpp

@@ -65,41 +65,42 @@ int _syscall_fork(interrupt_stack* data)
 
     readythds->push(newthd);
 
-    uint32_t newthd_oldesp = (uint32_t)newthd->esp;
+    uint32_t newthd_oldesp = (uint32_t)newthd->kstack.esp;
+    auto esp = &newthd->kstack.esp;
 
     // 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);
+    push_stack(esp, data->ss);
+    push_stack(esp, data->esp);
+    push_stack(esp, data->eflags);
+    push_stack(esp, data->cs);
+    push_stack(esp, (uint32_t)data->v_eip);
 
     // eax
-    push_stack(&newthd->esp, 0);
-    push_stack(&newthd->esp, data->s_regs.ecx);
+    push_stack(esp, 0);
+    push_stack(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);
+    push_stack(esp, 0);
+    push_stack(esp, data->s_regs.ebx);
+    push_stack(esp, data->s_regs.esp);
+    push_stack(esp, data->s_regs.ebp);
+    push_stack(esp, data->s_regs.esi);
+    push_stack(esp, data->s_regs.edi);
 
     // ctx_switch stack
     // return address
-    push_stack(&newthd->esp, (uint32_t)_syscall_stub_fork_return);
+    push_stack(esp, (uint32_t)_syscall_stub_fork_return);
     // ebx
-    push_stack(&newthd->esp, 0);
+    push_stack(esp, 0);
     // edi
-    push_stack(&newthd->esp, 0);
+    push_stack(esp, 0);
     // esi
-    push_stack(&newthd->esp, 0);
+    push_stack(esp, 0);
     // ebp
-    push_stack(&newthd->esp, 0);
+    push_stack(esp, 0);
     // eflags
-    push_stack(&newthd->esp, 0);
-    // original esp
-    push_stack(&newthd->esp, newthd_oldesp);
+    push_stack(esp, 0);
+    // original
+    push_stack(esp, newthd_oldesp);
 
     return newproc.pid;
 }
@@ -443,7 +444,13 @@ int _syscall_getppid(interrupt_stack*)
 int _syscall_set_thread_area(interrupt_stack* data)
 {
     SYSCALL_ARG1(kernel::user::user_desc* __user, ptr);
-    return kernel::user::set_thread_area(ptr);
+
+    auto ret = current_thread->set_thread_area(ptr);
+    if (ret != 0)
+        return ret;
+
+    current_thread->load_thread_area();
+    return 0;
 }
 
 int _syscall_set_tid_address(interrupt_stack* data)

+ 10 - 23
src/kernel/user/thread_local.cc

@@ -1,3 +1,4 @@
+#include <kernel/process.hpp>
 #include <kernel/mem.h>
 #include <kernel/user/thread_local.hpp>
 
@@ -6,30 +7,16 @@
 
 namespace kernel::user {
 
-int set_thread_area(user_desc* ptr)
+void load_thread_area(const segment_descriptor& desc)
 {
-    if (ptr->read_exec_only && ptr->seg_not_present) {
-        void* dst = (void*)ptr->base_addr;
-        std::size_t len = ptr->limit;
-        if (len > 0 && dst)
-            memset(dst, 0x00, len);
-        return 0;
-    }
-
-    if (ptr->entry_number == -1U)
-        ptr->entry_number = 6;
-    else
-        return -1;
-
-    gdt[6].limit_low = ptr->limit & 0xFFFF;
-    gdt[6].base_low = ptr->base_addr & 0xFFFF;
-    gdt[6].base_mid = (ptr->base_addr >> 16) & 0xFF;
-    gdt[6].access = SD_TYPE_DATA_USER;
-    gdt[6].limit_high = (ptr->limit >> 16) & 0xF;
-    gdt[6].flags = (ptr->limit_in_pages << 3) | (ptr->seg_32bit << 2);
-    gdt[6].base_high = (ptr->base_addr >> 24) & 0xFF;
-
-    return 0;
+    gdt[6] = desc;
+    asm volatile(
+        "mov %%gs, %%ax\n\t"
+        "mov %%ax, %%gs\n\t"
+        :
+        :
+        : "ax"
+    );
 }
 
 } // namespace kernel::user