Kernel: Introduce a timer interface
This commit is contained in:
parent
047f445651
commit
83bcac7a16
@ -4,6 +4,7 @@ set(SOURCES
|
||||
src/memory/MemoryManager.cpp
|
||||
src/boot/Init.cpp
|
||||
src/arch/Serial.cpp
|
||||
src/arch/Timer.cpp
|
||||
)
|
||||
|
||||
if("${ARCH}" MATCHES "x86_64")
|
||||
@ -13,6 +14,7 @@ if("${ARCH}" MATCHES "x86_64")
|
||||
src/arch/x86_64/Serial.cpp
|
||||
src/arch/x86_64/MMU.cpp
|
||||
src/arch/x86_64/CPU.cpp
|
||||
src/arch/x86_64/Timer.cpp
|
||||
)
|
||||
endif()
|
||||
|
||||
|
@ -9,8 +9,13 @@ namespace CPU
|
||||
const char* platform_string();
|
||||
|
||||
void platform_init();
|
||||
void platform_finish_init();
|
||||
|
||||
[[noreturn]] void efficient_halt();
|
||||
|
||||
void switch_kernel_stack(u64 top);
|
||||
|
||||
void enable_interrupts();
|
||||
void disable_interrupts();
|
||||
void wait_for_interrupt();
|
||||
}
|
80
kernel/src/arch/Timer.cpp
Normal file
80
kernel/src/arch/Timer.cpp
Normal 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
33
kernel/src/arch/Timer.h
Normal 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();
|
||||
}
|
@ -149,4 +149,5 @@ _isr18: ; machine check (#MC)
|
||||
ISR 19 ; SIMD floating-point exception (#XM)
|
||||
ISR 20 ; virtualization exception (#VE)
|
||||
ISR_ERROR 21 ; control-protection exception (#CP)
|
||||
; ISR 22-31 reserved
|
||||
; ISR 22-31 reserved
|
||||
IRQ 32, 0 ; timer interrupt
|
@ -1,5 +1,7 @@
|
||||
#include "arch/x86_64/CPU.h"
|
||||
#include "arch/Serial.h"
|
||||
#include "arch/Timer.h"
|
||||
#include "arch/x86_64/IO.h"
|
||||
#include <String.h>
|
||||
#include <Types.h>
|
||||
#include <cpuid.h>
|
||||
@ -105,6 +107,59 @@ static void setup_gdt()
|
||||
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
|
||||
|
||||
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 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(1);
|
||||
@ -181,6 +237,7 @@ INT(18);
|
||||
INT(19);
|
||||
INT(20);
|
||||
INT(21);
|
||||
INT(32);
|
||||
|
||||
static void setup_idt()
|
||||
{
|
||||
@ -206,6 +263,7 @@ static void setup_idt()
|
||||
TRAP(19);
|
||||
TRAP(20);
|
||||
TRAP(21);
|
||||
IRQ(32);
|
||||
|
||||
static IDTR idtr;
|
||||
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)
|
||||
{
|
||||
if (regs->isr < 32) handle_x86_exception(regs);
|
||||
else if (regs->isr == 32)
|
||||
{
|
||||
Timer::tick();
|
||||
pic_eoi(regs);
|
||||
}
|
||||
else
|
||||
{
|
||||
Serial::println("IRQ catched! Halting.");
|
||||
@ -310,6 +373,26 @@ namespace CPU
|
||||
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
|
||||
// "hlt" instruction, which puts the CPU into a low-power idle state until the
|
||||
// next interrupt arrives... and we disable interrupts beforehand.
|
||||
|
@ -3,4 +3,5 @@ extern _start
|
||||
|
||||
_main:
|
||||
xor rbp, rbp
|
||||
cld
|
||||
call _start
|
16
kernel/src/arch/x86_64/Timer.cpp
Normal file
16
kernel/src/arch/x86_64/Timer.cpp
Normal 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));
|
||||
}
|
@ -20,6 +20,8 @@ void Init::check_magic()
|
||||
|
||||
void Init::early_init()
|
||||
{
|
||||
CPU::disable_interrupts();
|
||||
|
||||
Framebuffer::init();
|
||||
MemoryManager::init();
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
#include "arch/CPU.h"
|
||||
#include "arch/MMU.h"
|
||||
#include "arch/Serial.h"
|
||||
#include "arch/Timer.h"
|
||||
#include "boot/Init.h"
|
||||
#include "memory/MemoryManager.h"
|
||||
#include "video/Framebuffer.h"
|
||||
@ -92,9 +93,20 @@ extern "C" [[noreturn]] void _start()
|
||||
|
||||
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();
|
||||
}
|
@ -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)
|
||||
{
|
||||
usize i = 0;
|
||||
if (!value && max)
|
||||
if (!max) return 0;
|
||||
if (!value)
|
||||
{
|
||||
buf[i] = '0';
|
||||
return 1;
|
||||
|
Loading…
Reference in New Issue
Block a user