mirror of
https://github.com/justinian/jsix.git
synced 2025-12-10 00:14:32 -08:00
Simplify task switches
No longer using the rsp from the entry to the kernel, but instead switching rsp at task-switching time in assembly. This currently breaks fork()
This commit is contained in:
@@ -41,6 +41,7 @@ modules:
|
||||
- src/kernel/serial.cpp
|
||||
- src/kernel/syscall.cpp
|
||||
- src/kernel/syscall.s
|
||||
- src/kernel/task.s
|
||||
- src/kernel/crtn.s
|
||||
|
||||
boot:
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
extern "C" {
|
||||
int32_t getpid();
|
||||
int32_t fork();
|
||||
void sleep(uint64_t til);
|
||||
void debug();
|
||||
|
||||
@@ -14,6 +15,7 @@ int
|
||||
main(int argc, const char **argv)
|
||||
{
|
||||
int32_t pid = getpid();
|
||||
//int32_t child = fork();
|
||||
debug();
|
||||
for (int i = 1; i < 5; ++i)
|
||||
sleep(i*10);
|
||||
|
||||
@@ -39,6 +39,18 @@ sleep:
|
||||
pop rbp
|
||||
ret
|
||||
|
||||
global fork
|
||||
fork:
|
||||
push rbp
|
||||
mov rbp, rsp
|
||||
|
||||
mov rax, 8
|
||||
syscall ; pid left in rax
|
||||
|
||||
pop rbp
|
||||
ret
|
||||
|
||||
|
||||
global _start
|
||||
_start:
|
||||
xor rbp, rbp ; Sentinel rbp
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
struct process;
|
||||
|
||||
struct cpu_state
|
||||
{
|
||||
uint64_t r15, r14, r13, r12, r11, r10, r9, r8;
|
||||
@@ -10,10 +12,13 @@ struct cpu_state
|
||||
uint64_t rip, cs, rflags, user_rsp, ss;
|
||||
};
|
||||
|
||||
/// Per-cpu state data. If you change this, remember to update the assembly
|
||||
/// version in 'tasking.inc'
|
||||
struct cpu_data
|
||||
{
|
||||
uintptr_t rsp0;
|
||||
uintptr_t rsp3;
|
||||
process *tcb;
|
||||
};
|
||||
|
||||
extern cpu_data bsp_cpu_data;
|
||||
|
||||
@@ -12,6 +12,9 @@ print_regs(const cpu_state ®s)
|
||||
{
|
||||
console *cons = console::get();
|
||||
|
||||
uint64_t cr2 = 0;
|
||||
__asm__ __volatile__ ("mov %%cr2, %0" : "=r"(cr2));
|
||||
|
||||
print_regL("rax", regs.rax);
|
||||
print_regM("rbx", regs.rbx);
|
||||
print_regR("rcx", regs.rcx);
|
||||
@@ -36,7 +39,9 @@ print_regs(const cpu_state ®s)
|
||||
|
||||
print_regL("rip", regs.rip);
|
||||
print_regM("cr3", page_manager::get()->get_pml4());
|
||||
cons->puts("\n\n");
|
||||
print_regR("cr2", cr2);
|
||||
|
||||
cons->puts("\n");
|
||||
}
|
||||
|
||||
struct frame
|
||||
|
||||
@@ -9,6 +9,7 @@ extern "C" {
|
||||
uintptr_t get_rip();
|
||||
uintptr_t get_frame(int frame);
|
||||
uintptr_t get_gsbase();
|
||||
void _halt();
|
||||
}
|
||||
|
||||
extern size_t __counter_syscall_enter;
|
||||
|
||||
@@ -18,9 +18,9 @@ static const uint16_t PIC2 = 0xa0;
|
||||
extern "C" {
|
||||
void _halt();
|
||||
|
||||
uintptr_t isr_handler(uintptr_t, cpu_state*);
|
||||
uintptr_t irq_handler(uintptr_t, cpu_state*);
|
||||
uintptr_t syscall_handler(uintptr_t, cpu_state);
|
||||
void isr_handler(cpu_state*);
|
||||
void irq_handler(cpu_state*);
|
||||
void syscall_handler(cpu_state*);
|
||||
|
||||
#define ISR(i, name) extern void name ();
|
||||
#define EISR(i, name) extern void name ();
|
||||
@@ -104,8 +104,8 @@ interrupts_init()
|
||||
log::info(logs::boot, "Interrupts enabled.");
|
||||
}
|
||||
|
||||
uintptr_t
|
||||
isr_handler(uintptr_t return_rsp, cpu_state *regs)
|
||||
void
|
||||
isr_handler(cpu_state *regs)
|
||||
{
|
||||
console *cons = console::get();
|
||||
|
||||
@@ -193,21 +193,14 @@ isr_handler(uintptr_t return_rsp, cpu_state *regs)
|
||||
if (regs->errorcode & 0x08) cons->puts(" reserved");
|
||||
if (regs->errorcode & 0x10) cons->puts(" ip");
|
||||
cons->puts("\n");
|
||||
|
||||
uint64_t cr2 = 0;
|
||||
__asm__ __volatile__ ("mov %%cr2, %0" : "=r"(cr2));
|
||||
print_regL("cr2", cr2);
|
||||
print_regM("rsp", regs->user_rsp);
|
||||
print_regR("rip", regs->rip);
|
||||
//print_stacktrace(2);
|
||||
print_regs(*regs);
|
||||
print_stacktrace(2);
|
||||
}
|
||||
_halt();
|
||||
break;
|
||||
|
||||
case isr::isrTimer: {
|
||||
scheduler &s = scheduler::get();
|
||||
return_rsp = s.tick(return_rsp);
|
||||
}
|
||||
case isr::isrTimer:
|
||||
scheduler::get().tick();
|
||||
break;
|
||||
|
||||
case isr::isrLINT0:
|
||||
@@ -226,14 +219,13 @@ isr_handler(uintptr_t return_rsp, cpu_state *regs)
|
||||
_halt();
|
||||
break;
|
||||
|
||||
case isr::isrSyscall: {
|
||||
return_rsp = syscall_dispatch(return_rsp, *regs);
|
||||
}
|
||||
case isr::isrSyscall:
|
||||
syscall_dispatch(regs);
|
||||
break;
|
||||
|
||||
case isr::isrSpurious:
|
||||
// No EOI for the spurious interrupt
|
||||
return return_rsp;
|
||||
return;
|
||||
|
||||
case isr::isrIgnore0:
|
||||
case isr::isrIgnore1:
|
||||
@@ -274,12 +266,10 @@ isr_handler(uintptr_t return_rsp, cpu_state *regs)
|
||||
_halt();
|
||||
}
|
||||
*reinterpret_cast<uint32_t *>(0xffffff80fee000b0) = 0;
|
||||
|
||||
return return_rsp;
|
||||
}
|
||||
|
||||
uintptr_t
|
||||
irq_handler(uintptr_t return_rsp, cpu_state *regs)
|
||||
void
|
||||
irq_handler(cpu_state *regs)
|
||||
{
|
||||
console *cons = console::get();
|
||||
uint8_t irq = get_irq(regs->interrupt);
|
||||
@@ -293,11 +283,10 @@ irq_handler(uintptr_t return_rsp, cpu_state *regs)
|
||||
}
|
||||
|
||||
*reinterpret_cast<uint32_t *>(0xffffff80fee000b0) = 0;
|
||||
return return_rsp;
|
||||
}
|
||||
|
||||
uintptr_t
|
||||
syscall_handler(uintptr_t return_rsp, cpu_state regs)
|
||||
void
|
||||
syscall_handler(cpu_state *regs)
|
||||
{
|
||||
return syscall_dispatch(return_rsp, regs);
|
||||
syscall_dispatch(regs);
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@ isr_handler_prelude:
|
||||
mov rdi, rsp
|
||||
mov rsi, rsp
|
||||
call isr_handler
|
||||
mov rsp, rax
|
||||
jmp isr_handler_return
|
||||
|
||||
extern irq_handler
|
||||
@@ -21,7 +20,6 @@ irq_handler_prelude:
|
||||
mov rdi, rsp
|
||||
mov rsi, rsp
|
||||
call irq_handler
|
||||
mov rsp, rax
|
||||
; fall through to isr_handler_return
|
||||
|
||||
global isr_handler_return
|
||||
|
||||
@@ -7,12 +7,11 @@ ramdisk_process_loader:
|
||||
; create_process already pushed a cpu_state onto the stack for us, this
|
||||
; acts both as the cpu_state parameter to load_process, and the saved
|
||||
; state for the following iretq
|
||||
;
|
||||
; Additional parameters:
|
||||
; rdi - the address of the program image
|
||||
; rsi - the size of the program image
|
||||
; rdx - the address of this process' process structure
|
||||
; rcx - the stack pointer, which points at the cpu_state
|
||||
|
||||
pop rdi ; the address of the program image
|
||||
pop rsi ; the size of the program image
|
||||
pop rdx ; the address of this process' process structure
|
||||
pop rcx ; the cpu_state
|
||||
call load_process
|
||||
|
||||
swapgs
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include "kutil/memory.h"
|
||||
#include "console.h"
|
||||
#include "log.h"
|
||||
#include "scheduler.h"
|
||||
|
||||
namespace logs {
|
||||
|
||||
@@ -21,7 +22,7 @@ output_log(log::area_t area, log::level severity, const char *message)
|
||||
{
|
||||
auto *cons = console::get();
|
||||
cons->set_color(level_colors[static_cast<int>(severity)]);
|
||||
cons->printf("%9s %8s: %s\n",
|
||||
cons->printf("%7s %5s: %s\n",
|
||||
g_logger.area_name(area),
|
||||
g_logger.level_name(severity),
|
||||
message);
|
||||
@@ -35,21 +36,23 @@ logger_task()
|
||||
auto *ent = reinterpret_cast<log::logger::entry *>(buffer);
|
||||
auto *cons = console::get();
|
||||
|
||||
g_logger.set_immediate(nullptr);
|
||||
//g_logger.set_immediate(nullptr);
|
||||
log::info(logs::task, "Starting kernel logger task");
|
||||
|
||||
scheduler &s = scheduler::get();
|
||||
|
||||
while (true) {
|
||||
__asm__ ("cli");
|
||||
if(g_logger.get_entry(buffer, sizeof(buffer))) {
|
||||
buffer[ent->bytes] = 0;
|
||||
cons->set_color(level_colors[static_cast<int>(ent->severity)]);
|
||||
cons->printf("%9s %8s: %s\n",
|
||||
cons->printf("%7s %5s: %s\n",
|
||||
g_logger.area_name(ent->area),
|
||||
g_logger.level_name(ent->severity),
|
||||
ent->message);
|
||||
cons->set_color();
|
||||
} else {
|
||||
s.schedule();
|
||||
}
|
||||
__asm__ ("sti");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
LOG(apic, info);
|
||||
LOG(device, debug);
|
||||
LOG(paging, info);
|
||||
LOG(driver, debug);
|
||||
LOG(device, info);
|
||||
LOG(paging, debug);
|
||||
LOG(driver, info);
|
||||
LOG(memory, debug);
|
||||
LOG(fs, debug);
|
||||
LOG(fs, info);
|
||||
LOG(task, debug);
|
||||
LOG(boot, debug);
|
||||
|
||||
@@ -150,7 +150,7 @@ page_manager::delete_process_map(page_table *pml4)
|
||||
void
|
||||
page_manager::map_offset_pointer(void **pointer, size_t length)
|
||||
{
|
||||
log::info(logs::paging, "Mapping offset pointer region at %016lx size 0x%lx", *pointer, length);
|
||||
log::debug(logs::paging, "Mapping offset pointer region at %016lx size 0x%lx", *pointer, length);
|
||||
*pointer = kutil::offset_pointer(*pointer, page_offset);
|
||||
}
|
||||
|
||||
@@ -185,6 +185,7 @@ page_manager::get_table_page()
|
||||
|
||||
free_page_header *page = m_page_cache;
|
||||
m_page_cache = page->next;
|
||||
|
||||
return reinterpret_cast<page_table *>(page);
|
||||
}
|
||||
|
||||
@@ -294,8 +295,10 @@ page_manager::check_needs_page(page_table *table, unsigned index, bool user)
|
||||
void
|
||||
page_manager::page_in(page_table *pml4, uintptr_t phys_addr, uintptr_t virt_addr, size_t count, bool user, bool large)
|
||||
{
|
||||
/*
|
||||
log::debug(logs::paging, "page_in for table %016lx p:%016lx v:%016lx c:%4d u:%d l:%d",
|
||||
pml4, phys_addr, virt_addr, count, user, large);
|
||||
*/
|
||||
|
||||
page_table_indices idx{virt_addr};
|
||||
page_table *tables[4] = {pml4, nullptr, nullptr, nullptr};
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "cpu.h"
|
||||
#include "debug.h"
|
||||
#include "log.h"
|
||||
#include "process.h"
|
||||
#include "scheduler.h"
|
||||
@@ -12,7 +13,7 @@ process::exit(uint32_t code)
|
||||
}
|
||||
|
||||
pid_t
|
||||
process::fork(uintptr_t in_rsp)
|
||||
process::fork(cpu_state *regs)
|
||||
{
|
||||
auto &sched = scheduler::get();
|
||||
auto *child = sched.create_process();
|
||||
@@ -25,40 +26,49 @@ process::fork(uintptr_t in_rsp)
|
||||
|
||||
sched.m_runlists[child->priority].push_back(child);
|
||||
|
||||
child->rsp = in_rsp;
|
||||
|
||||
child->pml4 = page_manager::get()->copy_table(pml4);
|
||||
kassert(child->pml4, "process::fork() got null pml4");
|
||||
|
||||
child->setup_kernel_stack(kernel_stack_size, kernel_stack);
|
||||
child->rsp = child->kernel_stack + (in_rsp - kernel_stack);
|
||||
|
||||
log::debug(logs::task, "Copied process %d to %d, new PML4 %016lx.",
|
||||
pid, child->pid, child->pml4);
|
||||
log::debug(logs::task, " copied stack %016lx to %016lx, rsp %016lx to %016lx.",
|
||||
kernel_stack, child->kernel_stack, in_rsp, child->rsp);
|
||||
log::debug(logs::task, " copied stack %016lx to %016lx, rsp %016lx.",
|
||||
kernel_stack, child->kernel_stack, child->rsp);
|
||||
|
||||
// Add in the faked fork return value
|
||||
cpu_state *regs = reinterpret_cast<cpu_state *>(child->rsp);
|
||||
regs->rax = 0;
|
||||
child->setup_kernel_stack();
|
||||
task_fork(child); // Both parent and child will return from this
|
||||
|
||||
if (bsp_cpu_data.tcb->pid == child->pid) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return child->pid;
|
||||
}
|
||||
|
||||
void *
|
||||
process::setup_kernel_stack(size_t size, uintptr_t orig)
|
||||
process::setup_kernel_stack()
|
||||
{
|
||||
void *stack0 = kutil::malloc(size);
|
||||
constexpr unsigned null_frame_entries = 2;
|
||||
constexpr size_t null_frame_size = null_frame_entries * sizeof(uint64_t);
|
||||
|
||||
if (orig)
|
||||
kutil::memcpy(stack0, reinterpret_cast<void*>(orig), size);
|
||||
else
|
||||
kutil::memset(stack0, 0, size);
|
||||
void *stack_bottom = kutil::malloc(initial_stack_size);
|
||||
kutil::memset(stack_bottom, 0, initial_stack_size);
|
||||
|
||||
kernel_stack_size = size;
|
||||
kernel_stack = reinterpret_cast<uintptr_t>(stack0);
|
||||
log::debug(logs::memory, "Created kernel stack at %016lx size 0x%lx",
|
||||
stack_bottom, initial_stack_size);
|
||||
|
||||
return stack0;
|
||||
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;
|
||||
|
||||
kernel_stack_size = initial_stack_size;
|
||||
kernel_stack = reinterpret_cast<uintptr_t>(stack_bottom);
|
||||
rsp0 = reinterpret_cast<uintptr_t>(stack_top);
|
||||
|
||||
return stack_top;
|
||||
}
|
||||
|
||||
bool
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "page_manager.h"
|
||||
|
||||
typedef int32_t pid_t;
|
||||
struct cpu_state;
|
||||
|
||||
|
||||
enum class process_flags : uint32_t
|
||||
@@ -32,9 +33,19 @@ enum class process_wait : uint8_t
|
||||
receive
|
||||
};
|
||||
|
||||
/// A process
|
||||
/// A process.
|
||||
///
|
||||
struct process
|
||||
{
|
||||
static const size_t initial_stack_size = 0x1000;
|
||||
|
||||
// Fields used by assembly routines go first. If you change any of these,
|
||||
// be sure to change the assembly definitions in 'tasking.inc'
|
||||
uintptr_t rsp;
|
||||
uintptr_t rsp0;
|
||||
page_table *pml4;
|
||||
// End of assembly fields
|
||||
|
||||
pid_t pid;
|
||||
pid_t ppid;
|
||||
|
||||
@@ -51,9 +62,6 @@ struct process
|
||||
|
||||
uint32_t reserved1;
|
||||
|
||||
uintptr_t rsp;
|
||||
page_table *pml4;
|
||||
|
||||
uintptr_t kernel_stack;
|
||||
size_t kernel_stack_size;
|
||||
|
||||
@@ -62,10 +70,10 @@ struct process
|
||||
void exit(unsigned code);
|
||||
|
||||
/// Copy this process.
|
||||
/// \arg in_rsp The RSP of the calling process
|
||||
/// \arg regs The saved state from the fork syscall
|
||||
/// \returns Returns the child's pid to the parent, and
|
||||
/// 0 to the child.
|
||||
pid_t fork(uint64_t in_rsp);
|
||||
pid_t fork(cpu_state *regs);
|
||||
|
||||
/// Unready this process until it gets a signal
|
||||
/// \arg sigmask A bitfield of signals to wake on
|
||||
@@ -122,13 +130,10 @@ struct process
|
||||
private:
|
||||
friend class scheduler;
|
||||
|
||||
/// Set up a new kernel stack for this process, optionally copying the
|
||||
/// given stack. Sets the kernel stack on the process object, but also
|
||||
/// returns it.
|
||||
/// \arg size Size of the stack to allocate
|
||||
/// \arg orig Address of a stack to copy, or 0 for no copying.
|
||||
/// \returns The address of the new stack as a pointer
|
||||
void * setup_kernel_stack(size_t size, uintptr_t orig);
|
||||
/// Set up a new empty kernel stack for this process. Sets rsp0 on this
|
||||
/// process object, but also returns it.
|
||||
/// \returns The new rsp0 as a pointer
|
||||
void * setup_kernel_stack();
|
||||
};
|
||||
|
||||
using process_list = kutil::linked_list<process>;
|
||||
|
||||
@@ -18,7 +18,6 @@ using memory::initial_stack;
|
||||
|
||||
scheduler scheduler::s_instance(nullptr);
|
||||
|
||||
const int stack_size = 0x1000;
|
||||
const uint64_t rflags_noint = 0x002;
|
||||
const uint64_t rflags_int = 0x202;
|
||||
|
||||
@@ -32,6 +31,7 @@ scheduler::scheduler(lapic *apic) :
|
||||
m_next_pid(1)
|
||||
{
|
||||
auto *idle = m_process_allocator.pop();
|
||||
idle->setup_kernel_stack();
|
||||
|
||||
uint8_t last_pri = num_priorities - 1;
|
||||
|
||||
@@ -49,6 +49,9 @@ scheduler::scheduler(lapic *apic) :
|
||||
|
||||
m_runlists[last_pri].push_back(idle);
|
||||
m_current = idle;
|
||||
|
||||
bsp_cpu_data.rsp0 = idle->rsp0;
|
||||
bsp_cpu_data.tcb = idle;
|
||||
}
|
||||
|
||||
void
|
||||
@@ -120,6 +123,24 @@ scheduler::create_process(pid_t pid)
|
||||
return proc;
|
||||
}
|
||||
|
||||
static uintptr_t
|
||||
add_fake_stack_return(uintptr_t rsp, uintptr_t rbp, uintptr_t rip)
|
||||
{
|
||||
// Initialize a new empty stack with a fake return segment
|
||||
// for returning out of task_switch
|
||||
rsp -= sizeof(uintptr_t) * 7;
|
||||
uintptr_t *stack = reinterpret_cast<uintptr_t*>(rsp);
|
||||
|
||||
stack[0] = rbp; // rbp
|
||||
stack[1] = 0xbbbbbbbb; // rbx
|
||||
stack[2] = 0x12121212; // r12
|
||||
stack[3] = 0x13131313; // r13
|
||||
stack[4] = 0x14141414; // r14
|
||||
stack[5] = 0x15151515; // r15
|
||||
stack[6] = rip; // return rip
|
||||
return rsp;
|
||||
}
|
||||
|
||||
void
|
||||
scheduler::load_process(const char *name, const void *data, size_t size)
|
||||
{
|
||||
@@ -132,15 +153,10 @@ scheduler::load_process(const char *name, const void *data, size_t size)
|
||||
uint16_t ss = (4 << 3) | 3; // User SS is GDT entry 4, ring 3
|
||||
|
||||
// Set up the page tables - this also allocates an initial user stack
|
||||
page_table *pml4 = page_manager::get()->create_process_map();
|
||||
|
||||
// Create a one-page kernel stack space
|
||||
void *stack0 = proc->setup_kernel_stack(stack_size, 0);
|
||||
|
||||
// Stack grows down, point to the end, resere space for initial null frame
|
||||
static const size_t null_frame = sizeof(uint64_t);
|
||||
void *sp0 = kutil::offset_pointer(stack0, stack_size - null_frame);
|
||||
proc->pml4 = page_manager::get()->create_process_map();
|
||||
|
||||
// Create an initial kernel stack space
|
||||
void *sp0 = proc->setup_kernel_stack();
|
||||
cpu_state *state = reinterpret_cast<cpu_state *>(sp0) - 1;
|
||||
|
||||
// Highest state in the stack is the process' kernel stack for the loader
|
||||
@@ -151,24 +167,18 @@ scheduler::load_process(const char *name, const void *data, size_t size)
|
||||
state->rip = 0; // to be filled by the loader
|
||||
state->user_rsp = initial_stack;
|
||||
|
||||
// Next state in the stack is the loader's kernel stack. The scheduler will
|
||||
// iret to this which will kick off the loading:
|
||||
cpu_state *loader_state = reinterpret_cast<cpu_state *>(sp0) - 2;
|
||||
// Pass args to ramdisk_process_loader on the stack
|
||||
uintptr_t *stack = reinterpret_cast<uintptr_t *>(state) - 4;
|
||||
stack[0] = reinterpret_cast<uintptr_t>(data);
|
||||
stack[1] = reinterpret_cast<uintptr_t>(size);
|
||||
stack[2] = reinterpret_cast<uintptr_t>(proc);
|
||||
stack[3] = reinterpret_cast<uintptr_t>(state);
|
||||
|
||||
loader_state->ss = kss;
|
||||
loader_state->cs = kcs;
|
||||
loader_state->rflags = rflags_noint;
|
||||
loader_state->rip = reinterpret_cast<uint64_t>(ramdisk_process_loader);
|
||||
loader_state->user_rsp = reinterpret_cast<uint64_t>(state);
|
||||
proc->rsp = add_fake_stack_return(
|
||||
reinterpret_cast<uintptr_t>(stack),
|
||||
proc->rsp0,
|
||||
reinterpret_cast<uintptr_t>(ramdisk_process_loader));
|
||||
|
||||
// Set up the registers to have the arguments to the load_process call
|
||||
loader_state->rdi = reinterpret_cast<uint64_t>(data); // arg 1
|
||||
loader_state->rsi = size; // arg 2
|
||||
loader_state->rdx = reinterpret_cast<uint64_t>(proc); // arg 3
|
||||
loader_state->rcx = loader_state->user_rsp; // arg 4
|
||||
|
||||
proc->rsp = reinterpret_cast<uintptr_t>(loader_state);
|
||||
proc->pml4 = pml4;
|
||||
proc->quanta = process_quanta;
|
||||
proc->flags =
|
||||
process_flags::running |
|
||||
@@ -177,10 +187,9 @@ scheduler::load_process(const char *name, const void *data, size_t size)
|
||||
|
||||
m_runlists[default_priority].push_back(proc);
|
||||
|
||||
|
||||
log::debug(logs::task, "Creating process %s: pid %d pri %d", name, proc->pid, proc->priority);
|
||||
log::debug(logs::task, " RSP0 %016lx", state);
|
||||
log::debug(logs::task, " PML4 %016lx", pml4);
|
||||
log::debug(logs::task, " PML4 %016lx", proc->pml4);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -191,24 +200,12 @@ scheduler::create_kernel_task(pid_t pid, void (*task)())
|
||||
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
|
||||
|
||||
// Create a one-page kernel stack space
|
||||
void *stack0 = proc->setup_kernel_stack(stack_size, 0);
|
||||
// Create an initial kernel stack space
|
||||
proc->setup_kernel_stack();
|
||||
proc->rsp = add_fake_stack_return(
|
||||
proc->rsp0, proc->rsp0,
|
||||
reinterpret_cast<uintptr_t>(task));
|
||||
|
||||
// Stack grows down, point to the end, resere space for initial null frame
|
||||
static const size_t null_frame = sizeof(uint64_t);
|
||||
void *sp0 = kutil::offset_pointer(stack0, stack_size - null_frame);
|
||||
|
||||
cpu_state *state = reinterpret_cast<cpu_state *>(sp0) - 1;
|
||||
|
||||
// Highest state in the stack is the process' kernel stack for the loader
|
||||
// to iret to:
|
||||
state->ss = kss;
|
||||
state->cs = kcs;
|
||||
state->rflags = rflags_int;
|
||||
state->rip = reinterpret_cast<uint64_t>(task);
|
||||
state->user_rsp = reinterpret_cast<uint64_t>(state);
|
||||
|
||||
proc->rsp = reinterpret_cast<uintptr_t>(state);
|
||||
proc->pml4 = page_manager::get()->get_kernel_pml4();
|
||||
proc->quanta = process_quanta;
|
||||
proc->flags =
|
||||
@@ -218,7 +215,8 @@ scheduler::create_kernel_task(pid_t pid, void (*task)())
|
||||
m_runlists[default_priority].push_back(proc);
|
||||
|
||||
log::debug(logs::task, "Creating kernel task: pid %d pri %d", proc->pid, proc->priority);
|
||||
log::debug(logs::task, " RSP0 %016lx", state);
|
||||
log::debug(logs::task, " RSP0 %016lx", proc->rsp0);
|
||||
log::debug(logs::task, " RSP %016lx", proc->rsp);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -278,17 +276,15 @@ void scheduler::prune(uint64_t now)
|
||||
}
|
||||
}
|
||||
|
||||
uintptr_t
|
||||
scheduler::schedule(uintptr_t rsp0)
|
||||
void
|
||||
scheduler::schedule()
|
||||
{
|
||||
// TODO: lol a real clock
|
||||
static uint64_t now = 0;
|
||||
|
||||
pid_t lastpid = m_current->pid;
|
||||
|
||||
m_current->rsp = rsp0;
|
||||
m_runlists[m_current->priority].remove(m_current);
|
||||
|
||||
if (m_current->flags && process_flags::ready) {
|
||||
m_runlists[m_current->priority].push_back(m_current);
|
||||
} else {
|
||||
@@ -304,34 +300,25 @@ scheduler::schedule(uintptr_t rsp0)
|
||||
}
|
||||
|
||||
m_current = m_runlists[pri].pop_front();
|
||||
rsp0 = m_current->rsp;
|
||||
|
||||
// Set rsp0 to after the end of the about-to-be-popped cpu state
|
||||
tss_set_stack(0, rsp0 + sizeof(cpu_state));
|
||||
bsp_cpu_data.rsp0 = rsp0;
|
||||
|
||||
// Swap page tables
|
||||
page_table *pml4 = m_current->pml4;
|
||||
page_manager::set_pml4(pml4);
|
||||
|
||||
if (lastpid != m_current->pid) {
|
||||
|
||||
bool loading = m_current->flags && process_flags::loading;
|
||||
log::debug(logs::task, "Scheduler switched to process %d, priority %d%s.",
|
||||
log::debug(logs::task, "Scheduler switching to process %d, priority %d%s.",
|
||||
m_current->pid, m_current->priority, loading ? " (loading)" : "");
|
||||
|
||||
task_switch(m_current);
|
||||
}
|
||||
}
|
||||
|
||||
return rsp0;
|
||||
}
|
||||
|
||||
uintptr_t
|
||||
scheduler::tick(uintptr_t rsp0)
|
||||
void
|
||||
scheduler::tick()
|
||||
{
|
||||
if (--m_current->quanta == 0) {
|
||||
m_current->quanta = process_quanta;
|
||||
rsp0 = schedule(rsp0);
|
||||
schedule();
|
||||
}
|
||||
m_apic->reset_timer(m_tick_count);
|
||||
return rsp0;
|
||||
}
|
||||
|
||||
process_node *
|
||||
|
||||
@@ -10,7 +10,9 @@ class lapic;
|
||||
struct page_table;
|
||||
struct cpu_state;
|
||||
|
||||
extern "C" uintptr_t isr_handler(uintptr_t, cpu_state*);
|
||||
extern "C" void isr_handler(cpu_state*);
|
||||
extern "C" void task_switch(process *next);
|
||||
extern "C" void task_fork(process *child);
|
||||
|
||||
|
||||
/// The task scheduler
|
||||
@@ -46,9 +48,7 @@ public:
|
||||
void start();
|
||||
|
||||
/// Run the scheduler, possibly switching to a new task
|
||||
/// \arg rsp0 The stack pointer of the current interrupt handler
|
||||
/// \returns The stack pointer to switch to
|
||||
uintptr_t schedule(uintptr_t rsp0);
|
||||
void schedule();
|
||||
|
||||
/// Get the current process.
|
||||
/// \returns A pointer to the current process' process struct
|
||||
@@ -65,7 +65,7 @@ public:
|
||||
|
||||
private:
|
||||
friend uintptr_t syscall_dispatch(uintptr_t, cpu_state &);
|
||||
friend uintptr_t isr_handler(uintptr_t, cpu_state*);
|
||||
friend void isr_handler(cpu_state*);
|
||||
friend class process;
|
||||
|
||||
/// Create a new process object. This process will have its pid
|
||||
@@ -75,9 +75,7 @@ private:
|
||||
process_node * create_process(pid_t pid = 0);
|
||||
|
||||
/// Handle a timer tick
|
||||
/// \arg rsp0 The stack pointer of the current interrupt handler
|
||||
/// \returns The stack pointer to switch to
|
||||
uintptr_t tick(uintptr_t rsp0);
|
||||
void tick();
|
||||
|
||||
void prune(uint64_t now);
|
||||
|
||||
|
||||
@@ -34,11 +34,11 @@ syscall_enable()
|
||||
wrmsr(msr::ia32_fmask, 0x200);
|
||||
}
|
||||
|
||||
uintptr_t
|
||||
syscall_dispatch(uintptr_t return_rsp, cpu_state ®s)
|
||||
void
|
||||
syscall_dispatch(cpu_state *regs)
|
||||
{
|
||||
console *cons = console::get();
|
||||
syscall call = static_cast<syscall>(regs.rax);
|
||||
syscall call = static_cast<syscall>(regs->rax);
|
||||
|
||||
auto &s = scheduler::get();
|
||||
auto *p = s.current();
|
||||
@@ -51,7 +51,7 @@ syscall_dispatch(uintptr_t return_rsp, cpu_state ®s)
|
||||
cons->set_color(11);
|
||||
cons->printf("\nProcess %d: Received DEBUG syscall\n", p->pid);
|
||||
cons->set_color();
|
||||
print_regs(regs);
|
||||
print_regs(*regs);
|
||||
cons->printf("\n Syscall enters: %8d\n", __counter_syscall_enter);
|
||||
cons->printf(" Syscall sysret: %8d\n", __counter_syscall_sysret);
|
||||
break;
|
||||
@@ -71,7 +71,7 @@ syscall_dispatch(uintptr_t return_rsp, cpu_state ®s)
|
||||
p->wait_on_signal(-1ull);
|
||||
cons->printf("\nProcess %d: Received PAUSE syscall\n", p->pid);
|
||||
cons->set_color();
|
||||
return_rsp = s.schedule(return_rsp);
|
||||
s.schedule();
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -79,11 +79,11 @@ syscall_dispatch(uintptr_t return_rsp, cpu_state ®s)
|
||||
{
|
||||
cons->set_color(11);
|
||||
cons->printf("\nProcess %d: Received SLEEP syscall\n", p->pid);
|
||||
cons->printf("Sleeping until %lu\n", regs.rdi);
|
||||
cons->printf("Sleeping until %lu\n", regs->rdi);
|
||||
cons->set_color();
|
||||
|
||||
p->wait_on_time(regs.rdi);
|
||||
return_rsp = s.schedule(return_rsp);
|
||||
p->wait_on_time(regs->rdi);
|
||||
s.schedule();
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -91,34 +91,34 @@ syscall_dispatch(uintptr_t return_rsp, cpu_state ®s)
|
||||
cons->set_color(11);
|
||||
cons->printf("\nProcess %d: Received GETPID syscall\n", p->pid);
|
||||
cons->set_color();
|
||||
regs.rax = p->pid;
|
||||
regs->rax = p->pid;
|
||||
break;
|
||||
|
||||
case syscall::send:
|
||||
{
|
||||
pid_t target = regs.rdi;
|
||||
uintptr_t data = regs.rsi;
|
||||
pid_t target = regs->rdi;
|
||||
uintptr_t data = regs->rsi;
|
||||
|
||||
cons->set_color(11);
|
||||
cons->printf("\nProcess %d: Received SEND syscall, target %d, data %016lx\n", p->pid, target, data);
|
||||
cons->set_color();
|
||||
|
||||
if (p->wait_on_send(target))
|
||||
return_rsp = s.schedule(return_rsp);
|
||||
s.schedule();
|
||||
}
|
||||
break;
|
||||
|
||||
case syscall::receive:
|
||||
{
|
||||
pid_t source = regs.rdi;
|
||||
uintptr_t data = regs.rsi;
|
||||
pid_t source = regs->rdi;
|
||||
uintptr_t data = regs->rsi;
|
||||
|
||||
cons->set_color(11);
|
||||
cons->printf("\nProcess %d: Received RECEIVE syscall, source %d, dat %016lx\n", p->pid, source, data);
|
||||
cons->set_color();
|
||||
|
||||
if (p->wait_on_receive(source))
|
||||
return_rsp = s.schedule(return_rsp);
|
||||
s.schedule();
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -128,8 +128,9 @@ syscall_dispatch(uintptr_t return_rsp, cpu_state ®s)
|
||||
cons->printf("\nProcess %d: Received FORK syscall\n", p->pid);
|
||||
cons->set_color();
|
||||
|
||||
pid_t pid = p->fork(return_rsp);
|
||||
regs.rax = pid;
|
||||
pid_t pid = p->fork(regs);
|
||||
cons->printf("\n fork returning %d\n", pid);
|
||||
regs->rax = pid;
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -137,8 +138,8 @@ syscall_dispatch(uintptr_t return_rsp, cpu_state ®s)
|
||||
cons->set_color(11);
|
||||
cons->printf("\nProcess %d: Received EXIT syscall\n", p->pid);
|
||||
cons->set_color();
|
||||
p->exit(regs.rdi);
|
||||
return_rsp = s.schedule(return_rsp);
|
||||
p->exit(regs->rdi);
|
||||
s.schedule();
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -148,7 +149,5 @@ syscall_dispatch(uintptr_t return_rsp, cpu_state ®s)
|
||||
_halt();
|
||||
break;
|
||||
}
|
||||
|
||||
return return_rsp;
|
||||
}
|
||||
|
||||
|
||||
@@ -21,5 +21,5 @@ enum class syscall : uint64_t
|
||||
};
|
||||
|
||||
void syscall_enable();
|
||||
uintptr_t syscall_dispatch(uintptr_t, cpu_state &);
|
||||
void syscall_dispatch(cpu_state *);
|
||||
|
||||
|
||||
@@ -28,7 +28,6 @@ syscall_handler_prelude:
|
||||
|
||||
mov rdi, rsp
|
||||
call syscall_handler
|
||||
mov rsp, rax
|
||||
|
||||
mov rax, [rsp + 0x90]
|
||||
and rax, 0x3
|
||||
|
||||
85
src/kernel/task.s
Normal file
85
src/kernel/task.s
Normal file
@@ -0,0 +1,85 @@
|
||||
%include "tasking.inc"
|
||||
|
||||
extern g_tss
|
||||
|
||||
global task_switch
|
||||
task_switch:
|
||||
push rbp
|
||||
mov rbp, rsp
|
||||
|
||||
; Save the rest of the callee-saved regs
|
||||
push rbx
|
||||
push r12
|
||||
push r13
|
||||
push r14
|
||||
push r15
|
||||
|
||||
; Update previous task's TCB
|
||||
mov rax, [gs:CPU_DATA.tcb] ; rax: current task TCB
|
||||
mov [rax + TCB.rsp], rsp
|
||||
|
||||
; Install next task's TCB
|
||||
mov [gs:CPU_DATA.tcb], rdi ; rdi: next TCB (function param)
|
||||
mov rsp, [rdi + TCB.rsp] ; next task's stack pointer
|
||||
mov rax, 0x0000007fffffffff
|
||||
and rax, [rdi + TCB.pml4] ; rax: next task's pml4 (phys portion of address)
|
||||
|
||||
; Update syscall/interrupt rsp
|
||||
mov rcx, [rdi + TCB.rsp0] ; rcx: top of next task's kernel stack
|
||||
mov [gs:CPU_DATA.rsp0], rcx
|
||||
|
||||
lea rdx, [rel g_tss] ; rdx: address of TSS
|
||||
mov [rdx + TSS.rsp0], rcx
|
||||
|
||||
; check if we need to update CR3
|
||||
mov rdx, cr3 ; rdx: old CR3
|
||||
cmp rax, rdx
|
||||
je .no_cr3
|
||||
mov cr3, rax
|
||||
.no_cr3:
|
||||
|
||||
pop r15
|
||||
pop r14
|
||||
pop r13
|
||||
pop r12
|
||||
pop rbx
|
||||
|
||||
pop rbp
|
||||
ret
|
||||
|
||||
global task_fork
|
||||
task_fork:
|
||||
push rbp
|
||||
mov rbp, rsp
|
||||
|
||||
; Save the rest of the callee-saved regs
|
||||
push rbx
|
||||
push r12
|
||||
push r13
|
||||
push r14
|
||||
push r15
|
||||
|
||||
mov r14, rdi ; r14: child task TCB (function argument)
|
||||
|
||||
mov rax, [gs:CPU_DATA.tcb] ; rax: current task TCB
|
||||
mov rax, [rax + TCB.rsp0] ; rax: current task rsp0
|
||||
sub rax, rsp ; rax: size of kernel stack in bytes
|
||||
|
||||
mov rcx, rax
|
||||
shr rcx, 3 ; rcx: size of kernel stack in qwords
|
||||
|
||||
mov rdi, [r14 + TCB.rsp0] ; rdi: child task rsp0
|
||||
sub rdi, rax ; rdi: child task rsp
|
||||
mov rsi, rsp ; rsi: current rsp
|
||||
|
||||
rep movsq
|
||||
|
||||
pop r15
|
||||
pop r14
|
||||
pop r13
|
||||
pop r12
|
||||
pop rbx
|
||||
|
||||
pop rbp
|
||||
ret
|
||||
|
||||
32
src/kernel/tasking.inc
Normal file
32
src/kernel/tasking.inc
Normal file
@@ -0,0 +1,32 @@
|
||||
struc TCB
|
||||
.rsp: resq 1
|
||||
.rsp0: resq 1
|
||||
.pml4: resq 1
|
||||
endstruc
|
||||
|
||||
struc CPU_DATA
|
||||
.rsp0: resq 1
|
||||
.rsp3: resq 1
|
||||
.tcb: resq 1
|
||||
endstruc
|
||||
|
||||
struc TSS
|
||||
.res0: resd 1
|
||||
.rsp0: resq 1
|
||||
.rsp1: resq 1
|
||||
.rsp2: resq 1
|
||||
.ist0: resq 1
|
||||
.ist1: resq 1
|
||||
.ist2: resq 1
|
||||
.ist3: resq 1
|
||||
.ist4: resq 1
|
||||
.ist5: resq 1
|
||||
.ist6: resq 1
|
||||
.ist7: resq 1
|
||||
.res1: resq 1
|
||||
.res2: resw 1
|
||||
.iomap: resw 1
|
||||
endstruc
|
||||
|
||||
|
||||
; vim: ft=asm
|
||||
Reference in New Issue
Block a user