Set up initial task switching (ring0 only)

This commit is contained in:
Justin C. Miller
2018-05-18 23:55:15 -07:00
parent 0ddcf668cb
commit bfaab294e6
12 changed files with 239 additions and 45 deletions

View File

@@ -52,28 +52,38 @@ lapic::lapic(uint32_t *base, isr spurious) :
void
lapic::enable_timer(isr vector, uint8_t divisor, uint32_t count, bool repeat)
{
uint32_t divbits = 0;
switch (divisor) {
case 1: divisor = 11; break;
case 2: divisor = 0; break;
case 4: divisor = 1; break;
case 8: divisor = 2; break;
case 16: divisor = 3; break;
case 32: divisor = 8; break;
case 64: divisor = 9; break;
case 128: divisor = 10; break;
case 1: divbits = 0xb; break;
case 2: divbits = 0x0; break;
case 4: divbits = 0x1; break;
case 8: divbits = 0x2; break;
case 16: divbits = 0x3; break;
case 32: divbits = 0x8; break;
case 64: divbits = 0x9; break;
case 128: divbits = 0xa; break;
default:
kassert(0, "Invalid divisor passed to lapic::enable_timer");
}
apic_write(m_base, 0x3e0, divisor);
apic_write(m_base, 0x380, count);
uint32_t lvte = static_cast<uint8_t>(vector);
if (repeat)
lvte |= 0x20000;
log::debug(logs::apic, "Enabling APIC timer with isr %d.", vector);
apic_write(m_base, 0x320, lvte);
apic_write(m_base, 0x3e0, divbits);
reset_timer(count);
}
uint32_t
lapic::reset_timer(uint32_t count)
{
uint32_t remaining = apic_read(m_base, 0x380);
apic_write(m_base, 0x380, count);
return remaining;
}
void

View File

@@ -37,6 +37,15 @@ public:
/// \arg repeat If false, this timer is one-off, otherwise repeating
void enable_timer(isr vector, uint8_t divisor, uint32_t count, bool repeat = true);
/// Reset the timer countdown.
/// \arg count The count of ticks before an interrupt, or 0 to stop the timer
/// \returns The count of ticks that were remaining before reset
uint32_t reset_timer(uint32_t count);
/// Stop the timer.
/// \returns The count of ticks remaining before an interrupt was to happen
inline uint32_t stop_timer() { return reset_timer(0); }
/// Enable interrupts for the LAPIC LINT0 pin.
/// \arg num Local interrupt number (0 or 1)
/// \arg vector Interrupt vector LINT0 should use

View File

@@ -28,8 +28,7 @@ _start:
extern kernel_main
call kernel_main
cli
; Kernel init is over, wait for interrupts
.hang:
hlt
jmp .hang

View File

