Kernel: Introduce a timer interface

This commit is contained in:
apio 2022-11-19 20:01:01 +01:00
parent 047f445651
commit 83bcac7a16
11 changed files with 240 additions and 4 deletions

View File

@ -4,6 +4,7 @@ set(SOURCES
src/memory/MemoryManager.cpp src/memory/MemoryManager.cpp
src/boot/Init.cpp src/boot/Init.cpp
src/arch/Serial.cpp src/arch/Serial.cpp
src/arch/Timer.cpp
) )
if("${ARCH}" MATCHES "x86_64") if("${ARCH}" MATCHES "x86_64")
@ -13,6 +14,7 @@ if("${ARCH}" MATCHES "x86_64")
src/arch/x86_64/Serial.cpp src/arch/x86_64/Serial.cpp
src/arch/x86_64/MMU.cpp src/arch/x86_64/MMU.cpp
src/arch/x86_64/CPU.cpp src/arch/x86_64/CPU.cpp
src/arch/x86_64/Timer.cpp
) )
endif() endif()

View File

@ -9,8 +9,13 @@ namespace CPU
const char* platform_string(); const char* platform_string();
void platform_init(); void platform_init();
void platform_finish_init();
[[noreturn]] void efficient_halt(); [[noreturn]] void efficient_halt();
void switch_kernel_stack(u64 top); void switch_kernel_stack(u64 top);
void enable_interrupts();
void disable_interrupts();
void wait_for_interrupt();
} }

80
kernel/src/arch/Timer.cpp Normal file
View File

@ -0,0 +1,80 @@
#include "arch/Timer.h"
static u64 timer_ticks = 0;
static u64 boot_timestamp;
namespace Timer
{
void tick()
{
timer_ticks++;
}
usize ticks()
{
return ticks_ms() / 1000;
}
usize ticks_ms()
{
return timer_ticks / ARCH_TIMER_FREQ;
}
usize ticks_us() // We want a bit of precision; if there are 10 ticks/ms, do not return the truncated ms value *
// 1000, but ticks * 100 (1000/10), which is more precise
{
if (ARCH_TIMER_FREQ > 1000) [[unlikely]]
return timer_ticks / (ARCH_TIMER_FREQ / 1000);
return timer_ticks * (1000 / ARCH_TIMER_FREQ);
}
usize ticks_ns()
{
return ticks_us() * 1000;
}
usize boot()
{
return boot_timestamp;
}
usize boot_ms()
{
return boot_timestamp * MS_PER_SECOND;
}
usize boot_us()
{
return boot_timestamp * US_PER_SECOND;
}
usize boot_ns()
{
return boot_timestamp * NS_PER_SECOND;
}
usize clock()
{
return boot() + ticks();
}
usize clock_ms()
{
return boot_ms() + ticks_ms();
}
usize clock_us()
{
return boot_us() + ticks_us();
}
usize clock_ns()
{
return boot_ns() + ticks_ns();
}
void init()
{
arch_init();
}
}

33
kernel/src/arch/Timer.h Normal file
View File

@ -0,0 +1,33 @@
#pragma once
#include <Types.h>
extern const usize ARCH_TIMER_FREQ; // How many timer ticks in one millisecond?
static const usize MS_PER_SECOND = 1000;
static const usize US_PER_SECOND = MS_PER_SECOND * 1000;
static const usize NS_PER_SECOND = US_PER_SECOND * 1000;
namespace Timer
{
void tick();
usize raw_ticks();
usize ticks();
usize ticks_ms();
usize ticks_us();
usize ticks_ns();
usize boot();
usize boot_ms();
usize boot_us();
usize boot_ns();
usize clock();
usize clock_ms();
usize clock_us();
usize clock_ns();
void arch_init();
void init();
}

View File

