Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 1 addition & 9 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
.PHONY: docker-build docker-run docker-run-kernel docker-test test clean
.PHONY: docker-build docker-run docker-run-kernel clean

DEBUG ?= 0

Expand Down Expand Up @@ -40,13 +40,5 @@ docker-run-debug: docker-build
run: docker-build
qemu-system-i386 -m 256M -cdrom build/exodoom.iso -no-reboot -serial stdio

#TODO: Add proper testing pipeline with CUnit
test:
@mkdir -p build
gcc -std=c99 -Wall -Wextra -fno-builtin -o build/test_string tests/test_string.c src/string.c
./build/test_string
gcc -std=c99 -Wall -Wextra -fno-builtin -o build/test_ctype tests/test_ctype.c src/ctype.c
./build/test_ctype

clean:
rm -rf build
7 changes: 7 additions & 0 deletions docker/scripts/build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,13 @@ for c in src/*.c; do
objs+=("$o")
done

#assemble isr.s
if [ -f src/isr.s ]; then
echo " AS isr.s"
i686-elf-as src/isr.s -o build/isr.o
objs+=(build/isr.o)
fi

echo "[3/6] Link kernel -> build/exodoom"
i686-elf-gcc "${LDFLAGS[@]}" -o build/exodoom \
"${objs[@]}" -lgcc
Expand Down
15 changes: 7 additions & 8 deletions src/boot.s
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
.set ALIGN, 1<<0
.set MEMINFO, 1<<1
.set VIDEO, 1<<2
.set AOUT_KLUDGE, 1<<16
.set FLAGS, ALIGN | MEMINFO | VIDEO | AOUT_KLUDGE
.set FLAGS, ALIGN | MEMINFO | VIDEO
.set MAGIC, 0x1BADB002
.set CHECKSUM, -(MAGIC + FLAGS)

Expand All @@ -14,12 +13,12 @@ multiboot_header:
.long FLAGS
.long CHECKSUM

/* a.out kludge fields */
.long multiboot_header /* header_addr */
.long _load_start /* load_addr */
.long _load_end /* load_end_addr */
.long _bss_end /* bss_end_addr */
.long _start /* entry_addr */
/* reserved (offsets 12-28 unused without AOUT_KLUDGE, kept to preserve VIDEO field offsets) */
.long 0
.long 0
.long 0
.long 0
.long 0

/* graphics fields */
.long 0 /* mode_type: 0=linear graphics */
Expand Down
51 changes: 51 additions & 0 deletions src/idt.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
#include "idt.h"
#include "io.h"

#define IDT_ENTRIES 256

struct idt_entry{
uint16_t baseLow;
uint16_t sel;
uint8_t always0;
uint8_t flags;
uint16_t baseHigh;
} __attribute__((packed));

struct idt_ptr{
uint16_t limit;
uint32_t base;
} __attribute__((packed));


static struct idt_entry idt[IDT_ENTRIES];
static struct idt_ptr idtp;
static uint16_t kernel_cs;

extern void idt_load(uint32_t);
extern void default_stub(void);

void idt_init() {
/* Read the actual code segment selector GRUB gave us rather than
assuming 0x08 — a mismatch causes iret to GP-fault on the way out
of any interrupt handler. */
__asm__ volatile ("mov %%cs, %0" : "=r"(kernel_cs));

idtp.limit = sizeof(struct idt_entry) * IDT_ENTRIES - 1;
idtp.base = (uint32_t)&idt;

/* Fill every entry with a safe default so any stray vector irets
cleanly instead of cascading into a triple fault. */
for (int i = 0; i < IDT_ENTRIES; i++)
idt_set_gate(i, (uint32_t)default_stub);

idt_load((uint32_t)&idtp);
}