@@ -1,5 +1,13 @@
#pragma once
struct cpu_state
{
uint64_t ds;
uint64_t r15, r14, r13, r12, r11, r10, r9, r8;
uint64_t rdi, rsi, rbp, rbx, rdx, rcx, rax;
uint64_t interrupt, errorcode;
uint64_t rip, cs, rflags, user_rsp, ss;
};
class cpu_id
{

View File

@@ -1,17 +1,17 @@
#include <stdint.h>
#include "console.h"
#include "cpu.h"
#include "device_manager.h"
#include "gdt.h"
#include "interrupts.h"
#include "io.h"
#include "log.h"
struct registers;
#include "scheduler.h"
extern "C" {
void isr_handler(registers);
void irq_handler(registers);
addr_t isr_handler(addr_t, cpu_state);
void irq_handler(cpu_state);
#define ISR(i, name) extern void name ();
#define EISR(i, name) extern void name ();
@@ -93,14 +93,6 @@ interrupts_init()
log::info(logs::boot, "Interrupts enabled.");
}
struct registers
{
uint64_t ds;
uint64_t rdi, rsi, rbp, rsp, rbx, rdx, rcx, rax;
uint64_t interrupt, errorcode;
uint64_t rip, cs, eflags, user_esp, ss;
};
#define print_reg(name, value) cons->printf(" %s: %016lx\n", name, (value));
extern "C" uint64_t get_frame(int frame);
@@ -117,14 +109,17 @@ print_stacktrace(int skip = 0)
}
}
void
isr_handler(registers regs)
addr_t
isr_handler(addr_t return_rsp, cpu_state regs)
{
log::debug(logs::task, "Starting RSP %016lx", return_rsp);
console *cons = console::get();
switch (static_cast<isr>(regs.interrupt & 0xff)) {
case isr::isrTimer:
cons->puts("\nTICK\n");
case isr::isrTimer: {
scheduler &s = scheduler::get();
return_rsp = s.tick(return_rsp);
}
break;
case isr::isrLINT0:
@@ -196,14 +191,13 @@ isr_handler(registers regs)
cons->puts("\n");
print_reg("rbp", regs.rbp);
print_reg("rsp", regs.rsp);
cons->puts("\n");
print_reg("rip", regs.rip);
print_stacktrace(2);
cons->puts("\nStack:\n");
uint64_t sp = regs.rsp;
uint64_t sp = regs.user_rsp;
while (sp <= regs.rbp) {
cons->printf("%016x: %016x\n", sp, *reinterpret_cast<uint64_t *>(sp));
sp += sizeof(uint64_t);
@@ -229,6 +223,7 @@ isr_handler(registers regs)
__asm__ __volatile__ ("mov %%cr2, %0" : "=r"(cr2));
print_reg("cr2", cr2);
print_reg("rsp", regs.user_rsp);
print_reg("rip", regs.rip);
cons->puts("\n");
@@ -250,7 +245,7 @@ isr_handler(registers regs)
cons->puts("\n");
print_reg("rbp", regs.rbp);
print_reg("rsp", regs.rsp);
print_reg("rsp", regs.user_rsp);
cons->puts("\n");
print_reg("rip", regs.rip);
@@ -272,7 +267,6 @@ isr_handler(registers regs)
print_reg("rdi", regs.rdi);
print_reg("rsi", regs.rsi);
print_reg("rbp", regs.rbp);
print_reg("rsp", regs.rsp);
print_reg("rbx", regs.rbx);
print_reg("rdx", regs.rdx);
print_reg("rcx", regs.rcx);
@@ -281,8 +275,8 @@ isr_handler(registers regs)
print_reg("rip", regs.rip);
print_reg(" cs", regs.cs);
print_reg(" ef", regs.eflags);
print_reg("esp", regs.user_esp);
print_reg(" ef", regs.rflags);
print_reg("rsp", regs.user_rsp);
print_reg(" ss", regs.ss);
cons->puts("\n");
@@ -291,10 +285,13 @@ isr_handler(registers regs)
}
*reinterpret_cast<uint32_t *>(0xffffff80fee000b0) = 0;
log::debug(logs::task, "Returning RSP %016lx", return_rsp);
return return_rsp;
}
void
irq_handler(registers regs)
irq_handler(cpu_state regs)
{
console *cons = console::get();
uint8_t irq = get_irq(regs.interrupt);
@@ -308,7 +305,6 @@ irq_handler(registers regs)
print_reg("rdi", regs.rdi);
print_reg("rsi", regs.rsi);
print_reg("rbp", regs.rbp);
print_reg("rsp", regs.rsp);
print_reg("rbx", regs.rbx);
print_reg("rdx", regs.rdx);
print_reg("rcx", regs.rcx);
@@ -317,8 +313,8 @@ irq_handler(registers regs)
print_reg("rip", regs.rip);
print_reg(" cs", regs.cs);
print_reg(" ef", regs.eflags);
print_reg("esp", regs.user_esp);
print_reg(" ef", regs.rflags);
print_reg("rsp", regs.user_rsp);
print_reg(" ss", regs.ss);
while(1) asm("hlt");
}

View File

@@ -1,6 +1,7 @@
#pragma once
/// \file interrupts.h
/// Free functions and definitions related to interrupt service vectors
#include <stdint.h>
/// Enum of all defined ISR/IRQ vectors

View File

@@ -38,11 +38,19 @@ gdt_load:
push rcx
push rdx
push rbx
push rsp
push rbp
push rsi
push rdi
push r8
push r9
push r10
push r11
push r12
push r13
push r14
push r15
mov ax, ds
push rax
%endmacro
@@ -54,10 +62,18 @@ gdt_load:
mov fs, ax
mov gs, ax
pop r15
pop r14
pop r13
pop r12
pop r11
pop r10
pop r9
pop r8
pop rdi
pop rsi
pop rbp
pop rsp
pop rbx
pop rdx
pop rcx
@@ -76,9 +92,11 @@ extern isr_handler
global isr_handler_prelude
isr_handler_prelude:
push_all_and_segments
load_kernel_segments
;load_kernel_segments
mov rdi, rsp
call isr_handler
mov rsp, rax
pop_all_and_segments

View File

@@ -16,6 +16,7 @@ static const char *areas[] = {
"dev ",
"driv",
"file",
"task",
nullptr
};

View File

@@ -13,6 +13,7 @@ enum class logs
device,
driver,
fs,
task,
max
};

View File

