mirror of
https://github.com/justinian/jsix.git
synced 2025-12-10 08:24:32 -08:00
[kernel] Add userspace threading
Implement the syscalls necessary for threads to create other threads in their same process. This involved rearranging a number of syscalls, as well as implementing object_wait and a basic implementation of a process' list of handles.
This commit is contained in:
@@ -50,6 +50,8 @@ modules:
|
|||||||
- src/kernel/syscall.s
|
- src/kernel/syscall.s
|
||||||
- src/kernel/syscalls/object.cpp
|
- src/kernel/syscalls/object.cpp
|
||||||
- src/kernel/syscalls/process.cpp
|
- src/kernel/syscalls/process.cpp
|
||||||
|
- src/kernel/syscalls/system.cpp
|
||||||
|
- src/kernel/syscalls/thread.cpp
|
||||||
- src/kernel/task.s
|
- src/kernel/task.s
|
||||||
- src/kernel/crtn.s
|
- src/kernel/crtn.s
|
||||||
|
|
||||||
|
|||||||
@@ -3,32 +3,53 @@
|
|||||||
|
|
||||||
#include "j6/types.h"
|
#include "j6/types.h"
|
||||||
#include "j6/errors.h"
|
#include "j6/errors.h"
|
||||||
|
#include "j6/signals.h"
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
j6_status_t get_process_koid(j6_koid_t *koid);
|
j6_status_t system_log(const char *msg);
|
||||||
j6_status_t sleep(uint64_t til);
|
|
||||||
j6_status_t debug();
|
j6_status_t object_wait(j6_handle_t obj, j6_signal_t sig, j6_signal_t *out);
|
||||||
j6_status_t message(const char *msg);
|
|
||||||
|
j6_status_t process_koid(j6_koid_t *koid);
|
||||||
|
|
||||||
|
j6_status_t thread_koid(j6_koid_t *koid);
|
||||||
|
j6_status_t thread_create(void (*koid)(), j6_handle_t *handle);
|
||||||
|
j6_status_t thread_sleep(uint64_t til);
|
||||||
|
j6_status_t thread_exit(int64_t status);
|
||||||
|
|
||||||
int main(int, const char **);
|
int main(int, const char **);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
thread_proc()
|
||||||
|
{
|
||||||
|
system_log("sub thread starting");
|
||||||
|
for (int i = 1; i < 5; ++i)
|
||||||
|
thread_sleep(i*10);
|
||||||
|
|
||||||
|
system_log("sub thread exiting");
|
||||||
|
thread_exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
main(int argc, const char **argv)
|
main(int argc, const char **argv)
|
||||||
{
|
{
|
||||||
uint64_t pid = 0;
|
j6_handle_t child = 0;
|
||||||
uint64_t child = 0;
|
j6_signal_t out = 0;
|
||||||
j6_koid_t process = 0;
|
|
||||||
|
|
||||||
j6_status_t result = get_process_koid(&process);
|
system_log("main thread starting");
|
||||||
|
|
||||||
|
j6_status_t result = thread_create(&thread_proc, &child);
|
||||||
if (result != j6_status_ok)
|
if (result != j6_status_ok)
|
||||||
return result;
|
return result;
|
||||||
|
|
||||||
message("hello from nulldrv!");
|
system_log("main thread waiting on child");
|
||||||
|
|
||||||
for (int i = 1; i < 5; ++i)
|
result = object_wait(child, -1ull, &out);
|
||||||
sleep(i*10);
|
if (result != j6_status_ok)
|
||||||
|
return result;
|
||||||
|
|
||||||
return pid;
|
system_log("main thread done, exiting");
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,52 +6,30 @@ extern main
|
|||||||
extern exit
|
extern exit
|
||||||
|
|
||||||
section .text
|
section .text
|
||||||
global get_process_koid
|
|
||||||
get_process_koid:
|
|
||||||
push rbp
|
|
||||||
mov rbp, rsp
|
|
||||||
|
|
||||||
; address of out var should already be in rdi
|
%macro SYSCALL 2
|
||||||
mov rax, 0x10 ; getpid syscall
|
global %1
|
||||||
syscall ; result is now already in rax, so just return
|
%1:
|
||||||
|
push rbp
|
||||||
|
mov rbp, rsp
|
||||||
|
|
||||||
pop rbp
|
; address of args should already be in rdi, etc
|
||||||
ret
|
mov rax, %2
|
||||||
|
syscall
|
||||||
|
; result is now already in rax, so just return
|
||||||
|
|
||||||
global debug
|
pop rbp
|
||||||
debug:
|
ret
|
||||||
push rbp
|
%endmacro
|
||||||
mov rbp, rsp
|
|
||||||
|
|
||||||
mov rax, 0x00 ; debug syscall
|
|
||||||
syscall
|
|
||||||
|
|
||||||
pop rbp
|
|
||||||
ret
|
|
||||||
|
|
||||||
global sleep
|
|
||||||
sleep:
|
|
||||||
push rbp
|
|
||||||
mov rbp, rsp
|
|
||||||
|
|
||||||
mov rax, 0x14 ; sleep syscall
|
|
||||||
syscall
|
|
||||||
|
|
||||||
pop rbp
|
|
||||||
ret
|
|
||||||
|
|
||||||
global message
|
|
||||||
message:
|
|
||||||
push rbp
|
|
||||||
mov rbp, rsp
|
|
||||||
|
|
||||||
; message should already be in rdi
|
|
||||||
mov rax, 0x12
|
|
||||||
syscall
|
|
||||||
|
|
||||||
pop rbp
|
|
||||||
ret
|
|
||||||
|
|
||||||
|
SYSCALL system_log, 0x00
|
||||||
|
SYSCALL object_wait, 0x09
|
||||||
|
SYSCALL process_koid, 0x10
|
||||||
|
SYSCALL thread_koid, 0x18
|
||||||
|
SYSCALL thread_create, 0x19
|
||||||
|
SYSCALL thread_exit, 0x1a
|
||||||
|
SYSCALL thread_pause, 0x1b
|
||||||
|
SYSCALL thread_sleep, 0x1c
|
||||||
|
|
||||||
global _start
|
global _start
|
||||||
_start:
|
_start:
|
||||||
|
|||||||
@@ -6,8 +6,13 @@
|
|||||||
#define j6_signal_no_handles (1ull << 0)
|
#define j6_signal_no_handles (1ull << 0)
|
||||||
|
|
||||||
// Signals 16-47 are defined per-object-type
|
// Signals 16-47 are defined per-object-type
|
||||||
|
|
||||||
|
// Process signals
|
||||||
#define j6_signal_process_exit (1ull << 16)
|
#define j6_signal_process_exit (1ull << 16)
|
||||||
|
|
||||||
|
// Thread signals
|
||||||
|
#define j6_signal_thread_exit (1ull << 16)
|
||||||
|
|
||||||
// Signals 48-63 are user-defined signals
|
// Signals 48-63 are user-defined signals
|
||||||
#define j6_signal_user0 (1ull << 48)
|
#define j6_signal_user0 (1ull << 48)
|
||||||
#define j6_signal_user1 (1ull << 49)
|
#define j6_signal_user1 (1ull << 49)
|
||||||
|
|||||||
@@ -18,3 +18,5 @@ typedef uint64_t j6_signal_t;
|
|||||||
|
|
||||||
/// The rights of a handle/capability are a bitmap of 64 possible rights
|
/// The rights of a handle/capability are a bitmap of 64 possible rights
|
||||||
typedef uint64_t j6_rights_t;
|
typedef uint64_t j6_rights_t;
|
||||||
|
|
||||||
|
#define j6_handle_invalid 0xffffffff
|
||||||
|
|||||||
@@ -16,9 +16,6 @@ namespace memory {
|
|||||||
/// Offset from physical where page tables are mapped.
|
/// Offset from physical where page tables are mapped.
|
||||||
constexpr uintptr_t page_offset = 0xffffc00000000000;
|
constexpr uintptr_t page_offset = 0xffffc00000000000;
|
||||||
|
|
||||||
/// Initial process thread's stack address
|
|
||||||
constexpr uintptr_t initial_stack = 0x0000800000000000;
|
|
||||||
|
|
||||||
/// Initial process thread's stack size, in pages
|
/// Initial process thread's stack size, in pages
|
||||||
constexpr unsigned initial_stack_pages = 1;
|
constexpr unsigned initial_stack_pages = 1;
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,8 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
struct TCB;
|
struct TCB;
|
||||||
|
class thread;
|
||||||
|
class process;
|
||||||
|
|
||||||
struct cpu_state
|
struct cpu_state
|
||||||
{
|
{
|
||||||
@@ -19,6 +21,8 @@ struct cpu_data
|
|||||||
uintptr_t rsp0;
|
uintptr_t rsp0;
|
||||||
uintptr_t rsp3;
|
uintptr_t rsp3;
|
||||||
TCB *tcb;
|
TCB *tcb;
|
||||||
|
thread *t;
|
||||||
|
process *p;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern cpu_data bsp_cpu_data;
|
extern cpu_data bsp_cpu_data;
|
||||||
|
|||||||
@@ -2,6 +2,8 @@
|
|||||||
#include "cpu.h"
|
#include "cpu.h"
|
||||||
#include "debug.h"
|
#include "debug.h"
|
||||||
#include "gdt.h"
|
#include "gdt.h"
|
||||||
|
#include "objects/process.h"
|
||||||
|
#include "objects/thread.h"
|
||||||
#include "page_manager.h"
|
#include "page_manager.h"
|
||||||
|
|
||||||
size_t __counter_syscall_enter = 0;
|
size_t __counter_syscall_enter = 0;
|
||||||
@@ -15,6 +17,9 @@ print_regs(const cpu_state ®s)
|
|||||||
uint64_t cr2 = 0;
|
uint64_t cr2 = 0;
|
||||||
__asm__ __volatile__ ("mov %%cr2, %0" : "=r"(cr2));
|
__asm__ __volatile__ ("mov %%cr2, %0" : "=r"(cr2));
|
||||||
|
|
||||||
|
cons->printf(" process: %llx", bsp_cpu_data.p->koid());
|
||||||
|
cons->printf(" thread: %llx\n", bsp_cpu_data.t->koid());
|
||||||
|
|
||||||
print_regL("rax", regs.rax);
|
print_regL("rax", regs.rax);
|
||||||
print_regM("rbx", regs.rbx);
|
print_regM("rbx", regs.rbx);
|
||||||
print_regR("rcx", regs.rcx);
|
print_regR("rcx", regs.rcx);
|
||||||
|
|||||||
@@ -41,6 +41,9 @@ public:
|
|||||||
/// \returns The object type for the koid
|
/// \returns The object type for the koid
|
||||||
static type koid_type(j6_koid_t koid);
|
static type koid_type(j6_koid_t koid);
|
||||||
|
|
||||||
|
/// Get this object's type
|
||||||
|
inline type get_type() const { return koid_type(m_koid); }
|
||||||
|
|
||||||
/// Get this object's koid
|
/// Get this object's koid
|
||||||
inline j6_koid_t koid() const { return m_koid; }
|
inline j6_koid_t koid() const { return m_koid; }
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,8 @@ kutil::vector<process*> process::s_processes;
|
|||||||
|
|
||||||
process::process(page_table *pml4) :
|
process::process(page_table *pml4) :
|
||||||
kobject(kobject::type::process),
|
kobject(kobject::type::process),
|
||||||
m_pml4(pml4)
|
m_pml4(pml4),
|
||||||
|
m_state(state::running)
|
||||||
{
|
{
|
||||||
s_processes.append(this);
|
s_processes.append(this);
|
||||||
}
|
}
|
||||||
@@ -21,6 +22,12 @@ process::~process()
|
|||||||
void
|
void
|
||||||
process::exit(unsigned code)
|
process::exit(unsigned code)
|
||||||
{
|
{
|
||||||
|
// TODO: make this thread-safe
|
||||||
|
if (m_state != state::running)
|
||||||
|
return;
|
||||||
|
else
|
||||||
|
m_state = state::exited;
|
||||||
|
|
||||||
for (auto *thread : m_threads) {
|
for (auto *thread : m_threads) {
|
||||||
thread->exit(code);
|
thread->exit(code);
|
||||||
}
|
}
|
||||||
@@ -53,10 +60,23 @@ process::update()
|
|||||||
}
|
}
|
||||||
|
|
||||||
thread *
|
thread *
|
||||||
process::create_thread(uint8_t priority)
|
process::create_thread(uint8_t priority, bool user)
|
||||||
{
|
{
|
||||||
thread *th = new thread(*this, priority);
|
thread *th = new thread(*this, priority);
|
||||||
kassert(th, "Failed to create thread!");
|
kassert(th, "Failed to create thread!");
|
||||||
|
|
||||||
|
if (user) {
|
||||||
|
uintptr_t stack_top = stacks_top - (m_threads.count() * stack_size);
|
||||||
|
auto *pm = page_manager::get();
|
||||||
|
pm->map_pages(
|
||||||
|
stack_top - stack_size,
|
||||||
|
page_manager::page_count(stack_size),
|
||||||
|
true, // user stack
|
||||||
|
m_pml4);
|
||||||
|
|
||||||
|
th->tcb()->rsp3 = stack_top;
|
||||||
|
}
|
||||||
|
|
||||||
m_threads.append(th);
|
m_threads.append(th);
|
||||||
return th;
|
return th;
|
||||||
}
|
}
|
||||||
@@ -76,3 +96,36 @@ process::thread_exited(thread *th)
|
|||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
j6_handle_t
|
||||||
|
process::add_handle(kobject *obj)
|
||||||
|
{
|
||||||
|
if (!obj)
|
||||||
|
return j6_handle_invalid;
|
||||||
|
|
||||||
|
obj->handle_retain();
|
||||||
|
size_t len = m_handles.count();
|
||||||
|
m_handles.append(obj);
|
||||||
|
return static_cast<j6_handle_t>(len);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
process::remove_handle(j6_handle_t handle)
|
||||||
|
{
|
||||||
|
if (handle < m_handles.count()) {
|
||||||
|
kobject *obj = m_handles[handle];
|
||||||
|
m_handles[handle] = nullptr;
|
||||||
|
if (obj)
|
||||||
|
obj->handle_release();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
kobject *
|
||||||
|
process::lookup_handle(j6_handle_t handle)
|
||||||
|
{
|
||||||
|
if (handle < m_handles.count())
|
||||||
|
return m_handles[handle];
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|||||||
@@ -9,6 +9,12 @@ class process :
|
|||||||
public kobject
|
public kobject
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
/// Top of memory area where thread stacks are allocated
|
||||||
|
constexpr static uintptr_t stacks_top = 0x0000800000000000;
|
||||||
|
|
||||||
|
/// Size of userspace thread stacks
|
||||||
|
constexpr static size_t stack_size = 0x4000;
|
||||||
|
|
||||||
/// Constructor.
|
/// Constructor.
|
||||||
/// \args pml4 Root of the process' page tables
|
/// \args pml4 Root of the process' page tables
|
||||||
process(page_table *pml4);
|
process(page_table *pml4);
|
||||||
@@ -28,19 +34,44 @@ public:
|
|||||||
|
|
||||||
/// Create a new thread in this process
|
/// Create a new thread in this process
|
||||||
/// \args priority The new thread's scheduling priority
|
/// \args priority The new thread's scheduling priority
|
||||||
|
/// \args user If true, create a userspace stack for this thread
|
||||||
/// \returns The newly created thread object
|
/// \returns The newly created thread object
|
||||||
thread * create_thread(uint8_t priorty);
|
thread * create_thread(uint8_t priorty, bool user = true);
|
||||||
|
|
||||||
|
/// Start tracking an object with a handle.
|
||||||
|
/// \args obj The object this handle refers to
|
||||||
|
/// \returns The new handle for this object
|
||||||
|
j6_handle_t add_handle(kobject *obj);
|
||||||
|
|
||||||
|
/// Stop tracking an object with a handle.
|
||||||
|
/// \args handle The handle that refers to the object
|
||||||
|
/// \returns True if the handle was removed
|
||||||
|
bool remove_handle(j6_handle_t handle);
|
||||||
|
|
||||||
|
/// Lookup an object for a handle
|
||||||
|
/// \args handle The handle to the object
|
||||||
|
/// \returns Pointer to the object, or null if not found
|
||||||
|
kobject * lookup_handle(j6_handle_t handle);
|
||||||
|
|
||||||
/// Inform the process of an exited thread
|
/// Inform the process of an exited thread
|
||||||
/// \args th The thread which has exited
|
/// \args th The thread which has exited
|
||||||
/// \returns True if this thread ending has ended the process
|
/// \returns True if this thread ending has ended the process
|
||||||
bool thread_exited(thread *th);
|
bool thread_exited(thread *th);
|
||||||
|
|
||||||
|
/// Create the special kernel process that owns kernel tasks
|
||||||
|
/// \arg pml4 The kernel-only pml4
|
||||||
|
/// \arg idle_rsp The idle thread's rsp
|
||||||
|
static process * create_kernel_process(page_table *pml4, uintptr_t idle_rsp);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
uint32_t m_return_code;
|
uint32_t m_return_code;
|
||||||
|
|
||||||
page_table *m_pml4;
|
page_table *m_pml4;
|
||||||
kutil::vector<thread*> m_threads;
|
kutil::vector<thread*> m_threads;
|
||||||
|
kutil::vector<kobject*> m_handles;
|
||||||
|
|
||||||
|
enum class state : uint8_t { running, exited };
|
||||||
|
state m_state;
|
||||||
|
|
||||||
static kutil::vector<process*> s_processes;
|
static kutil::vector<process*> s_processes;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
#include "j6/signals.h"
|
#include "j6/signals.h"
|
||||||
|
#include "log.h"
|
||||||
#include "objects/thread.h"
|
#include "objects/thread.h"
|
||||||
#include "objects/process.h"
|
#include "objects/process.h"
|
||||||
#include "scheduler.h"
|
#include "scheduler.h"
|
||||||
|
|
||||||
|
extern "C" void kernel_to_user_trampoline();
|
||||||
static constexpr j6_signal_t thread_default_signals = 0;
|
static constexpr j6_signal_t thread_default_signals = 0;
|
||||||
|
|
||||||
thread::thread(process &parent, uint8_t pri) :
|
thread::thread(process &parent, uint8_t pri, bool user) :
|
||||||
kobject(kobject::type::thread, thread_default_signals),
|
kobject(kobject::type::thread, thread_default_signals),
|
||||||
m_parent(parent),
|
m_parent(parent),
|
||||||
m_state(state::loading),
|
m_state(state::loading),
|
||||||
@@ -16,6 +18,22 @@ thread::thread(process &parent, uint8_t pri) :
|
|||||||
TCB *tcbp = tcb();
|
TCB *tcbp = tcb();
|
||||||
tcbp->pml4 = parent.pml4();
|
tcbp->pml4 = parent.pml4();
|
||||||
tcbp->priority = pri;
|
tcbp->priority = pri;
|
||||||
|
setup_kernel_stack();
|
||||||
|
set_state(state::ready);
|
||||||
|
}
|
||||||
|
|
||||||
|
thread::thread(process &parent, uint8_t pri, uintptr_t rsp0) :
|
||||||
|
kobject(kobject::type::thread, thread_default_signals),
|
||||||
|
m_parent(parent),
|
||||||
|
m_state(state::loading),
|
||||||
|
m_wait_type(wait_type::none),
|
||||||
|
m_wait_data(0),
|
||||||
|
m_wait_obj(0)
|
||||||
|
{
|
||||||
|
TCB *tcbp = tcb();
|
||||||
|
tcbp->pml4 = parent.pml4();
|
||||||
|
tcbp->priority = pri;
|
||||||
|
tcbp->rsp0 = rsp0;
|
||||||
set_state(state::ready);
|
set_state(state::ready);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -51,7 +69,8 @@ thread::wake_on_signals(kobject *obj, j6_signal_t signals)
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
m_wait_type = wait_type::none;
|
m_wait_type = wait_type::none;
|
||||||
m_wait_data = j6_status_ok;
|
m_wait_result = j6_status_ok;
|
||||||
|
m_wait_data = signals;
|
||||||
m_wait_obj = obj->koid();
|
m_wait_obj = obj->koid();
|
||||||
set_state(state::ready);
|
set_state(state::ready);
|
||||||
return true;
|
return true;
|
||||||
@@ -65,7 +84,8 @@ thread::wake_on_time(uint64_t now)
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
m_wait_type = wait_type::none;
|
m_wait_type = wait_type::none;
|
||||||
m_wait_data = j6_status_ok;
|
m_wait_result = j6_status_ok;
|
||||||
|
m_wait_data = now;
|
||||||
m_wait_obj = 0;
|
m_wait_obj = 0;
|
||||||
set_state(state::ready);
|
set_state(state::ready);
|
||||||
return true;
|
return true;
|
||||||
@@ -75,7 +95,8 @@ void
|
|||||||
thread::wake_on_result(kobject *obj, j6_status_t result)
|
thread::wake_on_result(kobject *obj, j6_status_t result)
|
||||||
{
|
{
|
||||||
m_wait_type = wait_type::none;
|
m_wait_type = wait_type::none;
|
||||||
m_wait_data = result;
|
m_wait_result = result;
|
||||||
|
m_wait_data = 0;
|
||||||
m_wait_obj = obj->koid();
|
m_wait_obj = obj->koid();
|
||||||
set_state(state::ready);
|
set_state(state::ready);
|
||||||
}
|
}
|
||||||
@@ -86,5 +107,77 @@ thread::exit(uint32_t code)
|
|||||||
m_return_code = code;
|
m_return_code = code;
|
||||||
set_state(state::exited);
|
set_state(state::exited);
|
||||||
clear_state(state::ready);
|
clear_state(state::ready);
|
||||||
|
assert_signal(j6_signal_thread_exit);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
thread::add_thunk_kernel(uintptr_t rip)
|
||||||
|
{
|
||||||
|
m_tcb.rsp -= sizeof(uintptr_t) * 7;
|
||||||
|
uintptr_t *stack = reinterpret_cast<uintptr_t*>(m_tcb.rsp);
|
||||||
|
|
||||||
|
stack[6] = rip; // return rip
|
||||||
|
stack[5] = m_tcb.rsp0; // rbp
|
||||||
|
stack[4] = 0xbbbbbbbb; // rbx
|
||||||
|
stack[3] = 0x12121212; // r12
|
||||||
|
stack[2] = 0x13131313; // r13
|
||||||
|
stack[1] = 0x14141414; // r14
|
||||||
|
stack[0] = 0x15151515; // r15
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
thread::add_thunk_user(uintptr_t rip)
|
||||||
|
{
|
||||||
|
m_tcb.rsp -= sizeof(uintptr_t) * 8;
|
||||||
|
uintptr_t *stack = reinterpret_cast<uintptr_t*>(m_tcb.rsp);
|
||||||
|
|
||||||
|
stack[7] = rip; // return rip in rcx
|
||||||
|
stack[6] = m_tcb.rsp3; // rbp
|
||||||
|
stack[5] = 0xbbbbbbbb; // rbx
|
||||||
|
stack[4] = 0x00000200; // r11 sets RFLAGS
|
||||||
|
stack[3] = 0x12121212; // r12
|
||||||
|
stack[2] = 0x13131313; // r13
|
||||||
|
stack[1] = 0x14141414; // r14
|
||||||
|
stack[0] = 0x15151515; // r15
|
||||||
|
|
||||||
|
static const uintptr_t trampoline =
|
||||||
|
reinterpret_cast<uintptr_t>(kernel_to_user_trampoline);
|
||||||
|
add_thunk_kernel(trampoline);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
thread::setup_kernel_stack()
|
||||||
|
{
|
||||||
|
constexpr size_t initial_stack_size = 0x1000;
|
||||||
|
constexpr unsigned null_frame_entries = 2;
|
||||||
|
constexpr size_t null_frame_size = null_frame_entries * sizeof(uint64_t);
|
||||||
|
|
||||||
|
void *stack_bottom = kutil::kalloc(initial_stack_size);
|
||||||
|
kutil::memset(stack_bottom, 0, initial_stack_size);
|
||||||
|
|
||||||
|
log::debug(logs::memory, "Created kernel stack at %016lx size 0x%lx",
|
||||||
|
stack_bottom, initial_stack_size);
|
||||||
|
|
||||||
|
void *stack_top =
|
||||||
|
kutil::offset_pointer(stack_bottom,
|
||||||
|
initial_stack_size - null_frame_size);
|
||||||
|
|
||||||
|
uint64_t *null_frame = reinterpret_cast<uint64_t*>(stack_top);
|
||||||
|
for (unsigned i = 0; i < null_frame_entries; ++i)
|
||||||
|
null_frame[i] = 0;
|
||||||
|
|
||||||
|
m_tcb.kernel_stack_size = initial_stack_size;
|
||||||
|
m_tcb.kernel_stack = reinterpret_cast<uintptr_t>(stack_bottom);
|
||||||
|
m_tcb.rsp0 = reinterpret_cast<uintptr_t>(stack_top);
|
||||||
|
m_tcb.rsp = m_tcb.rsp0;
|
||||||
|
}
|
||||||
|
|
||||||
|
thread *
|
||||||
|
thread::create_idle_thread(process &kernel, uint8_t pri, uintptr_t rsp0)
|
||||||
|
{
|
||||||
|
thread *idle = new thread(kernel, pri, rsp0);
|
||||||
|
idle->set_state(thread::state::constant);
|
||||||
|
log::info(logs::task, "Created idle thread as koid %llx", idle->koid());
|
||||||
|
|
||||||
|
return idle;
|
||||||
|
}
|
||||||
|
|||||||
@@ -92,6 +92,12 @@ public:
|
|||||||
/// \arg result Result code to return to the thread
|
/// \arg result Result code to return to the thread
|
||||||
void wake_on_result(kobject *obj, j6_status_t result);
|
void wake_on_result(kobject *obj, j6_status_t result);
|
||||||
|
|
||||||
|
/// Get the result status code from the last blocking operation
|
||||||
|
j6_status_t get_wait_result() const { return m_wait_result; }
|
||||||
|
|
||||||
|
/// Get the current blocking opreation's wait data
|
||||||
|
uint64_t get_wait_data() const { return m_wait_data; }
|
||||||
|
|
||||||
inline bool has_state(state s) const {
|
inline bool has_state(state s) const {
|
||||||
return static_cast<uint8_t>(m_state) & static_cast<uint8_t>(s);
|
return static_cast<uint8_t>(m_state) & static_cast<uint8_t>(s);
|
||||||
}
|
}
|
||||||
@@ -111,6 +117,20 @@ public:
|
|||||||
/// \arg code The return code to exit with.
|
/// \arg code The return code to exit with.
|
||||||
void exit(unsigned code);
|
void exit(unsigned code);
|
||||||
|
|
||||||
|
/// Add a stack header that returns to the given address in kernel space.
|
||||||
|
/// \arg rip The address to return to, must be kernel space
|
||||||
|
void add_thunk_kernel(uintptr_t rip);
|
||||||
|
|
||||||
|
/// Add a stack header that returns to the given address in user space.
|
||||||
|
/// \arg rip The address to return to, must be user space
|
||||||
|
void add_thunk_user(uintptr_t rip);
|
||||||
|
|
||||||
|
/// Create the kernel idle thread
|
||||||
|
/// \arg kernel The process object that owns kernel tasks
|
||||||
|
/// \arg pri The idle thread priority value
|
||||||
|
/// \arg rsp The existing stack for the idle thread
|
||||||
|
static thread * create_idle_thread(process &kernel, uint8_t pri, uintptr_t rsp);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
thread() = delete;
|
thread() = delete;
|
||||||
thread(const thread &other) = delete;
|
thread(const thread &other) = delete;
|
||||||
@@ -118,14 +138,24 @@ private:
|
|||||||
friend class process;
|
friend class process;
|
||||||
|
|
||||||
/// Constructor.
|
/// Constructor.
|
||||||
/// \arg p The process which owns this thread
|
/// \arg parent The process which owns this thread
|
||||||
/// \arg pri Initial priority level of this thread
|
/// \arg pri Initial priority level of this thread
|
||||||
thread(process &parent, uint8_t pri);
|
/// \arg user True if this is a userspace thread
|
||||||
|
thread(process &parent, uint8_t pri, bool user = true);
|
||||||
|
|
||||||
process &m_parent;
|
/// Constructor. Used when a kernel stack already exists.
|
||||||
|
/// \arg parent The process which owns this thread
|
||||||
|
/// \arg pri Initial priority level of this thread
|
||||||
|
/// \arg rsp0 The existing kernel stack rsp
|
||||||
|
thread(process &parent, uint8_t pri, uintptr_t rsp0);
|
||||||
|
|
||||||
|
/// Set up a new empty kernel stack for this thread.
|
||||||
|
void setup_kernel_stack();
|
||||||
|
|
||||||
tcb_node m_tcb;
|
tcb_node m_tcb;
|
||||||
|
|
||||||
|
process &m_parent;
|
||||||
|
|
||||||
state m_state;
|
state m_state;
|
||||||
wait_type m_wait_type;
|
wait_type m_wait_type;
|
||||||
// There should be 1 byte of padding here
|
// There should be 1 byte of padding here
|
||||||
@@ -133,5 +163,6 @@ private:
|
|||||||
uint32_t m_return_code;
|
uint32_t m_return_code;
|
||||||
|
|
||||||
uint64_t m_wait_data;
|
uint64_t m_wait_data;
|
||||||
|
j6_status_t m_wait_result;
|
||||||
j6_koid_t m_wait_obj;
|
j6_koid_t m_wait_obj;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -60,13 +60,6 @@ page_manager::create_process_map()
|
|||||||
for (unsigned i = pml4e_kernel; i < table_entries; ++i)
|
for (unsigned i = pml4e_kernel; i < table_entries; ++i)
|
||||||
table->entries[i] = m_kernel_pml4->entries[i];
|
table->entries[i] = m_kernel_pml4->entries[i];
|
||||||
|
|
||||||
// Create the initial user stack
|
|
||||||
map_pages(
|
|
||||||
memory::initial_stack - (memory::initial_stack_pages * frame_size),
|
|
||||||
memory::initial_stack_pages,
|
|
||||||
true, // This is the ring3 stack, user = true
|
|
||||||
table);
|
|
||||||
|
|
||||||
return table;
|
return table;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,8 +16,6 @@
|
|||||||
#include "elf/elf.h"
|
#include "elf/elf.h"
|
||||||
#include "kutil/assert.h"
|
#include "kutil/assert.h"
|
||||||
|
|
||||||
using memory::initial_stack;
|
|
||||||
|
|
||||||
scheduler scheduler::s_instance(nullptr);
|
scheduler scheduler::s_instance(nullptr);
|
||||||
|
|
||||||
const uint64_t rflags_noint = 0x002;
|
const uint64_t rflags_noint = 0x002;
|
||||||
@@ -30,57 +28,6 @@ extern "C" {
|
|||||||
|
|
||||||
extern uint64_t idle_stack_end;
|
extern uint64_t idle_stack_end;
|
||||||
|
|
||||||
/// Set up a new empty kernel stack for this thread. Sets rsp0 on the
|
|
||||||
/// TCB object, but also returns it.
|
|
||||||
/// \returns The new rsp0 as a pointer
|
|
||||||
static void *
|
|
||||||
setup_kernel_stack(TCB *tcb)
|
|
||||||
{
|
|
||||||
constexpr size_t initial_stack_size = 0x1000;
|
|
||||||
constexpr unsigned null_frame_entries = 2;
|
|
||||||
constexpr size_t null_frame_size = null_frame_entries * sizeof(uint64_t);
|
|
||||||
|
|
||||||
void *stack_bottom = kutil::kalloc(initial_stack_size);
|
|
||||||
kutil::memset(stack_bottom, 0, initial_stack_size);
|
|
||||||
|
|
||||||
log::debug(logs::memory, "Created kernel stack at %016lx size 0x%lx",
|
|
||||||
stack_bottom, initial_stack_size);
|
|
||||||
|
|
||||||
void *stack_top =
|
|
||||||
kutil::offset_pointer(stack_bottom,
|
|
||||||
initial_stack_size - null_frame_size);
|
|
||||||
|
|
||||||
uint64_t *null_frame = reinterpret_cast<uint64_t*>(stack_top);
|
|
||||||
for (unsigned i = 0; i < null_frame_entries; ++i)
|
|
||||||
null_frame[i] = 0;
|
|
||||||
|
|
||||||
tcb->kernel_stack_size = initial_stack_size;
|
|
||||||
tcb->kernel_stack = reinterpret_cast<uintptr_t>(stack_bottom);
|
|
||||||
tcb->rsp0 = reinterpret_cast<uintptr_t>(stack_top);
|
|
||||||
tcb->rsp = tcb->rsp0;
|
|
||||||
|
|
||||||
return stack_top;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Initialize this process' kenrel stack with a fake return segment for
|
|
||||||
/// returning out of task_switch.
|
|
||||||
/// \arg tcb TCB of the thread to modify
|
|
||||||
/// \arg rip The rip to return to
|
|
||||||
static void
|
|
||||||
add_fake_task_return(TCB *tcb, uintptr_t rip)
|
|
||||||
{
|
|
||||||
tcb->rsp -= sizeof(uintptr_t) * 7;
|
|
||||||
uintptr_t *stack = reinterpret_cast<uintptr_t*>(tcb->rsp);
|
|
||||||
|
|
||||||
stack[6] = rip; // return rip
|
|
||||||
stack[5] = tcb->rsp0; // rbp
|
|
||||||
stack[4] = 0xbbbbbbbb; // rbx
|
|
||||||
stack[3] = 0x12121212; // r12
|
|
||||||
stack[2] = 0x13131313; // r13
|
|
||||||
stack[1] = 0x14141414; // r14
|
|
||||||
stack[0] = 0x15151515; // r15
|
|
||||||
}
|
|
||||||
|
|
||||||
scheduler::scheduler(lapic *apic) :
|
scheduler::scheduler(lapic *apic) :
|
||||||
m_apic(apic),
|
m_apic(apic),
|
||||||
m_next_pid(1),
|
m_next_pid(1),
|
||||||
@@ -88,25 +35,24 @@ scheduler::scheduler(lapic *apic) :
|
|||||||
m_last_promotion(0)
|
m_last_promotion(0)
|
||||||
{
|
{
|
||||||
page_table *pml4 = page_manager::get_pml4();
|
page_table *pml4 = page_manager::get_pml4();
|
||||||
m_kernel_process = new process(pml4);
|
process *kp = new process(pml4);
|
||||||
thread *idle = m_kernel_process->create_thread(max_priority);
|
m_kernel_process = kp;
|
||||||
|
|
||||||
auto *tcb = idle->tcb();
|
log::debug(logs::task, "Kernel process koid %llx", kp->koid());
|
||||||
|
|
||||||
|
thread *idle = thread::create_idle_thread(*kp, max_priority,
|
||||||
|
reinterpret_cast<uintptr_t>(&idle_stack_end));
|
||||||
|
|
||||||
log::debug(logs::task, "Idle thread koid %llx", idle->koid());
|
log::debug(logs::task, "Idle thread koid %llx", idle->koid());
|
||||||
|
|
||||||
// The kernel idle task, also the thread we're in now
|
auto *tcb = idle->tcb();
|
||||||
tcb->rsp = 0; // This will get set when we switch away
|
|
||||||
tcb->rsp3 = 0; // Never used for the idle task
|
|
||||||
tcb->rsp0 = reinterpret_cast<uintptr_t>(&idle_stack_end);
|
|
||||||
|
|
||||||
idle->set_state(thread::state::constant);
|
|
||||||
|
|
||||||
m_runlists[max_priority].push_back(tcb);
|
m_runlists[max_priority].push_back(tcb);
|
||||||
m_current = tcb;
|
m_current = tcb;
|
||||||
|
|
||||||
bsp_cpu_data.rsp0 = tcb->rsp0;
|
bsp_cpu_data.rsp0 = tcb->rsp0;
|
||||||
bsp_cpu_data.tcb = tcb;
|
bsp_cpu_data.tcb = tcb;
|
||||||
|
bsp_cpu_data.p = kp;
|
||||||
|
bsp_cpu_data.t = idle;
|
||||||
}
|
}
|
||||||
|
|
||||||
uintptr_t
|
uintptr_t
|
||||||
@@ -171,10 +117,10 @@ load_process_image(const void *image_start, size_t bytes, TCB *tcb)
|
|||||||
}
|
}
|
||||||
|
|
||||||
thread *
|
thread *
|
||||||
scheduler::create_process(page_table *pml4)
|
scheduler::create_process(page_table *pml4, bool user)
|
||||||
{
|
{
|
||||||
process *p = new process(pml4);
|
process *p = new process(pml4);
|
||||||
thread *th = p->create_thread(default_priority);
|
thread *th = p->create_thread(default_priority, user);
|
||||||
auto *tcb = th->tcb();
|
auto *tcb = th->tcb();
|
||||||
|
|
||||||
tcb->time_left = quantum(default_priority) + startup_bonus;
|
tcb->time_left = quantum(default_priority) + startup_bonus;
|
||||||
@@ -198,12 +144,11 @@ scheduler::load_process(const char *name, const void *data, size_t size)
|
|||||||
// Set up the page tables - this also allocates an initial user stack
|
// Set up the page tables - this also allocates an initial user stack
|
||||||
page_table *pml4 = page_manager::get()->create_process_map();
|
page_table *pml4 = page_manager::get()->create_process_map();
|
||||||
|
|
||||||
thread* th = create_process(pml4);
|
thread* th = create_process(pml4, true);
|
||||||
auto *tcb = th->tcb();
|
auto *tcb = th->tcb();
|
||||||
|
|
||||||
// Create an initial kernel stack space
|
// Create an initial kernel stack space
|
||||||
void *sp0 = setup_kernel_stack(tcb);
|
uintptr_t *stack = reinterpret_cast<uintptr_t *>(tcb->rsp0) - 7;
|
||||||
uintptr_t *stack = reinterpret_cast<uintptr_t *>(sp0) - 7;
|
|
||||||
|
|
||||||
// Pass args to ramdisk_process_loader on the stack
|
// Pass args to ramdisk_process_loader on the stack
|
||||||
stack[0] = reinterpret_cast<uintptr_t>(data);
|
stack[0] = reinterpret_cast<uintptr_t>(data);
|
||||||
@@ -211,16 +156,15 @@ scheduler::load_process(const char *name, const void *data, size_t size)
|
|||||||
stack[2] = reinterpret_cast<uintptr_t>(tcb);
|
stack[2] = reinterpret_cast<uintptr_t>(tcb);
|
||||||
|
|
||||||
tcb->rsp = reinterpret_cast<uintptr_t>(stack);
|
tcb->rsp = reinterpret_cast<uintptr_t>(stack);
|
||||||
add_fake_task_return(tcb,
|
th->add_thunk_kernel(reinterpret_cast<uintptr_t>(ramdisk_process_loader));
|
||||||
reinterpret_cast<uintptr_t>(ramdisk_process_loader));
|
|
||||||
|
|
||||||
// Arguments for iret - rip will be pushed on before these
|
// Arguments for iret - rip will be pushed on before these
|
||||||
stack[3] = cs;
|
stack[3] = cs;
|
||||||
stack[4] = rflags_int;
|
stack[4] = rflags_int;
|
||||||
stack[5] = initial_stack;
|
stack[5] = process::stacks_top;
|
||||||
stack[6] = ss;
|
stack[6] = ss;
|
||||||
|
|
||||||
tcb->rsp3 = initial_stack;
|
tcb->rsp3 = process::stacks_top;
|
||||||
|
|
||||||
m_runlists[default_priority].push_back(tcb);
|
m_runlists[default_priority].push_back(tcb);
|
||||||
|
|
||||||
@@ -234,16 +178,13 @@ void
|
|||||||
scheduler::create_kernel_task(void (*task)(), uint8_t priority, bool constant)
|
scheduler::create_kernel_task(void (*task)(), uint8_t priority, bool constant)
|
||||||
{
|
{
|
||||||
page_table *pml4 = page_manager::get()->get_kernel_pml4();
|
page_table *pml4 = page_manager::get()->get_kernel_pml4();
|
||||||
thread *th = create_process(pml4);
|
thread *th = create_process(pml4, false);
|
||||||
auto *tcb = th->tcb();
|
auto *tcb = th->tcb();
|
||||||
|
|
||||||
uint16_t kcs = (1 << 3) | 0; // Kernel CS is GDT entry 1, ring 0
|
uint16_t kcs = (1 << 3) | 0; // Kernel CS is GDT entry 1, ring 0
|
||||||
uint16_t kss = (2 << 3) | 0; // Kernel SS is GDT entry 2, ring 0
|
uint16_t kss = (2 << 3) | 0; // Kernel SS is GDT entry 2, ring 0
|
||||||
|
|
||||||
// Create an initial kernel stack space
|
th->add_thunk_kernel(reinterpret_cast<uintptr_t>(task));
|
||||||
setup_kernel_stack(tcb);
|
|
||||||
add_fake_task_return(tcb,
|
|
||||||
reinterpret_cast<uintptr_t>(task));
|
|
||||||
|
|
||||||
tcb->priority = priority;
|
tcb->priority = priority;
|
||||||
tcb->pml4 = page_manager::get()->get_kernel_pml4();
|
tcb->pml4 = page_manager::get()->get_kernel_pml4();
|
||||||
@@ -389,32 +330,13 @@ scheduler::schedule()
|
|||||||
|
|
||||||
if (next != m_current) {
|
if (next != m_current) {
|
||||||
m_current = next;
|
m_current = next;
|
||||||
|
bsp_cpu_data.t = thread::from_tcb(m_current);
|
||||||
|
bsp_cpu_data.p = &th->parent();
|
||||||
|
thread *next_thread = thread::from_tcb(m_current);
|
||||||
|
|
||||||
|
log::debug(logs::task, "Scheduler switching threads %llx->%llx, priority %d time left %d @ %lld.",
|
||||||
|
th->koid(), next_thread->koid(), m_current->priority, m_current->time_left, m_clock);
|
||||||
|
|
||||||
task_switch(m_current);
|
task_switch(m_current);
|
||||||
|
|
||||||
log::debug(logs::task, "Scheduler switched to thread %llx, priority %d time left %d @ %lld.",
|
|
||||||
th->koid(), m_current->priority, m_current->time_left, m_clock);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
process_node *
|
|
||||||
scheduler::get_process_by_id(uint32_t pid)
|
|
||||||
{
|
|
||||||
// TODO: this needs to be a hash map
|
|
||||||
for (auto *proc : m_blocked) {
|
|
||||||
if (proc->pid == pid) return proc;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < num_priorities; ++i) {
|
|
||||||
for (auto *proc : m_runlists[i]) {
|
|
||||||
if (proc->pid == pid) return proc;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto *proc : m_exited) {
|
|
||||||
if (proc->pid == pid) return proc;
|
|
||||||
}
|
|
||||||
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|||||||
@@ -72,12 +72,7 @@ public:
|
|||||||
/// \returns A pointer to the current thread's TCB
|
/// \returns A pointer to the current thread's TCB
|
||||||
inline TCB * current() { return m_current; }
|
inline TCB * current() { return m_current; }
|
||||||
|
|
||||||
/*
|
inline void add_thread(TCB *t) { m_runlists[t->priority].push_back(static_cast<tcb_node*>(t)); }
|
||||||
/// Look up a process by its PID
|
|
||||||
/// \arg pid The requested PID
|
|
||||||
/// \returns The process matching that PID, or nullptr
|
|
||||||
tcb_node * get_process_by_id(uint32_t pid);
|
|
||||||
*/
|
|
||||||
|
|
||||||
/// Get a reference to the system scheduler
|
/// Get a reference to the system scheduler
|
||||||
/// \returns A reference to the global system scheduler
|
/// \returns A reference to the global system scheduler
|
||||||
@@ -92,8 +87,9 @@ private:
|
|||||||
/// Create a new process object. This process will have its pid
|
/// Create a new process object. This process will have its pid
|
||||||
/// set but nothing else.
|
/// set but nothing else.
|
||||||
/// \arg pml4 The root page table of the process
|
/// \arg pml4 The root page table of the process
|
||||||
|
/// \arg user True if this thread will enter userspace
|
||||||
/// \returns The new process' main thread
|
/// \returns The new process' main thread
|
||||||
thread * create_process(page_table *pml4);
|
thread * create_process(page_table *pml4, bool user);
|
||||||
|
|
||||||
void prune(uint64_t now);
|
void prune(uint64_t now);
|
||||||
void check_promotions(uint64_t now);
|
void check_promotions(uint64_t now);
|
||||||
@@ -114,3 +110,4 @@ private:
|
|||||||
|
|
||||||
static scheduler s_instance;
|
static scheduler s_instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,14 @@
|
|||||||
SYSCALL(0x00, object_noop, void)
|
SYSCALL(0x00, system_log, const char *)
|
||||||
SYSCALL(0x01, object_wait, j6_handle_t, j6_signal_t, j6_signal_t *)
|
SYSCALL(0x01, system_noop, void)
|
||||||
|
|
||||||
|
SYSCALL(0x09, object_wait, j6_handle_t, j6_signal_t, j6_signal_t *)
|
||||||
|
|
||||||
SYSCALL(0x10, process_koid, j6_koid_t *)
|
SYSCALL(0x10, process_koid, j6_koid_t *)
|
||||||
SYSCALL(0x11, process_exit, int64_t)
|
SYSCALL(0x11, process_exit, int64_t)
|
||||||
SYSCALL(0x12, process_log, const char *)
|
|
||||||
SYSCALL(0x13, process_pause, void)
|
SYSCALL(0x18, thread_koid, j6_koid_t *)
|
||||||
SYSCALL(0x14, process_sleep, uint64_t)
|
SYSCALL(0x19, thread_create, void *, j6_handle_t *)
|
||||||
|
SYSCALL(0x1a, thread_exit, int64_t)
|
||||||
|
SYSCALL(0x1b, thread_pause, void)
|
||||||
|
SYSCALL(0x1c, thread_sleep, uint64_t)
|
||||||
|
|
||||||
|
|||||||
@@ -2,24 +2,32 @@
|
|||||||
#include "j6/types.h"
|
#include "j6/types.h"
|
||||||
|
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
|
#include "objects/process.h"
|
||||||
|
#include "objects/thread.h"
|
||||||
#include "scheduler.h"
|
#include "scheduler.h"
|
||||||
|
|
||||||
namespace syscalls {
|
namespace syscalls {
|
||||||
|
|
||||||
j6_status_t
|
j6_status_t
|
||||||
object_noop()
|
object_wait(j6_handle_t handle, j6_signal_t mask, j6_signal_t *sigs)
|
||||||
{
|
{
|
||||||
auto &s = scheduler::get();
|
scheduler &s = scheduler::get();
|
||||||
TCB *tcb = s.current();
|
thread *th = thread::from_tcb(s.current());
|
||||||
thread *th = thread::from_tcb(tcb);
|
process &p = th->parent();
|
||||||
log::debug(logs::syscall, "Thread %llx called noop syscall.", th->koid());
|
|
||||||
return j6_status_ok;
|
|
||||||
}
|
|
||||||
|
|
||||||
j6_status_t
|
kobject *obj = p.lookup_handle(handle);
|
||||||
object_wait(j6_handle_t, j6_signal_t, j6_signal_t*)
|
if (!obj)
|
||||||
{
|
return j6_err_invalid_arg;
|
||||||
return j6_err_nyi;
|
|
||||||
|
obj->add_blocked_thread(th);
|
||||||
|
th->wait_on_signals(obj, mask);
|
||||||
|
s.schedule();
|
||||||
|
|
||||||
|
j6_status_t result = th->get_wait_result();
|
||||||
|
if (result == j6_status_ok) {
|
||||||
|
*sigs = th->get_wait_data();
|
||||||
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace syscalls
|
} // namespace syscalls
|
||||||
|
|||||||
@@ -1,28 +1,13 @@
|
|||||||
#include "j6/errors.h"
|
#include "j6/errors.h"
|
||||||
#include "j6/types.h"
|
#include "j6/types.h"
|
||||||
|
|
||||||
#include "objects/process.h"
|
|
||||||
|
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
|
#include "objects/process.h"
|
||||||
|
#include "objects/thread.h"
|
||||||
#include "scheduler.h"
|
#include "scheduler.h"
|
||||||
|
|
||||||
namespace syscalls {
|
namespace syscalls {
|
||||||
|
|
||||||
j6_status_t
|
|
||||||
process_exit(int64_t status)
|
|
||||||
{
|
|
||||||
auto &s = scheduler::get();
|
|
||||||
TCB *tcb = s.current();
|
|
||||||
thread *th = thread::from_tcb(tcb);
|
|
||||||
log::debug(logs::syscall, "Thread %llx exiting with code %d", th->koid(), status);
|
|
||||||
|
|
||||||
th->exit(status);
|
|
||||||
s.schedule();
|
|
||||||
|
|
||||||
log::error(logs::syscall, "returned to exit syscall");
|
|
||||||
return j6_err_unexpected;
|
|
||||||
}
|
|
||||||
|
|
||||||
j6_status_t
|
j6_status_t
|
||||||
process_koid(j6_koid_t *koid)
|
process_koid(j6_koid_t *koid)
|
||||||
{
|
{
|
||||||
@@ -38,41 +23,19 @@ process_koid(j6_koid_t *koid)
|
|||||||
}
|
}
|
||||||
|
|
||||||
j6_status_t
|
j6_status_t
|
||||||
process_log(const char *message)
|
process_exit(int64_t status)
|
||||||
{
|
|
||||||
if (message == nullptr) {
|
|
||||||
return j6_err_invalid_arg;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto &s = scheduler::get();
|
|
||||||
TCB *tcb = s.current();
|
|
||||||
thread *th = thread::from_tcb(tcb);
|
|
||||||
log::info(logs::syscall, "Message[%llx]: %s", th->koid(), message);
|
|
||||||
return j6_status_ok;
|
|
||||||
}
|
|
||||||
|
|
||||||
j6_status_t
|
|
||||||
process_pause()
|
|
||||||
{
|
{
|
||||||
auto &s = scheduler::get();
|
auto &s = scheduler::get();
|
||||||
TCB *tcb = s.current();
|
TCB *tcb = s.current();
|
||||||
thread *th = thread::from_tcb(tcb);
|
process &p = thread::from_tcb(tcb)->parent();
|
||||||
th->wait_on_signals(th, -1ull);
|
|
||||||
|
log::debug(logs::syscall, "Process %llx exiting with code %d", p.koid(), status);
|
||||||
|
|
||||||
|
p.exit(status);
|
||||||
s.schedule();
|
s.schedule();
|
||||||
return j6_status_ok;
|
|
||||||
}
|
|
||||||
|
|
||||||
j6_status_t
|
log::error(logs::syscall, "returned to exit syscall");
|
||||||
process_sleep(uint64_t til)
|
return j6_err_unexpected;
|
||||||
{
|
|
||||||
auto &s = scheduler::get();
|
|
||||||
TCB *tcb = s.current();
|
|
||||||
thread *th = thread::from_tcb(tcb);
|
|
||||||
log::debug(logs::syscall, "Thread %llx sleeping until %llu", th->koid(), til);
|
|
||||||
|
|
||||||
th->wait_on_time(til);
|
|
||||||
s.schedule();
|
|
||||||
return j6_status_ok;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace syscalls
|
} // namespace syscalls
|
||||||
|
|||||||
33
src/kernel/syscalls/system.cpp
Normal file
33
src/kernel/syscalls/system.cpp
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
#include "j6/errors.h"
|
||||||
|
#include "j6/types.h"
|
||||||
|
|
||||||
|
#include "log.h"
|
||||||
|
#include "scheduler.h"
|
||||||
|
|
||||||
|
namespace syscalls {
|
||||||
|
|
||||||
|
j6_status_t
|
||||||
|
system_log(const char *message)
|
||||||
|
{
|
||||||
|
if (message == nullptr) {
|
||||||
|
return j6_err_invalid_arg;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto &s = scheduler::get();
|
||||||
|
TCB *tcb = s.current();
|
||||||
|
thread *th = thread::from_tcb(tcb);
|
||||||
|
log::info(logs::syscall, "Message[%llx]: %s", th->koid(), message);
|
||||||
|
return j6_status_ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
j6_status_t
|
||||||
|
system_noop()
|
||||||
|
{
|
||||||
|
auto &s = scheduler::get();
|
||||||
|
TCB *tcb = s.current();
|
||||||
|
thread *th = thread::from_tcb(tcb);
|
||||||
|
log::debug(logs::syscall, "Thread %llx called noop syscall.", th->koid());
|
||||||
|
return j6_status_ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace syscalls
|
||||||
80
src/kernel/syscalls/thread.cpp
Normal file
80
src/kernel/syscalls/thread.cpp
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
#include "j6/errors.h"
|
||||||
|
#include "j6/types.h"
|
||||||
|
|
||||||
|
#include "log.h"
|
||||||
|
#include "objects/process.h"
|
||||||
|
#include "scheduler.h"
|
||||||
|
|
||||||
|
namespace syscalls {
|
||||||
|
|
||||||
|
j6_status_t
|
||||||
|
thread_koid(j6_koid_t *koid)
|
||||||
|
{
|
||||||
|
if (koid == nullptr) {
|
||||||
|
return j6_err_invalid_arg;
|
||||||
|
}
|
||||||
|
|
||||||
|
TCB *tcb = scheduler::get().current();
|
||||||
|
*koid = thread::from_tcb(tcb)->koid();
|
||||||
|
return j6_status_ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
j6_status_t
|
||||||
|
thread_create(void *rip, j6_handle_t *handle)
|
||||||
|
{
|
||||||
|
scheduler &s = scheduler::get();
|
||||||
|
TCB *tcb = s.current();
|
||||||
|
thread *parent = thread::from_tcb(tcb);
|
||||||
|
process &p = parent->parent();
|
||||||
|
|
||||||
|
thread *child = p.create_thread(scheduler::default_priority);
|
||||||
|
child->add_thunk_user(reinterpret_cast<uintptr_t>(rip));
|
||||||
|
*handle = p.add_handle(child);
|
||||||
|
s.add_thread(child->tcb());
|
||||||
|
|
||||||
|
log::debug(logs::syscall, "Thread %llx spawned new thread %llx, handle %d",
|
||||||
|
parent->koid(), child->koid(), *handle);
|
||||||
|
|
||||||
|
return j6_status_ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
j6_status_t
|
||||||
|
thread_exit(int64_t status)
|
||||||
|
{
|
||||||
|
auto &s = scheduler::get();
|
||||||
|
TCB *tcb = s.current();
|
||||||
|
thread *th = thread::from_tcb(tcb);
|
||||||
|
log::debug(logs::syscall, "Thread %llx exiting with code %d", th->koid(), status);
|
||||||
|
|
||||||
|
th->exit(status);
|
||||||
|
s.schedule();
|
||||||
|
|
||||||
|
log::error(logs::syscall, "returned to exit syscall");
|
||||||
|
return j6_err_unexpected;
|
||||||
|
}
|
||||||
|
|
||||||
|
j6_status_t
|
||||||
|
thread_pause()
|
||||||
|
{
|
||||||
|
auto &s = scheduler::get();
|
||||||
|
TCB *tcb = s.current();
|
||||||
|
thread *th = thread::from_tcb(tcb);
|
||||||
|
th->wait_on_signals(th, -1ull);
|
||||||
|
s.schedule();
|
||||||
|
return j6_status_ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
j6_status_t
|
||||||
|
thread_sleep(uint64_t til)
|
||||||
|
{
|
||||||
|
auto &s = scheduler::get();
|
||||||
|
TCB *tcb = s.current();
|
||||||
|
thread *th = thread::from_tcb(tcb);
|
||||||
|
log::debug(logs::syscall, "Thread %llx sleeping until %llu", th->koid(), til);
|
||||||
|
|
||||||
|
th->wait_on_time(til);
|
||||||
|
s.schedule();
|
||||||
|
return j6_status_ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace syscalls
|
||||||
@@ -24,20 +24,20 @@ task_switch:
|
|||||||
; Install next task's TCB
|
; Install next task's TCB
|
||||||
mov [gs:CPU_DATA.tcb], rdi ; rdi: next TCB (function param)
|
mov [gs:CPU_DATA.tcb], rdi ; rdi: next TCB (function param)
|
||||||
mov rsp, [rdi + TCB.rsp] ; next task's stack pointer
|
mov rsp, [rdi + TCB.rsp] ; next task's stack pointer
|
||||||
mov rax, 0x0000007fffffffff
|
mov rax, 0x00003fffffffffff
|
||||||
and rax, [rdi + TCB.pml4] ; rax: next task's pml4 (phys portion of address)
|
and rax, [rdi + TCB.pml4] ; rax: next task's pml4 (phys portion of address)
|
||||||
|
|
||||||
; Update syscall/interrupt rsp
|
; Update syscall/interrupt rsp
|
||||||
mov rcx, [rdi + TCB.rsp0] ; rcx: top of next task's kernel stack
|
mov rcx, [rdi + TCB.rsp0] ; rcx: top of next task's kernel stack
|
||||||
mov [gs:CPU_DATA.rsp0], rcx
|
mov [gs:CPU_DATA.rsp0], rcx
|
||||||
|
|
||||||
|
lea rdx, [rel g_tss] ; rdx: address of TSS
|
||||||
|
mov [rdx + TSS.rsp0], rcx
|
||||||
|
|
||||||
; Update saved user rsp
|
; Update saved user rsp
|
||||||
mov rcx, [rdi + TCB.rsp3] ; rcx: new task's saved user rsp
|
mov rcx, [rdi + TCB.rsp3] ; rcx: new task's saved user rsp
|
||||||
mov [gs:CPU_DATA.rsp3], rcx
|
mov [gs:CPU_DATA.rsp3], rcx
|
||||||
|
|
||||||
lea rdx, [rel g_tss] ; rdx: address of TSS
|
|
||||||
mov [rdx + TSS.rsp0], rcx
|
|
||||||
|
|
||||||
; check if we need to update CR3
|
; check if we need to update CR3
|
||||||
mov rdx, cr3 ; rdx: old CR3
|
mov rdx, cr3 ; rdx: old CR3
|
||||||
cmp rax, rdx
|
cmp rax, rdx
|
||||||
@@ -56,8 +56,7 @@ task_switch:
|
|||||||
|
|
||||||
|
|
||||||
extern syscall_handler_prelude.return
|
extern syscall_handler_prelude.return
|
||||||
global task_fork_return_thunk
|
global kernel_to_user_trampoline
|
||||||
task_fork_return_thunk:
|
kernel_to_user_trampoline:
|
||||||
mov rax, 0
|
|
||||||
jmp syscall_handler_prelude.return
|
jmp syscall_handler_prelude.return
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user