LD = i686-elf-ld
CC = i686-elf-gcc
-KERNEL_OBJ = kernel/entry.o kernel/arch/x86/tty/tty.o kernel/kernel.o
+GIT_REV = $(shell git rev-parse --short HEAD)
+
+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
all: boot.img kernel/kernel.elf
$(AS) -f elf kernel/entry.s -o $@
kernel/arch/x86/tty/tty.o: kernel/arch/x86/tty/tty.c
- $(CC) -g -o $@ -Iinclude/arch/x86 -ffreestanding -c kernel/arch/x86/tty/tty.c
+ $(CC) -std=gnu89 -g -o $@ -Iinclude/arch/x86 -ffreestanding -c kernel/arch/x86/tty/tty.c
+
+kernel/drivers/irq/i8259a.o: kernel/drivers/irq/i8259a.c
+ $(CC) -std=gnu89 -g -o $@ -Iinclude/arch/x86 -ffreestanding -c kernel/drivers/irq/i8259a.c
+
+kernel/arch/x86/irq/idt.o: kernel/arch/x86/irq/idt.c
+ $(CC) -std=gnu89 -g -o $@ -Iinclude/arch/x86 -ffreestanding -c kernel/arch/x86/irq/idt.c
+
+kernel/arch/x86/irq/sample_handler.o: kernel/arch/x86/irq/sample_handler.c
+ $(CC) -std=gnu89 -g -o $@ -Iinclude/arch/x86 -ffreestanding -mgeneral-regs-only -c kernel/arch/x86/irq/sample_handler.c
kernel/kernel.o: kernel/kernel.c
- $(CC) -g -o $@ -Iinclude/arch/x86 -ffreestanding -c kernel/kernel.c
+ $(CC) -std=gnu89 -g -o $@ -Iinclude/arch/x86 -ffreestanding -DGIT_COMMIT=\"$(GIT_REV)\" -c kernel/kernel.c
kernel/kernel.elf: kernel/kernel.bin
$(LD) -o $@ -T kernel/linker.ld ${KERNEL_OBJ} --oformat=elf32-i386
return ret;
}
+static inline void io_wait(void)
+{
+ inb(0x80);
+}
+
#endif
--- /dev/null
+#ifndef X86_I8259A_H
+#define X86_I8259A_H
+
+#define PIC1_COMMAND 0x20
+#define PIC1_DATA 0x21
+#define PIC2_COMMAND 0xA0
+#define PIC2_DATA 0xA1
+
+#define PIC_ICW1_INIT 0x10
+#define PIC_ICW1_ICW4 0x1
+
+#define PIC_ICW4_8086 0x1
+
+#define PIC_EOI 0x20
+
+extern void pic_init(int offset1, int offset2);
+extern void pic_mask(uint8_t irq);
+extern void pic_unmask(uint8_t irq);
+extern void pic_mask_all(void);
+extern void pic_unmask_all(void);
+extern void pic_send_eoi(uint8_t irq);
+
+#endif
--- /dev/null
+#ifndef X86_IDT_H
+#define X86_IDT_H
+
+#include <stdint.h>
+
+#define IDT_VECTOR_COUNT 256 /* our IDT will have 256 vectors */
+#define IDT_DESCRIPTOR_SIZE 8 /* each IDT descriptor is 8 bytes long */
+
+#define IDT_TRAP_GATE 0x1F
+#define IDT_INTERRUPT_GATE 0x0E
+
+struct idt_descriptor {
+ uint16_t offset_1; /* bits 0-15 of offset */
+ uint16_t segsel; /* segment selector */
+ unsigned unused : 8, type : 5, dpl : 2, present : 1;
+ uint16_t offset_2; /* bits 16-31 of offset */
+} __attribute__((packed));
+
+struct idtr {
+ uint16_t limit; /* size of IDT minus 1 */
+ uint32_t base; /* starting address of IDT */
+} __attribute__((packed));
+
+extern void idt_set_descriptor(struct idt_descriptor *idt, uint8_t vector, uint16_t segment, uint32_t offset, uint8_t type, uint8_t dpl);
+extern inline void load_idt(struct idtr idtr);
+extern inline void populate_idtr(struct idtr *idtr, struct idt_descriptor *idt);
+
+#endif
--- /dev/null
+#include <irq/idt.h>
+#include <stdint.h>
+
+/* Note to self: passing a pointer to an asm constraint as "m" will give a pointer to pointer */
+inline void load_idt(struct idtr idtr)
+{
+ asm volatile ("lidt %0": : "m" (idtr));
+}
+
+void idt_set_descriptor(struct idt_descriptor *idt, uint8_t vector, uint16_t segment, uint32_t offset, uint8_t type, uint8_t dpl)
+{
+ struct idt_descriptor *descriptor = &idt[vector];
+
+ descriptor->offset_1 = offset & 0xFFFF;
+ descriptor->segsel = segment;
+ descriptor->unused = 0;
+ descriptor->type = type;
+ descriptor->dpl = dpl;
+ descriptor->present = 1;
+ descriptor->offset_2 = offset >> 16;
+}
+
+inline void populate_idtr(struct idtr *idtr, struct idt_descriptor *idt)
+{
+ idtr->limit = ((IDT_VECTOR_COUNT * IDT_DESCRIPTOR_SIZE) - 1); /* limit must be size - 1 */
+ idtr->base = (uint32_t) idt;
+}
--- /dev/null
+#include <tty.h>
+#include <irq/i8259a.h>
+#include <stdint.h>
+
+typedef uint32_t uword_t;
+
+struct interrupt_frame {
+ uword_t ip;
+ uword_t cs;
+ uword_t flags;
+};
+
+struct abort_frame;
+
+__attribute__((interrupt))
+void keyb_handler(struct interrupt_frame *frame)
+{
+ pic_send_eoi(1);
+ kprint("Got a keyboard interrupt!\n", 0);
+}
+
+
+__attribute__((interrupt))
+void double_fault(struct abort_frame *frame)
+{
+ *(volatile char *) (0xb8000) = ":";
+ *(volatile char *) (0xb8002) = "(";
+ asm volatile ("cli; hlt");
+}
+
--- /dev/null
+#include <io.h>
+#include <tty.h>
+#include <irq/i8259a.h>
+
+void pic_init(int offset1, int offset2)
+{
+ uint8_t a1, a2;
+
+ a1 = inb(PIC1_DATA);
+ a2 = inb(PIC2_DATA);
+
+ outb(PIC1_COMMAND, PIC_ICW1_INIT | PIC_ICW1_ICW4);
+ io_wait();
+ outb(PIC2_COMMAND, PIC_ICW1_INIT | PIC_ICW1_ICW4);
+ io_wait();
+ outb(PIC1_DATA, offset1);
+ io_wait();
+ outb(PIC2_DATA, offset2);
+ io_wait();
+ outb(PIC1_DATA, 4);
+ io_wait();
+ outb(PIC2_DATA, 2);
+ io_wait();
+
+ outb(PIC1_DATA, PIC_ICW4_8086);
+ io_wait();
+ outb(PIC2_DATA, PIC_ICW4_8086);
+ io_wait();
+
+ outb(PIC1_DATA, a1);
+ outb(PIC2_DATA, a2);
+}
+
+void pic_mask(uint8_t irq)
+{
+ uint16_t port;
+ uint8_t value;
+
+ if (irq < 8) {
+ port = PIC1_DATA;
+ } else {
+ port = PIC2_DATA;
+ }
+
+ value = inb(port) | (1 << irq);
+ outb(port, value);
+}
+
+void pic_unmask(uint8_t irq)
+{
+ uint16_t port;
+ uint8_t value;
+
+ if (irq < 8) {
+ port = PIC1_DATA;
+ } else {
+ port = PIC2_DATA;
+ }
+
+ value = inb(port) & ~(1 << irq);
+ outb(port, value);
+}
+
+void pic_mask_all(void)
+{
+ outb(PIC1_DATA, 0xFF);
+}
+
+void pic_unmask_all(void)
+{
+ outb(PIC1_DATA, 0);
+}
+
+void pic_send_eoi(uint8_t irq)
+{
+ if (irq >= 8)
+ outb(PIC2_COMMAND, PIC_EOI);
+
+ outb(PIC1_COMMAND, PIC_EOI);
+}
#include <tty.h>
#include <io.h>
+#include <irq/idt.h>
+#include <irq/i8259a.h>
#include <stdint.h>
-const char *string = "Hello there!\n\n\
-Hopefully your machine manages to print this text.\n\
-If it did, that's great news because I managed to write a partial VGA driver.\n\n\
-Right now, the short-term roadmap is as follows:\n\n\
-* Complete the text-mode part of the VGA driver.\n\
-* Enable interrupts using the PIC.\n\
-* Write a driver for the Intel 8042 PS/2 controller so the OS can receive keystrokes.\n\
-* Once all that is done, it would be desirable to start the switch to userspace so the OS can actually be remotely usable.\n\n\
-Feel free to mess around with the code, although I doubt it will be very interesting at the moment.\n";
+extern void double_fault(struct abort_frame *frame);
+extern void keyb_handler(struct interrupt_frame *frame);
+struct idt_descriptor idt[256] __attribute__((aligned(0x10)));
+struct idtr idtr __attribute__((aligned(0x10)));
void _start(void)
{
screen_clear();
- /*kprint(string, 0x07);*/
- kprint("Color 0x01\n", VGA_COLOR_BLUE);
- kprint("Color 0x02\n", VGA_COLOR_GREEN);
- kprint("Color 0x03\n", VGA_COLOR_TEAL);
- kprint("Color 0x04\n", VGA_COLOR_DARK_RED);
- kprint("Color 0x05\n", VGA_COLOR_MAGENTA);
- kprint("Color 0x06\n", VGA_COLOR_BROWN);
- kprint("Color 0x07\n", VGA_COLOR_LIGHT_GRAY);
- kprint("Color 0x08\n", VGA_COLOR_DARK_GRAY);
- kprint("Color 0x09\n", VGA_COLOR_PURPLE);
- kprint("Color 0x0A\n", VGA_COLOR_LIME);
- kprint("Color 0x0B\n", VGA_COLOR_CYAN);
- kprint("Color 0x0C\n", VGA_COLOR_BRIGHT_RED);
- kprint("Color 0x0D\n", VGA_COLOR_PINK);
- kprint("Color 0x0E\n", VGA_COLOR_YELLOW);
- kprint("Color 0x0F\n", VGA_COLOR_WHITE);
- kprintb(0xAE);
+ kprint("Welcome to Nameless OS!\nRunning revision: ", 0);
+ kprint(GIT_COMMIT, 0);
kprint("\n", 0);
- kprintw(0xDEAD);
- kprint("\n", 0);
- kprintd(0x89ABCDEF);
+ kprint("Preparing IDT...\n", 0);
+ idt_set_descriptor(idt, 0x8, 0x8, (uint32_t) double_fault, IDT_TRAP_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);
+ load_idt(idtr);
+ kprint("IDT loaded, enabling interrupts...\n", 0);
+ pic_init(0x20, 0x28);
+ pic_mask_all();
+ pic_unmask(1);
+ asm volatile ("sti");
+ kprint("All done\n", 0);
+ while(1);
}