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/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()
|
||||||
|
|
||||||
|
@ -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
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 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
|
@ -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.
|
||||||
|
@ -3,4 +3,5 @@ extern _start
|
|||||||
|
|
||||||
_main:
|
_main:
|
||||||
xor rbp, rbp
|
xor rbp, rbp
|
||||||
|
cld
|
||||||
call _start
|
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()
|
void Init::early_init()
|
||||||
{
|
{
|
||||||
|
CPU::disable_interrupts();
|
||||||
|
|
||||||
Framebuffer::init();
|
Framebuffer::init();
|
||||||
MemoryManager::init();
|
MemoryManager::init();
|
||||||
|
|
||||||
|
@ -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();
|
||||||
}
|
}
|
@ -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;
|
||||||
|
Loading…
Reference in New Issue
Block a user