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:
Justin C. Miller
2019-03-31 22:49:24 -07:00
parent 5cdbedd4d1
commit ca2362f858
21 changed files with 311 additions and 178 deletions

View File

@@ -41,6 +41,7 @@ modules:
- src/kernel/serial.cpp - src/kernel/serial.cpp
- src/kernel/syscall.cpp - src/kernel/syscall.cpp
- src/kernel/syscall.s - src/kernel/syscall.s
- src/kernel/task.s
- src/kernel/crtn.s - src/kernel/crtn.s
boot: boot:

View File

@@ -3,6 +3,7 @@
extern "C" { extern "C" {
int32_t getpid(); int32_t getpid();
int32_t fork();
void sleep(uint64_t til); void sleep(uint64_t til);
void debug(); void debug();
@@ -14,6 +15,7 @@ int
main(int argc, const char **argv) main(int argc, const char **argv)
{ {
int32_t pid = getpid(); int32_t pid = getpid();
//int32_t child = fork();
debug(); debug();
for (int i = 1; i < 5; ++i) for (int i = 1; i < 5; ++i)
sleep(i*10); sleep(i*10);

View File

@@ -39,6 +39,18 @@ sleep:
pop rbp pop rbp
ret ret
global fork
fork:
push rbp
mov rbp, rsp
mov rax, 8
syscall ; pid left in rax
pop rbp
ret
global _start global _start
_start: _start:
xor rbp, rbp ; Sentinel rbp xor rbp, rbp ; Sentinel rbp

View File

@@ -2,6 +2,8 @@
#include <stdint.h> #include <stdint.h>
struct process;
struct cpu_state struct cpu_state
{ {
uint64_t r15, r14, r13, r12, r11, r10, r9, r8; 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; 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 struct cpu_data
{ {
uintptr_t rsp0; uintptr_t rsp0;
uintptr_t rsp3; uintptr_t rsp3;
process *tcb;
}; };
extern cpu_data bsp_cpu_data; extern cpu_data bsp_cpu_data;

View File

@@ -12,6 +12,9 @@ print_regs(const cpu_state &regs)
{ {
console *cons = console::get(); console *cons = console::get();
uint64_t cr2 = 0;
__asm__ __volatile__ ("mov %%cr2, %0" : "=r"(cr2));
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);
@@ -36,7 +39,9 @@ print_regs(const cpu_state &regs)
print_regL("rip", regs.rip); print_regL("rip", regs.rip);
print_regM("cr3", page_manager::get()->get_pml4()); print_regM("cr3", page_manager::get()->get_pml4());
cons->puts("\n\n"); print_regR("cr2", cr2);
cons->puts("\n");
} }
struct frame struct frame

View File

@@ -9,6 +9,7 @@ extern "C" {
uintptr_t get_rip(); uintptr_t get_rip();
uintptr_t get_frame(int frame); uintptr_t get_frame(int frame);
uintptr_t get_gsbase(); uintptr_t get_gsbase();
void _halt();
} }
extern size_t __counter_syscall_enter; extern size_t __counter_syscall_enter;

View File

@@ -18,9 +18,9 @@ static const uint16_t PIC2 = 0xa0;
extern "C" { extern "C" {
void _halt(); void _halt();
uintptr_t isr_handler(uintptr_t, cpu_state*); void isr_handler(cpu_state*);
uintptr_t irq_handler(uintptr_t, cpu_state*); void irq_handler(cpu_state*);
uintptr_t syscall_handler(uintptr_t, cpu_state); void syscall_handler(cpu_state*);
#define ISR(i, name) extern void name (); #define ISR(i, name) extern void name ();
#define EISR(i, name) extern void name (); #define EISR(i, name) extern void name ();
@@ -104,8 +104,8 @@ interrupts_init()
log::info(logs::boot, "Interrupts enabled."); log::info(logs::boot, "Interrupts enabled.");
} }
uintptr_t void
isr_handler(uintptr_t return_rsp, cpu_state *regs) isr_handler(cpu_state *regs)
{ {
console *cons = console::get(); 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 & 0x08) cons->puts(" reserved");
if (regs->errorcode & 0x10) cons->puts(" ip"); if (regs->errorcode & 0x10) cons->puts(" ip");
cons->puts("\n"); cons->puts("\n");
print_regs(*regs);
uint64_t cr2 = 0; print_stacktrace(2);
__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);
} }
_halt(); _halt();
break; break;
case isr::isrTimer: { case isr::isrTimer:
scheduler &s = scheduler::get(); scheduler::get().tick();
return_rsp = s.tick(return_rsp);
}
break; break;
case isr::isrLINT0: case isr::isrLINT0:
@@ -226,14 +219,13 @@ isr_handler(uintptr_t return_rsp, cpu_state *regs)
_halt(); _halt();
break; break;
case isr::isrSyscall: { case isr::isrSyscall:
return_rsp = syscall_dispatch(return_rsp, *regs); syscall_dispatch(regs);
}
break; break;
case isr::isrSpurious: case isr::isrSpurious:
// No EOI for the spurious interrupt // No EOI for the spurious interrupt
return return_rsp; return;
case isr::isrIgnore0: case isr::isrIgnore0:
case isr::isrIgnore1: case isr::isrIgnore1:
@@ -274,12 +266,10 @@ isr_handler(uintptr_t return_rsp, cpu_state *regs)
_halt(); _halt();
} }
*reinterpret_cast<uint32_t *>(0xffffff80fee000b0) = 0; *reinterpret_cast<uint32_t *>(0xffffff80fee000b0) = 0;
return return_rsp;
} }
uintptr_t void
irq_handler(uintptr_t return_rsp, cpu_state *regs) irq_handler(cpu_state *regs)
{ {
console *cons = console::get(); console *cons = console::get();
uint8_t irq = get_irq(regs->interrupt); 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; *reinterpret_cast<uint32_t *>(0xffffff80fee000b0) = 0;
return return_rsp;
} }
uintptr_t void
syscall_handler(uintptr_t return_rsp, cpu_state regs) syscall_handler(cpu_state *regs)
{ {
return syscall_dispatch(return_rsp, regs); syscall_dispatch(regs);
} }

