From ea8f6b4ce0b6a65f6f2fdc3e1928d10df1041bbf Mon Sep 17 00:00:00 2001 From: assada Date: Sat, 25 May 2024 19:20:33 +0300 Subject: [PATCH] dead@root --- .gitignore | 8 ++ Makefile | 45 ++++++++++++ README.md | 37 ++++++++++ boot.s | 29 ++++++++ gdt.c | 63 ++++++++++++++++ gdt.h | 63 ++++++++++++++++ gdt_flush.s | 14 ++++ grub.cfg | 6 ++ idt.c | 27 +++++++ idt.h | 28 +++++++ idt_load.s | 5 ++ interrupts.h | 44 +++++++++++ io.c | 20 +++++ io.h | 10 +++ irq.c | 93 ++++++++++++++++++++++++ irq_flush.s | 59 +++++++++++++++ isr.c | 156 +++++++++++++++++++++++++++++++++++++++ isr_flush.s | 85 ++++++++++++++++++++++ kernel.c | 46 ++++++++++++ keyboard.c | 202 +++++++++++++++++++++++++++++++++++++++++++++++++++ keyboard.h | 18 +++++ linker.ld | 32 ++++++++ shell.c | 112 ++++++++++++++++++++++++++++ shell.h | 9 +++ string.c | 125 +++++++++++++++++++++++++++++++ string.h | 14 ++++ sys.c | 16 ++++ sys.h | 8 ++ syscall.c | 47 ++++++++++++ syscall.h | 6 ++ timer.c | 35 +++++++++ timer.h | 13 ++++ tss_flush.s | 9 +++ tty.c | 184 ++++++++++++++++++++++++++++++++++++++++++++++ tty.h | 13 ++++ user_space.c | 22 ++++++ user_space.h | 6 ++ vga.c | 31 ++++++++ vga.h | 29 ++++++++ 39 files changed, 1769 insertions(+) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 README.md create mode 100644 boot.s create mode 100644 gdt.c create mode 100644 gdt.h create mode 100644 gdt_flush.s create mode 100644 grub.cfg create mode 100644 idt.c create mode 100644 idt.h create mode 100644 idt_load.s create mode 100644 interrupts.h create mode 100644 io.c create mode 100644 io.h create mode 100644 irq.c create mode 100644 irq_flush.s create mode 100644 isr.c create mode 100644 isr_flush.s create mode 100644 kernel.c create mode 100644 keyboard.c create mode 100644 keyboard.h create mode 100644 linker.ld create mode 100644 shell.c create mode 100644 shell.h create mode 100644 string.c create mode 100644 string.h create mode 100644 sys.c create mode 100644 sys.h create mode 100644 syscall.c create mode 100644 syscall.h create mode 100644 timer.c create mode 100644 timer.h create mode 100644 tss_flush.s create mode 100644 tty.c create mode 100644 tty.h create mode 100644 user_space.c create mode 100644 user_space.h create mode 100644 vga.c create mode 100644 vga.h diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a1fad8b --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +*.o +*.bin +*.elf +*.img +isodir/ +.vscode/ +.idea/ +*.iso \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..55bdb48 --- /dev/null +++ b/Makefile @@ -0,0 +1,45 @@ +CFLAGS = -ffreestanding -nostdlib -fno-builtin -fno-stack-protector -Wall -Wextra +LDFLAGS = -ffreestanding -nostdlib -lgcc +ASFLAGS = +CC = i686-elf-gcc +AS = i686-elf-as +LD = i686-elf-gcc +GRUB_FILE = grub-file +GRUB_MKRESCUE = grub-mkrescue +KERNEL = deados.bin +ISO = deados.iso + +SOURCES_C = kernel.c tty.c idt.c timer.c io.c vga.c gdt.c sys.c irq.c isr.c keyboard.c shell.c string.c user_space.c syscall.c +SOURCES_ASM = boot.s idt_load.s gdt_flush.s tss_flush.s irq_flush.s isr_flush.s +OBJECTS = $(SOURCES_ASM:.s=.o) $(SOURCES_C:.c=.o) + +ISO_DIR = isodir +GRUB_DIR = $(ISO_DIR)/boot/grub + +.PHONY: all clean iso + +all: $(ISO) + +$(KERNEL): $(OBJECTS) linker.ld + $(LD) -T linker.ld -o $(KERNEL) $(OBJECTS) $(LDFLAGS) + +%.o: %.c + $(CC) $(CFLAGS) -c $< -o $@ + +%.o: %.s + $(AS) $(ASFLAGS) $< -o $@ + +iso: $(ISO) + +$(ISO): $(KERNEL) grub.cfg + mkdir -p $(GRUB_DIR) + cp $(KERNEL) $(ISO_DIR)/boot/$(KERNEL) + cp grub.cfg $(GRUB_DIR)/grub.cfg + $(GRUB_MKRESCUE) -o $(ISO) $(ISO_DIR) + +check-multiboot: $(KERNEL) + $(GRUB_FILE) --is-x86-multiboot $(KERNEL) && echo "multiboot confirmed" || echo "the file is not multiboot" + +clean: + rm -f $(OBJECTS) $(KERNEL) $(ISO) + rm -rf $(ISO_DIR) diff --git a/README.md b/README.md new file mode 100644 index 0000000..3f578d5 --- /dev/null +++ b/README.md @@ -0,0 +1,37 @@ +# Simple OS implementation + +DeadOS is a minimalist operating system designed for x86 architecture, created solely for educational purposes. It is an ideal platform for learning and understanding the fundamental concepts of operating system development. While it is not intended to evolve into a fully-fledged OS, DeadOS provides a straightforward and manageable codebase that makes it perfect for experimentation and study. + +## Requirements + +- make +- GCC Cross-Compiler +- QEMU + +## How to run + +- `make` +- `qemu-system-x86_64 -monitor stdio -cdrom deados.iso` + +## Features + +- [x] Bootloader +- [x] Kernel +- [x] VGA Driver +- [x] Keyboard Driver +- [x] Shell +- [x] Basic commands +- [x] Timers +- [x] Interrupts +- [x] Kernel Extensions handling +- [ ] Paging +- [] Memory Management +- [] File System +- [x] User Space +- [x] Syscalls +- [ ] Multitasking +- [] stdlib + +## Screenshot: + +![image](https://github.com/assada/os/assets/1472664/9b67c053-36e1-4816-ad7f-093b89b03fce) diff --git a/boot.s b/boot.s new file mode 100644 index 0000000..792ba00 --- /dev/null +++ b/boot.s @@ -0,0 +1,29 @@ +.set ALIGN, 1<<0 +.set MEMINFO, 1<<1 +.set FLAGS, ALIGN | MEMINFO +.set MAGIC, 0x1BADB002 /* multiboot 1 */ +.set CHECKSUM, -(MAGIC + FLAGS) + +.section .multiboot +.align 4 +.long MAGIC +.long FLAGS +.long CHECKSUM + +.section .stack, "aw", @nobits +.global stack_top +stack_bottom: +.skip 16384 +stack_top: + +.section .text +.global _start +.type _start, @function +_start: + movl $stack_top, %esp + + push %ebx + + call kernel_main +1: + jmp 1b diff --git a/gdt.c b/gdt.c new file mode 100644 index 0000000..4496f93 --- /dev/null +++ b/gdt.c @@ -0,0 +1,63 @@ +#include "gdt.h" +#include "string.h" + +#define GDT_ENTRY 6 + +Gdt_entry gdt[GDT_ENTRY]; +Gdt_ptr gdtp; + +extern void gdt_flush(uint32_t); + +Tss_entry tss; +extern void tss_flush(void); + +static void gdt_set_gate(uint8_t num, uint32_t base, uint32_t limit, uint8_t access, uint8_t granularity) +{ + gdt[num].base_low = base & 0xFFFF; + gdt[num].base_middle = (base >> 16) & 0xFF; + gdt[num].base_high = (base >> 24) & 0xFF; + + gdt[num].limit_low = limit & 0xFFFF; + + gdt[num].granularity = (limit >> 16) & 0xF; + gdt[num].granularity |= (granularity & 0xF0); + + gdt[num].access = access; +} + +static void tss_install(uint8_t num, uint16_t kernel_ss, uint16_t kernel_esp) +{ + uint32_t base = (uint32_t)&tss; + uint32_t limit = base + sizeof(Tss_entry); + + gdt_set_gate(num, base, limit, 0xE9, 0x0); + + memset(&tss, 0, sizeof(Tss_entry)); + + tss.ss0 = kernel_ss; + tss.esp0 = kernel_esp; + + tss.cs = 0x1B; + tss.ss = 0x23; + tss.es = 0x23; + tss.ds = 0x23; + tss.fs = 0x23; + tss.gs = 0x23; +} + +void gdt_install() +{ + gdt_set_gate(0, 0x0, 0x0, 0x0, 0x0); // Null segment + gdt_set_gate(1, 0x0, 0xFFFFFFFF, 0x9A, 0xCF); // Kernel code segment + gdt_set_gate(2, 0x0, 0xFFFFFFFF, 0x92, 0xCF); // Data segment + gdt_set_gate(3, 0x0, 0xFFFFFFFF, 0xFA, 0xCF); // User code segment + gdt_set_gate(4, 0x0, 0xFFFFFFFF, 0xF2, 0xCF); // User data segment + + tss_install(5, 0x10, stack_top); + + gdtp.limit = (sizeof(Gdt_entry) * GDT_ENTRY) - 1; + gdtp.base = (uint32_t)&gdt; + + gdt_flush((uint32_t)&gdtp); + tss_flush(); +} diff --git a/gdt.h b/gdt.h new file mode 100644 index 0000000..c7dc4f9 --- /dev/null +++ b/gdt.h @@ -0,0 +1,63 @@ +#ifndef GDT_H +#define GDT_H + +#include + +extern uint32_t stack_top; + +typedef struct gdt_entry_t Gdt_entry; +struct gdt_entry_t +{ + uint16_t limit_low; + uint16_t base_low; + uint8_t base_middle; + uint8_t access; + uint8_t granularity; + uint8_t base_high; +} __attribute__((packed)); + +typedef struct gdt_ptr_t Gdt_ptr; +struct gdt_ptr_t +{ + uint16_t limit; + uint32_t base; +} __attribute__((packed)); + +static void gdt_set_gate(uint8_t num, uint32_t base, uint32_t limit, uint8_t access, uint8_t granularity); +void gdt_install(); + +void set_kernel_stack(uint32_t stack); + +typedef struct tss_entry_t Tss_entry; +struct tss_entry_t +{ + uint32_t prevTss; + uint32_t esp0; + uint32_t ss0; + uint32_t esp1; + uint32_t ss1; + uint32_t esp2; + uint32_t ss2; + uint32_t cr3; + uint32_t eip; + uint32_t eflags; + uint32_t eax; + uint32_t ecx; + uint32_t edx; + uint32_t ebx; + uint32_t esp; + uint32_t ebp; + uint32_t esi; + uint32_t edi; + uint32_t es; + uint32_t cs; + uint32_t ss; + uint32_t ds; + uint32_t fs; + uint32_t gs; + uint32_t ldt; + uint16_t trap; + uint16_t iomap; +} __attribute__((packed)); + +#endif \ No newline at end of file diff --git a/gdt_flush.s b/gdt_flush.s new file mode 100644 index 0000000..a3307b6 --- /dev/null +++ b/gdt_flush.s @@ -0,0 +1,14 @@ +.global gdt_flush + +gdt_flush: + lgdt gdtp + jmp $0x08, $reload_cs + + reload_cs: + movw $0x10, %ax + movw %ax, %ds + movw %ax, %es + movw %ax, %fs + movw %ax, %gs + movw %ax, %ss + ret diff --git a/grub.cfg b/grub.cfg new file mode 100644 index 0000000..1ec4b55 --- /dev/null +++ b/grub.cfg @@ -0,0 +1,6 @@ +default=0 +timeout=0 + +menuentry "deadOS" { + multiboot /boot/deados.bin +} \ No newline at end of file diff --git a/idt.c b/idt.c new file mode 100644 index 0000000..672a51a --- /dev/null +++ b/idt.c @@ -0,0 +1,27 @@ +#include + +#include "idt.h" +#include "io.h" +#include "string.h" + +struct idt_entry idt[IDT_ENTRIES]; +struct idt_ptr idtp; + +void idt_set_gate(uint8_t num, uint32_t base, uint16_t sel, uint8_t flags) +{ + idt[num].base_lo = base & 0xFFFF; + idt[num].base_hi = (base >> 16) & 0xFFFF; + idt[num].sel = sel; + idt[num].always0 = 0; + idt[num].flags = flags | 0x60; +} + +void idt_install() +{ + idtp.limit = (sizeof(struct idt_entry) * IDT_ENTRIES) - 1; + idtp.base = (uint32_t)&idt; + + memset(&idt, 0, sizeof(struct idt_entry) * IDT_ENTRIES); + + idt_load(); +} diff --git a/idt.h b/idt.h new file mode 100644 index 0000000..a09a916 --- /dev/null +++ b/idt.h @@ -0,0 +1,28 @@ +#ifndef IDT_H +#define IDT_H + +#include + +#define IDT_ENTRIES 256 + +struct idt_entry +{ + uint16_t base_lo; + uint16_t sel; + uint8_t always0; + uint8_t flags; + uint16_t base_hi; +} __attribute__((packed)); + +struct idt_ptr +{ + uint16_t limit; + uint32_t base; +} __attribute__((packed)); + +extern void idt_load(); + +void idt_install(); +void idt_set_gate(uint8_t num, uint32_t base, uint16_t sel, uint8_t flags); + +#endif \ No newline at end of file diff --git a/idt_load.s b/idt_load.s new file mode 100644 index 0000000..5712341 --- /dev/null +++ b/idt_load.s @@ -0,0 +1,5 @@ +.section .text +.global idt_load +idt_load: + lidt idtp + ret diff --git a/interrupts.h b/interrupts.h new file mode 100644 index 0000000..5e0b4da --- /dev/null +++ b/interrupts.h @@ -0,0 +1,44 @@ +#ifndef INTERUPTS_H +#define INTERUPTS_H + +#include + +typedef struct stack_t Stack; +struct stack_t +{ + uint32_t gs, fs, es, ds; + uint32_t edi, esi, ebp, esp, ebx, edx, ecx, eax; + uint32_t id, err_code; + uint32_t eip, cs, eflags, useresp, ss; +}; + +#define NB_ISR_ROUTINES 256 + +void isr_install_handler(uint8_t isr, void (*handler)(Stack *registers)); +void isr_uninstall_handler(uint8_t isr); +void isr_call_handler(Stack *registers); +void isr_install(void); + +#define NB_IRQ_ROUTINES 16 + +#define PIC1 0x20 +#define PIC2 0xA0 +#define PIC1_CMD PIC1 +#define PIC1_DATA (PIC1 + 1) +#define PIC2_CMD PIC2 +#define PIC2_DATA (PIC2 + 1) +#define PIC1_OFFSET 0x20 +#define PIC2_OFFSET 0x28 + +#define PIC_EOI 0x20 + +#define ICW1_ICW4 0x01 +#define ICW1_INIT 0x10 +#define ICW4_8086 0x01 + +void irq_install_handler(uint8_t irq, void (*handler)(Stack *registers)); +void irq_uninstall_handler(uint8_t irq); +void irq_call_handler(Stack *registers); +void irq_install(void); + +#endif diff --git a/io.c b/io.c new file mode 100644 index 0000000..2311d59 --- /dev/null +++ b/io.c @@ -0,0 +1,20 @@ +#include "io.h" + +void outb(uint16_t port, uint8_t val) +{ + asm volatile("outb %0, %1" : : "a"(val), "Nd"(port)); +} + +uint8_t inb(uint16_t port) +{ + uint8_t ret; + asm volatile("inb %1, %0" : "=a"(ret) : "Nd"(port)); + return ret; +} + +void wait(void) +{ + asm volatile("jmp 1f\n\t" + "1:jmp 2f\n\t" + "2:"); +} diff --git a/io.h b/io.h new file mode 100644 index 0000000..1babee8 --- /dev/null +++ b/io.h @@ -0,0 +1,10 @@ +#ifndef IO_H +#define IO_H + +#include + +void outb(uint16_t port, uint8_t val); +uint8_t inb(uint16_t port); +void io_wait(void); + +#endif \ No newline at end of file diff --git a/irq.c b/irq.c new file mode 100644 index 0000000..19a9eb4 --- /dev/null +++ b/irq.c @@ -0,0 +1,93 @@ +#include + +#include "interrupts.h" +#include "io.h" +#include "sys.h" +#include "idt.h" + +extern void irq0(void); +extern void irq1(void); +extern void irq2(void); +extern void irq3(void); +extern void irq4(void); +extern void irq5(void); +extern void irq6(void); +extern void irq7(void); +extern void irq8(void); +extern void irq9(void); +extern void irq10(void); +extern void irq11(void); +extern void irq12(void); +extern void irq13(void); +extern void irq14(void); +extern void irq15(void); + +void *irq_routines[NB_IRQ_ROUTINES] = {0}; + +void irq_install_handler(uint8_t irq, void (*handler)(Stack *registers)) +{ + irq_routines[irq] = handler; +} + +void irq_uninstall_handler(uint8_t irq) +{ + irq_routines[irq] = 0; +} + +void irq_call_handler(Stack *registers) +{ + void (*handler)(Stack *registers); + handler = irq_routines[registers->id - 32]; + if (handler) + { + handler(registers); + } +} + +static void irq_remap(void) +{ + 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); +} + +void irq_install(void) +{ + irq_remap(); + + idt_set_gate(32, (uint32_t)irq0, 0x08, 0x8E); + idt_set_gate(33, (uint32_t)irq1, 0x08, 0x8E); + idt_set_gate(34, (uint32_t)irq2, 0x08, 0x8E); + idt_set_gate(35, (uint32_t)irq3, 0x08, 0x8E); + idt_set_gate(36, (uint32_t)irq4, 0x08, 0x8E); + idt_set_gate(37, (uint32_t)irq5, 0x08, 0x8E); + idt_set_gate(38, (uint32_t)irq6, 0x08, 0x8E); + idt_set_gate(39, (uint32_t)irq7, 0x08, 0x8E); + idt_set_gate(40, (uint32_t)irq8, 0x08, 0x8E); + idt_set_gate(41, (uint32_t)irq9, 0x08, 0x8E); + idt_set_gate(42, (uint32_t)irq10, 0x08, 0x8E); + idt_set_gate(43, (uint32_t)irq11, 0x08, 0x8E); + idt_set_gate(44, (uint32_t)irq12, 0x08, 0x8E); + idt_set_gate(45, (uint32_t)irq13, 0x08, 0x8E); + idt_set_gate(46, (uint32_t)irq14, 0x08, 0x8E); + idt_set_gate(47, (uint32_t)irq15, 0x08, 0x8E); + + sys_enable_interrupts(); +} + +void irq_handler(Stack *registers) +{ + irq_call_handler(registers); + + if (registers->id >= 40) + outb(0xA0, 0x20); + + outb(0x20, 0x20); +} diff --git a/irq_flush.s b/irq_flush.s new file mode 100644 index 0000000..5561d1f --- /dev/null +++ b/irq_flush.s @@ -0,0 +1,59 @@ +.section .text +.align 4 + +.macro IRQ index entry + .global irq\index + .type irq\index, @function + irq\index: + cli + push $0x00 + push $\entry + jmp irq_flush +.endm + +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 + +irq_flush: + pusha + + push %ds + push %es + push %fs + push %gs + + mov $0x10, %ax + mov %ax, %ds + mov %ax, %es + mov %ax, %fs + mov %ax, %gs + + mov %esp, %eax + push %eax + mov $irq_handler, %eax + + call *%eax + pop %eax + + pop %gs + pop %fs + pop %es + pop %ds + popa + + add $8, %esp + iret diff --git a/isr.c b/isr.c new file mode 100644 index 0000000..bac24fd --- /dev/null +++ b/isr.c @@ -0,0 +1,156 @@ +#include "interrupts.h" +#include "io.h" +#include "sys.h" +#include "idt.h" +#include "tty.h" + +extern void isr0(void); +extern void isr1(void); +extern void isr2(void); +extern void isr3(void); +extern void isr4(void); +extern void isr5(void); +extern void isr6(void); +extern void isr7(void); +extern void isr8(void); +extern void isr9(void); +extern void isr10(void); +extern void isr11(void); +extern void isr12(void); +extern void isr13(void); +extern void isr14(void); +extern void isr15(void); +extern void isr16(void); +extern void isr17(void); +extern void isr18(void); +extern void isr19(void); +extern void isr20(void); +extern void isr21(void); +extern void isr22(void); +extern void isr23(void); +extern void isr24(void); +extern void isr25(void); +extern void isr26(void); +extern void isr27(void); +extern void isr28(void); +extern void isr29(void); +extern void isr30(void); +extern void isr31(void); +extern void isr128(void); + +void *isr_routines[NB_ISR_ROUTINES] = {0}; + +void isr_install_handler(uint8_t isr, void (*handler)(Stack *registers)) +{ + isr_routines[isr] = handler; +} + +void isr_uninstall_handler(uint8_t isr) +{ + isr_routines[isr] = 0; +} + +void isr_call_handler(Stack *registers) +{ + void (*handler)(Stack *registers); + handler = isr_routines[registers->id]; + if (handler) + handler(registers); +} + +void isr_install(void) +{ + idt_set_gate(0, (uint32_t)isr0, 0x08, 0x8E); + idt_set_gate(1, (uint32_t)isr1, 0x08, 0x8E); + idt_set_gate(2, (uint32_t)isr2, 0x08, 0x8E); + idt_set_gate(3, (uint32_t)isr3, 0x08, 0x8E); + idt_set_gate(4, (uint32_t)isr4, 0x08, 0x8E); + idt_set_gate(5, (uint32_t)isr5, 0x08, 0x8E); + idt_set_gate(6, (uint32_t)isr6, 0x08, 0x8E); + idt_set_gate(7, (uint32_t)isr7, 0x08, 0x8E); + + idt_set_gate(8, (uint32_t)isr8, 0x08, 0x8E); + idt_set_gate(9, (uint32_t)isr9, 0x08, 0x8E); + idt_set_gate(10, (uint32_t)isr10, 0x08, 0x8E); + idt_set_gate(11, (uint32_t)isr11, 0x08, 0x8E); + idt_set_gate(12, (uint32_t)isr12, 0x08, 0x8E); + idt_set_gate(13, (uint32_t)isr13, 0x08, 0x8E); + idt_set_gate(14, (uint32_t)isr14, 0x08, 0x8E); + idt_set_gate(15, (uint32_t)isr15, 0x08, 0x8E); + + idt_set_gate(16, (uint32_t)isr16, 0x08, 0x8E); + idt_set_gate(17, (uint32_t)isr17, 0x08, 0x8E); + idt_set_gate(18, (uint32_t)isr18, 0x08, 0x8E); + idt_set_gate(19, (uint32_t)isr19, 0x08, 0x8E); + idt_set_gate(20, (uint32_t)isr20, 0x08, 0x8E); + idt_set_gate(21, (uint32_t)isr21, 0x08, 0x8E); + idt_set_gate(22, (uint32_t)isr22, 0x08, 0x8E); + idt_set_gate(23, (uint32_t)isr23, 0x08, 0x8E); + + idt_set_gate(24, (uint32_t)isr24, 0x08, 0x8E); + idt_set_gate(25, (uint32_t)isr25, 0x08, 0x8E); + idt_set_gate(26, (uint32_t)isr26, 0x08, 0x8E); + idt_set_gate(27, (uint32_t)isr27, 0x08, 0x8E); + idt_set_gate(28, (uint32_t)isr28, 0x08, 0x8E); + idt_set_gate(29, (uint32_t)isr29, 0x08, 0x8E); + idt_set_gate(30, (uint32_t)isr30, 0x08, 0x8E); + idt_set_gate(31, (uint32_t)isr31, 0x08, 0x8E); + + idt_set_gate(128, (uint32_t)isr128, 0x08, 0x8E); +} + +static const char *exception_messages[] = + { + "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 fault_handler(Stack *registers) +{ + if (registers->id == 128) + isr_call_handler(registers); + + if (registers->id < 32) + { + terminal_printf("Exception. System Halted!\n"); + terminal_printf("Exception: "); + terminal_printf(exception_messages[registers->id]); + + isr_call_handler(registers); + + for (;;) + sys_halt(); + } +} diff --git a/isr_flush.s b/isr_flush.s new file mode 100644 index 0000000..eccfda7 --- /dev/null +++ b/isr_flush.s @@ -0,0 +1,85 @@ +.section .text +.align 4 + +.macro ISR_NO_ERR index + .global isr\index + isr\index: + cli + push $0 + push $\index + jmp isr_flush +.endm + +.macro ISR_ERR index + .global isr\index + isr\index: + cli + push $\index + jmp isr_flush +.endm + + +ISR_NO_ERR 0 # Division By Zero Exception +ISR_NO_ERR 1 # Debug Exception +ISR_NO_ERR 2 # Non Maskable Interrupt Exception +ISR_NO_ERR 3 # Breakpoint Exception +ISR_NO_ERR 4 # Into Detected Overflow Exception +ISR_NO_ERR 5 # Out of Bounds Exception +ISR_NO_ERR 6 # Invalid Opcode Exception +ISR_NO_ERR 7 # No Coprocessor Exception +ISR_ERR 8 # Double Fault Exception +ISR_NO_ERR 9 # Coprocessor Segment Overrun Exception +ISR_ERR 10 # Bad TSS Exception +ISR_ERR 11 # Segment Not Present Exception +ISR_ERR 12 # Stack Fault Exception +ISR_ERR 13 # General Protection Fault Exception +ISR_ERR 14 # Page Fault Exception +ISR_NO_ERR 15 # Unknown Interrupt Exception +ISR_NO_ERR 16 # Coprocessor Fault Exception +ISR_NO_ERR 17 # Alignment Check Exception +ISR_NO_ERR 18 # Machine Check Exception + +ISR_NO_ERR 19 # Other +ISR_NO_ERR 20 +ISR_NO_ERR 21 +ISR_NO_ERR 22 +ISR_NO_ERR 23 +ISR_NO_ERR 24 +ISR_NO_ERR 25 +ISR_NO_ERR 26 +ISR_NO_ERR 27 +ISR_NO_ERR 28 +ISR_NO_ERR 29 +ISR_NO_ERR 30 +ISR_NO_ERR 31 +ISR_NO_ERR 128 # 80h syscall + + +isr_flush: + pusha + push %ds + push %es + push %fs + push %gs + + mov $0x10, %ax + mov %ax, %ds + mov %ax, %es + mov %ax, %fs + mov %ax, %gs + + mov %esp, %eax + push %eax + mov $fault_handler, %eax + + call *%eax + pop %eax + + pop %gs + pop %fs + pop %es + pop %ds + popa + + add $8, %esp + iret diff --git a/kernel.c b/kernel.c new file mode 100644 index 0000000..aa9ad66 --- /dev/null +++ b/kernel.c @@ -0,0 +1,46 @@ +#include +#include +#include + +#include "tty.h" +#include "gdt.h" +#include "idt.h" +#include "timer.h" +#include "sys.h" +#include "interrupts.h" +#include "keyboard.h" +#include "shell.h" +#include "user_space.h" +#include "syscall.h" + +void kernel_main(void) +{ + gdt_install(); + + idt_install(); + isr_install(); + irq_install(); + + timer_install(); + keyboard_install(); + + syscall_init(); + enter_user_space(); +} + +void main(void) +{ + terminal_initialize(); + for (int i = 1; i < 4; i++) + { + terminal_printf("&cTest &7%d\n", i); + delay(500); + } + terminal_clear(); + + terminal_printf("Welcome to &cDeadOS&7.\n"); + terminal_printf("You are now in user space.\n"); + terminal_printf("Type 'help' for a list of available commands.\n"); + + shell_init(); +} diff --git a/keyboard.c b/keyboard.c new file mode 100644 index 0000000..7f3b8c5 --- /dev/null +++ b/keyboard.c @@ -0,0 +1,202 @@ +#include +#include + +#include "interrupts.h" +#include "io.h" +#include "keyboard.h" + +unsigned char us_layout[] = { + /* SHIFT CTRL ALT */ + 0x1B, 0x1B, 0x1B, 0x1B, /* esc (0x01) */ + '1', '!', '1', '1', + '2', '@', '2', '2', + '3', '#', '3', '3', + '4', '$', '4', '4', + '5', '%', '5', '5', + '6', '^', '6', '6', + '7', '&', '7', '7', + '8', '*', '8', '8', + '9', '(', '9', '9', + '0', ')', '0', '0', + '-', '_', '-', '-', + '=', '+', '=', '=', + 0x08, 0x08, 0x7F, 0x08, /* backspace */ + 0x09, 0x09, 0x09, 0x09, /* tab */ + 'q', 'Q', 'q', 'q', + 'w', 'W', 'w', 'w', + 'e', 'E', 'e', 'e', + 'r', 'R', 'r', 'r', + 't', 'T', 't', 't', + 'y', 'Y', 'y', 'y', + 'u', 'U', 'u', 'u', + 'i', 'I', 'i', 'i', + 'o', 'O', 'o', 'o', + 'p', 'P', 'p', 'p', + '[', '{', '[', '[', + ']', '}', ']', ']', + 0x0A, 0x0A, 0x0A, 0x0A, /* enter */ + 0xFF, 0xFF, 0xFF, 0xFF, /* ctrl */ + 'a', 'A', 'a', 'a', + 's', 'S', 's', 's', + 'd', 'D', 'd', 'd', + 'f', 'F', 'f', 'f', + 'g', 'G', 'g', 'g', + 'h', 'H', 'h', 'h', + 'j', 'J', 'j', 'j', + 'k', 'K', 'k', 'k', + 'l', 'L', 'l', 'l', + ';', ':', ';', ';', + 0x27, 0x22, 0x27, 0x27, /* '" */ + '`', '~', '`', '`', /* `~ */ + 0xFF, 0xFF, 0xFF, 0xFF, /* Lshift (0x2a) */ + '\\', '|', '\\', '\\', + 'z', 'Z', 'z', 'z', + 'x', 'X', 'x', 'x', + 'c', 'C', 'c', 'c', + 'v', 'V', 'v', 'v', + 'b', 'B', 'b', 'b', + 'n', 'N', 'n', 'n', + 'm', 'M', 'm', 'm', + 0x2C, 0x3C, 0x2C, 0x2C, /* ,< */ + 0x2E, 0x3E, 0x2E, 0x2E, /* .> */ + 0x2F, 0x3F, 0x2F, 0x2F, /* /? */ + 0xFF, 0xFF, 0xFF, 0xFF, /* Rshift (0x36) */ + 0xFF, 0xFF, 0xFF, 0xFF, /* (0x37) */ + 0xFF, 0xFF, 0xFF, 0xFF, /* (0x38) */ + ' ', ' ', ' ', ' ', /* space */ + 0xFF, 0xFF, 0xFF, 0xFF, /* (0x3a) */ + 0xFF, 0xFF, 0xFF, 0xFF, /* (0x3b) */ + 0xFF, 0xFF, 0xFF, 0xFF, /* (0x3c) */ + 0xFF, 0xFF, 0xFF, 0xFF, /* (0x3d) */ + 0xFF, 0xFF, 0xFF, 0xFF, /* (0x3e) */ + 0xFF, 0xFF, 0xFF, 0xFF, /* (0x3f) */ + 0xFF, 0xFF, 0xFF, 0xFF, /* (0x40) */ + 0xFF, 0xFF, 0xFF, 0xFF, /* (0x41) */ + 0xFF, 0xFF, 0xFF, 0xFF, /* (0x42) */ + 0xFF, 0xFF, 0xFF, 0xFF, /* (0x43) */ + 0xFF, 0xFF, 0xFF, 0xFF, /* (0x44) */ + 0xFF, 0xFF, 0xFF, 0xFF, /* (0x45) */ + 0xFF, 0xFF, 0xFF, 0xFF, /* (0x46) */ + 0xFF, 0xFF, 0xFF, 0xFF, /* (0x47) */ + 0xFF, 0xFF, 0xFF, 0xFF, /* (0x48) */ + 0xFF, 0xFF, 0xFF, 0xFF, /* (0x49) */ + 0xFF, 0xFF, 0xFF, 0xFF, /* (0x4a) */ + 0xFF, 0xFF, 0xFF, 0xFF, /* (0x4b) */ + 0xFF, 0xFF, 0xFF, 0xFF, /* (0x4c) */ + 0xFF, 0xFF, 0xFF, 0xFF, /* (0x4d) */ + 0xFF, 0xFF, 0xFF, 0xFF, /* (0x4e) */ + 0xFF, 0xFF, 0xFF, 0xFF, /* (0x4f) */ + 0xFF, 0xFF, 0xFF, 0xFF, /* (0x50) */ + 0xFF, 0xFF, 0xFF, 0xFF, /* (0x51) */ + 0xFF, 0xFF, 0xFF, 0xFF, /* (0x52) */ + 0xFF, 0xFF, 0xFF, 0xFF, /* (0x53) */ + 0xFF, 0xFF, 0xFF, 0xFF, /* (0x54) */ + 0xFF, 0xFF, 0xFF, 0xFF, /* (0x55) */ + 0xFF, 0xFF, 0xFF, 0xFF, /* (0x56) */ + 0xFF, 0xFF, 0xFF, 0xFF, /* (0x57) */ + 0xFF, 0xFF, 0xFF, 0xFF, /* (0x58) */ + 0xFF, 0xFF, 0xFF, 0xFF, /* (0x59) */ + 0xFF, 0xFF, 0xFF, 0xFF, /* (0x5a) */ + 0xFF, 0xFF, 0xFF, 0xFF, /* (0x5b) */ + 0xFF, 0xFF, 0xFF, 0xFF, /* (0x5c) */ + 0xFF, 0xFF, 0xFF, 0xFF, /* (0x5d) */ + 0xFF, 0xFF, 0xFF, 0xFF, /* (0x5e) */ + 0xFF, 0xFF, 0xFF, 0xFF, /* (0x5f) */ + 0xFF, 0xFF, 0xFF, 0xFF, /* (0x60) */ + 0xFF, 0xFF, 0xFF, 0xFF /* (0x61) */ +}; + +volatile unsigned char buffer[KEYBOARD_BUFFER_SIZE]; +volatile size_t buffer_index; +volatile size_t read_index; + +void keyboard_clear_buffer(void) +{ + buffer_index = 0; + read_index = 0; +} + +int keyboard_getchar(void) +{ + while (read_index == buffer_index) + ; + + int c = (int)buffer[read_index]; + ++read_index; + return c; +} + +unsigned char keyboard_getscancode(void) +{ + uint8_t scancode; + uint16_t index; + static uint8_t offset = 0; + + scancode = inb(KEYBOARD_DATA); + --scancode; + + if (scancode < 0x80) + { + switch (scancode) + { + case LSHIFT: + offset = 1; + break; + case RSHIFT: + offset = 1; + break; + case CTRL: + offset = 2; + break; + case ALT: + offset = 3; + break; + /* Character */ + default: + index = scancode * 4 + offset; + return us_layout[index]; + } + } + else + { + scancode -= 0x80; + switch (scancode) + { + case LSHIFT: + offset = 0; + break; + case RSHIFT: + offset = 0; + break; + case CTRL: + offset = 0; + break; + case ALT: + offset = 0; + break; + } + } + + return 0; +} + +void keyboard_handler(Stack *registers) +{ + (void)(registers); + + unsigned char c = keyboard_getscancode(); + + if (c) + { + buffer[buffer_index] = c; + ++buffer_index; + if (buffer_index == KEYBOARD_BUFFER_SIZE) + keyboard_clear_buffer(); + } +} + +void keyboard_install(void) +{ + keyboard_clear_buffer(); + irq_install_handler(1, keyboard_handler); +} diff --git a/keyboard.h b/keyboard.h new file mode 100644 index 0000000..6517c2e --- /dev/null +++ b/keyboard.h @@ -0,0 +1,18 @@ +#ifndef KEYBOARD_H +#define KEYBOARD_H + +#define KEYBOARD_BUFFER_SIZE 256 + +#define KEYBOARD_DATA 0x60 + +#define LSHIFT 0x29 +#define RSHIFT 0x35 +#define CTRL 0x1C +#define ALT 0x37 + +void keyboard_clear_buffer(void); +int keyboard_getchar(void); +unsigned char keyboard_getscancode(void); +void keyboard_install(void); + +#endif diff --git a/linker.ld b/linker.ld new file mode 100644 index 0000000..5d6b6bb --- /dev/null +++ b/linker.ld @@ -0,0 +1,32 @@ +ENTRY(_start) + +SECTIONS +{ + . = 1M; + + kernel_start = .; + + .text BLOCK(4K) : ALIGN(4K) + { + *(.multiboot) + *(.text) + } + + .rodata BLOCK(4K) : ALIGN(4K) + { + *(.rodata) + } + + .data BLOCK(4K) : ALIGN(4K) + { + *(.data) + } + + .bss BLOCK(4K) : ALIGN(4K) + { + *(.bss) + *(.stack) + } + + kernel_end = .; +} diff --git a/shell.c b/shell.c new file mode 100644 index 0000000..2a7471e --- /dev/null +++ b/shell.c @@ -0,0 +1,112 @@ +#include +#include "keyboard.h" +#include "shell.h" +#include "tty.h" +#include "string.h" + +void shell_init(void) +{ + char input_buffer[SHELL_BUFFER_SIZE + 1]; + size_t i; + char c; + + while (true) + { + terminal_printf("&7dead@&croot&7: /> "); + + i = 0; + c = 0; + + while (i < SHELL_BUFFER_SIZE) + { + c = keyboard_getchar(); + + if (c == '\b') + { + if (i > 0) + { + terminal_printf("\b \b"); + --i; + } + } + else + { + terminal_putchar(c); + input_buffer[i] = c; + ++i; + + if (c == '\n') + break; + } + } + + input_buffer[i] = '\0'; + keyboard_clear_buffer(); + shell_parse_input(input_buffer); + } +} + +void shell_help_command(void) +{ + terminal_printf("List of commands:\n"); + terminal_printf("help - Print out the list of commands.\n"); + terminal_printf("exit - Exit the shell.\n"); + terminal_printf("echo - Print out the message. &eWITH colors!&7\n"); + terminal_printf("clear - Clear the terminal.\n"); +} + +void shell_exit_command(void) +{ + terminal_printf("Exiting shell...\n"); + terminal_printf("Goodbye!\n"); + while (1) + ; +} + +void shell_echo_command(char *input) +{ + char *message = input; + + while (*message == ' ') + { + message++; + } + + terminal_printf(message); + terminal_printf("\n"); +} + +void shell_parse_input(char *input) +{ + char *command = strtok(input, " \n"); + char *args = input + strlen(command) + 1; + + if (command == NULL || *command == '\0') + { + return; + } + + if (strcmp(command, "help") == 0) + { + shell_help_command(); + } + else if (strcmp(command, "echo") == 0) + { + shell_echo_command(args); + } + else if (strcmp(command, "exit") == 0) + { + shell_exit_command(); + } + else if (strcmp(command, "clear") == 0) + { + terminal_clear(); + } + else + { + terminal_printf("Unknown command: '"); + terminal_print(command); + terminal_printf("'\n"); + terminal_printf("Type 'help' to print out the list of commands.\n"); + } +} diff --git a/shell.h b/shell.h new file mode 100644 index 0000000..58fc9aa --- /dev/null +++ b/shell.h @@ -0,0 +1,9 @@ +#ifndef _KERNEL_SHELL_H +#define _KERNEL_SHELL_H + +#define SHELL_BUFFER_SIZE 256 + +void shell_init(void); +void shell_parse_input(char *input); + +#endif diff --git a/string.c b/string.c new file mode 100644 index 0000000..4d2089a --- /dev/null +++ b/string.c @@ -0,0 +1,125 @@ +#include "string.h" +#include + +size_t strlen(const char *str) +{ + size_t len = 0; + + while (str[len]) + ++len; + + return len; +} + +void *memset(void *ptr, int value, size_t size) +{ + unsigned char *buf = (unsigned char *)ptr; + + for (size_t i = 0; i < size; ++i) + buf[i] = (unsigned char)value; + + return ptr; +} + +int strcmp(const char *str1, const char *str2) +{ + while (*str1 && (*str1 == *str2)) + { + str1++; + str2++; + } + return *(const unsigned char *)str1 - *(const unsigned char *)str2; +} + +char *strchr(const char *str, int character) +{ + size_t i = 0; + while (str[i] != (char)character) + { + if (str[i] == '\0') + return NULL; + ++i; + } + + return (char *)(str + i); +} + +char *strtok(char *str, const char *delim) +{ + static char *last_token = NULL; + char *token_start; + char *current; + + if (str != NULL) + { + current = str; + } + else + { + current = last_token; + } + + if (current == NULL) + { + return NULL; + } + + while (*current != '\0' && strchr(delim, *current) != NULL) + { + current++; + } + + if (*current == '\0') + { + last_token = NULL; + return NULL; + } + + token_start = current; + + while (*current != '\0' && strchr(delim, *current) == NULL) + { + current++; + } + + if (*current != '\0') + { + *current = '\0'; + last_token = current + 1; + } + else + { + last_token = NULL; + } + + return token_start; +} + +void itoa(int value, char *str, int base) +{ + char *rc; + char *ptr; + char *low; + + if (value < 0 && base == 10) + { + *str++ = '-'; + value = -value; + } + + rc = ptr = str; + do + { + *ptr++ = "0123456789abcdef"[value % base]; + value /= base; + } while (value); + + *ptr-- = '\0'; + + for (low = rc; low < ptr; low++, ptr--) + { + char tmp = *low; + *low = *ptr; + *ptr = tmp; + } +} diff --git a/string.h b/string.h new file mode 100644 index 0000000..67636e4 --- /dev/null +++ b/string.h @@ -0,0 +1,14 @@ +#ifndef STRING_H +#define STRING_H + +#include + +char *strchr(const char *str, int c); +char *strtok(char *str, const char *delim); +int strcmp(const char *str1, const char *str2); +size_t strlen(const char *str); +void itoa(int value, char *str, int base); + +void *memset(void *ptr, int value, size_t size); + +#endif \ No newline at end of file diff --git a/sys.c b/sys.c new file mode 100644 index 0000000..2c30ad0 --- /dev/null +++ b/sys.c @@ -0,0 +1,16 @@ +#include "sys.h" + +void sys_halt(void) +{ + asm volatile("hlt" ::: "memory"); +} + +void sys_enable_interrupts(void) +{ + asm volatile("sti" ::: "memory"); +} + +void sys_disable_interrupts(void) +{ + asm volatile("cli" ::: "memory"); +} \ No newline at end of file diff --git a/sys.h b/sys.h new file mode 100644 index 0000000..90f39a6 --- /dev/null +++ b/sys.h @@ -0,0 +1,8 @@ +#ifndef _KERNEL_SYS_H +#define _KERNEL_SYS_H + +void sys_halt(void); +void sys_enable_interrupts(void); +void sys_disable_interrupts(void); + +#endif \ No newline at end of file diff --git a/syscall.c b/syscall.c new file mode 100644 index 0000000..8cfdace --- /dev/null +++ b/syscall.c @@ -0,0 +1,47 @@ +#include + +#include "interrupts.h" +#include "keyboard.h" +#include "sys.h" +#include "syscall.h" +#include "tty.h" +#include "timer.h" + +#define NB_SYSCALL 2 + +void *syscalls[NB_SYSCALL] = { + keyboard_getchar, + + sys_halt}; + +void syscall_handler(Stack *registers) +{ + int sys_index = registers->eax; + + if (sys_index >= NB_SYSCALL) + return; + + void *function = syscalls[sys_index]; + + int ret; + asm volatile(" push %1; \ + push %2; \ + push %3; \ + push %4; \ + push %5; \ + call *%6; \ + pop %%ebx; \ + pop %%ebx; \ + pop %%ebx; \ + pop %%ebx; \ + pop %%ebx; \ + " : "=a"(ret) : "r"(registers->edi), "r"(registers->esi), + "r"(registers->edx), "r"(registers->ecx), + "r"(registers->ebx), "r"(function)); + registers->eax = ret; +} + +void syscall_init(void) +{ + isr_install_handler(128, syscall_handler); +} \ No newline at end of file diff --git a/syscall.h b/syscall.h new file mode 100644 index 0000000..5973d40 --- /dev/null +++ b/syscall.h @@ -0,0 +1,6 @@ +#ifndef SYSCALL_H +#define SYSCALL_H + +void syscall_init(void); + +#endif \ No newline at end of file diff --git a/timer.c b/timer.c new file mode 100644 index 0000000..4fef449 --- /dev/null +++ b/timer.c @@ -0,0 +1,35 @@ +#include + +#include "interrupts.h" +#include "io.h" +#include "timer.h" + +volatile uint32_t timer_ticks = 0; + +void delay(const uint32_t ticks) +{ + uint32_t total_ticks = timer_ticks + ticks; + while (timer_ticks < total_ticks) + ; +} + +void timer_handler(Stack *registers) +{ + ++timer_ticks; +} + +static void timer_phase(const uint32_t hz) +{ + uint32_t divisor = PIT_FREQUENCY / hz; + + outb(PIT_COMMAND, 0x36); + outb(PIT_CHANNEL_0, divisor & 0xFF); + outb(PIT_CHANNEL_0, divisor >> 8); +} + +void timer_install(void) +{ + irq_install_handler(0, timer_handler); + + timer_phase(1000); +} diff --git a/timer.h b/timer.h new file mode 100644 index 0000000..5e66459 --- /dev/null +++ b/timer.h @@ -0,0 +1,13 @@ +#ifndef TIMER_H +#define TIMER_H + +#include + +#define PIT_FREQUENCY 1193182 +#define PIT_CHANNEL_0 0x40 +#define PIT_COMMAND 0x43 + +void delay(uint32_t milliseconds); +void timer_install(void); + +#endif \ No newline at end of file diff --git a/tss_flush.s b/tss_flush.s new file mode 100644 index 0000000..8e3c53f --- /dev/null +++ b/tss_flush.s @@ -0,0 +1,9 @@ +.section .text +.align 4 + +.global tss_flush +.type tss_flush, @function +tss_flush: + mov $0x2B, %ax + ltr %ax + ret \ No newline at end of file diff --git a/tty.c b/tty.c new file mode 100644 index 0000000..51a4e33 --- /dev/null +++ b/tty.c @@ -0,0 +1,184 @@ +#include +#include +#include +#include + +#include "tty.h" +#include "vga.h" +#include "io.h" +#include "string.h" + +static const size_t VGA_WIDTH = 80; +static const size_t VGA_HEIGHT = 25; + +size_t terminal_row; +size_t terminal_column; +uint8_t terminal_color; +uint16_t *terminal_buffer; + +void terminal_initialize(void) +{ + terminal_row = 0; + terminal_column = 0; + terminal_color = vga_entry_color(VGA_COLOR_LIGHT_GREY, VGA_COLOR_BLACK); + terminal_buffer = (uint16_t *)0xB8000; + for (size_t i = 0; i < VGA_WIDTH * VGA_HEIGHT; i++) + { + terminal_buffer[i] = vga_entry(' ', terminal_color); + } +} + +void terminal_setcolor(uint8_t color) +{ + terminal_color = color; +} + +void terminal_putentryat(char c, uint8_t color, size_t x, size_t y) +{ + const size_t index = y * VGA_WIDTH + x; + terminal_buffer[index] = vga_entry(c, color); +} + +static uint16_t terminal_pos(uint8_t x, uint8_t y) +{ + return y * VGA_WIDTH + x; +} + +void terminal_scroll(void) +{ + for (size_t y = 0; y < VGA_HEIGHT - 1; y++) + { + for (size_t x = 0; x < VGA_WIDTH; x++) + { + terminal_buffer[y * VGA_WIDTH + x] = terminal_buffer[(y + 1) * VGA_WIDTH + x]; + } + } + for (size_t x = 0; x < VGA_WIDTH; x++) + { + terminal_buffer[(VGA_HEIGHT - 1) * VGA_WIDTH + x] = vga_entry(' ', terminal_color); + } + terminal_row = VGA_HEIGHT - 1; +} + +void terminal_update_cursor(void) +{ + uint16_t pos = terminal_row * VGA_WIDTH + terminal_column; + + outb(0x3D4, 14); + outb(0x3D5, pos >> 8); + outb(0x3D4, 15); + outb(0x3D5, pos); +} + +static void terminal_backspace(void) +{ + if (terminal_column == 0) + { + terminal_column = VGA_WIDTH - 1; + if (terminal_row > 0) + { + --terminal_row; + } + } + else + { + --terminal_column; + } + terminal_buffer[terminal_pos(terminal_column, terminal_row)] = vga_entry(' ', terminal_color); +} + +void terminal_putchar(char c) +{ + if (c == '\n') + { + terminal_column = 0; + if (++terminal_row == VGA_HEIGHT) + { + terminal_scroll(); + } + } + else if (c == '\b') + { + terminal_backspace(); + } + else + { + terminal_putentryat(c, terminal_color, terminal_column, terminal_row); + if (++terminal_column == VGA_WIDTH) + { + terminal_column = 0; + if (++terminal_row == VGA_HEIGHT) + { + terminal_scroll(); + } + } + } + terminal_update_cursor(); +} + +void terminal_write(const char *data, size_t size) +{ + for (size_t i = 0; i < size; i++) + { + terminal_putchar(data[i]); + } +} + +void terminal_print(const char *data) +{ + terminal_write(data, strlen(data)); +} + +void terminal_printf(const char *format, ...) +{ + va_list args; + va_start(args, format); + + const char *p = format; + while (*p) + { + if (*p == '%' && (*(p + 1) == 's' || *(p + 1) == 'd' || *(p + 1) == 'x')) + { + p++; + if (*p == 's') + { + terminal_print(va_arg(args, const char *)); + } + else if (*p == 'd') + { + char buffer[12]; + itoa(va_arg(args, int), buffer, 10); + terminal_print(buffer); + } + else if (*p == 'x') + { + char buffer[12]; + itoa(va_arg(args, unsigned int), buffer, 16); + terminal_print(buffer); + } + } + else if (*p == '&' && ((*(p + 1) >= '0' && *(p + 1) <= '9') || (*(p + 1) >= 'a' && *(p + 1) <= 'f'))) + { + terminal_setcolor(vga_entry_color(vga_color_from_char(*(p + 1)), VGA_COLOR_BLACK)); + p++; + } + else + { + terminal_putchar(*p); + } + p++; + } + + va_end(args); +} + +void terminal_clear(void) +{ + for (size_t i = 0; i < VGA_WIDTH * VGA_HEIGHT; i++) + { + terminal_buffer[i] = vga_entry(' ', terminal_color); + } + terminal_column = 0; + terminal_row = 0; + terminal_update_cursor(); +} diff --git a/tty.h b/tty.h new file mode 100644 index 0000000..dadbd0f --- /dev/null +++ b/tty.h @@ -0,0 +1,13 @@ +#ifndef TERMINAL_H +#define TERMINAL_H + +#include +#include + +void terminal_initialize(void); +void terminal_printf(const char *data, ...); +void terminal_print(const char *data); +void terminal_clear(void); +void terminal_putchar(char c); + +#endif \ No newline at end of file diff --git a/user_space.c b/user_space.c new file mode 100644 index 0000000..8b7545c --- /dev/null +++ b/user_space.c @@ -0,0 +1,22 @@ +void enter_user_space() +{ + asm volatile("cli; \ + mov $0x23, %ax; \ + mov %ax, %ds; \ + mov %ax, %es; \ + mov %ax, %fs; \ + mov %ax, %gs; \ + mov %esp, %eax; \ + push $0x23; \ + push %eax; \ + pushf; \ + pop %eax; \ + or $0x200, %eax; \ + push %eax; \ + push $0x1B; \ + push $user_code_entry; \ + iret; \ + user_code_entry: \ + call main; \ + "); +} \ No newline at end of file diff --git a/user_space.h b/user_space.h new file mode 100644 index 0000000..7102d6a --- /dev/null +++ b/user_space.h @@ -0,0 +1,6 @@ +#ifndef _USER_SPACE_H +#define _USER_SPACE_H + +void enter_user_space(void); + +#endif \ No newline at end of file diff --git a/vga.c b/vga.c new file mode 100644 index 0000000..a23632e --- /dev/null +++ b/vga.c @@ -0,0 +1,31 @@ +#include "vga.h" + +uint8_t vga_entry_color(enum vga_color fg, enum vga_color bg) { + return fg | bg << 4; +} + +uint16_t vga_entry(unsigned char uc, uint8_t color) { + return (uint16_t) uc | (uint16_t) color << 8; +} + +uint8_t vga_color_from_char(char color_code) { + switch (color_code) { + case '0': return VGA_COLOR_BLACK; + case '1': return VGA_COLOR_BLUE; + case '2': return VGA_COLOR_GREEN; + case '3': return VGA_COLOR_CYAN; + case '4': return VGA_COLOR_RED; + case '5': return VGA_COLOR_MAGENTA; + case '6': return VGA_COLOR_BROWN; + case '7': return VGA_COLOR_LIGHT_GREY; + case '8': return VGA_COLOR_DARK_GREY; + case '9': return VGA_COLOR_LIGHT_BLUE; + case 'a': return VGA_COLOR_LIGHT_GREEN; + case 'b': return VGA_COLOR_LIGHT_CYAN; + case 'c': return VGA_COLOR_LIGHT_RED; + case 'd': return VGA_COLOR_LIGHT_MAGENTA; + case 'e': return VGA_COLOR_LIGHT_BROWN; + case 'f': return VGA_COLOR_WHITE; + default: return VGA_COLOR_LIGHT_GREY; + } +} \ No newline at end of file diff --git a/vga.h b/vga.h new file mode 100644 index 0000000..1a4e48f --- /dev/null +++ b/vga.h @@ -0,0 +1,29 @@ +#ifndef VGA_H +#define VGA_H + +#include + +enum vga_color { + VGA_COLOR_BLACK = 0, + VGA_COLOR_BLUE = 1, + VGA_COLOR_GREEN = 2, + VGA_COLOR_CYAN = 3, + VGA_COLOR_RED = 4, + VGA_COLOR_MAGENTA = 5, + VGA_COLOR_BROWN = 6, + VGA_COLOR_LIGHT_GREY = 7, + VGA_COLOR_DARK_GREY = 8, + VGA_COLOR_LIGHT_BLUE = 9, + VGA_COLOR_LIGHT_GREEN = 10, + VGA_COLOR_LIGHT_CYAN = 11, + VGA_COLOR_LIGHT_RED = 12, + VGA_COLOR_LIGHT_MAGENTA = 13, + VGA_COLOR_LIGHT_BROWN = 14, + VGA_COLOR_WHITE = 15, +}; + +uint8_t vga_entry_color(enum vga_color fg, enum vga_color bg); +uint16_t vga_entry(unsigned char uc, uint8_t color); +uint8_t vga_color_from_char(char color_code); + +#endif \ No newline at end of file