From 2c251fa51cb39b9abafeaa2af62d0614d25bea7a Mon Sep 17 00:00:00 2001 From: Mathieu Maret Date: Fri, 20 Jul 2018 15:41:58 +0200 Subject: [PATCH] Initial import from mbr_asm project --- Makefile | 43 ++++++ core/exception.c | 24 ++++ core/exception.h | 46 ++++++ core/exception_handler.c | 12 ++ core/gdt.c | 147 +++++++++++++++++++ core/gdt.h | 39 +++++ core/idt.c | 68 +++++++++ core/idt.h | 45 ++++++ core/interrupt.h | 11 ++ core/io.h | 21 +++ core/irq.c | 32 +++++ core/irq.h | 40 ++++++ core/irq_handler.c | 22 +++ core/klibc.c | 11 ++ core/klibc.h | 4 + core/main.c | 37 +++++ core/segment.h | 59 ++++++++ core/types.h | 47 ++++++ drivers/keyboard.c | 301 +++++++++++++++++++++++++++++++++++++++ drivers/keyboard.h | 6 + drivers/pic.c | 73 ++++++++++ drivers/pic.h | 21 +++ drivers/pit.c | 14 ++ drivers/pit.h | 16 +++ drivers/vga.c | 142 ++++++++++++++++++ drivers/vga.h | 27 ++++ linker.ld | 26 ++++ mbr.asm | 161 +++++++++++++++++++++ 28 files changed, 1495 insertions(+) create mode 100644 Makefile create mode 100644 core/exception.c create mode 100644 core/exception.h create mode 100644 core/exception_handler.c create mode 100644 core/gdt.c create mode 100644 core/gdt.h create mode 100644 core/idt.c create mode 100644 core/idt.h create mode 100644 core/interrupt.h create mode 100644 core/io.h create mode 100644 core/irq.c create mode 100644 core/irq.h create mode 100644 core/irq_handler.c create mode 100644 core/klibc.c create mode 100644 core/klibc.h create mode 100644 core/main.c create mode 100644 core/segment.h create mode 100644 core/types.h create mode 100644 drivers/keyboard.c create mode 100644 drivers/keyboard.h create mode 100644 drivers/pic.c create mode 100644 drivers/pic.h create mode 100644 drivers/pit.c create mode 100644 drivers/pit.h create mode 100644 drivers/vga.c create mode 100644 drivers/vga.h create mode 100644 linker.ld create mode 100644 mbr.asm diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..87de96c --- /dev/null +++ b/Makefile @@ -0,0 +1,43 @@ +# Used to generated .d file that deal with header dependencies +CPPFLAGS = -MMD +AS=nasm +ASFLAGS += -f elf32 +LDFLAGS += -m32 -nostdlib -static -fno-common -fno-use-cxa-atexit -fno-exceptions -fno-non-call-exceptions -fno-weak -fno-rtti -fno-stack-protector +CFLAGS += -m32 -Wall -Wextra -Werror -ffreestanding -fno-exceptions -fno-pie -fno-stack-protector +CXXFLAGS += -m32 -Wall -Wextra -Werror -ffreestanding -fno-exceptions -fno-rtti -fno-pie + +SUBDIRS := core drivers + +CPPFLAGS += $(foreach dir, $(SUBDIRS), -I$(dir)) + +asmsrc=$(wildcard *.asm) +asmobj=$(asmsrc:%.asm=%.o) +csrc=$(shell find $(SUBDIRS) -type f -name "*.c")# $(wildcard *.c) +cobj=$(csrc:%.c=%.o) +deps = $(csrc:%.c=%.d) + +kernel:$(asmobj) $(cobj) linker.ld + $(CXX) $(LDFLAGS) $(cobj) $(asmobj) -o $@ -T linker.ld + +fd.img: kernel + dd if=/dev/zero of=$@ bs=512 count=2880 + dd if=$< of=$@ conv=notrunc + +#https://gcc.gnu.org/onlinedocs/gcc/x86-Function-Attributes.html#x86-Function-Attributes +core/exception_handler.o:core/exception_handler.c + $(CC) $(CPPFLAGS) $(CFLAGS) -mgeneral-regs-only -c $< -o $@ +core/irq_handler.o:core/irq_handler.c + $(CC) $(CPPFLAGS) $(CFLAGS) -mgeneral-regs-only -c $< -o $@ + +%.o:%.asm + $(AS) $(ASFLAGS) -o $@ $< + +test:kernel + qemu-system-x86_64 -fda $< + +clean: + $(RM) kernel $(asmobj) $(cobj) $(deps) + +ifneq ($(MAKECMDGOALS),clean) +-include $(deps) +endif diff --git a/core/exception.c b/core/exception.c new file mode 100644 index 0000000..d2743dc --- /dev/null +++ b/core/exception.c @@ -0,0 +1,24 @@ +#include "exception.h" +#include "idt.h" +#include "interrupt.h" +#include "irq.h" + +exception_handler exception_handler_array[EXCEPTION_NUM] = { + NULL, +}; + +int exceptionSetRoutine(int exception, exception_handler handler) +{ + uint32_t flags; + if ((exception < 0) || exception >= EXCEPTION_NUM) + return -1; + + disable_IRQs(flags); + + exception_handler_array[exception] = handler; + + idt_set_handler(EXCEPTION_INTERRUPT_BASE_ADDRESS + exception, (unsigned int)handler, + 0); + restore_IRQs(flags); + return 0; +} diff --git a/core/exception.h b/core/exception.h new file mode 100644 index 0000000..465f752 --- /dev/null +++ b/core/exception.h @@ -0,0 +1,46 @@ +#pragma once +#include "interrupt.h" +#include "types.h" + +#define EXCEPTION_INTERRUPT_BASE_ADDRESS 0 + +// list and description https://wiki.osdev.org/Exceptions + +#define EXCEPTION_DIVIDE_ZERO 0 +#define EXCEPTION_DEBUG 1 +#define EXCEPTION_NMI 2 +#define EXCEPTION_BREAKPOINT 3 +#define EXCEPTION_OVERFLOW 4 +#define EXCEPTION_BOUND_RANGE_EXCEEDED 5 +#define EXCEPTION_INVALID_OPCODE 6 +#define EXCEPTION_DEVICE_NOT_AVAILABLE 7 +#define EXCEPTION_DOUBLE_FAULT 8 +#define EXCEPTION_COPRO_OVERRUN 9 +#define EXCEPTION_INVALID_TSS 10 +#define EXCEPTION_SEGMENT_NOT_PRESENT 11 +#define EXCEPTION_STACK_SEGMENT_FAULT 12 +#define EXCEPTION_GENERAL_PROTECTION_FAULT 13 +#define EXCEPTION_PAGE_FAULT 14 +#define EXCEPTION_RESERVED_1 15 +#define EXCEPTION_X87_FP_EXCEPTION 16 +#define EXCEPTION_ALIGNMENT_CHECK 17 +#define EXCEPTION_MACHINE_CHECK 18 +#define EXCEPTION_SIMD_FP 19 +#define EXCEPTION_VIRTUALIZATION 20 +#define EXCEPTION_RESERVED_2 21 +#define EXCEPTION_RESERVED_3 22 +#define EXCEPTION_RESERVED_4 23 +#define EXCEPTION_RESERVED_5 24 +#define EXCEPTION_RESERVED_6 25 +#define EXCEPTION_RESERVED_7 26 +#define EXCEPTION_RESERVED_8 27 +#define EXCEPTION_RESERVED_9 28 +#define EXCEPTION_RESERVED_10 29 +#define EXCEPTION_SECURITY 30 +#define EXCEPTION_RESERVED_11 31 + +#define EXCEPTION_NUM 32 + +typedef void (*exception_handler) (struct interrupt_frame *frame, ulong error_code); +int exceptionSetRoutine(int exception, exception_handler handler); + diff --git a/core/exception_handler.c b/core/exception_handler.c new file mode 100644 index 0000000..d130c13 --- /dev/null +++ b/core/exception_handler.c @@ -0,0 +1,12 @@ +#include "exception.h" +#include "vga.h" + +// Need GCC > 6 +__attribute__((interrupt)) void print_handler(struct interrupt_frame *frame, ulong error_code) +{ + + printStringDetails("EXCEPTION", RED, BLACK, 0, VGA_HEIGHT - 1); + printIntDetails(error_code, RED, BLACK, 11, VGA_HEIGHT - 1); + (void)frame; + (void)error_code; +} diff --git a/core/gdt.c b/core/gdt.c new file mode 100644 index 0000000..ff0df71 --- /dev/null +++ b/core/gdt.c @@ -0,0 +1,147 @@ +/* Copyright (C) 2004 David Decotigny + Copyright (C) 1999 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + USA. +*/ +#include "segment.h" + +#include "gdt.h" + +/** + * The sructure of a segment descriptor. + * + * @see Intel x86 doc, Vol 3, section 3.4.3, figure 3-8. For segment + * types, see section 3.5 + */ +struct x86_segment_descriptor { + /* Lowest dword */ + uint16_t limit_15_0; /* Segment limit, bits 15..0 */ + uint16_t base_paged_addr_15_0; /* Base address, bits 15..0 */ + + /* Highest dword */ + uint8_t base_paged_addr_23_16; /* Base address bits 23..16 */ + uint8_t segment_type : 4; /* Section 3.4.3.1 (code/data) + and 3.5 (system) of Intel x86 vol 3 */ + uint8_t descriptor_type : 1; /* 0=system, 1=Code/Data */ + uint8_t dpl : 2; + uint8_t present : 1; + + uint8_t limit_19_16 : 4; /* Segment limit, bits 19..16 */ + uint8_t custom : 1; + uint8_t zero : 1; + uint8_t op_size : 1; /* 0=16bits instructions, 1=32bits */ + uint8_t granularity : 1; /* 0=limit in bytes, 1=limit in pages */ + + uint8_t base_paged_addr_31_24; /* Base address bits 31..24 */ +} __attribute__((packed, aligned(8))); + +/** + * The GDT register, which stores the address and size of the + * GDT. + * + * @see Intel x86 doc vol 3, section 2.4, figure 2-4; and section + * 3.5.1 + */ +struct x86_gdt_register { + /* The maximum GDT offset allowed to access an entry in the GDT */ + uint16_t limit; + + /* This is not exactly a "virtual" address, ie an adddress such as + those of instructions and data; this is a "linear" address, ie an + address in the paged memory. However, in SOS we configure the + segmented memory as a "flat" space: the 0-4GB segment-based (ie + "virtual") addresses directly map to the 0-4GB paged memory (ie + "linear"), so that the "linear" addresses are numerically equal + to the "virtual" addresses: this base_addr will thus be the same + as the address of the gdt array */ + uint32_t base_addr; +} __attribute__((packed, aligned(8))); + +/** + * Helper macro that builds a Segment descriptor for the virtual + * 0..4GB addresses to be mapped to the linear 0..4GB linear + * addresses. + */ +#define BUILD_GDTE(descr_privilege_level, is_code) \ + ((struct x86_segment_descriptor){ \ + .limit_15_0 = 0xffff, \ + .base_paged_addr_15_0 = 0, \ + .base_paged_addr_23_16 = 0, \ + .segment_type = \ + ((is_code) ? 0xb : 0x3), /* With descriptor_type (below) = 1 (code/data), \ + * see Figure 3-1 of section 3.4.3.1 in Intel \ + * x86 vol 3: \ + * - Code (bit 3 = 1): \ + * bit 0: 1=Accessed \ + * bit 1: 1=Readable \ + * bit 2: 0=Non-Conforming \ + * - Data (bit 3 = 0): \ + * bit 0: 1=Accessed \ + * bit 1: 1=Writable \ + * bit 2: 0=Expand up (stack-related) \ + * For Conforming/non conforming segments, see \ + * Intel x86 Vol 3 section 4.8.1.1 \ + */ \ + .descriptor_type = 1, /* 1=Code/Data */ \ + .dpl = ((descr_privilege_level)&0x3), \ + .present = 1, \ + .limit_19_16 = 0xf, \ + .custom = 0, \ + .op_size = 1, /* 32 bits instr/data */ \ + .granularity = 1 /* limit is in 4kB Pages */ \ + }) + +/** The actual GDT */ +static struct x86_segment_descriptor gdt[] = { + [SOS_SEG_NULL] = + (struct x86_segment_descriptor){ + 0, + }, + [SOS_SEG_KCODE] = BUILD_GDTE(0, 1), + [SOS_SEG_KDATA] = BUILD_GDTE(0, 0), +}; + +int gdtSetup(void) +{ + struct x86_gdt_register gdtr; + + /* Address of the GDT */ + gdtr.base_addr = (uint32_t)gdt; + + /* The limit is the maximum offset in bytes from the base address of + the GDT */ + gdtr.limit = sizeof(gdt) - 1; + + /* Commit the GDT into the CPU, and update the segment + registers. The CS register may only be updated with a long jump + to an absolute address in the given segment (see Intel x86 doc + vol 3, section 4.8.1). */ + asm volatile("lgdt %0 \n\ + ljmp %1,$1f \n\ + 1: \n\ + movw %2, %%ax \n\ + movw %%ax, %%ss \n\ + movw %%ax, %%ds \n\ + movw %%ax, %%es \n\ + movw %%ax, %%fs \n\ + movw %%ax, %%gs" + : + : "m"(gdtr), "i"(SOS_BUILD_SEGMENT_REG_VALUE(0, FALSE, SOS_SEG_KCODE)), + "i"(SOS_BUILD_SEGMENT_REG_VALUE(0, FALSE, SOS_SEG_KDATA)) + : "memory", "eax"); + + return 0; +} diff --git a/core/gdt.h b/core/gdt.h new file mode 100644 index 0000000..8957891 --- /dev/null +++ b/core/gdt.h @@ -0,0 +1,39 @@ +/* Copyright (C) 2004 David Decotigny + Copyright (C) 1999 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + USA. +*/ +#ifndef _SOS_GDT_H_ +#define _SOS_GDT_H_ + +/** + * @file gdt.h + * + * The routines that manage the GDT, the table that maps the virtual + * addresses (data/instructions, segment-relative), to "linear" + * addresses (ie paged-memory). In SOS/x86, we use a "flat" virtual + * space, ie the virtual and linear spaces are equivalent. + * + * @see Intel x86 doc vol 3, chapter 3 + */ + +/** + * Configure the virtual space as a direct mapping to the linear + * address space (ie "flat" virtual space). + */ +int gdtSetup(void); + +#endif /* _SOS_GDT_H_ */ diff --git a/core/idt.c b/core/idt.c new file mode 100644 index 0000000..063611d --- /dev/null +++ b/core/idt.c @@ -0,0 +1,68 @@ +#include "idt.h" +static struct idtEntry idt[IDT_NUM]; + +int idtSetup() +{ + struct idtRegister idtr; + + for (int i = 0; i < IDT_NUM; i++) { + struct idtEntry *idte = idt + i; + + /* Setup an empty IDTE interrupt gate, see figure 5-2 in Intel + x86 doc, vol 3 */ + idte->seg_sel = BUILD_SEGMENT_SELECTOR(RING_0, 0, SEGMENT_IDX_CODE); + idte->reserved = 0; + idte->flags = 0; + idte->type = 0x6; /* Interrupt gate (110b) */ + idte->op_size = 1; /* 32bits instructions */ + + /* Disabled it for now */ + idte->zero = 0; + idte->offset_low = 0; + idte->offset_high = 0; + idte->dpl = 0; + idte->present = 0; + } + + /* + * Setup the IDT register, see Intel x86 doc vol 3, section 5.8. + */ + + /* Address of the IDT */ + idtr.base_addr = (uint32_t)idt; + + /* The limit is the maximum offset in bytes from the base address of + the IDT */ + idtr.limit = sizeof(idt) - 1; + + /* Commit the IDT into the CPU */ + asm volatile("lidt %0\n" ::"m"(idtr) : "memory"); + + return 0; +} + +int idt_set_handler(int index, unsigned int addr, int priviledge) +{ + struct idtEntry *idte; + + if (index < 0 || index >= IDT_NUM) + return -1; + if ((priviledge < 0) || priviledge > 3) + return -1; + + idte = idt + index; + + if (addr != (unsigned int)NULL) { + idte->offset_low = addr & 0xffff; + idte->offset_high = (addr >> 16) & 0xffff; + idte->dpl = priviledge; + idte->present = 1; + } else { + idte->offset_low = 0; + idte->offset_high = 0; + idte->dpl = 0; + idte->present = 0; + } + + return 0; +} diff --git a/core/idt.h b/core/idt.h new file mode 100644 index 0000000..a87266d --- /dev/null +++ b/core/idt.h @@ -0,0 +1,45 @@ +#pragma once +#include "types.h" + +#define IDT_NUM 256 + +#define RING_0 0 +#define RING_1 1 +#define RING_2 2 +#define RING_3 3 + +#define SEGMENT_IDX_NULL 0 +#define SEGMENT_IDX_CODE 1 +#define SEGMENT_IDX_DATA 2 + +struct idtEntry { + uint16_t offset_low; + uint16_t seg_sel; + uint8_t reserved : 5; + uint8_t flags : 3; + uint8_t type : 3; + uint8_t op_size : 1; + uint8_t zero : 1; + uint8_t dpl : 2; + uint8_t present : 1; + uint16_t offset_high; +} __attribute__((packed)); + +/** + * The IDT register, which stores the address and size of the + * IDT. + * + * @see Intel x86 doc vol 3, section 2.4, figure 2-4 + */ +struct idtRegister { + uint16_t limit; + uint32_t base_addr; +} __attribute__((packed, aligned(8))); + +/* Build segment http://wiki.osdev.org/Selector*/ +#define BUILD_SEGMENT_SELECTOR(desc_privilege, in_ldt, index) \ + ((((desc_privilege)&0x3) << 0) | (((in_ldt) ? 1 : 0) << 2) | \ + ((index) << 3)) + +int idtSetup(); +int idt_set_handler(int index, unsigned int addr, int priviledge); diff --git a/core/interrupt.h b/core/interrupt.h new file mode 100644 index 0000000..45b7bfc --- /dev/null +++ b/core/interrupt.h @@ -0,0 +1,11 @@ +#pragma once +#include "types.h" + +struct interrupt_frame; + +//Exception +void print_handler(struct interrupt_frame *frame, ulong error_code); + +//IRQ +void keyboard_handler(struct interrupt_frame *frame); +void timer_handler(struct interrupt_frame *frame); diff --git a/core/io.h b/core/io.h new file mode 100644 index 0000000..9f0a62b --- /dev/null +++ b/core/io.h @@ -0,0 +1,21 @@ +#pragma once +#include "types.h" + +// NIH http://wiki.osdev.org/Inline_Assembly/Examples#I.2FO_access +static inline void outb(uint16_t port, uint8_t val) +{ + asm volatile ( "outb %0, %1" : : "a"(val), "Nd"(port) ); + /* There's an outb %al, $imm8 encoding, for compile-time constant port numbers that fit in 8b. (N constraint). + * Wider immediate constants would be truncated at assemble-time (e.g. "i" constraint). + * The outb %al, %dx encoding is the only option for all other cases. + * %1 expands to %dx because port is a uint16_t. %w1 could be used if we had the port number a wider C type */ +} + +static inline uint8_t inb(uint16_t port) +{ + uint8_t ret; + asm volatile ( "inb %1, %0" + : "=a"(ret) + : "Nd"(port) ); + return ret; +} diff --git a/core/irq.c b/core/irq.c new file mode 100644 index 0000000..09ee6b2 --- /dev/null +++ b/core/irq.c @@ -0,0 +1,32 @@ +#include "irq.h" +#include "idt.h" +#include "pic.h" +#include "types.h" + +int irqSetup() +{ + initPic(); + return 0; +} + +irq_handler irq_handler_array[IRQ_NUM] = {NULL, }; + +int irqSetRoutine(int irq, irq_handler handler) +{ + uint32_t flags; + if ((irq < 0) || irq >= IRQ_NUM) + return -1; + + disable_IRQs(flags); + + irq_handler_array[irq] = handler; + + if (handler != NULL) { + int ret = + idt_set_handler(IRQ_INTERRUPT_BASE_ADDRESS + irq, (unsigned int)irq_handler_array[irq], 0); + if (!ret) + enableIrq(irq); + } + restore_IRQs(flags); + return 0; +} diff --git a/core/irq.h b/core/irq.h new file mode 100644 index 0000000..aab25f0 --- /dev/null +++ b/core/irq.h @@ -0,0 +1,40 @@ +#pragma once +#include "interrupt.h" + +#define save_flags(flags) asm volatile("pushfl ; popl %0" : "=g"(flags)::"memory") +#define restore_flags(flags) asm volatile("push %0; popfl" ::"g"(flags) : "memory") + +#define disable_IRQs(flags) \ + ({ \ + save_flags(flags); \ + asm("cli\n"); \ + }) +#define restore_IRQs(flags) restore_flags(flags) + + +#define IRQ_TIMER 0 // MASTER IRQ +#define IRQ_KEYBOARD 1 +#define IRQ_SLAVE_PIC 2 +#define IRQ_COM2 3 +#define IRQ_COM1 4 +#define IRQ_LPT2 5 +#define IRQ_FLOPPY 6 +#define IRQ_LPT1 7 +#define IRQ_8_NOT_DEFINED 8 // SLAVE +#define IRQ_RESERVED_1 9 // SLAVE IRQ +#define IRQ_RESERVED_2 10 +#define IRQ_RESERVED_3 11 +#define IRQ_RESERVED_4 12 +#define IRQ_COPROCESSOR 13 +#define IRQ_HARDDISK 14 +#define IRQ_RESERVED_5 15 + +#define IRQ_INTERRUPT_BASE_ADDRESS 0x20 +#define IRQ_NUM 16 + +// An handler should finish by the iret opcode -> https://wiki.osdev.org/Interrupt_Service_Routines +// That's why we use wrapper around them or the gcc interrupt attribut +// __attribute__((interrupt)) void (*irq_handler)(int irq); +typedef void (*irq_handler)(struct interrupt_frame *frame); +int irqSetup(); +int irqSetRoutine(int irq, irq_handler handler); diff --git a/core/irq_handler.c b/core/irq_handler.c new file mode 100644 index 0000000..971f699 --- /dev/null +++ b/core/irq_handler.c @@ -0,0 +1,22 @@ +#include "interrupt.h" +#include "io.h" +#include "irq.h" +#include "keyboard.h" +#include "pic.h" +#include "vga.h" + +// Need GCC > 6 +__attribute__((interrupt)) void keyboard_handler(struct interrupt_frame *frame) +{ + EOIIrq(IRQ_KEYBOARD); + keyboard_do_irq(); + (void)frame; +} + +__attribute__((interrupt)) void timer_handler(struct interrupt_frame *frame) +{ + static int timeCnt = 0; + EOIIrq(IRQ_TIMER); + printIntDetails(timeCnt++, RED, BLACK, 20, VGA_HEIGHT - 1); + (void)frame; +} diff --git a/core/klibc.c b/core/klibc.c new file mode 100644 index 0000000..e0aea0c --- /dev/null +++ b/core/klibc.c @@ -0,0 +1,11 @@ +#include "klibc.h" + +void *memcpy(void *dst, const void *src, size_t n) +{ + char *dstChar = dst; + const char *srcChar = src; + for (size_t i = 0; i < n; i++) { + *(dstChar++) = *(srcChar++); + } + return dst; +} diff --git a/core/klibc.h b/core/klibc.h new file mode 100644 index 0000000..02b3479 --- /dev/null +++ b/core/klibc.h @@ -0,0 +1,4 @@ +#pragma once +#include "types.h" + +void *memcpy(void *dest, const void *src, size_t n ); diff --git a/core/main.c b/core/main.c new file mode 100644 index 0000000..12f5841 --- /dev/null +++ b/core/main.c @@ -0,0 +1,37 @@ +#include "exception.h" +#include "gdt.h" +#include "idt.h" +#include "interrupt.h" +#include "io.h" +#include "irq.h" +#include "pit.h" +#include "types.h" +#include "vga.h" + +void cpuid(int code, uint32_t *a, uint32_t *d) +{ + asm volatile("cpuid" : "=a"(*a), "=d"(*d) : "0"(code) : "ebx", "ecx"); +} + +void kmain() +{ + initVGA(BLACK, GREEN); + printString("Setting up IDT\n"); + gdtSetup(); + idtSetup(); + irqSetup(); + initPit(100); + + printString("Setting up IRQ handlers\n"); + irqSetRoutine(IRQ_KEYBOARD, keyboard_handler); + irqSetRoutine(IRQ_TIMER, timer_handler); + printString("Enabling HW interrupts\n"); + exceptionSetRoutine(EXCEPTION_DOUBLE_FAULT, print_handler); + // Enabling the HW interrupts + asm volatile("sti\n"); + int count = 0; + while (1) { + printIntDetails(count++, GREEN, BLACK, 0, VGA_HEIGHT - 1); + } + printString("exiting\n"); +} diff --git a/core/segment.h b/core/segment.h new file mode 100644 index 0000000..0a4b24e --- /dev/null +++ b/core/segment.h @@ -0,0 +1,59 @@ +/* Copyright (C) 2004 The SOS Team + Copyright (C) 1999 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + USA. +*/ +#ifndef _SOS_HWSEGS_H_ +#define _SOS_HWSEGS_H_ + +/** + * @file segments.h + * + * Global and local (GDT/LDT) segment descriptor definition and + * structure. These segments map virtual addresses (ie + * data/instruction addresses, relative to these segment descriptors) + * to linear addresses (ie addresses in the paged-memory space). + * + * @see Intel x86 doc, vol 3 chapter 3. + */ + +#include "types.h" + +/* + * Global segment selectors (GDT) for SOS/x86. + * + * @see gdt.h + */ +#define SOS_SEG_NULL 0 /* NULL segment, unused by the procesor */ +#define SOS_SEG_KCODE 1 /* Kernel code segment */ +#define SOS_SEG_KDATA 2 /* Kernel data segment */ + + +/** + * Helper macro that builds a segment register's value + */ +#define SOS_BUILD_SEGMENT_REG_VALUE(desc_privilege,in_ldt,seg_index) \ + ( (((desc_privilege) & 0x3) << 0) \ + | (((in_ldt)?1:0) << 2) \ + | ((seg_index) << 3) ) + + +/* + * Local segment selectors (LDT) for SOS/x86 + */ +/* None */ + +#endif /* _SOS_HWSEGS_H_ */ diff --git a/core/types.h b/core/types.h new file mode 100644 index 0000000..f816264 --- /dev/null +++ b/core/types.h @@ -0,0 +1,47 @@ +#pragma once + +typedef __signed__ char __s8; +typedef unsigned char __u8; + +typedef __signed__ short __s16; +typedef unsigned short __u16; + +typedef __signed__ int __s32; +typedef unsigned int __u32; + +#ifdef __GNUC__ +__extension__ typedef __signed__ long long __s64; +__extension__ typedef unsigned long long __u64; +#else +typedef __signed__ long long __s64; +typedef unsigned long long __u64; +#endif + +/* sysv */ +typedef unsigned char unchar; +typedef unsigned short ushort; +typedef unsigned int uint; +typedef unsigned long ulong; + +typedef __s8 int8_t; +typedef __s16 int16_t; +typedef __s32 int32_t; + +typedef __u8 uint8_t; +typedef __u16 uint16_t; +typedef __u32 uint32_t; + +typedef __u64 uint64_t; +typedef __u64 u_int64_t; +typedef __s64 int64_t; + +typedef enum { FALSE=0, TRUE } bool_t; +#define NULL ((void*)0) + +#if __x86_64__ +typedef unsigned long size_t; +typedef long ssize_t; +#else +typedef unsigned int size_t; +typedef int ssize_t; +#endif diff --git a/drivers/keyboard.c b/drivers/keyboard.c new file mode 100644 index 0000000..a111e72 --- /dev/null +++ b/drivers/keyboard.c @@ -0,0 +1,301 @@ +#include "keyboard.h" +#include "io.h" +#include "vga.h" + +const char *scancode[128] = { + /* 0 */ 0, + /* 1 */ "\e", /* escape */ + /* 2 */ "&", + /* 3 */ "é", + /* 4 */ "\"", + /* 5 */ "'", + /* 6 */ "(", + /* 7 */ "-", + /* 8 */ "è", + /* 9 */ "_", + /* 10 */ "ç", + /* 11 */ "à", + /* 12 */ ")", + /* 13 */ "=", + /* 14 */ "\b", /* Backspace */ + /* 15 */ "\t", /* Tab */ + /* 16 */ "a", + /* 17 */ "z", + /* 18 */ "e", + /* 19 */ "r", + /* 20 */ "t", + /* 21 */ "y", + /* 22 */ "u", + /* 23 */ "i", + /* 24 */ "o", + /* 25 */ "p", + /* 26 */ "^", + /* 27 */ "$", + /* 28 */ "\n", + /* 29 */ 0, /* left control */ + /* 30 */ "q", + /* 31 */ "s", + /* 32 */ "d", + /* 33 */ "f", + /* 34 */ "g", + /* 35 */ "h", + /* 36 */ "j", + /* 37 */ "k", + /* 38 */ "l", + /* 39 */ "m", + /* 40 */ "ù", + /* 41 */ 0, /*²*/ + /* 42 */ 0, /* left shift */ + /* 43 */ "*", + /* 44 */ "w", + /* 45 */ "x", + /* 46 */ "c", + /* 47 */ "v", + /* 48 */ "b", + /* 49 */ "n", + /* 50 */ ",", + /* 51 */ ";", + /* 52 */ ":", + /* 53 */ "!", + /* 54 */ 0, /* right shift*/ + /* 55 */ 0, + /* 56 */ 0, /* left alt*/ + /* 57 */ " ", + /* 58 */ 0, + /* 59 */ "\eOP", /* F1 */ + /* 60 */ "\eOQ", /* F2 */ + /* 61 */ "\eOR", /* F3 */ + /* 62 */ "\eOS", /* F4 */ + /* 63 */ "\e[15~", /* F5 */ + /* 64 */ "\e[17~", /* F6 */ + /* 65 */ "\e[18~", /* F7 */ + /* 66 */ "\e[19~", /* F8 */ + /* 67 */ "\e[20~", /* F9 */ + /* 68 */ "\e[21~", /* F10 */ + /* 69 */ 0, + /* 70 */ 0, + /* 71 */ 0, + /* 72 */ 0, + /* 73 */ 0, + /* 74 */ 0, + /* 75 */ 0, + /* 76 */ 0, + /* 77 */ 0, + /* 78 */ 0, + /* 79 */ 0, + /* 80 */ 0, + /* 81 */ 0, + /* 82 */ 0, + /* 83 */ 0, + /* 84 */ 0, + /* 85 */ 0, + /* 86 */ "<", + /* 87 */ "\e[23~", /* F11 */ + /* 88 */ "\e[24~", /* F12 */ + /* 89 */ 0, + /* 90 */ 0, + /* 91 */ 0, + /* 92 */ 0, + /* 93 */ 0, + /* 94 */ 0, + /* 95 */ 0, + /* 96 */ 0, + /* 97 */ 0, + /* 98 */ 0, + /* 99 */ 0, + /* 100 */ 0, + /* 101 */ 0, + /* 102 */ 0, + /* 103 */ 0, + /* 104 */ 0, + /* 105 */ 0, + /* 106 */ 0, + /* 107 */ 0, + /* 108 */ 0, + /* 109 */ 0, + /* 110 */ 0, + /* 111 */ 0, + /* 112 */ 0, + /* 113 */ 0, + /* 114 */ 0, + /* 115 */ 0, + /* 116 */ 0, + /* 117 */ 0, + /* 118 */ 0, + /* 119 */ 0, + /* 120 */ 0, + /* 121 */ 0, + /* 122 */ 0, + /* 123 */ 0, + /* 124 */ 0, + /* 125 */ 0, + /* 126 */ 0, + /* 127 */ 0}; + +const char *scancode_shift[128] = { + /* 0 */ 0, + /* 1 */ "\e", + /* 2 */ "1", + /* 3 */ "2", + /* 4 */ "3", + /* 5 */ "4", + /* 6 */ "5", + /* 7 */ "6", + /* 8 */ "7", + /* 9 */ "8", + /* 10 */ "9", + /* 11 */ "0", + /* 12 */ "°", + /* 13 */ "+", + /* 14 */ "\b", /* Shift-Backspace */ + /* 15 */ "\e[Z", /* Shift-Tab */ + /* 16 */ "A", + /* 17 */ "Z", + /* 18 */ "E", + /* 19 */ "R", + /* 20 */ "T", + /* 21 */ "Y", + /* 22 */ "U", + /* 23 */ "I", + /* 24 */ "O", + /* 25 */ "P", + /* 26 */ "\"", + /* 27 */ "£", + /* 28 */ "\n", + /* 29 */ 0, /* left control */ + /* 30 */ "Q", + /* 31 */ "S", + /* 32 */ "D", + /* 33 */ "F", + /* 34 */ "G", + /* 35 */ "H", + /* 36 */ "J", + /* 37 */ "K", + /* 38 */ "L", + /* 39 */ "M", + /* 40 */ "%", + /* 41 */ 0, + /* 42 */ 0, + /* 43 */ "µ", + /* 44 */ "W", + /* 45 */ "X", + /* 46 */ "C", + /* 47 */ "V", + /* 48 */ "B", + /* 49 */ "N", + /* 50 */ "?", + /* 51 */ ".", + /* 52 */ "/", + /* 53 */ "§", + /* 54 */ 0, + /* 55 */ 0, + /* 56 */ 0, + /* 57 */ 0, + /* 58 */ 0, + /* 59 */ "\eOP", /* Shift-F1 */ + /* 60 */ "\eOQ", /* Shift-F2 */ + /* 61 */ "\eOR", /* Shift-F3 */ + /* 62 */ "\eOS", /* Shift-F4 */ + /* 63 */ "\e[15;2~", /* Shift-F5 */ + /* 64 */ "\e[17;2~", /* Shift-F6 */ + /* 65 */ "\e[18;2~", /* Shift-F7 */ + /* 66 */ "\e[19;2~", /* Shift-F8 */ + /* 67 */ "\e[20:2~", /* Shift-F9 */ + /* 68 */ "\e[21:2~", /* Shift-F10 */ + /* 69 */ 0, + /* 70 */ 0, + /* 71 */ 0, + /* 72 */ 0, + /* 73 */ 0, + /* 74 */ 0, + /* 75 */ 0, + /* 76 */ 0, + /* 77 */ 0, + /* 78 */ 0, + /* 79 */ 0, + /* 80 */ 0, + /* 81 */ 0, + /* 82 */ 0, + /* 83 */ 0, + /* 84 */ 0, + /* 85 */ 0, + /* 86 */ ">", + /* 87 */ "\e[23;2~", /* Shift-F11 */ + /* 88 */ "\e[24;2~", /* Shift-F12 */ + /* 89 */ 0, + /* 90 */ 0, + /* 91 */ 0, + /* 92 */ 0, + /* 93 */ 0, + /* 94 */ 0, + /* 95 */ 0, + /* 96 */ 0, + /* 97 */ 0, + /* 98 */ 0, + /* 99 */ 0, + /* 100 */ 0, + /* 101 */ 0, + /* 102 */ 0, + /* 103 */ 0, + /* 104 */ 0, + /* 105 */ 0, + /* 106 */ 0, + /* 107 */ 0, + /* 108 */ 0, + /* 109 */ 0, + /* 110 */ 0, + /* 111 */ 0, + /* 112 */ 0, + /* 113 */ 0, + /* 114 */ 0, + /* 115 */ 0, + /* 116 */ 0, + /* 117 */ 0, + /* 118 */ 0, + /* 119 */ 0, + /* 120 */ 0, + /* 121 */ 0, + /* 122 */ 0, + /* 123 */ 0, + /* 124 */ 0, + /* 125 */ 0, + /* 126 */ 0, + /* 127 */ 0}; + +void keyboard_do_irq() +{ + static int lshift = 0; + static int rshift = 0; + unsigned char c = 0; + if (inb(0x60) != c) { + c = inb(0x60); + if (c > 0) { + if (c < BREAK_CODE) { + switch (c) { + case 42: + lshift = 1; + break; + case 54: + rshift = 1; + break; + default: + if (lshift || rshift) + printString(scancode_shift[(int)c]); + else + printString(scancode[(int)c]); + } + }else { + c = c - BREAK_CODE; + switch (c) { + case 42: + lshift = 0; + break; + case 54: + rshift = 0; + break; + } + } + } + } +} + diff --git a/drivers/keyboard.h b/drivers/keyboard.h new file mode 100644 index 0000000..7f1efb4 --- /dev/null +++ b/drivers/keyboard.h @@ -0,0 +1,6 @@ +#pragma once + +// Pushing generate MAKE_CODE (<0x80) +// Releasing generate BREAK_CODE (break_code = make_code + 0x80) +#define BREAK_CODE 0x80 +void keyboard_do_irq(); diff --git a/drivers/pic.c b/drivers/pic.c new file mode 100644 index 0000000..bdae842 --- /dev/null +++ b/drivers/pic.c @@ -0,0 +1,73 @@ +#include "pic.h" +#include "io.h" +#include "irq.h" + +#define ICW1_ICW4 0x01 /* ICW4 (not) needed */ +#define ICW1_SINGLE 0x02 /* Single (cascade) mode */ +#define ICW1_INTERVAL4 0x04 /* Call address interval 4 (8) */ +#define ICW1_LEVEL 0x08 /* Level triggered (edge) mode */ +#define ICW1_INIT 0x10 /* Initialization - required! */ + +#define ICW4_8086 0x01 /* 8086/88 (MCS-80/85) mode */ +#define ICW4_AUTO 0x02 /* Auto (normal) EOI */ +#define ICW4_BUF_SLAVE 0x08 /* Buffered mode/slave */ +#define ICW4_BUF_MASTER 0x0C /* Buffered mode/master */ +#define ICW4_SFNM 0x10 /* Special fully nested (not) */ + +void initPic(void) +{ + /* Send CMD: Init + sequence in 4 DATA */ + outb(PIC_MASTER_CMD, ICW1_INIT + ICW1_ICW4); + outb(PIC_SLAVE_CMD, ICW1_INIT + ICW1_ICW4); + + /* Send ICW2: ctrl base address. Remap IRQ from interupt range 0x0-0xF to 0x20-0x2F as + * intel + * reserve interupt 0x0-0x1F in protected mode (e.g. 0-7 are CPU exception) */ + outb(PIC_MASTER_DATA, IRQ_INTERRUPT_BASE_ADDRESS); + outb(PIC_SLAVE_DATA, IRQ_INTERRUPT_BASE_ADDRESS + 8); + + /* Send ICW3 master: mask where slaves are connected */ + outb(PIC_MASTER_DATA, 0x4); + /* Send ICW3 slave: index where the slave is connected on master */ + outb(PIC_SLAVE_DATA, 0x2); + + /* Send ICW4: 8086 mode, fully nested, not buffered, no implicit EOI */ + outb(PIC_MASTER_DATA, ICW4_8086); + outb(PIC_SLAVE_DATA, ICW4_8086); + + /* Send OCW1: + * Closing all IRQs : waiting for a correct handler The only IRQ + * enabled is the cascade (that's why we use 0xFB for the master) */ + outb(PIC_MASTER_DATA, 0xFB); + outb(PIC_SLAVE_DATA, 0xFF); +} + +void EOIIrq(int irq) +{ + if (irq >= 8) + outb(PIC_SLAVE_CMD, PIC_EOI); + + outb(PIC_MASTER_CMD, PIC_EOI); +} + +void disableIrq(int irq) +{ + if (irq < 8) { + uint8_t status = inb(PIC_MASTER_DATA); + outb(PIC_MASTER_DATA, (status | (1 << irq))); + } else { + uint8_t status = inb(PIC_SLAVE_DATA); + outb(PIC_SLAVE_DATA, (status | (1 << (irq - 8)))); + } +} + +void enableIrq(int irq) +{ + if (irq < 8) { + uint8_t status = inb(PIC_MASTER_DATA); + outb(PIC_MASTER_DATA, (status & ~(1 << irq))); + } else { + uint8_t status = inb(PIC_SLAVE_DATA); + outb(PIC_SLAVE_DATA, (status & ~(1 << (irq - 8)))); + } +} diff --git a/drivers/pic.h b/drivers/pic.h new file mode 100644 index 0000000..da6f99d --- /dev/null +++ b/drivers/pic.h @@ -0,0 +1,21 @@ +#pragma once +// 2 PIC 8259 are available on x86 +// +// Master - command: 0x20, data: 0x21 +// Slave - command: 0xA0, data: 0xA1 +// +// http://www.jamesmolloy.co.uk/tutorial_html/5.-IRQs%20and%20the%20PIT.html +// SimpleOS art2 +// http://wiki.osdev.org/PIC + +#define PIC_MASTER_CMD 0x20 +#define PIC_SLAVE_CMD 0xa0 +#define PIC_MASTER_DATA 0x21 +#define PIC_SLAVE_DATA 0xa1 +#define PIC_EOI 0x20 /* End-of-interrupt command code */ + + +void EOIIrq(int irq); +void initPic(void); +void enableIrq(int irq); +void disableIrq(int irq); diff --git a/drivers/pit.c b/drivers/pit.c new file mode 100644 index 0000000..f6592a8 --- /dev/null +++ b/drivers/pit.c @@ -0,0 +1,14 @@ +#include "pit.h" +#include "io.h" + +int initPit(unsigned int freq) +{ + unsigned int divisor = PIT_FREQ / freq; + if (divisor > 65535) + divisor = 0; // Used to represent 35536 + outb(PIT_CMD, 0x34); // chan 0; low then high; mode 2 + outb(PIT_CHAN_0, divisor & 0xFF); + outb(PIT_CHAN_0, divisor >> 8u); + + return 0; +} diff --git a/drivers/pit.h b/drivers/pit.h new file mode 100644 index 0000000..2e5f85c --- /dev/null +++ b/drivers/pit.h @@ -0,0 +1,16 @@ +#pragma once + +// C.f https://wiki.osdev.org/PIT + +#define PIT_FREQ 1193182 +#define PIT_CHAN_0 0x40 // IRQ0 +#define PIT_CHAN_1 0x41 // Used for DRAM refresh. Not used anymore +#define PIT_CHAN_2 0x42 // PC Speaker +#define PIT_CMD 0x43 +// Cmd are +// 7-6: select channel. 0 ->chan0, 1 -> chan1, 2 -> chan 2, 3 -> read back +// 5-4: access mode. 0 -> latch count; 1 -> low value only; 2 -> high value only; +// 3 -> low then high 3-1: mode. See https://wiki.osdev.org/PIT + +int initPit(unsigned int freq); + diff --git a/drivers/vga.c b/drivers/vga.c new file mode 100644 index 0000000..82eea9f --- /dev/null +++ b/drivers/vga.c @@ -0,0 +1,142 @@ +#include "vga.h" +#include "io.h" +#include "klibc.h" + +static uint vgaBgColor; +static uint vgaColor; +static int line, col; + +int initVGA(uint bgColor, uint color) +{ + vgaBgColor = bgColor; + vgaColor = color; + clearScreen(bgColor); + line = 0; + col = 0; + return 0; +} + +void clearScreen(uint bgColor) +{ + volatile short *vga = (short *)VGA_ADDR; + long int colorAttr = bgColor << 12; + for (int i = 0; i < VGA_WIDTH * VGA_HEIGHT; i++) { + vga[i] = colorAttr; + } +} + +void printInt(int integer) +{ + char num[sizeof(int) * + 3]; // int max is 2^(sizeof(int)*8) which is (2^3)^(sizeof(int)*8/3) = + // 8^(sizeof(int)*8/3) ~ 10^(sizeof(int)*8/3) + int i = 0, k = 0; + if (integer < 0) { + printChar('-'); + } + while (integer != 0) { + int digit = integer % 10; + num[i++] = (digit > 0) ? digit : -digit; + integer = integer / 10; + } + for (k = i - 1; k >= 0; k--) { + printChar(num[k] + '0'); + } +} + +void printIntDetails(int integer, uint color, uint bgColor, int startX, int startY) +{ + char num[sizeof(int) * + 3]; // int max is 2^(sizeof(int)*8) which is (2^3)^(sizeof(int)*8/3) = + // 8^(sizeof(int)*8/3) ~ 10^(sizeof(int)*8/3) + int x = startX; + int i = 0, k = 0; + if (integer < 0) { + printCharDetails('-', color, bgColor, x++, startY); + } + while (integer != 0) { + int digit = integer % 10; + num[i++] = (digit > 0) ? digit : -digit; + integer = integer / 10; + } + for (k = i - 1; k >= 0; k--) { + printCharDetails(num[k] + '0', color, bgColor, x++, startY); + } +} + +void printCharDetails(const char str, uint color, uint bgColor, int startX, int startY) +{ + volatile short *vga = (short *)VGA_ADDR; + long int colorAttr = (bgColor << 4 | (color & 0x0f)) << 8; + vga[VGA_WIDTH * startY + startX] = colorAttr | str; +} + +void vgaScrollUp(void) +{ + long int colorAttr = vgaBgColor << 12; + volatile short *vga = (short *)VGA_ADDR; + for (int i = 1; i < VGA_HEIGHT - 2; + i++) { // last line is status line. Do not scroll it + memcpy((void *)&vga[VGA_WIDTH * (i - 1)], (void *)&vga[VGA_WIDTH * i], + VGA_WIDTH * sizeof(short)); + } + for (int i = 0; i < VGA_WIDTH; i++) { + vga[(VGA_HEIGHT - 2) * VGA_WIDTH + i] = colorAttr; + } +} + +void printString(const char *str) +{ + while (*str) { + printChar(*(str++)); + } +} + +void printChar(const char str) +{ + if (str == '\n') { + line++; + col = 0; + if (line >= VGA_HEIGHT - 1) { + vgaScrollUp(); + line--; + } + } + else if(str == '\r') + { + col = 0; + } + else if(str == '\b') + { + col--; + if (col < 0) { + col = VGA_WIDTH - 1; + line--; + } + printCharDetails(' ', vgaColor, vgaBgColor, col, line); + } + else + { + printCharDetails(str, vgaColor, vgaBgColor, col++, line); + if (col == VGA_WIDTH) { + col = 0; + line++; + } + if (line >= VGA_HEIGHT - 1) { + vgaScrollUp(); + line--; + } + } +} + +void printStringDetails(const char *str, uint color, uint bgColor, int startX, int startY) +{ + volatile short *vga = (short *)VGA_ADDR; + int i = 0; + long int colorAttr = (bgColor << 4 | (color & 0x0f)) << 8; + while (*str) { + vga[VGA_WIDTH * startY + startX + i] = colorAttr | *str; + str++; + i++; + } +} diff --git a/drivers/vga.h b/drivers/vga.h new file mode 100644 index 0000000..a488719 --- /dev/null +++ b/drivers/vga.h @@ -0,0 +1,27 @@ +#pragma once +#include "types.h" + +// https://wiki.osdev.org/Text_UI +#define BLACK 0x00 +#define BLUE 0x01 +#define GREEN 0x02 +#define CYAN 0x03 +#define RED 0x04 +#define MAGENTA 0x05 +#define BROWN 0x06 +#define GREY 0x07 +#define WHITE 0x0F + +#define VGA_ADDR 0xB8000 +#define VGA_WIDTH 80 +#define VGA_HEIGHT 25 + +int initVGA(uint bgColor, uint color); +void clearScreen(uint bgColor); +void printInt(int integer); +void printIntDetails(int integer, uint color, uint bgColor, int startX, int startY); +void printCharDetails(char str, uint color, uint bgColor, int startX, int startY); +void printStringDetails(const char *str, uint color, uint bgColor, int startX, int startY); +void printString(const char *str); +void printChar(const char str); +void vgaScrollUp(void); diff --git a/linker.ld b/linker.ld new file mode 100644 index 0000000..19d67b2 --- /dev/null +++ b/linker.ld @@ -0,0 +1,26 @@ +ENTRY(boot) +OUTPUT_FORMAT("binary") +SECTIONS { + . = 0x7c00; + .text : + { + *(.boot) + *(.text) + } + + .rodata : + { + *(.rodata) + } + + .data : + { + *(.data) + } + + .bss : + { + *(.bss) + } +} + diff --git a/mbr.asm b/mbr.asm new file mode 100644 index 0000000..8120b83 --- /dev/null +++ b/mbr.asm @@ -0,0 +1,161 @@ +section .boot +bits 16 +global boot +boot: + jmp main + +display_enable: + push bp + mov bp, sp + mov ah, 0h ; 00h Set Video Mode + mov al, 07h ; Txt, monochrome, 80x25 + + int 10h + + mov sp, bp + pop bp + ret + +print: + push bp + mov bp, sp + mov si, [bp + 4]; put first function arg in si. sp is stask pointer +.loop: + lodsb ; load si content into al then inc si + cmp al, 0; + je .end + + mov ah, 0eh + mov bx, 0 + int 10h + + jmp .loop + +.end: + mov sp, bp + pop bp + ret + +println: + push bp + mov bp, sp + push word [bp + 4] + call print + add sp, 2 + + mov ah, 03h ; read cursor position + int 10h ; row number in dh. Col in dl + + inc dh ; goto next line + mov dl, 0 + + mov ah, 02h ; Set Cursor Position + int 10h + mov sp, bp + pop bp + ret + +hello db 'Booting matOs', 0 + +main: + sti ; enable virtual interupts + mov [disk],dl ; save disk used to boot by bios + + call display_enable + + push hello + call println + add sp, 2 + +; Switch in 32bits Protected mode + + ; Activate A20 http://wiki.osdev.org/A20_Line to be able to access more than 1Mb memory + mov ah, 0h + mov ax, 0x2401 + int 0x15 + + ; Change video mode to display VGA + mov ax, 0x3 + int 0x10 + + ; http://www.ctyme.com/intr/rb-0607.htm + ; Bios read first 512 bytes, read next disk sector + mov ah, 0x2 ;read sectors + mov al, 15 ;sectors to read + mov ch, 0 ;cylinder idx + mov dh, 0 ;head idx + mov cl, 2 ;sector idx + mov dl, [disk] ;disk idx + mov bx, copy_target;target pointer + int 0x13 + + cli ; disable interruption when setting GDT + + ; switch in 32 bits + lgdt [gdt_pointer] ; switch in 32bits here + mov eax, cr0 + or eax,0x1; set the protected mode bit on special CPU reg cr0 + mov cr0, eax + jmp CODE_SEG:boot2 ; In protected mode we need to add the segment selector + +; GDT table desciption could be found http://wiki.osdev.org/Global_Descriptor_Table +; here we define the 3 64bits segment needed: null segment, code segment and data segment +gdt_start: ;null segment + dq 0x0 +gdt_code: ;code segment + dw 0xFFFF ; limit [0:15] + dw 0x0 ; base [0:15] + db 0x0 ; base [16:23] + db 10011010b ; access byte: Present(1)| Priv(2) 0 ->kernel 3->userspace | 1 | Executable(1) | Direction/Conformity (1) | RW(1) | Accessed(1) + db 11001111b ; Granularity(1) | Size (1) 0-> 16bit mode 1->32protected mode | 0 | 0 | Limit [16:19] + db 0x0 ; base [24:31] +gdt_data: + dw 0xFFFF + dw 0x0 + db 0x0 + db 10010010b + db 11001111b + db 0x0 +gdt_end: +gdt_pointer: + dw gdt_end - gdt_start + dd gdt_start +disk: + db 0x0 +CODE_SEG equ gdt_code - gdt_start +DATA_SEG equ gdt_data - gdt_start + +times 510 - ($-$$) db 0 +dw 0xaa55 +copy_target: +bits 32 +boot2: + mov ax, DATA_SEG ; set all segments to point to DATA_SEG https://en.wikipedia.org/wiki/X86_memory_segmentation + mov ds, ax ; Data segment + mov es, ax ; Extra Segment (for string operation) + mov fs, ax ; No Specific use + mov gs, ax ; No Specific use + mov ss, ax ; stack segment + mov esi,hello32 + mov ebx,0xb8000 ; Cannot use BIOS anymore, use VGA Text buffer instead + +.loop32: + lodsb + or al,al + jz halt + or eax,0x0100 ; blue bg + mov word [ebx], ax + add ebx,2 + jmp .loop32 +halt: + mov esp,kernel_stack_top + extern kmain + call kmain + cli + hlt +hello32: db "Hello 32 bits world!",0 +section .bss +align 4 +kernel_stack_bottom: equ $ + resb 16384 ; 16 KB +kernel_stack_top: