mirror of
https://github.com/justinian/jsix.git
synced 2025-12-10 00:14:32 -08:00
Set up initial task switching (ring0 only)
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -28,8 +28,7 @@ _start:
|
||||
extern kernel_main
|
||||
call kernel_main
|
||||
|
||||
cli
|
||||
|
||||
; Kernel init is over, wait for interrupts
|
||||
.hang:
|
||||
hlt
|
||||
jmp .hang
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ static const char *areas[] = {
|
||||
"dev ",
|
||||
"driv",
|
||||
"file",
|
||||
"task",
|
||||
|
||||
nullptr
|
||||
};
|
||||
|
||||
@@ -13,6 +13,7 @@ enum class logs
|
||||
device,
|
||||
driver,
|
||||
fs,
|
||||
task,
|
||||
|
||||
max
|
||||
};
|
||||
|
||||
@@ -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
92
src/kernel/scheduler.cpp
Normal 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
44
src/kernel/scheduler.h
Normal 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;
|
||||
};
|
||||
Reference in New Issue
Block a user