diff --git a/src/kernel/apic.cpp b/src/kernel/apic.cpp index fc7dd69..192e679 100644 --- a/src/kernel/apic.cpp +++ b/src/kernel/apic.cpp @@ -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(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 diff --git a/src/kernel/apic.h b/src/kernel/apic.h index 7b6ae59..3360ba3 100644 --- a/src/kernel/apic.h +++ b/src/kernel/apic.h @@ -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 diff --git a/src/kernel/boot.s b/src/kernel/boot.s index 360b361..081185c 100644 --- a/src/kernel/boot.s +++ b/src/kernel/boot.s @@ -28,8 +28,7 @@ _start: extern kernel_main call kernel_main - cli - + ; Kernel init is over, wait for interrupts .hang: hlt jmp .hang diff --git a/src/kernel/cpu.h b/src/kernel/cpu.h index deaf68e..e0b3fe6 100644 --- a/src/kernel/cpu.h +++ b/src/kernel/cpu.h @@ -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 { diff --git a/src/kernel/interrupts.cpp b/src/kernel/interrupts.cpp index 49d78eb..a8696e4 100644 --- a/src/kernel/interrupts.cpp +++ b/src/kernel/interrupts.cpp @@ -1,17 +1,17 @@ #include #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(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(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(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"); } diff --git a/src/kernel/interrupts.h b/src/kernel/interrupts.h index 9a3fa3d..4966937 100644 --- a/src/kernel/interrupts.h +++ b/src/kernel/interrupts.h @@ -1,6 +1,7 @@ #pragma once /// \file interrupts.h /// Free functions and definitions related to interrupt service vectors +#include /// Enum of all defined ISR/IRQ vectors diff --git a/src/kernel/interrupts.s b/src/kernel/interrupts.s index 34c3842..129ebc0 100644 --- a/src/kernel/interrupts.s +++ b/src/kernel/interrupts.s @@ -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 diff --git a/src/kernel/log.cpp b/src/kernel/log.cpp index 04c61c8..439781a 100644 --- a/src/kernel/log.cpp +++ b/src/kernel/log.cpp @@ -16,6 +16,7 @@ static const char *areas[] = { "dev ", "driv", "file", + "task", nullptr }; diff --git a/src/kernel/log.h b/src/kernel/log.h index 1a2d5c5..c2b2e8a 100644 --- a/src/kernel/log.h +++ b/src/kernel/log.h @@ -13,6 +13,7 @@ enum class logs device, driver, fs, + task, max }; diff --git a/src/kernel/main.cpp b/src/kernel/main.cpp index 80a6c16..23961e3 100644 --- a/src/kernel/main.cpp +++ b/src/kernel/main.cpp @@ -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"); } diff --git a/src/kernel/scheduler.cpp b/src/kernel/scheduler.cpp new file mode 100644 index 0000000..a8409d5 --- /dev/null +++ b/src/kernel/scheduler.cpp @@ -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(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(sp); + state->rip = reinterpret_cast(rip); + + log::debug(logs::task, "Creating a user RSP of %016lx", state->user_rsp); + + return {pid, reinterpret_cast(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; +} diff --git a/src/kernel/scheduler.h b/src/kernel/scheduler.h new file mode 100644 index 0000000..13b1938 --- /dev/null +++ b/src/kernel/scheduler.h @@ -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 m_processes; + uint16_t m_current; + + static scheduler s_instance; +};