From 14f831f5fe5a6491e5ab31c4001e5eaf1dd9a3b1 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Duje=20Mihanovi=C4=87?= <duje.mihanovic@skole.hr> Date: Sat, 14 May 2022 13:22:39 +0200 Subject: [PATCH] Add Intel 8254 driver and print elapsed seconds --- Makefile | 2 +- include/arch/x86/time/i8254.h | 46 ++++++++++++++++++++++++++++ include/arch/x86/tty.h | 2 ++ kernel/arch/x86/irq/sample_handler.c | 10 ++++++ kernel/arch/x86/time/i8254.c | 37 ++++++++++++++++++++++ kernel/arch/x86/tty/tty.c | 19 +++++++++++- kernel/kernel.c | 11 ++++--- 7 files changed, 121 insertions(+), 6 deletions(-) create mode 100644 include/arch/x86/time/i8254.h create mode 100644 kernel/arch/x86/time/i8254.c diff --git a/Makefile b/Makefile index f5a33b6..5373d0b 100644 --- a/Makefile +++ b/Makefile @@ -6,7 +6,7 @@ export GIT_REV = $(shell git describe --long HEAD) CFLAGS = -std=gnu99 -g -Iinclude/arch/x86 -ffreestanding -DGIT_COMMIT=\"$(GIT_REV)\" -KERNEL_OBJ = kernel/entry.o kernel/arch/x86/tty/tty.o kernel/drivers/irq/i8259a.o kernel/arch/x86/irq/idt.o kernel/arch/x86/irq/sample_handler.o kernel/kernel.o +KERNEL_OBJ = kernel/entry.o kernel/arch/x86/tty/tty.o kernel/drivers/irq/i8259a.o kernel/arch/x86/irq/idt.o kernel/arch/x86/irq/sample_handler.o kernel/arch/x86/time/i8254.o kernel/kernel.o BOOTLOADER_OBJ = boot/x86/mbr boot/x86/vbr-fat32 boot/x86/stage3/LOADER.BIN default: kernel/kernel.elf bootloader diff --git a/include/arch/x86/time/i8254.h b/include/arch/x86/time/i8254.h new file mode 100644 index 0000000..18a8546 --- /dev/null +++ b/include/arch/x86/time/i8254.h @@ -0,0 +1,46 @@ +#ifndef X86_I8254_H +#define X86_I8254_H + +#include <stdint.h> +#include <io.h> +#include <irq/i8259a.h> + +/* for setting reload value and reading current count */ +#define PORT_CHANNEL_0_DATA 0x40 +#define PORT_CHANNEL_1_DATA 0x41 +#define PORT_CHANNEL_2_DATA 0x42 + +/* for configuring the channels */ +#define PORT_MODE_CMD_REG 0x43 + +/* operating modes */ +#define INT_ON_TERM_CNT 0x0 +#define HW_RETRIGGER_ONESHOT 0x1 +#define RATE_GENERATOR 0x2 +#define SQUARE_WAVE_GENERATOR 0x3 +#define SW_TRIGGERED_STROBE 0x4 +#define HW_TRIGGERED_STROBE 0x5 + +/* access modes */ +#define ACMODE_COUNTER_LATCH_CMD 0x0 +#define ACMODE_LSB_ONLY 0x1 +#define ACMODE_MSB_ONLY 0x2 +#define ACMODE_LSB_MSB 0x3 + +union mode_command { + struct { + unsigned bcd: 1, /* if set, uses BCD for reload value */ + opmode: 3, /* operating mode */ + acmode: 2, /* access mode */ + channel: 2; /* channel to configure */ + } fields; + uint8_t command; +}; + +extern unsigned int ticks; +extern uint16_t reload; + +extern void i8254_configure_channel(char channel, char opmode, uint16_t new_reload); +extern void i8254_irq_enable(); + +#endif diff --git a/include/arch/x86/tty.h b/include/arch/x86/tty.h index 42857ac..d1d3171 100644 --- a/include/arch/x86/tty.h +++ b/include/arch/x86/tty.h @@ -22,8 +22,10 @@ extern void screen_clear(void); extern void kprint(const char *string, uint8_t color); +extern void kprints(const char *string, uint8_t color); extern void kprintb(const uint8_t byte); extern void kprintw(const uint16_t word); extern void kprintd(const uint32_t dword); +extern void kprintc(const char character, uint8_t color); extern int kprintdec(uint32_t num); #endif diff --git a/kernel/arch/x86/irq/sample_handler.c b/kernel/arch/x86/irq/sample_handler.c index f4f4a4b..be68298 100644 --- a/kernel/arch/x86/irq/sample_handler.c +++ b/kernel/arch/x86/irq/sample_handler.c @@ -2,6 +2,9 @@ #include <irq/i8259a.h> #include <io.h> #include <stdint.h> +#include <time/i8254.h> + +unsigned int ticks = 0; typedef uint32_t uword_t; @@ -31,3 +34,10 @@ halt: goto halt; } + +__attribute__((interrupt)) +void timer_tick(struct interrupt_frame *frame) +{ + pic_send_eoi(0); + ticks++; +} diff --git a/kernel/arch/x86/time/i8254.c b/kernel/arch/x86/time/i8254.c new file mode 100644 index 0000000..d205f11 --- /dev/null +++ b/kernel/arch/x86/time/i8254.c @@ -0,0 +1,37 @@ +#include <time/i8254.h> + +uint16_t reload; + +void i8254_configure_channel(char channel, char opmode, uint16_t new_reload) +{ + union mode_command command; + + command.fields.bcd = 0; + command.fields.channel = channel; + command.fields.acmode = ACMODE_LSB_MSB; + command.fields.opmode = opmode; + + outb(PORT_MODE_CMD_REG, command.command); + + switch (channel) { + case 0: + outb(PORT_CHANNEL_0_DATA, new_reload & 0xff); + outb(PORT_CHANNEL_0_DATA, new_reload >> 8); + break; + case 1: + outb(PORT_CHANNEL_1_DATA, new_reload & 0xff); + outb(PORT_CHANNEL_1_DATA, new_reload >> 8); + break; + case 2: + outb(PORT_CHANNEL_2_DATA, new_reload & 0xff); + outb(PORT_CHANNEL_2_DATA, new_reload >> 8); + break; + } + + reload = new_reload; +} + +void i8254_irq_enable() +{ + pic_unmask(0); +} diff --git a/kernel/arch/x86/tty/tty.c b/kernel/arch/x86/tty/tty.c index 51686a3..83678b0 100644 --- a/kernel/arch/x86/tty/tty.c +++ b/kernel/arch/x86/tty/tty.c @@ -1,6 +1,7 @@ #include <io.h> #include <tty.h> #include <stdint.h> +#include <time/i8254.h> #define VGA_WIDTH 80 #define VGA_HEIGHT 25 @@ -41,6 +42,14 @@ void scroll_up(void) } void kprint(const char *string, uint8_t color) +{ + kprintc('[', 0); + kprintdec(ticks / (1193182 / 65536)); + kprints("] ", 0); + kprints(string, color); +} + +void kprints(const char *string, uint8_t color) { char next_char; uint8_t vga_misc_output; @@ -142,11 +151,18 @@ int kprintdec(uint32_t num) { char buffer[11]; int digits = 10; + /* TODO: make an actual memset function to use instead of this */ for (int i=0; i<11; i++) { buffer[i] = 0; } + /* handle edge case */ + if (num==0) { + *buffer = '0'; + goto print; + } + /* put the numbers in the buffer */ for (int i=9; i>0 && num>0; i--) { uint8_t currdigit = num%10; @@ -169,6 +185,7 @@ int kprintdec(uint32_t num) } } - kprint(buffer, 0); +print: + kprints(buffer, 0); return digits; } diff --git a/kernel/kernel.c b/kernel/kernel.c index 24deeb9..35100f1 100644 --- a/kernel/kernel.c +++ b/kernel/kernel.c @@ -2,21 +2,25 @@ #include <io.h> #include <irq/idt.h> #include <irq/i8259a.h> +#include <time/i8254.h> #include <stdint.h> extern void double_fault(struct abort_frame *frame); extern void keyb_handler(struct interrupt_frame *frame); +extern void timer_tick(struct interrupt_frame *frame); struct idt_descriptor idt[256] __attribute__((aligned(0x10))); struct idtr idtr __attribute__((aligned(0x10))); void kmain(void) { + ticks = 0; screen_clear(); kprint("Welcome to Nameless OS!\nRunning revision: ", 0); kprint(GIT_COMMIT, 0); kprint("\n", 0); kprint("Preparing IDT...\n", 0); idt_set_descriptor(idt, 0x8, 0x8, (uint32_t) double_fault, IDT_TRAP_GATE, 0x0); + idt_set_descriptor(idt, 0x20, 0x8, (uint32_t) timer_tick, IDT_INTERRUPT_GATE, 0x0); idt_set_descriptor(idt, 0x21, 0x8, (uint32_t) keyb_handler, IDT_INTERRUPT_GATE, 0x0); kprint("IDT prepared, loading...\n", 0); populate_idtr(&idtr, idt); @@ -26,10 +30,9 @@ void kmain(void) pic_mask_all(); pic_unmask(1); asm volatile ("sti"); + kprint("Setting up timer...\n", 0); + i8254_configure_channel(0, SQUARE_WAVE_GENERATOR, 0); + i8254_irq_enable(); kprint("All done\n", 0); - kprintdec(12345); - kprintc('\n'); - kprintdec(6789); - kprintc('\n'); while(1); } -- 2.39.5