mirror of
https://github.com/justinian/jsix.git
synced 2025-12-10 00:14:32 -08:00
[kernel] Split loading from scheduler
In preparation for moving things to the init process, move process loading out of the scheduler. memory_bootstrap now has a load_simple_process function for mapping an args::program into memory, and the stack setup has been simplified (though all the initv values are still being added by the kernel - this needs rework) and normalized to use the thread::add_thunk_user code path.
This commit is contained in:
@@ -1,20 +0,0 @@
|
||||
%include "push_all.inc"
|
||||
|
||||
extern load_process_image
|
||||
|
||||
global preloaded_process_init
|
||||
preloaded_process_init:
|
||||
|
||||
; create_process already pushed the arguments for load_process_image and
|
||||
; the following iretq onto the stack for us
|
||||
|
||||
pop rdi ; a pointer to the program descriptor
|
||||
call load_process_image
|
||||
|
||||
; user rsp is now in rax, put it in the right place for iret
|
||||
mov [rsp + 0x18], rax
|
||||
|
||||
; the entrypoint should already be on the stack
|
||||
swapgs
|
||||
iretq
|
||||
|
||||
@@ -35,12 +35,13 @@ extern "C" {
|
||||
|
||||
extern void __kernel_assert(const char *, unsigned, const char *);
|
||||
|
||||
using namespace kernel;
|
||||
|
||||
/// Bootstrap the memory managers.
|
||||
void setup_pat();
|
||||
void memory_initialize_pre_ctors(kernel::args::header &kargs);
|
||||
void memory_initialize_post_ctors(kernel::args::header &kargs);
|
||||
|
||||
using namespace kernel;
|
||||
void memory_initialize_pre_ctors(args::header &kargs);
|
||||
void memory_initialize_post_ctors(args::header &kargs);
|
||||
process * load_simple_process(args::program &program);
|
||||
|
||||
/// TODO: not this. this is awful.
|
||||
args::framebuffer *fb = nullptr;
|
||||
@@ -150,13 +151,8 @@ kernel_main(args::header *header)
|
||||
scheduler *sched = new scheduler(devices.get_lapic());
|
||||
|
||||
// Skip program 0, which is the kernel itself
|
||||
for (size_t i = 1; i < header->num_programs; ++i) {
|
||||
args::program *prog = memory::to_virtual(&header->programs[i]);
|
||||
thread *th = sched->load_process(*prog);
|
||||
if (i == 2) {
|
||||
//th->set_state(thread::state::constant);
|
||||
}
|
||||
}
|
||||
for (unsigned i = 1; i < header->num_programs; ++i)
|
||||
load_simple_process(header->programs[i]);
|
||||
|
||||
if (!has_video)
|
||||
sched->create_kernel_task(logger_task, scheduler::max_priority/2, true);
|
||||
|
||||
@@ -1,16 +1,20 @@
|
||||
#include <utility>
|
||||
|
||||
#include "kernel_args.h"
|
||||
#include "j6/init.h"
|
||||
|
||||
#include "kutil/assert.h"
|
||||
#include "kutil/heap_allocator.h"
|
||||
#include "kutil/no_construct.h"
|
||||
|
||||
#include "device_manager.h"
|
||||
#include "frame_allocator.h"
|
||||
#include "io.h"
|
||||
#include "log.h"
|
||||
#include "msr.h"
|
||||
#include "objects/process.h"
|
||||
#include "objects/thread.h"
|
||||
#include "objects/system.h"
|
||||
#include "objects/vm_area.h"
|
||||
#include "vm_space.h"
|
||||
|
||||
@@ -19,6 +23,8 @@ using memory::kernel_max_heap;
|
||||
|
||||
using namespace kernel;
|
||||
|
||||
extern "C" void initialize_main_thread();
|
||||
extern "C" uintptr_t initialize_main_user_stack();
|
||||
|
||||
// These objects are initialized _before_ global constructors are called,
|
||||
// so we don't want them to have global constructors at all, lest they
|
||||
@@ -178,3 +184,99 @@ setup_pat()
|
||||
}
|
||||
|
||||
|
||||
process *
|
||||
load_simple_process(args::program &program)
|
||||
{
|
||||
using kernel::args::section_flags;
|
||||
|
||||
process *p = new process;
|
||||
vm_space &space = p->space();
|
||||
|
||||
for (const auto § : program.sections) {
|
||||
vm_flags flags =
|
||||
(bitfield_has(sect.type, section_flags::execute) ? vm_flags::exec : vm_flags::none) |
|
||||
(bitfield_has(sect.type, section_flags::write) ? vm_flags::write : vm_flags::none);
|
||||
|
||||
vm_area *vma = new vm_area_fixed(sect.size, flags);
|
||||
space.add(sect.virt_addr, vma);
|
||||
vma->commit(sect.phys_addr, 0, memory::page_count(sect.size));
|
||||
}
|
||||
|
||||
uint64_t iopl = (3ull << 12);
|
||||
uintptr_t trampoline = reinterpret_cast<uintptr_t>(initialize_main_thread);
|
||||
|
||||
thread *main = p->create_thread();
|
||||
main->add_thunk_user(program.entrypoint, trampoline, iopl);
|
||||
main->set_state(thread::state::ready);
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline T * push(uintptr_t &rsp, size_t size = sizeof(T)) {
|
||||
rsp -= size;
|
||||
T *p = reinterpret_cast<T*>(rsp);
|
||||
rsp &= ~(sizeof(uint64_t)-1); // Align the stack
|
||||
return p;
|
||||
}
|
||||
|
||||
uintptr_t
|
||||
initialize_main_user_stack()
|
||||
{
|
||||
process &proc = process::current();
|
||||
thread &th = thread::current();
|
||||
TCB *tcb = th.tcb();
|
||||
|
||||
const char message[] = "Hello from the kernel!";
|
||||
char *message_arg = push<char>(tcb->rsp3, sizeof(message));
|
||||
kutil::memcpy(message_arg, message, sizeof(message));
|
||||
|
||||
extern args::framebuffer *fb;
|
||||
j6_init_framebuffer *fb_desc = push<j6_init_framebuffer>(tcb->rsp3);
|
||||
fb_desc->addr = fb ? reinterpret_cast<void*>(0x100000000) : nullptr;
|
||||
fb_desc->size = fb ? fb->size : 0;
|
||||
fb_desc->vertical = fb ? fb->vertical : 0;
|
||||
fb_desc->horizontal = fb ? fb->horizontal : 0;
|
||||
fb_desc->scanline = fb ? fb->scanline : 0;
|
||||
fb_desc->flags = 0;
|
||||
|
||||
if (fb && fb->type == kernel::args::fb_type::bgr8)
|
||||
fb_desc->flags |= 1;
|
||||
|
||||
unsigned n = 0;
|
||||
|
||||
j6_init_value *initv = push<j6_init_value>(tcb->rsp3);
|
||||
initv->type = j6_init_handle_other;
|
||||
initv->handle.type = j6_object_type_system;
|
||||
initv->handle.handle = proc.add_handle(&system::get());
|
||||
++n;
|
||||
|
||||
initv = push<j6_init_value>(tcb->rsp3);
|
||||
initv->type = j6_init_handle_self;
|
||||
initv->handle.type = j6_object_type_process;
|
||||
initv->handle.handle = proc.self_handle();
|
||||
++n;
|
||||
|
||||
initv = push<j6_init_value>(tcb->rsp3);
|
||||
initv->type = j6_init_handle_self;
|
||||
initv->handle.type = j6_object_type_thread;
|
||||
initv->handle.handle = th.self_handle();
|
||||
++n;
|
||||
|
||||
initv = push<j6_init_value>(tcb->rsp3);
|
||||
initv->type = j6_init_desc_framebuffer;
|
||||
initv->data = fb_desc;
|
||||
++n;
|
||||
|
||||
uint64_t *initc = push<uint64_t>(tcb->rsp3);
|
||||
*initc = n;
|
||||
|
||||
char **argv0 = push<char*>(tcb->rsp3);
|
||||
*argv0 = message_arg;
|
||||
|
||||
uint64_t *argc = push<uint64_t>(tcb->rsp3);
|
||||
*argc = 1;
|
||||
|
||||
th.clear_state(thread::state::loading);
|
||||
return tcb->rsp3;
|
||||
}
|
||||
|
||||
@@ -106,7 +106,9 @@ process::create_thread(uint8_t priority, bool user)
|
||||
vm_flags::zero|vm_flags::write);
|
||||
m_space.add(stack_top - stack_size, vma);
|
||||
|
||||
th->tcb()->rsp3 = stack_top;
|
||||
// Space for null frame - because the page gets zeroed on
|
||||
// allocation, just pointing rsp here does the trick
|
||||
th->tcb()->rsp3 = stack_top - 2 * sizeof(uint64_t);
|
||||
}
|
||||
|
||||
m_threads.append(th);
|
||||
|
||||
@@ -16,7 +16,7 @@ public:
|
||||
constexpr static uintptr_t stacks_top = 0x0000800000000000;
|
||||
|
||||
/// Size of userspace thread stacks
|
||||
constexpr static size_t stack_size = 0x4000;
|
||||
constexpr static size_t stack_size = 0x4000000; // 64MiB
|
||||
|
||||
/// Value that represents default priority
|
||||
constexpr static uint8_t default_priority = 0xff;
|
||||
|
||||
@@ -149,6 +149,10 @@ thread::exit(int32_t code)
|
||||
void
|
||||
thread::add_thunk_kernel(uintptr_t rip)
|
||||
{
|
||||
// This adds just enough values to the top of the
|
||||
// kernel stack to come out of task_switch correctly
|
||||
// and start executing at rip (still in kernel mode)
|
||||
|
||||
m_tcb.rsp -= sizeof(uintptr_t) * 7;
|
||||
uintptr_t *stack = reinterpret_cast<uintptr_t*>(m_tcb.rsp);
|
||||
|
||||
@@ -162,15 +166,24 @@ thread::add_thunk_kernel(uintptr_t rip)
|
||||
}
|
||||
|
||||
void
|
||||
thread::add_thunk_user(uintptr_t rip)
|
||||
thread::add_thunk_user(uintptr_t rip3, uintptr_t rip0, uint64_t flags)
|
||||
{
|
||||
// This sets up the stack to:
|
||||
// a) come out of task_switch and return to rip0 (default is the
|
||||
// kernel/user trampoline) (via add_thunk_kernel) - if this is
|
||||
// changed, it needs to end up at the trampoline with the stack
|
||||
// as it was
|
||||
// b) come out of the kernel/user trampoline and start executing
|
||||
// in user mode at rip
|
||||
|
||||
m_tcb.rsp -= sizeof(uintptr_t) * 8;
|
||||
uintptr_t *stack = reinterpret_cast<uintptr_t*>(m_tcb.rsp);
|
||||
flags |= 0x200;
|
||||
|
||||
stack[7] = rip; // return rip in rcx
|
||||
stack[7] = rip3; // return rip in rcx
|
||||
stack[6] = m_tcb.rsp3; // rbp
|
||||
stack[5] = 0xbbbbbbbb; // rbx
|
||||
stack[4] = 0x00000200; // r11 sets RFLAGS
|
||||
stack[4] = flags; // r11 sets RFLAGS
|
||||
stack[3] = 0x12121212; // r12
|
||||
stack[2] = 0x13131313; // r13
|
||||
stack[1] = 0x14141414; // r14
|
||||
@@ -178,7 +191,7 @@ thread::add_thunk_user(uintptr_t rip)
|
||||
|
||||
static const uintptr_t trampoline =
|
||||
reinterpret_cast<uintptr_t>(kernel_to_user_trampoline);
|
||||
add_thunk_kernel(trampoline);
|
||||
add_thunk_kernel(rip0 ? rip0 : trampoline);
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
@@ -137,9 +137,12 @@ public:
|
||||
/// \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);
|
||||
/// Add a stack header that returns to the given address in user space
|
||||
/// via a function in kernel space.
|
||||
/// \arg rip3 The user space address to return to
|
||||
/// \arg rip0 The kernel function to pass through, optional
|
||||
/// \arg flags Extra RFLAGS values to set, optional
|
||||
void add_thunk_user(uintptr_t rip3, uintptr_t rip0 = 0, uint64_t flags = 0);
|
||||
|
||||
/// Get the handle representing this thread to its process
|
||||
j6_handle_t self_handle() const { return m_self_handle; }
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include "console.h"
|
||||
#include "cpu.h"
|
||||
#include "debug.h"
|
||||
#include "device_manager.h"
|
||||
#include "gdt.h"
|
||||
#include "interrupts.h"
|
||||
#include "io.h"
|
||||
@@ -32,11 +33,6 @@ extern kernel::args::framebuffer *fb;
|
||||
const uint64_t rflags_noint = 0x002;
|
||||
const uint64_t rflags_int = 0x202;
|
||||
|
||||
extern "C" {
|
||||
void preloaded_process_init();
|
||||
uintptr_t load_process_image(const kernel::args::program*);
|
||||
};
|
||||
|
||||
extern uint64_t idle_stack_end;
|
||||
|
||||
scheduler::scheduler(lapic *apic) :
|
||||
@@ -75,98 +71,13 @@ inline T * push(uintptr_t &rsp, size_t size = sizeof(T)) {
|
||||
return p;
|
||||
}
|
||||
|
||||
uintptr_t
|
||||
load_process_image(const kernel::args::program *program)
|
||||
{
|
||||
using memory::page_align_down;
|
||||
using memory::page_align_up;
|
||||
using kernel::args::section_flags;
|
||||
|
||||
// We're now in the process space for this process, allocate memory for the
|
||||
// process code and load it
|
||||
process &proc = process::current();
|
||||
thread &th = thread::current();
|
||||
TCB *tcb = th.tcb();
|
||||
vm_space &space = proc.space();
|
||||
|
||||
for (const auto § : program->sections) {
|
||||
vm_flags flags =
|
||||
(bitfield_has(sect.type, section_flags::execute) ? vm_flags::exec : vm_flags::none) |
|
||||
(bitfield_has(sect.type, section_flags::write) ? vm_flags::write : vm_flags::none);
|
||||
|
||||
vm_area *vma = new vm_area_open(sect.size, space, flags);
|
||||
space.add(sect.virt_addr, vma);
|
||||
vma->commit(sect.phys_addr, 0, memory::page_count(sect.size));
|
||||
}
|
||||
|
||||
// double zero stack sentinel
|
||||
*push<uint64_t>(tcb->rsp3) = 0;
|
||||
*push<uint64_t>(tcb->rsp3) = 0;
|
||||
|
||||
const char message[] = "Hello from the kernel!";
|
||||
char *message_arg = push<char>(tcb->rsp3, sizeof(message));
|
||||
kutil::memcpy(message_arg, message, sizeof(message));
|
||||
|
||||
j6_init_framebuffer *fb_desc = push<j6_init_framebuffer>(tcb->rsp3);
|
||||
fb_desc->addr = fb ? reinterpret_cast<void*>(0x100000000) : nullptr;
|
||||
fb_desc->size = fb ? fb->size : 0;
|
||||
fb_desc->vertical = fb ? fb->vertical : 0;
|
||||
fb_desc->horizontal = fb ? fb->horizontal : 0;
|
||||
fb_desc->scanline = fb ? fb->scanline : 0;
|
||||
fb_desc->flags = 0;
|
||||
|
||||
if (fb && fb->type == kernel::args::fb_type::bgr8)
|
||||
fb_desc->flags |= 1;
|
||||
|
||||
j6_init_value *initv = push<j6_init_value>(tcb->rsp3);
|
||||
initv->type = j6_init_handle_other;
|
||||
initv->handle.type = j6_object_type_system;
|
||||
initv->handle.handle = proc.add_handle(&system::get());
|
||||
|
||||
initv = push<j6_init_value>(tcb->rsp3);
|
||||
initv->type = j6_init_handle_self;
|
||||
initv->handle.type = j6_object_type_process;
|
||||
initv->handle.handle = proc.self_handle();
|
||||
|
||||
initv = push<j6_init_value>(tcb->rsp3);
|
||||
initv->type = j6_init_handle_self;
|
||||
initv->handle.type = j6_object_type_thread;
|
||||
initv->handle.handle = th.self_handle();
|
||||
|
||||
initv = push<j6_init_value>(tcb->rsp3);
|
||||
initv->type = j6_init_desc_framebuffer;
|
||||
initv->data = fb_desc;
|
||||
|
||||
uint64_t *initc = push<uint64_t>(tcb->rsp3);
|
||||
*initc = 4;
|
||||
|
||||
char **argv0 = push<char*>(tcb->rsp3);
|
||||
*argv0 = message_arg;
|
||||
|
||||
uint64_t *argc = push<uint64_t>(tcb->rsp3);
|
||||
*argc = 1;
|
||||
|
||||
// Crazypants framebuffer part
|
||||
if (fb) {
|
||||
vm_area *vma = new vm_area_open(fb->size, space,
|
||||
vm_flags::write|vm_flags::mmio|vm_flags::write_combine);
|
||||
space.add(0x100000000, vma);
|
||||
vma->commit(fb->phys_addr, 0, memory::page_count(fb->size));
|
||||
}
|
||||
|
||||
th.clear_state(thread::state::loading);
|
||||
return tcb->rsp3;
|
||||
}
|
||||
|
||||
thread *
|
||||
scheduler::create_process(bool user)
|
||||
{
|
||||
process *p = new process;
|
||||
thread *th = p->create_thread(default_priority, user);
|
||||
|
||||
auto *tcb = th->tcb();
|
||||
tcb->time_left = quantum(default_priority);
|
||||
|
||||
TCB *tcb = th->tcb();
|
||||
log::debug(logs::task, "Creating thread %llx, priority %d, time slice %d",
|
||||
th->koid(), tcb->priority, tcb->time_left);
|
||||
|
||||
@@ -174,45 +85,6 @@ scheduler::create_process(bool user)
|
||||
return th;
|
||||
}
|
||||
|
||||
thread *
|
||||
scheduler::load_process(kernel::args::program &program)
|
||||
{
|
||||
|
||||
uint16_t kcs = (1 << 3) | 0; // Kernel CS is GDT entry 1, ring 0
|
||||
uint16_t cs = (5 << 3) | 3; // User CS is GDT entry 5, ring 3
|
||||
|
||||
uint16_t kss = (2 << 3) | 0; // Kernel SS is GDT entry 2, ring 0
|
||||
uint16_t ss = (4 << 3) | 3; // User SS is GDT entry 4, ring 3
|
||||
|
||||
thread* th = create_process(true);
|
||||
auto *tcb = th->tcb();
|
||||
|
||||
// Create an initial kernel stack space
|
||||
uintptr_t *stack = reinterpret_cast<uintptr_t *>(tcb->rsp0) - 6;
|
||||
|
||||
// Pass args to preloaded_process_init on the stack
|
||||
stack[0] = reinterpret_cast<uintptr_t>(&program);
|
||||
|
||||
tcb->rsp = reinterpret_cast<uintptr_t>(stack);
|
||||
th->add_thunk_kernel(reinterpret_cast<uintptr_t>(preloaded_process_init));
|
||||
|
||||
// Arguments for iret - rip will be pushed on before these
|
||||
stack[1] = reinterpret_cast<uintptr_t>(program.entrypoint);
|
||||
stack[2] = cs;
|
||||
stack[3] = rflags_int | (3 << 12);
|
||||
stack[4] = process::stacks_top;
|
||||
stack[5] = ss;
|
||||
|
||||
tcb->rsp3 = process::stacks_top;
|
||||
|
||||
log::debug(logs::task, "Loading thread %llx pri %d", th->koid(), tcb->priority);
|
||||
log::debug(logs::task, " RSP %016lx", tcb->rsp);
|
||||
log::debug(logs::task, " RSP0 %016lx", tcb->rsp0);
|
||||
log::debug(logs::task, " PML4 %016lx", tcb->pml4);
|
||||
|
||||
return th;
|
||||
}
|
||||
|
||||
void
|
||||
scheduler::create_kernel_task(void (*task)(), uint8_t priority, bool constant)
|
||||
{
|
||||
@@ -248,6 +120,14 @@ scheduler::start()
|
||||
m_apic->reset_timer(10);
|
||||
}
|
||||
|
||||
void
|
||||
scheduler::add_thread(TCB *t)
|
||||
{
|
||||
m_blocked.push_back(static_cast<tcb_node*>(t));
|
||||
t->time_left = quantum(t->priority);
|
||||
|
||||
}
|
||||
|
||||
void scheduler::prune(uint64_t now)
|
||||
{
|
||||
// Find processes that are ready or have exited and
|
||||
|
||||
@@ -60,7 +60,7 @@ public:
|
||||
bool constant = false);
|
||||
|
||||
/// Get the quantum for a given priority.
|
||||
uint32_t quantum(int priority);
|
||||
static uint32_t quantum(int priority);
|
||||
|
||||
/// Start the scheduler working. This may involve starting
|
||||
/// timer interrupts or other preemption methods.
|
||||
@@ -73,7 +73,9 @@ public:
|
||||
/// \returns A pointer to the current thread's TCB
|
||||
inline TCB * current() { return m_current; }
|
||||
|
||||
inline void add_thread(TCB *t) { m_blocked.push_back(static_cast<tcb_node*>(t)); }
|
||||
/// Start scheduling a new thread.
|
||||
/// \arg t The new thread's TCB
|
||||
void add_thread(TCB *t);
|
||||
|
||||
/// Get a reference to the system scheduler
|
||||
/// \returns A reference to the global system scheduler
|
||||
|
||||
@@ -12,7 +12,6 @@ extern syscall_registry
|
||||
extern syscall_invalid
|
||||
|
||||
global syscall_handler_prelude
|
||||
global syscall_handler_prelude.return
|
||||
syscall_handler_prelude:
|
||||
swapgs
|
||||
mov [gs:CPU_DATA.rsp3], rsp
|
||||
@@ -55,7 +54,8 @@ syscall_handler_prelude:
|
||||
|
||||
inc qword [rel __counter_syscall_sysret]
|
||||
|
||||
.return:
|
||||
global kernel_to_user_trampoline
|
||||
kernel_to_user_trampoline:
|
||||
pop r15
|
||||
pop r14
|
||||
pop r13
|
||||
|
||||
@@ -55,8 +55,14 @@ task_switch:
|
||||
ret
|
||||
|
||||
|
||||
extern syscall_handler_prelude.return
|
||||
global kernel_to_user_trampoline
|
||||
kernel_to_user_trampoline:
|
||||
jmp syscall_handler_prelude.return
|
||||
extern initialize_main_user_stack
|
||||
extern kernel_to_user_trampoline
|
||||
global initialize_main_thread
|
||||
initialize_main_thread:
|
||||
call initialize_main_user_stack
|
||||
|
||||
; user rsp is now in rax, put it in the right place for sysret
|
||||
mov [rsp + 0x30], rax
|
||||
|
||||
; the entrypoint should already be on the stack
|
||||
jmp kernel_to_user_trampoline
|
||||
|
||||
Reference in New Issue
Block a user