@@ -15,6 +15,7 @@
#include "log.h"
#include "memory.h"
#include "page_manager.h"
#include "scheduler.h"
#include "screen.h"
#include "serial.h"
@@ -54,8 +55,9 @@ init_console(const popcorn_data *header)
log::enable(logs::apic, log::level::info);
log::enable(logs::device, log::level::debug);
log::enable(logs::driver, log::level::debug);
log::enable(logs::memory, log::level::debug);
log::enable(logs::memory, log::level::info);
log::enable(logs::fs, log::level::debug);
log::enable(logs::task, log::level::debug);
}
void do_error_3() { volatile int x = 1; volatile int y = 0; volatile int z = x / y; }
@@ -94,9 +96,17 @@ kernel_main(popcorn_data *header)
log::info(logs::boot, "CPU Vendor: %s", cpu.vendor_id());
log::info(logs::boot, "CPU Family %x Model %x Stepping %x",
cpu.family(), cpu.model(), cpu.stepping());
auto r = cpu.get(0x15);
addr_t cr4 = 0;
__asm__ __volatile__ ( "mov %%cr4, %0" : "=r" (cr4) );
log::info(logs::boot, "cr4: cr4", r.ecx);
log::info(logs::boot, "CPU Crystal: %dHz", r.ecx);
devices->init_drivers();
/*
block_device *disk = devices->get_block_device(0);
if (disk) {
for (int i=0; i<1; ++i) {
@@ -118,10 +128,15 @@ kernel_main(popcorn_data *header)
} else {
log::warn(logs::boot, "No block devices present.");
}
*/
// do_error_1();
// __asm__ __volatile__("int $15");
g_console.puts("boogity!");
do_the_set_registers(header);
// pager->dump_pml4();
scheduler *sched = new (&scheduler::get()) scheduler(devices->get_lapic());
sched->start();
g_console.puts("boogity!\n");
}

92
src/kernel/scheduler.cpp Normal file
View File

@@ -0,0 +1,92 @@
#include "apic.h"
#include "console.h"
#include "cpu.h"
#include "gdt.h"
#include "interrupts.h"
#include "log.h"
#include "scheduler.h"
scheduler scheduler::s_instance(nullptr);
static const uint32_t quantum = 5000000;
const int stack_size = 0x1000;
char taskAstack[stack_size];
char taskBstack[stack_size];
uint64_t taskAcount = 0;
void taskA()
{
console *cons = console::get();
while(1) {
cons->putc('.');
}
}
void taskB()
{
console *cons = console::get();
while(1) {
cons->putc('+');
}
}
scheduler::scheduler(lapic *apic) :
m_apic(apic),
m_current(0)
{
m_processes.ensure_capacity(50);
}
static process
create_process(uint16_t pid, void *stack, void (*rip)())
{
uint64_t flags;
__asm__ __volatile__ ( "pushf; pop %0" : "=r" (flags) );
// This is a hack for now, until we get a lot more set up.
// I just want to see task switching working inside ring0 first
uint16_t kcs = (1 << 3) | 0;
uint16_t cs = (3 << 3) | 3;
uint16_t kss = (2 << 3) | 0;
uint16_t ss = (4 << 3) | 3;
void *sp = kutil::offset_pointer(stack, stack_size);
cpu_state *state = reinterpret_cast<cpu_state *>(sp) - 1;
kutil::memset(state, 0, sizeof(cpu_state));
state->ds = state->ss = kss;
state->cs = kcs;
state->rflags = 0x202;
state->user_rsp = reinterpret_cast<uint64_t>(sp);
state->rip = reinterpret_cast<uint64_t>(rip);
log::debug(logs::task, "Creating a user RSP of %016lx", state->user_rsp);
return {pid, reinterpret_cast<addr_t>(state)};
}
void
scheduler::start()
{
m_apic->enable_timer(isr::isrTimer, 128, quantum, false);
m_processes.append({0, 0}); // The kernel idle task
m_processes.append(create_process(1, &taskAstack[0], &taskA));
m_processes.append(create_process(2, &taskBstack[0], &taskB));
}
addr_t
scheduler::tick(addr_t rsp0)
{
log::debug(logs::task, "Scheduler tick.");
m_processes[m_current].rsp = rsp0;
m_current = (m_current + 1) % m_processes.count();
rsp0 = m_processes[m_current].rsp;
tss_set_stack(0, rsp0);
m_apic->reset_timer(quantum);
return rsp0;
}

44
src/kernel/scheduler.h Normal file
View File

@@ -0,0 +1,44 @@
#pragma once
/// \file scheduler.h
/// The task scheduler and related definitions
#include "kutil/memory.h"
#include "kutil/vector.h"
class lapic;
struct process
{
uint16_t pid;
addr_t rsp;
};
/// The task scheduler
class scheduler
{
public:
/// Constructor.
/// \arg apic Pointer to the local APIC object
scheduler(lapic *apic);
/// Start the scheduler working. This may involve starting
/// timer interrupts or other preemption methods.
void start();
/// Handle a timer tick
/// \arg rsp0 The stack pointer of the current interrupt handler
/// \returns The stack pointer to handler to switch to
addr_t tick(addr_t rsp0);
/// Get a reference to the system scheduler
/// \returns A reference to the global system scheduler
static scheduler & get() { return s_instance; }
private:
lapic *m_apic;
kutil::vector<process> m_processes;
uint16_t m_current;
static scheduler s_instance;
};