From bb1df4da6b01abddbd040714fd5da54c3adbc94e Mon Sep 17 00:00:00 2001 From: ice-bit Date: Sat, 6 Jul 2019 01:09:10 +0200 Subject: [PATCH] Added Programmable Interval Timer(PIT) driver --- kernel/drivers/Makefile | 2 +- kernel/drivers/gdt.h | 4 ++-- kernel/drivers/idt.h | 4 ++-- kernel/drivers/isr.h | 5 +++-- kernel/drivers/timer.c | 40 ++++++++++++++++++++++++++++++++++++++++ kernel/drivers/timer.h | 41 +++++++++++++++++++++++++++++++++++++++++ kernel/drivers/tty.h | 2 ++ kernel/kernel_main.c | 4 +++- 8 files changed, 94 insertions(+), 8 deletions(-) create mode 100644 kernel/drivers/timer.c create mode 100644 kernel/drivers/timer.h diff --git a/kernel/drivers/Makefile b/kernel/drivers/Makefile index 1e3bb34..eb3ec49 100644 --- a/kernel/drivers/Makefile +++ b/kernel/drivers/Makefile @@ -1,4 +1,4 @@ -OBJS = tty.o gdt.o idt.o isr.o +OBJS = tty.o gdt.o idt.o isr.o timer.o CC = i686-elf-gcc # cross-compiler CFLAGS = -m32 -fno-stack-protector -ffreestanding -Wall -Wextra -Werror -g -c diff --git a/kernel/drivers/gdt.h b/kernel/drivers/gdt.h index 0e31ade..833f7ce 100644 --- a/kernel/drivers/gdt.h +++ b/kernel/drivers/gdt.h @@ -7,6 +7,8 @@ ***************************************/ #ifndef _GDT_H_ #define _GDT_H_ + +#include /* * First a bit of theory: * GDT(Global Descriptor Table) is a complex data structure used in x86 systems @@ -29,8 +31,6 @@ * a new GDT ourself. */ -#include - /* Those values were taken from Intel's developer manual */ // GDT fields diff --git a/kernel/drivers/idt.h b/kernel/drivers/idt.h index d8e0727..6d9efcb 100644 --- a/kernel/drivers/idt.h +++ b/kernel/drivers/idt.h @@ -7,6 +7,8 @@ ***************************************/ #ifndef _IDT_H_ #define _IDT_H_ + +#include /* * First a bit of theory: * Sometimes you want to interrupt the processor from what it is currently doing @@ -18,8 +20,6 @@ * of interrupts handlers(ISRs) to the CPU. */ -#include - // Reserved bits in IDT entries #define IDT_FLAG_RESERVED 0x0E diff --git a/kernel/drivers/isr.h b/kernel/drivers/isr.h index 20b55c8..9234cb1 100644 --- a/kernel/drivers/isr.h +++ b/kernel/drivers/isr.h @@ -7,6 +7,9 @@ ***************************************/ #ifndef _ISR_H_ #define _ISR_H_ + +#include + /* * When we implement ISRs we have to keep in mind that the first 32 interrupts(and so the * first 32 ISRs) are reserved by the CPU to signal the kernel about critical actions, @@ -36,8 +39,6 @@ * 18 - Machine check exception * 19-31 - Reserved */ -#include - #define IRQ0 32 #define IRQ1 33 #define IRQ2 34 diff --git a/kernel/drivers/timer.c b/kernel/drivers/timer.c new file mode 100644 index 0000000..78746b3 --- /dev/null +++ b/kernel/drivers/timer.c @@ -0,0 +1,40 @@ +#include "timer.h" +#include "isr.h" +#include "tty.h" +#include "../libc/string.h" +#include "ports.h" + +// Start tick at zero +uint32_t tick = 0; + +static void timer_callback(registers_t regs) { + tick++; + uint8_t buf[8]; + itoa(tick, buf, 10); + kprint((uint8_t*)"Time: "); + kprint((uint8_t*)buf); + kprint((uint8_t*)"\n"); + + // Cast to void unused parameter + UNUSED_PAR(regs); +} + +void init_timer(uint32_t frequency) { + // Register a new ISR for IRQ0 + register_interrupt_handler(IRQ0, &timer_callback); + + /* As mentioned before, we'll send to PIT a value to divide for + * his system clock(1.1931 MHz). We have to keep in mind that + * this value must fit into a 16 bits variable */ + uint32_t divisor = 1193180 / frequency; + + // Send command to the right port + outb(0x43, 0x36); + // The two divisor has to be sent byte-wise, to do this we split them in two parts + uint8_t low = (uint8_t)(divisor & 0xFF); + uint8_t high = (uint8_t)((divisor >> 8) & 0xFF); + + // Send the frequency divisor + outb(0x40, low); + outb(0x40, high); +} \ No newline at end of file diff --git a/kernel/drivers/timer.h b/kernel/drivers/timer.h new file mode 100644 index 0000000..95a7bc2 --- /dev/null +++ b/kernel/drivers/timer.h @@ -0,0 +1,41 @@ +/************************************** + * iceOS Kernel * + * Developed by Marco 'icebit' Cetica * + * (c) 2019 * + * Released under GPLv3 * + * https://github.com/ice-bit/iceOS * + ***************************************/ +#ifndef _TIMER_H_ +#define _TIMER_H_ + +#include +/* + * The PIT(Programmable Interval Timer) is a chip that consist of an oscillator + * It's connected to IRQ0 and it can be configure at a user-defined rate + * between 10.Hz to 1.1931 MHz. The PIT is the primary method used to implement + * a system clock and for implement multitasking. + * The PIT's internal clock(~1.1931 MHz) is fed through a frequency divider that + * modulate the final signal. This chip has 3 channel, each with his own + * frequency divider: + * Channel 0: The most useful, it's output is connected to IRQ0 + * Channel 1: It were used to control refresh rates on DRAM(RAMs with capacitors) + * Channel 2: Controls the PC speakers. + * + * In our case, we will use only channel 0. + * So we have to set up PIT at a frequency 'f', so it interrupts us at regular + * intervals. We'll set the frequency to 100Hz(once every 10 ms); to do this + * we'll send the PIT a divisor that will be divided for it's input frequency. + * E.g. -> divisor = 1193180 Hz(1.1931MHz) / 100 Hz + * + * Apart of that, the PIT has 4 registers: 0x40-0x42(data ports) and 0x43(command port) + */ + +void init_timer(uint32_t frequency); + +/* Since regs parameter(from timer_callback) will be unused + * GCC(with -Werror flag) will dump an error, so we avoid this + * using the following macro + */ +#define UNUSED_PAR(x) (void)(x) + +#endif \ No newline at end of file diff --git a/kernel/drivers/tty.h b/kernel/drivers/tty.h index 1fab53f..36550a9 100644 --- a/kernel/drivers/tty.h +++ b/kernel/drivers/tty.h @@ -11,6 +11,8 @@ #include +// TODO: write something about Frame Buffer + // VGA colors enum TTY_COLORS { BLACK, // 0 diff --git a/kernel/kernel_main.c b/kernel/kernel_main.c index b657a43..812666f 100644 --- a/kernel/kernel_main.c +++ b/kernel/kernel_main.c @@ -1,6 +1,7 @@ #include "drivers/tty.h" #include "drivers/gdt.h" #include "drivers/idt.h" +#include "drivers/timer.h" #include "libc/stdio.h" void kernel_main() { @@ -9,7 +10,8 @@ void kernel_main() { clear_prompt(); init_prompt(); // Initialize frame buffer - puts("Hello World!"); + //puts("Hello World!"); + init_timer(1); /* // Testing some interrupts