@ -149,4 +149,5 @@ _isr18: ; machine check (#MC)
ISR 19 ; SIMD floating-point exception (#XM) ISR 19 ; SIMD floating-point exception (#XM)
ISR 20 ; virtualization exception (#VE) ISR 20 ; virtualization exception (#VE)
ISR_ERROR 21 ; control-protection exception (#CP) ISR_ERROR 21 ; control-protection exception (#CP)
; ISR 22-31 reserved ; ISR 22-31 reserved
IRQ 32, 0 ; timer interrupt

View File

@ -1,5 +1,7 @@
#include "arch/x86_64/CPU.h" #include "arch/x86_64/CPU.h"
#include "arch/Serial.h" #include "arch/Serial.h"
#include "arch/Timer.h"
#include "arch/x86_64/IO.h"
#include <String.h> #include <String.h>
#include <Types.h> #include <Types.h>
#include <cpuid.h> #include <cpuid.h>
@ -105,6 +107,59 @@ static void setup_gdt()
load_tr(0x2b); load_tr(0x2b);
} }
// PIC code
#define PIC1_COMMAND 0x20
#define PIC1_DATA 0x21
#define PIC2_COMMAND 0xA0
#define PIC2_DATA 0xA1
#define PIC_EOI 0x20
#define ICW1_INIT 0x10
#define ICW1_ICW4 0x01
#define ICW4_8086 0x01
#define io_delay() IO::outb(0x80, 0)
static void remap_pic()
{
IO::outb(PIC1_COMMAND, ICW1_INIT | ICW1_ICW4);
io_delay();
IO::outb(PIC2_COMMAND, ICW1_INIT | ICW1_ICW4);
io_delay();
IO::outb(PIC1_DATA, 0x20);
io_delay();
IO::outb(PIC2_DATA, 0x28);
io_delay();
IO::outb(PIC1_DATA, 4);
io_delay();
IO::outb(PIC2_DATA, 2);
io_delay();
IO::outb(PIC1_DATA, ICW4_8086);
io_delay();
IO::outb(PIC2_DATA, ICW4_8086);
io_delay();
IO::outb(PIC1_DATA, 0b11111110);
io_delay();
IO::outb(PIC2_DATA, 0b11111111);
}
static void pic_eoi(unsigned char irq)
{
if (irq >= 8) IO::outb(PIC2_COMMAND, PIC_EOI);
IO::outb(PIC1_COMMAND, PIC_EOI);
}
static void pic_eoi(Registers* regs)
{
pic_eoi((unsigned char)(regs->error)); // On IRQs, the error code is the IRQ number
}
// IDT code and definitions // IDT code and definitions
struct IDTEntry struct IDTEntry
@ -160,6 +215,7 @@ static void idt_add_handler(short num, void* handler, u8 type_attr)
#define INT(x) extern "C" void _isr##x() #define INT(x) extern "C" void _isr##x()
#define TRAP(x) idt_add_handler(x, (void*)_isr##x, IDT_TA_TrapGate) #define TRAP(x) idt_add_handler(x, (void*)_isr##x, IDT_TA_TrapGate)
#define IRQ(x) idt_add_handler(x, (void*)_isr##x, IDT_TA_InterruptGate)
INT(0); INT(0);
INT(1); INT(1);
@ -181,6 +237,7 @@ INT(18);
INT(19); INT(19);
INT(20); INT(20);
INT(21); INT(21);
INT(32);
static void setup_idt() static void setup_idt()
{ {
@ -206,6 +263,7 @@ static void setup_idt()
TRAP(19); TRAP(19);
TRAP(20); TRAP(20);
TRAP(21); TRAP(21);
IRQ(32);
static IDTR idtr; static IDTR idtr;
idtr.limit = 0x0FFF; idtr.limit = 0x0FFF;
@ -249,6 +307,11 @@ extern "C" void handle_x86_exception([[maybe_unused]] Registers* regs)
extern "C" void arch_interrupt_entry(Registers* regs) extern "C" void arch_interrupt_entry(Registers* regs)
{ {
if (regs->isr < 32) handle_x86_exception(regs); if (regs->isr < 32) handle_x86_exception(regs);
else if (regs->isr == 32)
{
Timer::tick();
pic_eoi(regs);
}
else else
{ {
Serial::println("IRQ catched! Halting."); Serial::println("IRQ catched! Halting.");
@ -310,6 +373,26 @@ namespace CPU
setup_idt(); setup_idt();
} }
void platform_finish_init()
{
remap_pic();
}
void enable_interrupts()
{
asm volatile("sti");
}
void disable_interrupts()
{
asm volatile("cli");
}
void wait_for_interrupt()
{
asm volatile("hlt");
}
[[noreturn]] void efficient_halt() // Halt the CPU, using the lowest power possible. On x86-64 we do this using the [[noreturn]] void efficient_halt() // Halt the CPU, using the lowest power possible. On x86-64 we do this using the
// "hlt" instruction, which puts the CPU into a low-power idle state until the // "hlt" instruction, which puts the CPU into a low-power idle state until the
// next interrupt arrives... and we disable interrupts beforehand. // next interrupt arrives... and we disable interrupts beforehand.

View File

@ -3,4 +3,5 @@ extern _start
_main: _main:
xor rbp, rbp xor rbp, rbp
cld
call _start call _start

View File

@ -0,0 +1,16 @@
#include "arch/Timer.h"
#include "arch/x86_64/IO.h"
#define PIT_CHANNEL_0 0x40
const u64 base_frequency = 1193182;
const usize ARCH_TIMER_FREQ = 10;
void Timer::arch_init()
{
constexpr u16 divisor = (uint16_t)(base_frequency / (ARCH_TIMER_FREQ * 1000));
static_assert(divisor >= 100);
IO::outb(PIT_CHANNEL_0, (uint8_t)(divisor & 0xFF));
IO::outb(0x80, 0); // short delay
IO::outb(PIT_CHANNEL_0, (uint8_t)((divisor & 0xFF00) >> 8));
}

View File

@ -20,6 +20,8 @@ void Init::check_magic()
void Init::early_init() void Init::early_init()
{ {
CPU::disable_interrupts();
Framebuffer::init(); Framebuffer::init();
MemoryManager::init(); MemoryManager::init();

View File

@ -1,6 +1,7 @@
#include "arch/CPU.h" #include "arch/CPU.h"
#include "arch/MMU.h" #include "arch/MMU.h"
#include "arch/Serial.h" #include "arch/Serial.h"
#include "arch/Timer.h"
#include "boot/Init.h" #include "boot/Init.h"
#include "memory/MemoryManager.h" #include "memory/MemoryManager.h"
#include "video/Framebuffer.h" #include "video/Framebuffer.h"
@ -92,9 +93,20 @@ extern "C" [[noreturn]] void _start()
Serial::println("Successfully unmapped address"); Serial::println("Successfully unmapped address");
*ptr = 16; Timer::init();
Serial::println("ERROR: Still here after page fault"); CPU::platform_finish_init();
CPU::enable_interrupts();
usize start = 0;
while (1)
{
while ((Timer::ticks_ms() - start) < 20) { CPU::wait_for_interrupt(); }
start = Timer::ticks_ms();
Serial::printf("%8zu milliseconds have passed!\n", start);
}
CPU::efficient_halt(); CPU::efficient_halt();
} }

View File

@ -185,7 +185,8 @@ static bool is_integer_format_specifier(char c)
static usize to_string(usize value, usize base, char* buf, usize max, bool uppercase) static usize to_string(usize value, usize base, char* buf, usize max, bool uppercase)
{ {
usize i = 0; usize i = 0;
if (!value && max) if (!max) return 0;
if (!value)
{ {
buf[i] = '0'; buf[i] = '0';
return 1; return 1;