View File

@@ -9,7 +9,6 @@ isr_handler_prelude:
mov rdi, rsp mov rdi, rsp
mov rsi, rsp mov rsi, rsp
call isr_handler call isr_handler
mov rsp, rax
jmp isr_handler_return jmp isr_handler_return
extern irq_handler extern irq_handler
@@ -21,7 +20,6 @@ irq_handler_prelude:
mov rdi, rsp mov rdi, rsp
mov rsi, rsp mov rsi, rsp
call irq_handler call irq_handler
mov rsp, rax
; fall through to isr_handler_return ; fall through to isr_handler_return
global isr_handler_return global isr_handler_return

View File

@@ -7,12 +7,11 @@ ramdisk_process_loader:
; create_process already pushed a cpu_state onto the stack for us, this ; 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 ; acts both as the cpu_state parameter to load_process, and the saved
; state for the following iretq ; state for the following iretq
;
; Additional parameters: pop rdi ; the address of the program image
; rdi - the address of the program image pop rsi ; the size of the program image
; rsi - the size of the program image pop rdx ; the address of this process' process structure
; rdx - the address of this process' process structure pop rcx ; the cpu_state
; rcx - the stack pointer, which points at the cpu_state
call load_process call load_process
swapgs swapgs

View File

@@ -2,6 +2,7 @@
#include "kutil/memory.h" #include "kutil/memory.h"
#include "console.h" #include "console.h"
#include "log.h" #include "log.h"
#include "scheduler.h"
namespace logs { namespace logs {
@@ -21,7 +22,7 @@ output_log(log::area_t area, log::level severity, const char *message)
{ {
auto *cons = console::get(); auto *cons = console::get();
cons->set_color(level_colors[static_cast<int>(severity)]); 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.area_name(area),
g_logger.level_name(severity), g_logger.level_name(severity),
message); message);
@@ -35,21 +36,23 @@ logger_task()
auto *ent = reinterpret_cast<log::logger::entry *>(buffer); auto *ent = reinterpret_cast<log::logger::entry *>(buffer);
auto *cons = console::get(); auto *cons = console::get();
g_logger.set_immediate(nullptr); //g_logger.set_immediate(nullptr);
log::info(logs::task, "Starting kernel logger task"); log::info(logs::task, "Starting kernel logger task");
scheduler &s = scheduler::get();
while (true) { while (true) {
__asm__ ("cli");
if(g_logger.get_entry(buffer, sizeof(buffer))) { if(g_logger.get_entry(buffer, sizeof(buffer))) {
buffer[ent->bytes] = 0; buffer[ent->bytes] = 0;
cons->set_color(level_colors[static_cast<int>(ent->severity)]); 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.area_name(ent->area),
g_logger.level_name(ent->severity), g_logger.level_name(ent->severity),
ent->message); ent->message);
cons->set_color(); cons->set_color();
} else {
s.schedule();
} }
__asm__ ("sti");
} }
} }

View File

@@ -1,8 +1,8 @@
LOG(apic, info); LOG(apic, info);
LOG(device, debug); LOG(device, info);
LOG(paging, info); LOG(paging, debug);
LOG(driver, debug); LOG(driver, info);
LOG(memory, debug); LOG(memory, debug);
LOG(fs, debug); LOG(fs, info);
LOG(task, debug); LOG(task, debug);
LOG(boot, debug); LOG(boot, debug);

View File

@@ -150,7 +150,7 @@ page_manager::delete_process_map(page_table *pml4)
void void
page_manager::map_offset_pointer(void **pointer, size_t length) 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); *pointer = kutil::offset_pointer(*pointer, page_offset);
} }
@@ -185,6 +185,7 @@ page_manager::get_table_page()
free_page_header *page = m_page_cache; free_page_header *page = m_page_cache;
m_page_cache = page->next; m_page_cache = page->next;
return reinterpret_cast<page_table *>(page); return reinterpret_cast<page_table *>(page);
} }
@@ -294,8 +295,10 @@ page_manager::check_needs_page(page_table *table, unsigned index, bool user)
void void
page_manager::page_in(page_table *pml4, uintptr_t phys_addr, uintptr_t virt_addr, size_t count, bool user, bool large) 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", 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); pml4, phys_addr, virt_addr, count, user, large);
*/
page_table_indices idx{virt_addr}; page_table_indices idx{virt_addr};
page_table *tables[4] = {pml4, nullptr, nullptr, nullptr}; page_table *tables[4] = {pml4, nullptr, nullptr, nullptr};

View File

@@ -1,4 +1,5 @@
#include "cpu.h" #include "cpu.h"
#include "debug.h"
#include "log.h" #include "log.h"
#include "process.h" #include "process.h"
#include "scheduler.h" #include "scheduler.h"
@@ -12,7 +13,7 @@ process::exit(uint32_t code)
} }
pid_t pid_t
process::fork(uintptr_t in_rsp) process::fork(cpu_state *regs)
{ {
auto &sched = scheduler::get(); auto &sched = scheduler::get();
auto *child = sched.create_process(); auto *child = sched.create_process();
@@ -25,40 +26,49 @@ process::fork(uintptr_t in_rsp)
sched.m_runlists[child->priority].push_back(child); sched.m_runlists[child->priority].push_back(child);
child->rsp = in_rsp;
child->pml4 = page_manager::get()->copy_table(pml4); child->pml4 = page_manager::get()->copy_table(pml4);
kassert(child->pml4, "process::fork() got null 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.", log::debug(logs::task, "Copied process %d to %d, new PML4 %016lx.",
pid, child->pid, child->pml4); pid, child->pid, child->pml4);
log::debug(logs::task, " copied stack %016lx to %016lx, rsp %016lx to %016lx.", log::debug(logs::task, " copied stack %016lx to %016lx, rsp %016lx.",
kernel_stack, child->kernel_stack, in_rsp, child->rsp); kernel_stack, child->kernel_stack, child->rsp);
// Add in the faked fork return value child->setup_kernel_stack();
cpu_state *regs = reinterpret_cast<cpu_state *>(child->rsp); task_fork(child); // Both parent and child will return from this
regs->rax = 0;
if (bsp_cpu_data.tcb->pid == child->pid) {
return 0;
}
return child->pid; return child->pid;
} }
void * 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) void *stack_bottom = kutil::malloc(initial_stack_size);
kutil::memcpy(stack0, reinterpret_cast<void*>(orig), size); kutil::memset(stack_bottom, 0, initial_stack_size);
else
kutil::memset(stack0, 0, size);
kernel_stack_size = size; log::debug(logs::memory, "Created kernel stack at %016lx size 0x%lx",
kernel_stack = reinterpret_cast<uintptr_t>(stack0); 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 bool

View File

@@ -8,6 +8,7 @@
#include "page_manager.h" #include "page_manager.h"
typedef int32_t pid_t; typedef int32_t pid_t;
struct cpu_state;
enum class process_flags : uint32_t enum class process_flags : uint32_t
@@ -32,9 +33,19 @@ enum class process_wait : uint8_t
receive receive
}; };
/// A process /// A process.
///
struct 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 pid;
pid_t ppid; pid_t ppid;
@@ -51,9 +62,6 @@ struct process
uint32_t reserved1; uint32_t reserved1;
uintptr_t rsp;
page_table *pml4;
uintptr_t kernel_stack; uintptr_t kernel_stack;
size_t kernel_stack_size; size_t kernel_stack_size;
@@ -62,10 +70,10 @@ struct process
void exit(unsigned code); void exit(unsigned code);
/// Copy this process. /// 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 /// \returns Returns the child's pid to the parent, and
/// 0 to the child. /// 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 /// Unready this process until it gets a signal
/// \arg sigmask A bitfield of signals to wake on /// \arg sigmask A bitfield of signals to wake on
@@ -122,13 +130,10 @@ struct process
private: private:
friend class scheduler; friend class scheduler;
/// Set up a new kernel stack for this process, optionally copying the /// Set up a new empty kernel stack for this process. Sets rsp0 on this
/// given stack. Sets the kernel stack on the process object, but also /// process object, but also returns it.
/// returns it. /// \returns The new rsp0 as a pointer
/// \arg size Size of the stack to allocate void * setup_kernel_stack();
/// \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);
}; };
using process_list = kutil::linked_list<process>; using process_list = kutil::linked_list<process>;

View File

@@ -18,7 +18,6 @@ using memory::initial_stack;
scheduler scheduler::s_instance(nullptr); scheduler scheduler::s_instance(nullptr);
const int stack_size = 0x1000;
const uint64_t rflags_noint = 0x002; const uint64_t rflags_noint = 0x002;
const uint64_t rflags_int = 0x202; const uint64_t rflags_int = 0x202;
@@ -32,6 +31,7 @@ scheduler::scheduler(lapic *apic) :
m_next_pid(1) m_next_pid(1)
{ {
auto *idle = m_process_allocator.pop(); auto *idle = m_process_allocator.pop();
idle->setup_kernel_stack();
uint8_t last_pri = num_priorities - 1; uint8_t last_pri = num_priorities - 1;
@@ -49,6 +49,9 @@ scheduler::scheduler(lapic *apic) :
m_runlists[last_pri].push_back(idle); m_runlists[last_pri].push_back(idle);
m_current = idle; m_current = idle;
bsp_cpu_data.rsp0 = idle->rsp0;
bsp_cpu_data.tcb = idle;
} }
void void
@@ -120,6 +123,24 @@ scheduler::create_process(pid_t pid)
return proc; 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 void
scheduler::load_process(const char *name, const void *data, size_t size) 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 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 // Set up the page tables - this also allocates an initial user stack
page_table *pml4 = page_manager::get()->create_process_map(); proc->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);
// Create an initial kernel stack space
void *sp0 = proc->setup_kernel_stack();
cpu_state *state = reinterpret_cast<cpu_state *>(sp0) - 1; cpu_state *state = reinterpret_cast<cpu_state *>(sp0) - 1;
// Highest state in the stack is the process' kernel stack for the loader // 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->rip = 0; // to be filled by the loader
state->user_rsp = initial_stack; state->user_rsp = initial_stack;
// Next state in the stack is the loader's kernel stack. The scheduler will // Pass args to ramdisk_process_loader on the stack
// iret to this which will kick off the loading: uintptr_t *stack = reinterpret_cast<uintptr_t *>(state) - 4;
cpu_state *loader_state = reinterpret_cast<cpu_state *>(sp0) - 2; 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; proc->rsp = add_fake_stack_return(
loader_state->cs = kcs; reinterpret_cast<uintptr_t>(stack),
loader_state->rflags = rflags_noint; proc->rsp0,
loader_state->rip = reinterpret_cast<uint64_t>(ramdisk_process_loader); reinterpret_cast<uintptr_t>(ramdisk_process_loader));
loader_state->user_rsp = reinterpret_cast<uint64_t>(state);
// 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->quanta = process_quanta;
proc->flags = proc->flags =
process_flags::running | 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); 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, "Creating process %s: pid %d pri %d", name, proc->pid, proc->priority);
log::debug(logs::task, " RSP0 %016lx", state); log::debug(logs::task, " RSP0 %016lx", state);
log::debug(logs::task, " PML4 %016lx", pml4); log::debug(logs::task, " PML4 %016lx", proc->pml4);
} }
void 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 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 a one-page kernel stack space // Create an initial kernel stack space
void *stack0 = proc->setup_kernel_stack(stack_size, 0); 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->pml4 = page_manager::get()->get_kernel_pml4();
proc->quanta = process_quanta; proc->quanta = process_quanta;
proc->flags = proc->flags =
@@ -218,7 +215,8 @@ scheduler::create_kernel_task(pid_t pid, void (*task)())
m_runlists[default_priority].push_back(proc); 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, "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 void
@@ -278,17 +276,15 @@ void scheduler::prune(uint64_t now)
} }
} }
uintptr_t void
scheduler::schedule(uintptr_t rsp0) scheduler::schedule()
{ {
// TODO: lol a real clock // TODO: lol a real clock
static uint64_t now = 0; static uint64_t now = 0;
pid_t lastpid = m_current->pid; pid_t lastpid = m_current->pid;
m_current->rsp = rsp0;
m_runlists[m_current->priority].remove(m_current); m_runlists[m_current->priority].remove(m_current);
if (m_current->flags && process_flags::ready) { if (m_current->flags && process_flags::ready) {
m_runlists[m_current->priority].push_back(m_current); m_runlists[m_current->priority].push_back(m_current);
} else { } else {
@@ -304,34 +300,25 @@ scheduler::schedule(uintptr_t rsp0)
} }
m_current = m_runlists[pri].pop_front(); 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) { if (lastpid != m_current->pid) {
bool loading = m_current->flags && process_flags::loading; 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)" : ""); m_current->pid, m_current->priority, loading ? " (loading)" : "");
task_switch(m_current);
}
} }
return rsp0; void
} scheduler::tick()
uintptr_t
scheduler::tick(uintptr_t rsp0)
{ {
if (--m_current->quanta == 0) { if (--m_current->quanta == 0) {
m_current->quanta = process_quanta; m_current->quanta = process_quanta;
rsp0 = schedule(rsp0); schedule();
} }
m_apic->reset_timer(m_tick_count); m_apic->reset_timer(m_tick_count);
return rsp0;
} }
process_node * process_node *

View File

@@ -10,7 +10,9 @@ class lapic;
struct page_table; struct page_table;
struct cpu_state; 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 /// The task scheduler
@@ -46,9 +48,7 @@ public:
void start(); void start();
/// Run the scheduler, possibly switching to a new task /// Run the scheduler, possibly switching to a new task
/// \arg rsp0 The stack pointer of the current interrupt handler void schedule();
/// \returns The stack pointer to switch to
uintptr_t schedule(uintptr_t rsp0);
/// Get the current process. /// Get the current process.
/// \returns A pointer to the current process' process struct /// \returns A pointer to the current process' process struct
@@ -65,7 +65,7 @@ public:
private: private:
friend uintptr_t syscall_dispatch(uintptr_t, cpu_state &); 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; friend class process;
/// Create a new process object. This process will have its pid /// Create a new process object. This process will have its pid
@@ -75,9 +75,7 @@ private:
process_node * create_process(pid_t pid = 0); process_node * create_process(pid_t pid = 0);
/// Handle a timer tick /// Handle a timer tick
/// \arg rsp0 The stack pointer of the current interrupt handler void tick();
/// \returns The stack pointer to switch to
uintptr_t tick(uintptr_t rsp0);
void prune(uint64_t now); void prune(uint64_t now);

View File