void idt_set_gate(int n, uint32_t handler) {
idt[n].baseLow = handler & 0xFFFF;
idt[n].baseHigh = (handler >> 16) & 0xFFFF;
idt[n].sel = kernel_cs;
idt[n].always0 = 0;
idt[n].flags = 0x8E;
}
6 changes: 6 additions & 0 deletions src/idt.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#pragma once
#include <stdint.h>

void idt_init();
void idt_set_gate(int n, uint32_t handler);

21 changes: 21 additions & 0 deletions src/isr.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
.global idt_load
idt_load:
mov 4(%esp), %eax
lidt (%eax)
ret

/* Default handler for unregistered vectors — just return silently.
No EOI is sent here; this stub covers CPU exception vectors (0-31)
where EOI is not appropriate. Hardware IRQs should have dedicated stubs. */
.global default_stub
default_stub:
iret

.global irq0_stub
.extern irq0_handler

irq0_stub:
pusha
call irq0_handler
popa
iret
65 changes: 62 additions & 3 deletions src/kernel.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,17 @@
#include "memory.h"
#include "mmap.h"

//IDT and Interrupt includes
#include "idt.h"
#include "pic.h"
#include "pit.h"
#include "sleep.h"
#include "fb.h"
#include "fb_console.h"

//IRQ0 stub from assembly
extern void irq0_stub();

static inline void qemu_exit(uint32_t code) {
__asm__ volatile ("outl %0, %1" : : "a"(code), "Nd"(0xF4));
}
Expand All @@ -15,14 +26,62 @@ void kernel_main(uint32_t mb_info_addr) {
serial_print("Kernel Booted\n");

mmap_init(mb);

memory_init();

serial_print("Memory subsystem initialized\n");
serial_print("Allocator base: ");
serial_print_hex(memory_base_address());
serial_print("\n");

serial_flush();

idt_init();
pic_remap();

//IRQ0 vector 32
idt_set_gate(32, (uint32_t)irq0_stub);
pit_init(1000); //1000hz
serial_print("Timer Initialized\n");

__asm__ volatile ("sti");

serial_print("Interrupts Enabled\n");

//Sleep test case
serial_print("Sleeping for 1 second...\n");
kernel_sleep_ms(1000);
serial_print("Done sleeping!\n");

//Print monotonic ms counter for 5 seconds then exit
uint32_t prints = 0;
while (prints < 5) {
if (pit_take_print_pending()) {
serial_print("ms: ");
serial_print_u32(kernel_get_ticks_ms());
serial_print("\n");
prints++;
}
__asm__ volatile ("hlt");
}

qemu_exit(0);

if (!(mb->flags & MULTIBOOT_INFO_FLAG_FRAMEBUFFER)) for(;;);

framebuffer_t fb;
if (!fb_init_bgrx8888(&fb,
(uintptr_t)mb->framebuffer_addr,
mb->framebuffer_pitch,
mb->framebuffer_width,
mb->framebuffer_height,
mb->framebuffer_bpp)) {
for(;;);
}

fb_console_t con;
if (!fbcon_init(&con, &fb)) for(;;);

fbcon_set_color(&con, 255,255,255, 0,0,0);
fbcon_write(&con, "ExoDoom fb console online.\n");
fbcon_write(&con, "Now printing to pixels like a proper gremlin.\n\n");
fbcon_write(&con, "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ\n");
fbcon_write(&con, "abcdefghijklmnopqrstuvwxyz !@#$%^&*()[]{}\n");
}
46 changes: 46 additions & 0 deletions src/pic.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
#include "pic.h"
#include "io.h"

#define PIC1 0x20
#define PIC2 0xA0
#define PIC1_COMMAND PIC1
#define PIC1_DATA (PIC1+1)
#define PIC2_COMMAND PIC2
#define PIC2_DATA (PIC2+1)

// ICW - Initialization Command Word
// Think of this as "Hey PIC, here is how we want interrupts to work".

