aboutsummaryrefslogtreecommitdiff
path: root/kernel/drivers
diff options
context:
space:
mode:
authorMarvin Borner2020-08-09 16:51:01 +0200
committerMarvin Borner2020-08-09 16:51:01 +0200
commit162d024a53e1e31e00ff0b6f47dd4590edebc551 (patch)
tree711d3886c300dfaddffdafaa89b690b45eb2101d /kernel/drivers
parent79f2fa136f26a0b87917336e089485712ee49bd6 (diff)
Heavy restructuring of libc, kernel and apps
Diffstat (limited to 'kernel/drivers')
-rw-r--r--kernel/drivers/cpu.c69
-rw-r--r--kernel/drivers/ide.c41
-rw-r--r--kernel/drivers/interrupts.asm142
-rw-r--r--kernel/drivers/interrupts.c231
-rw-r--r--kernel/drivers/keyboard.c68
-rw-r--r--kernel/drivers/serial.c32
-rw-r--r--kernel/drivers/timer.c39
-rw-r--r--kernel/drivers/vesa.c40
8 files changed, 662 insertions, 0 deletions
diff --git a/kernel/drivers/cpu.c b/kernel/drivers/cpu.c
new file mode 100644
index 0000000..5c27c51
--- /dev/null
+++ b/kernel/drivers/cpu.c
@@ -0,0 +1,69 @@
+// MIT License, Copyright (c) 2020 Marvin Borner
+// This file is a wrapper around some CPU asm calls
+
+#include <def.h>
+
+u8 inb(u16 port)
+{
+ u8 value;
+ __asm__ volatile("inb %1, %0" : "=a"(value) : "Nd"(port));
+ return value;
+}
+
+u16 inw(u16 port)
+{
+ u16 value;
+ __asm__ volatile("inw %1, %0" : "=a"(value) : "Nd"(port));
+ return value;
+}
+
+u32 inl(u16 port)
+{
+ u32 value;
+ __asm__ volatile("inl %1, %0" : "=a"(value) : "Nd"(port));
+ return value;
+}
+
+void insl(u16 port, void *addr, int n)
+{
+ __asm__ volatile("cld; rep insl"
+ : "=D"(addr), "=c"(n)
+ : "d"(port), "0"(addr), "1"(n)
+ : "memory", "cc");
+}
+
+void outb(u16 port, u8 data)
+{
+ __asm__ volatile("outb %0, %1" ::"a"(data), "Nd"(port));
+}
+
+void outw(u16 port, u16 data)
+{
+ __asm__ volatile("outw %0, %1" ::"a"(data), "Nd"(port));
+}
+
+void outl(u16 port, u32 data)
+{
+ __asm__ volatile("outl %0, %1" ::"a"(data), "Nd"(port));
+}
+
+void cli()
+{
+ __asm__ volatile("cli");
+}
+
+void sti()
+{
+ __asm__ volatile("sti");
+}
+
+void hlt()
+{
+ __asm__ volatile("hlt");
+}
+
+void idle()
+{
+ while (1)
+ hlt();
+}
diff --git a/kernel/drivers/ide.c b/kernel/drivers/ide.c
new file mode 100644
index 0000000..fe3955c
--- /dev/null
+++ b/kernel/drivers/ide.c
@@ -0,0 +1,41 @@
+// MIT License, Copyright (c) 2020 Marvin Borner
+
+#include <cpu.h>
+#include <def.h>
+#include <ide.h>
+
+int ide_wait(int check)
+{
+ char r;
+
+ // Wait while drive is busy. Once just ready is set, exit the loop
+ while (((r = (char)inb(IDE_IO | IDE_CMD)) & (IDE_BUSY | IDE_READY)) != IDE_READY)
+ ;
+
+ // Check for errors
+ if (check && (r & (IDE_DRIVE_FAULT | IDE_ERROR)) != 0)
+ return 0xF;
+ return 0;
+}
+
+void *ide_read(void *b, u32 block)
+{
+ int sector_per_block = BLOCK_SIZE / SECTOR_SIZE; // 2
+ int sector = block * sector_per_block;
+
+ ide_wait(0);
+ outb(IDE_IO | IDE_SECTOR_COUNT, sector_per_block); // Number of sectors
+ outb(IDE_IO | IDE_LOW, LBA_LOW(sector));
+ outb(IDE_IO | IDE_MID, LBA_MID(sector));
+ outb(IDE_IO | IDE_HIGH, LBA_HIGH(sector));
+
+ // Slave/Master << 4 and last 4 bits
+ outb(IDE_IO | IDE_HEAD, 0xE0 | (1 << 4) | LBA_LAST(sector));
+ outb(IDE_IO | IDE_CMD, IDE_CMD_READ);
+ ide_wait(0);
+
+ // Read-only
+ insl(IDE_IO, b, BLOCK_SIZE / 4);
+
+ return b;
+}
diff --git a/kernel/drivers/interrupts.asm b/kernel/drivers/interrupts.asm
new file mode 100644
index 0000000..59c323c
--- /dev/null
+++ b/kernel/drivers/interrupts.asm
@@ -0,0 +1,142 @@
+; MIT License, Copyright (c) 2020 Marvin Borner
+
+; IRQ
+
+%macro IRQ 2
+ global irq%1
+ irq%1:
+ cli
+ push byte 0
+ push byte %2
+ jmp irq_common_stub
+%endmacro
+
+IRQ 0, 32
+IRQ 1, 33
+IRQ 2, 34
+IRQ 3, 35
+IRQ 4, 36
+IRQ 5, 37
+IRQ 6, 38
+IRQ 7, 39
+IRQ 8, 40
+IRQ 9, 41
+IRQ 10, 42
+IRQ 11, 43
+IRQ 12, 44
+IRQ 13, 45
+IRQ 14, 46
+IRQ 15, 47
+
+extern irq_handler
+irq_common_stub:
+ pusha
+
+ push ds
+ push es
+ push fs
+ push gs
+
+ mov ax, 0x10
+ mov ds, ax
+ mov es, ax
+ mov fs, ax
+ mov gs, ax
+ cld
+
+ push esp
+ call irq_handler
+ add esp, 4
+
+ pop gs
+ pop fs
+ pop es
+ pop ds
+ popa
+
+ add esp, 8
+ sti
+ iret
+
+; ISR
+
+%macro ISR_NOERRCODE 1
+ global isr%1
+ isr%1:
+ cli
+ push byte 0
+ push %1
+ jmp isr_common_stub
+%endmacro
+
+%macro ISR_ERRCODE 1
+ global isr%1
+ isr%1:
+ cli
+ push byte %1
+ jmp isr_common_stub
+%endmacro
+
+ISR_NOERRCODE 0
+ISR_NOERRCODE 1
+ISR_NOERRCODE 2
+ISR_NOERRCODE 3
+ISR_NOERRCODE 4
+ISR_NOERRCODE 5
+ISR_NOERRCODE 6
+ISR_NOERRCODE 7
+ISR_ERRCODE 8
+ISR_NOERRCODE 9
+ISR_ERRCODE 10
+ISR_ERRCODE 11
+ISR_ERRCODE 12
+ISR_ERRCODE 13
+ISR_ERRCODE 14
+ISR_NOERRCODE 15
+ISR_NOERRCODE 16
+ISR_NOERRCODE 17
+ISR_NOERRCODE 18
+ISR_NOERRCODE 19
+ISR_NOERRCODE 20
+ISR_NOERRCODE 21
+ISR_NOERRCODE 22
+ISR_NOERRCODE 23
+ISR_NOERRCODE 24
+ISR_NOERRCODE 25
+ISR_NOERRCODE 26
+ISR_NOERRCODE 27
+ISR_NOERRCODE 28
+ISR_NOERRCODE 29
+ISR_NOERRCODE 30
+ISR_NOERRCODE 31
+ISR_NOERRCODE 128
+
+extern isr_handler
+isr_common_stub:
+ pusha
+
+ push ds
+ push es
+ push fs
+ push gs
+
+ mov ax, 0x10
+ mov ds, ax
+ mov es, ax
+ mov fs, ax
+ mov gs, ax
+ cld
+
+ push esp
+ call isr_handler
+ add esp, 4
+
+ pop gs
+ pop fs
+ pop es
+ pop ds
+ popa
+
+ add esp, 8
+ sti
+ iret
diff --git a/kernel/drivers/interrupts.c b/kernel/drivers/interrupts.c
new file mode 100644
index 0000000..0b94208
--- /dev/null
+++ b/kernel/drivers/interrupts.c
@@ -0,0 +1,231 @@
+// MIT License, Copyright (c) 2020 Marvin Borner
+// TODO: Remove some magic numbers
+
+#include <cpu.h>
+#include <def.h>
+#include <interrupts.h>
+#include <mem.h>
+#include <print.h>
+#include <serial.h>
+
+/**
+ * IDT
+ */
+
+void idt_set_gate(u8 num, u32 base, u16 sel, u8 flags)
+{
+ // Specify the interrupt routine's base address
+ idt[num].base_low = (u16)(base & 0xFFFF);
+ idt[num].base_high = (u16)((base >> 16) & 0xFFFF);
+
+ // Set selector/segment of IDT entry
+ idt[num].sel = sel;
+ idt[num].always0 = 0;
+ idt[num].flags = (u8)(flags | 0x60);
+}
+
+// Install IDT
+void idt_install()
+{
+ // Set IDT pointer and limit
+ idt_ptr.limit = (sizeof(struct idt_entry) * 256) - 1;
+ idt_ptr.base = &idt;
+
+ // Clear IDT by setting memory cells to 0
+ memset(&idt, 0, sizeof(struct idt_entry) * 256);
+
+ __asm__("lidt %0" : : "m"(idt_ptr));
+}
+
+/**
+ * IRQ
+ */
+
+void (*irq_routines[16])(struct regs *) = { 0 };
+
+// Install IRQ handler
+void irq_install_handler(int irq, void (*handler)(struct regs *r))
+{
+ irq_routines[irq] = handler;
+}
+
+// Remove IRQ handler
+void irq_uninstall_handler(int irq)
+{
+ irq_routines[irq] = 0;
+}
+
+// Remap the IRQ table
+void irq_remap()
+{
+ outb(0x20, 0x11);
+ outb(0xA0, 0x11);
+ outb(0x21, 0x20);
+ outb(0xA1, 0x28);
+ outb(0x21, 0x04);
+ outb(0xA1, 0x02);
+ outb(0x21, 0x01);
+ outb(0xA1, 0x01);
+ outb(0x21, 0x00);
+ outb(0xA1, 0x00);
+}
+
+// Handle IRQ ISRs
+void irq_handler(struct regs *r)
+{
+ void (*handler)(struct regs * r);
+
+ // Execute custom handler if exists
+ handler = irq_routines[r->int_no - 32];
+ if (handler)
+ handler(r);
+
+ // Send EOI to second (slave) PIC
+ if (r->int_no >= 40)
+ outb(0xA0, 0x20);
+
+ // Send EOI to master PIC
+ outb(0x20, 0x20);
+}
+
+// Map ISRs to the correct entries in the IDT
+void irq_install()
+{
+ irq_remap();
+
+ idt_set_gate(32, (u32)irq0, 0x08, 0x8E);
+ idt_set_gate(33, (u32)irq1, 0x08, 0x8E);
+ idt_set_gate(34, (u32)irq2, 0x08, 0x8E);
+ idt_set_gate(35, (u32)irq3, 0x08, 0x8E);
+ idt_set_gate(36, (u32)irq4, 0x08, 0x8E);
+ idt_set_gate(37, (u32)irq5, 0x08, 0x8E);
+ idt_set_gate(38, (u32)irq6, 0x08, 0x8E);
+ idt_set_gate(39, (u32)irq7, 0x08, 0x8E);
+
+ idt_set_gate(40, (u32)irq8, 0x08, 0x8E);
+ idt_set_gate(41, (u32)irq9, 0x08, 0x8E);
+ idt_set_gate(42, (u32)irq10, 0x08, 0x8E);
+ idt_set_gate(43, (u32)irq11, 0x08, 0x8E);
+ idt_set_gate(44, (u32)irq12, 0x08, 0x8E);
+ idt_set_gate(45, (u32)irq13, 0x08, 0x8E);
+ idt_set_gate(46, (u32)irq14, 0x08, 0x8E);
+ idt_set_gate(47, (u32)irq15, 0x08, 0x8E);
+}
+
+/**
+ * ISR
+ */
+
+void (*isr_routines[256])(struct regs *) = { 0 };
+
+const char *isr_exceptions[32] = { "Division By Zero",
+ "Debug",
+ "Non Maskable Interrupt",
+ "Breakpoint",
+ "Into Detected Overflow",
+ "Out of Bounds",
+ "Invalid Opcode",
+ "No Coprocessor",
+
+ "Double Fault",
+ "Coprocessor Segment Overrun",
+ "Bad TSS",
+ "Segment Not Present",
+ "Stack Fault",
+ "General Protection Fault",
+ "Page Fault",
+ "Unknown Interrupt",
+
+ "Coprocessor Fault",
+ "Alignment Check",
+ "Machine Check",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved" };
+
+void isr_install_handler(int isr, void (*handler)(struct regs *r))
+{
+ isr_routines[isr] = handler;
+}
+
+void isr_uninstall_handler(int isr)
+{
+ isr_routines[isr] = 0;
+}
+
+void isr_handler(struct regs *r)
+{
+ void (*handler)(struct regs * r);
+
+ // Execute fault handler if exists
+ handler = isr_routines[r->int_no];
+ if (handler) {
+ handler(r);
+ } else if (r->int_no <= 32) {
+ cli();
+ printf("\n%s Exception, halting!\n", isr_exceptions[r->int_no]);
+ printf("Error code: %d\n", r->err_code);
+ while (1) {
+ };
+ }
+}
+
+void isr_install()
+{
+ idt_set_gate(0, (u32)isr0, 0x08, 0x8E);
+ idt_set_gate(1, (u32)isr1, 0x08, 0x8E);
+ idt_set_gate(2, (u32)isr2, 0x08, 0x8E);
+ idt_set_gate(3, (u32)isr3, 0x08, 0x8E);
+ idt_set_gate(4, (u32)isr4, 0x08, 0x8E);
+ idt_set_gate(5, (u32)isr5, 0x08, 0x8E);
+ idt_set_gate(6, (u32)isr6, 0x08, 0x8E);
+ idt_set_gate(7, (u32)isr7, 0x08, 0x8E);
+
+ idt_set_gate(8, (u32)isr8, 0x08, 0x8E);
+ idt_set_gate(9, (u32)isr9, 0x08, 0x8E);
+ idt_set_gate(10, (u32)isr10, 0x08, 0x8E);
+ idt_set_gate(11, (u32)isr11, 0x08, 0x8E);
+ idt_set_gate(12, (u32)isr12, 0x08, 0x8E);
+ idt_set_gate(13, (u32)isr13, 0x08, 0x8E);
+ idt_set_gate(14, (u32)isr14, 0x08, 0x8E);
+ idt_set_gate(15, (u32)isr15, 0x08, 0x8E);
+
+ idt_set_gate(16, (u32)isr16, 0x08, 0x8E);
+ idt_set_gate(17, (u32)isr17, 0x08, 0x8E);
+ idt_set_gate(18, (u32)isr18, 0x08, 0x8E);
+ idt_set_gate(19, (u32)isr19, 0x08, 0x8E);
+ idt_set_gate(20, (u32)isr20, 0x08, 0x8E);
+ idt_set_gate(21, (u32)isr21, 0x08, 0x8E);
+ idt_set_gate(22, (u32)isr22, 0x08, 0x8E);
+ idt_set_gate(23, (u32)isr23, 0x08, 0x8E);
+
+ idt_set_gate(24, (u32)isr24, 0x08, 0x8E);
+ idt_set_gate(25, (u32)isr25, 0x08, 0x8E);
+ idt_set_gate(26, (u32)isr26, 0x08, 0x8E);
+ idt_set_gate(27, (u32)isr27, 0x08, 0x8E);
+ idt_set_gate(28, (u32)isr28, 0x08, 0x8E);
+ idt_set_gate(29, (u32)isr29, 0x08, 0x8E);
+ idt_set_gate(30, (u32)isr30, 0x08, 0x8E);
+ idt_set_gate(31, (u32)isr31, 0x08, 0x8E);
+}
+
+/**
+ * Combined
+ */
+void interrupts_install()
+{
+ idt_install();
+ isr_install();
+ irq_install();
+}
diff --git a/kernel/drivers/keyboard.c b/kernel/drivers/keyboard.c
new file mode 100644
index 0000000..38ff1f7
--- /dev/null
+++ b/kernel/drivers/keyboard.c
@@ -0,0 +1,68 @@
+#include <cpu.h>
+#include <def.h>
+#include <gui.h>
+#include <interrupts.h>
+
+char keymap[128];
+
+// TODO: Use keyboard as event and move logic to other file
+void keyboard_handler()
+{
+ u8 scan_code = inb(0x60);
+
+ if (scan_code > 128)
+ return;
+
+ if ((scan_code & 0x80) == 0) { // PRESS
+ gui_term_write_char(keymap[scan_code]);
+ }
+}
+
+void keyboard_acknowledge()
+{
+ while (inb(0x60) != 0xfa)
+ ;
+}
+
+void keyboard_rate()
+{
+ outb(0x60, 0xF3);
+ keyboard_acknowledge();
+ outb(0x60, 0x0); // Rate{00000} Delay{00} 0
+}
+
+void keyboard_install()
+{
+ //keyboard_rate(); TODO: Fix keyboard rate?
+ irq_install_handler(1, keyboard_handler);
+}
+
+char keymap[128] = {
+ 0, 27, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=',
+ '\b', '\t', 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']',
+ '\n', 17, 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', '\'', '`',
+ 14, '\\', 'z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '/', 14, '*',
+ 0, // Alt key
+ ' ', // Space bar
+ 15, // Caps lock
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // F keys
+ 0, // Num lock
+ 0, // Scroll lock
+ 0, // Home key
+ 0, // Up arrow
+ 0, // Page up
+ '-',
+ 0, // Left arrow
+ 0,
+ 0, // Right arrow
+ '+',
+ 0, // End key
+ 0, // Down arrow
+ 0, // Page down
+ 0, // Insert key
+ 0, // Delete key
+ 0, 0, 0,
+ 0, // F11
+ 0, // F12
+ 0, // Other keys
+};
diff --git a/kernel/drivers/serial.c b/kernel/drivers/serial.c
new file mode 100644
index 0000000..dcee4dd
--- /dev/null
+++ b/kernel/drivers/serial.c
@@ -0,0 +1,32 @@
+#include <cpu.h>
+#include <def.h>
+#include <str.h>
+
+void serial_install()
+{
+ outb(0x3f8 + 1, 0x00);
+ outb(0x3f8 + 3, 0x80);
+ outb(0x3f8 + 0, 0x03);
+ outb(0x3f8 + 1, 0x00);
+ outb(0x3f8 + 3, 0x03);
+ outb(0x3f8 + 2, 0xC7);
+ outb(0x3f8 + 4, 0x0B);
+}
+
+int is_transmit_empty()
+{
+ return inb(0x3f8 + 5) & 0x20;
+}
+
+void serial_put(char ch)
+{
+ while (is_transmit_empty() == 0)
+ ;
+ outb(0x3f8, (u8)ch);
+}
+
+void serial_print(const char *data)
+{
+ for (u32 i = 0; i < strlen(data); i++)
+ serial_put(data[i]);
+}
diff --git a/kernel/drivers/timer.c b/kernel/drivers/timer.c
new file mode 100644
index 0000000..a3b4137
--- /dev/null
+++ b/kernel/drivers/timer.c
@@ -0,0 +1,39 @@
+// MIT License, Copyright (c) 2020 Marvin Borner
+
+#include <cpu.h>
+#include <def.h>
+#include <interrupts.h>
+
+static u32 timer_ticks = 0;
+
+void timer_phase(int hz)
+{
+ int divisor = 3579545 / 3 / hz;
+ outb(0x43, 0x36); // 01 10 11 0b // CTR, RW, MODE, BCD
+ outb(0x40, divisor & 0xFF);
+ outb(0x40, divisor >> 8);
+}
+
+// Executed 1000 times per second
+void timer_handler()
+{
+ timer_ticks++;
+}
+
+// "Delay" function with CPU sleep
+void timer_wait(u32 ticks)
+{
+ u32 eticks;
+
+ eticks = timer_ticks + ticks;
+ while (timer_ticks < eticks) {
+ __asm__("sti//hlt//cli");
+ }
+}
+
+// Install timer handler into IRQ0
+void timer_install()
+{
+ timer_phase(1000);
+ irq_install_handler(0, timer_handler);
+}
diff --git a/kernel/drivers/vesa.c b/kernel/drivers/vesa.c
new file mode 100644
index 0000000..9402664
--- /dev/null
+++ b/kernel/drivers/vesa.c
@@ -0,0 +1,40 @@
+#include <def.h>
+#include <vesa.h>
+
+void vesa_draw_rectangle(int x1, int y1, int x2, int y2, const u32 color[3])
+{
+ int pos1 = x1 * vbe_bpl + y1 * vbe_pitch;
+ u8 *draw = &fb[pos1];
+ for (int i = 0; i <= y2 - y1; i++) {
+ for (int j = 0; j <= x2 - x1; j++) {
+ draw[vbe_bpl * j] = color[2];
+ draw[vbe_bpl * j + 1] = color[1];
+ draw[vbe_bpl * j + 2] = color[0];
+ }
+ draw += vbe_pitch;
+ }
+}
+
+void vesa_set_pixel(u16 x, u16 y, const u32 color[3])
+{
+ u8 pos = x * vbe_bpl + y * vbe_pitch;
+ u8 *draw = &fb[pos];
+ draw[pos] = (char)color[2];
+ draw[pos + 1] = (char)color[1];
+ draw[pos + 2] = (char)color[0];
+}
+
+void vesa_fill(const u32 color[3])
+{
+ vesa_draw_rectangle(0, 0, vbe->width - 1, vbe->height - 1, color);
+}
+
+void vesa_init(struct vbe *info)
+{
+ vbe = info;
+ vbe_height = vbe->height;
+ vbe_width = vbe->width;
+ vbe_bpl = vbe->bpp >> 3;
+ vbe_pitch = vbe->pitch;
+ fb = (u8 *)vbe->framebuffer;
+}