@@ -34,11 +34,11 @@ syscall_enable()
wrmsr(msr::ia32_fmask, 0x200); wrmsr(msr::ia32_fmask, 0x200);
} }
uintptr_t void
syscall_dispatch(uintptr_t return_rsp, cpu_state &regs) syscall_dispatch(cpu_state *regs)
{ {
console *cons = console::get(); console *cons = console::get();
syscall call = static_cast<syscall>(regs.rax); syscall call = static_cast<syscall>(regs->rax);
auto &s = scheduler::get(); auto &s = scheduler::get();
auto *p = s.current(); auto *p = s.current();
@@ -51,7 +51,7 @@ syscall_dispatch(uintptr_t return_rsp, cpu_state &regs)
cons->set_color(11); cons->set_color(11);
cons->printf("\nProcess %d: Received DEBUG syscall\n", p->pid); cons->printf("\nProcess %d: Received DEBUG syscall\n", p->pid);
cons->set_color(); cons->set_color();
print_regs(regs); print_regs(*regs);
cons->printf("\n Syscall enters: %8d\n", __counter_syscall_enter); cons->printf("\n Syscall enters: %8d\n", __counter_syscall_enter);
cons->printf(" Syscall sysret: %8d\n", __counter_syscall_sysret); cons->printf(" Syscall sysret: %8d\n", __counter_syscall_sysret);
break; break;
@@ -71,7 +71,7 @@ syscall_dispatch(uintptr_t return_rsp, cpu_state &regs)
p->wait_on_signal(-1ull); p->wait_on_signal(-1ull);
cons->printf("\nProcess %d: Received PAUSE syscall\n", p->pid); cons->printf("\nProcess %d: Received PAUSE syscall\n", p->pid);
cons->set_color(); cons->set_color();
return_rsp = s.schedule(return_rsp); s.schedule();
} }
break; break;
@@ -79,11 +79,11 @@ syscall_dispatch(uintptr_t return_rsp, cpu_state &regs)
{ {
cons->set_color(11); cons->set_color(11);
cons->printf("\nProcess %d: Received SLEEP syscall\n", p->pid); 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(); cons->set_color();
p->wait_on_time(regs.rdi); p->wait_on_time(regs->rdi);
return_rsp = s.schedule(return_rsp); s.schedule();
} }
break; break;
@@ -91,34 +91,34 @@ syscall_dispatch(uintptr_t return_rsp, cpu_state &regs)
cons->set_color(11); cons->set_color(11);
cons->printf("\nProcess %d: Received GETPID syscall\n", p->pid); cons->printf("\nProcess %d: Received GETPID syscall\n", p->pid);
cons->set_color(); cons->set_color();
regs.rax = p->pid; regs->rax = p->pid;
break; break;
case syscall::send: case syscall::send:
{ {
pid_t target = regs.rdi; pid_t target = regs->rdi;
uintptr_t data = regs.rsi; uintptr_t data = regs->rsi;
cons->set_color(11); cons->set_color(11);
cons->printf("\nProcess %d: Received SEND syscall, target %d, data %016lx\n", p->pid, target, data); cons->printf("\nProcess %d: Received SEND syscall, target %d, data %016lx\n", p->pid, target, data);
cons->set_color(); cons->set_color();
if (p->wait_on_send(target)) if (p->wait_on_send(target))
return_rsp = s.schedule(return_rsp); s.schedule();
} }
break; break;
case syscall::receive: case syscall::receive:
{ {
pid_t source = regs.rdi; pid_t source = regs->rdi;
uintptr_t data = regs.rsi; uintptr_t data = regs->rsi;
cons->set_color(11); cons->set_color(11);
cons->printf("\nProcess %d: Received RECEIVE syscall, source %d, dat %016lx\n", p->pid, source, data); cons->printf("\nProcess %d: Received RECEIVE syscall, source %d, dat %016lx\n", p->pid, source, data);
cons->set_color(); cons->set_color();
if (p->wait_on_receive(source)) if (p->wait_on_receive(source))
return_rsp = s.schedule(return_rsp); s.schedule();
} }
break; break;
@@ -128,8 +128,9 @@ syscall_dispatch(uintptr_t return_rsp, cpu_state &regs)
cons->printf("\nProcess %d: Received FORK syscall\n", p->pid); cons->printf("\nProcess %d: Received FORK syscall\n", p->pid);
cons->set_color(); cons->set_color();
pid_t pid = p->fork(return_rsp); pid_t pid = p->fork(regs);
regs.rax = pid; cons->printf("\n fork returning %d\n", pid);
regs->rax = pid;
} }
break; break;
@@ -137,8 +138,8 @@ syscall_dispatch(uintptr_t return_rsp, cpu_state &regs)
cons->set_color(11); cons->set_color(11);
cons->printf("\nProcess %d: Received EXIT syscall\n", p->pid); cons->printf("\nProcess %d: Received EXIT syscall\n", p->pid);
cons->set_color(); cons->set_color();
p->exit(regs.rdi); p->exit(regs->rdi);
return_rsp = s.schedule(return_rsp); s.schedule();
break; break;
default: default:
@@ -148,7 +149,5 @@ syscall_dispatch(uintptr_t return_rsp, cpu_state &regs)
_halt(); _halt();
break; break;
} }
return return_rsp;
} }

View File

@@ -21,5 +21,5 @@ enum class syscall : uint64_t
}; };
void syscall_enable(); void syscall_enable();
uintptr_t syscall_dispatch(uintptr_t, cpu_state &); void syscall_dispatch(cpu_state *);

View File

@@ -28,7 +28,6 @@ syscall_handler_prelude:
mov rdi, rsp mov rdi, rsp
call syscall_handler call syscall_handler
mov rsp, rax
mov rax, [rsp + 0x90] mov rax, [rsp + 0x90]
and rax, 0x3 and rax, 0x3

85
src/kernel/task.s Normal file
View 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
View 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