#define ICW1_INIT 0x10 // "Start init" - Without this, we would likely run into unpredictable situations where PIC stays as GRUB left it leading to critical overlap.
#define ICW1_ICW4 0x01 // Tell the PIC to expect ICW4 during initialization (required for 8086 mode).
#define ICW4_8086 0x01 // "Set operating mode" - puts PIC into 8086/88 mode - ensures interrupts behave correctly with CPU on x86

void pic_remap() {
// Begin initialization
outb(PIC1_COMMAND, ICW1_INIT | ICW1_ICW4); io_wait();
outb(PIC2_COMMAND, ICW1_INIT | ICW1_ICW4); io_wait();

// Set vector offsets
outb(PIC1_DATA, 0x20); io_wait();
outb(PIC2_DATA, 0x28); io_wait();

// Tell master about slave
outb(PIC1_DATA, 4); io_wait();
outb(PIC2_DATA, 2); io_wait();

// Set 8086 mode
outb(PIC1_DATA, ICW4_8086); io_wait();
outb(PIC2_DATA, ICW4_8086); io_wait();

// Mask
outb(PIC1_DATA, 0xFE); io_wait();
outb(PIC2_DATA, 0xFF); io_wait();
}

void pic_send_EOI(unsigned char irq) {
if (irq >= 8){
outb(PIC2_COMMAND, 0x20);
}

outb(PIC1_COMMAND, 0x20);
}
5 changes: 5 additions & 0 deletions src/pic.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#pragma once
#include <stdint.h>

void pic_remap();
void pic_send_EOI(unsigned char irq);
38 changes: 38 additions & 0 deletions src/pit.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#include "pit.h"
#include "io.h"
#include "pic.h"

static volatile uint32_t ticks = 0;
static uint32_t frequency = 1000;
static volatile uint8_t print_pending = 0;

void pit_init(uint32_t hz) {
frequency = hz;
uint32_t divisor = (1193180 + hz / 2) / hz;

outb(0x43, 0x36);

outb(0x40, divisor & 0xFF);
outb(0x40, (divisor >> 8) & 0xFF);
}

uint32_t kernel_get_ticks_ms() {
return (uint64_t)ticks * 1000 / frequency;
}

void irq0_handler() {
ticks++;

if (ticks % frequency == 0) {
print_pending = 1;
}

pic_send_EOI(0);
}

uint8_t pit_take_print_pending() {
if (!print_pending) return 0;

print_pending = 0;
return 1;
}
7 changes: 7 additions & 0 deletions src/pit.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#pragma once
#include <stdint.h>

void pit_init(uint32_t hz);
uint32_t kernel_get_ticks_ms();
uint8_t pit_take_print_pending();

9 changes: 9 additions & 0 deletions src/serial.c
Original file line number Diff line number Diff line change
Expand Up @@ -79,3 +79,12 @@ void serial_putc(char c) {
void serial_print(const char* s) {
while (*s) serial_putc(*s++);
}

void serial_print_u32(uint32_t val) {
char buf[11];
char *p = buf + 10;
*p = '\0';
if (val == 0) { serial_putc('0'); return; }
while (val > 0) { *--p = '0' + (val % 10); val /= 10; }
serial_print(p);
}
1 change: 1 addition & 0 deletions src/serial.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
void serial_init(void);
void serial_putc(char c);
void serial_print(const char* s);
void serial_print_u32(uint32_t val);
void serial_flush(void);
void serial_print_hex(uint32_t num);
void serial_print_hex64(uint64_t num);
Expand Down
11 changes: 11 additions & 0 deletions src/sleep.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#include <stdint.h>
#include "pit.h"
#include "sleep.h"

void kernel_sleep_ms(uint32_t ms) {
uint32_t start = kernel_get_ticks_ms();

while ((kernel_get_ticks_ms() - start) < ms) {
__asm__ volatile ("hlt");
}
}
4 changes: 4 additions & 0 deletions src/sleep.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#pragma once
#include <stdint.h>

void kernel_sleep_ms(uint32_t ms);
Loading