new file mode 100644
@@ -0,0 +1,33 @@
+CC=gcc
+AS=gcc
+CFLAGS=-m32 -I. -O2 -Wall
+ASFLAGS=-m32 -I.
+OBJS=kernel.o lib.o boot.o memory.o gdt.o idt.o isrs.o tss.o uart.o
+ALLOBJS=$(OBJS) tests/tests.o
+
+PHONY := all
+all: kernel.bin
+ $(MAKE) -C tests
+
+kernel.bin: $(ALLOBJS) kernel.ld
+ ld -T kernel.ld $(ALLOBJS) -o $@
+
+install: kernel.bin
+ cp $< /boot/
+
+tests/tests.o:
+ $(MAKE) -C tests
+
+-include $(OBJS:.o=.d)
+
+# compile and generate dependency info
+%.o: %.c
+ gcc -c $(CFLAGS) $*.c -o $*.o
+ gcc -MM $(CFLAGS) $*.c > $*.d
+
+PHONY += clean
+clean:
+ $(MAKE) -C tests
+ -rm *.o *~ *.d kernel.bin
+
+.PHONY: $(PHONY)
new file mode 100644
@@ -0,0 +1,357 @@
+/* boot.S - bootstrap the kernel */
+/* Copyright (C) 1999, 2001 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., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#define ASM 1
+#include <multiboot.h>
+#include <kernel.h>
+
+.text
+
+.globl start, _start
+start:
+_start:
+jmp multiboot_entry
+
+/* Align 32 bits boundary. */
+.align 4
+
+/* Multiboot header. */
+multiboot_header:
+/* magic */
+.long MULTIBOOT_HEADER_MAGIC
+/* flags */
+.long MULTIBOOT_HEADER_FLAGS
+/* checksum */
+.long -(MULTIBOOT_HEADER_MAGIC + MULTIBOOT_HEADER_FLAGS)
+#ifndef __ELF__
+ /* header_addr */
+ .long multiboot_header
+ /* load_addr */
+ .long _start
+ /* load_end_addr */
+ .long _edata
+ /* bss_end_addr */
+ .long _end
+ /* entry_addr */
+ .long multiboot_entry
+#endif /* ! __ELF__ */
+
+ multiboot_entry:
+ /* Initialize the stack pointer. */
+ movl $(STACK_START), %esp
+
+ /* Reset EFLAGS. */
+ pushl $0
+ popf
+
+ /* Push the pointer to the Multiboot information structure. */
+ pushl %ebx
+ /* Push the magic value. */
+ pushl %eax
+
+ /* Now enter the C main function... */
+ call cmain
+
+ /* Halt. */
+ pushl $halt_message
+ pushl $0
+ call printk
+
+ loop: hlt
+ jmp loop
+
+.globl isr0
+.globl isr1
+.globl isr2
+.globl isr3
+.globl isr4
+.globl isr5
+.globl isr6
+.globl isr7
+.globl isr8
+.globl isr9
+.globl isr10
+.globl isr11
+.globl isr12
+.globl isr13
+.globl isr14
+.globl isr15
+.globl isr16
+.globl isr17
+.globl isr18
+.globl isr19
+.globl isr20
+.globl isr21
+.globl isr22
+.globl isr23
+.globl isr24
+.globl isr25
+.globl isr26
+.globl isr27
+.globl isr28
+.globl isr29
+.globl isr30
+.globl isr31
+
+/* 0: Divide By Zero Exception */
+isr0:
+ cli
+ pushl $0
+ pushl $0
+ jmp isr_common_stub
+
+/* 1: Debug Exception */
+isr1:
+ cli
+ pushl $0
+ pushl $1
+ jmp isr_common_stub
+
+/* 2: Non Maskable Interrupt Exception */
+isr2:
+ cli
+ pushl $0
+ pushl $2
+ jmp isr_common_stub
+
+/* 3: Int 3 Exception */
+isr3:
+ cli
+ pushl $0
+ pushl $3
+ jmp isr_common_stub
+
+/* 4: INTO Exception */
+isr4:
+ cli
+ pushl $0
+ pushl $4
+ jmp isr_common_stub
+
+/* 5: Out of Bounds Exception */
+isr5:
+ cli
+ pushl $0
+ pushl $5
+ jmp isr_common_stub
+
+/* 6: Invalid Opcode Exception */
+isr6:
+ cli
+ pushl $0
+ pushl $6
+ jmp isr_common_stub
+
+/* 7: Coprocessor Not Available Exception */
+isr7:
+ cli
+ pushl $0
+ pushl $7
+ jmp isr_common_stub
+
+/* 8: Double Fault Exception (With Error Code!) */
+isr8:
+ cli
+ pushl $8
+ jmp isr_common_stub
+
+/* 9: Coprocessor Segment Overrun Exception */
+isr9:
+ cli
+ pushl $0
+ pushl $9
+ jmp isr_common_stub
+
+/* 10: Bad TSS Exception (With Error Code!) */
+isr10:
+ cli
+ pushl $10
+ jmp isr_common_stub
+
+/* 11: Segment Not Present Exception (With Error Code!) */
+isr11:
+ cli
+ pushl $11
+ jmp isr_common_stub
+
+/* 12: Stack Fault Exception (With Error Code!) */
+isr12:
+ cli
+ pushl $12
+ jmp isr_common_stub
+
+/* 13: General Protection Fault Exception (With Error Code!) */
+isr13:
+ cli
+ pushl $13
+ jmp isr_common_stub
+
+/* 14: Page Fault Exception (With Error Code!) */
+isr14:
+ cli
+ pushl $14
+ jmp isr_common_stub
+
+/* 15: Reserved Exception */
+isr15:
+ cli
+ pushl $0
+ pushl $15
+ jmp isr_common_stub
+
+/* 16: Floating Point Exception */
+isr16:
+ cli
+ pushl $0
+ pushl $16
+ jmp isr_common_stub
+
+/* 17: Alignment Check Exception */
+isr17:
+ cli
+ pushl $0
+ pushl $17
+ jmp isr_common_stub
+
+/* 18: Machine Check Exception */
+isr18:
+ cli
+ pushl $0
+ pushl $18
+ jmp isr_common_stub
+
+/* 19: Reserved */
+isr19:
+ cli
+ pushl $0
+ pushl $19
+ jmp isr_common_stub
+
+/* 20: Reserved */
+isr20:
+ cli
+ pushl $0
+ pushl $20
+ jmp isr_common_stub
+
+/* 21: Reserved */
+isr21:
+ cli
+ pushl $0
+ pushl $21
+ jmp isr_common_stub
+
+/* 22: Reserved */
+isr22:
+ cli
+ pushl $0
+ pushl $22
+ jmp isr_common_stub
+
+/* 23: Reserved */
+isr23:
+ cli
+ pushl $0
+ pushl $23
+ jmp isr_common_stub
+
+/* 24: Reserved */
+isr24:
+ cli
+ pushl $0
+ pushl $24
+ jmp isr_common_stub
+
+/* 25: Reserved */
+isr25:
+ cli
+ pushl $0
+ pushl $25
+ jmp isr_common_stub
+
+/* 26: Reserved */
+isr26:
+ cli
+ pushl $0
+ pushl $26
+ jmp isr_common_stub
+
+/* 27: Reserved */
+isr27:
+ cli
+ pushl $0
+ pushl $27
+ jmp isr_common_stub
+
+/* 28: Reserved */
+isr28:
+ cli
+ pushl $0
+ pushl $28
+ jmp isr_common_stub
+
+/* 29: Reserved */
+isr29:
+ cli
+ pushl $0
+ pushl $29
+ jmp isr_common_stub
+
+/* 30: Reserved */
+isr30:
+ cli
+ pushl $0
+ pushl $30
+ jmp isr_common_stub
+
+/* 31: Reserved */
+isr31:
+ cli
+ pushl $0
+ pushl $31
+ jmp isr_common_stub
+
+
+/* This is our common ISR stub. It saves the processor state, sets
+ up for kernel mode segments, calls the C-level fault handler,
+ and finally restores the stack frame. */
+isr_common_stub:
+ pusha
+ pushl %ds
+ pushl %es
+ pushl %fs
+ pushl %gs
+ mov $0x10, %ax
+ mov %ax, %ds
+ mov %ax, %es
+ mov %ax, %fs
+ mov %ax, %gs
+ mov %esp, %eax
+ pushl %eax
+ call fault_handler
+ popl %eax
+ popl %gs
+ popl %fs
+ popl %es
+ popl %ds
+ popa
+ add $8, %esp
+ iret
+
+.data
+ halt_message:
+ .asciz "Halted.\n"
new file mode 100644
@@ -0,0 +1,84 @@
+#include <kernel.h>
+#include <lib.h>
+
+/* Defines a GDT entry */
+struct gdt_entry
+{
+ uint16_t limit_low;
+ uint16_t base_low;
+ uint8_t base_middle;
+ uint8_t access;
+ uint8_t granularity;
+ uint8_t base_high;
+} __attribute__((packed));
+
+struct gdt_ptr
+{
+ uint16_t limit;
+ uint32_t base;
+} __attribute__((packed));
+
+/* GDT, with 5 entries:
+ * 0x00 - NULL descriptor
+ * 0x08 - Code segment
+ * 0x10 - Data segment
+ * 0x18 - Primery task
+ * 0x20 - Interrupt task */
+static struct gdt_entry gdt[3 + TSS_COUNT];
+static struct gdt_ptr gp;
+
+static void gdt_flush(const struct gdt_ptr *gp_ptr)
+{
+ asm volatile ("lgdt %0\n\t"
+ "mov $0x10, %%ax\n\t"
+ "mov %%ax, %%ds\n\t"
+ "mov %%ax, %%es\n\t"
+ "mov %%ax, %%fs\n\t"
+ "mov %%ax, %%gs\n\t"
+ "mov %%ax, %%ss\n\t"
+ "jmp $0x08, $.Lflush2\n\t"
+ ".Lflush2: "::"m"(*gp_ptr));
+}
+
+void gdt_set_gate(int num, uint32_t base, uint32_t limit, uint8_t access, uint8_t gran)
+{
+ /* Setup the descriptor base address */
+ gdt[num].base_low = (base & 0xFFFF);
+ gdt[num].base_middle = (base >> 16) & 0xFF;
+ gdt[num].base_high = (base >> 24) & 0xFF;
+
+ /* Setup the descriptor limits */
+ gdt[num].limit_low = (limit & 0xFFFF);
+ gdt[num].granularity = ((limit >> 16) & 0x0F);
+
+ /* Finally, set up the granularity and access flags */
+ gdt[num].granularity |= (gran & 0xF0);
+ gdt[num].access = access;
+}
+
+void gdt_install(void)
+{
+ /* Setup the GDT pointer and limit */
+ gp.limit = sizeof(gdt) - 1;
+ gp.base = (uint32_t)&gdt;
+
+ memset(gdt, 0, sizeof(gdt));
+
+ /* Our NULL descriptor */
+ gdt_set_gate(0, 0, 0, 0, 0);
+
+ /* The second entry is our Code Segment. The base address
+ * is 0, the limit is 4GBytes, it uses 4KByte granularity,
+ * uses 32-bit opcodes, and is a Code Segment descriptor.
+ * Please check the table above in the tutorial in order
+ * to see exactly what each value means */
+ gdt_set_gate(1, 0, 0xFFFFFFFF, 0x9A, 0xcf);
+
+ /* The third entry is our Data Segment. It's EXACTLY the
+ * same as our code segment, but the descriptor type in
+ * this entry's access byte says it's a Data Segment */
+ gdt_set_gate(2, 0, 0xFFFFFFFF, 0x92, 0xcf);
+
+ /* Flush out the old GDT and install the new changes! */
+ gdt_flush(&gp);
+}
new file mode 100644
@@ -0,0 +1,47 @@
+#include <kernel.h>
+#include <lib.h>
+
+/* Defines an IDT entry */
+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));
+
+struct idt_entry idt[256];
+struct idt_ptr idtp;
+
+
+static void idt_load(const struct idt_ptr *idtptr)
+{
+ asm volatile("lidt %0"::"m" (*idtptr));
+}
+
+void idt_set_gate(uint8_t num, uint32_t base, uint16_t sel, uint8_t flags)
+{
+ /* The interrupt routine's base address */
+ 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;
+}
+
+void idt_install()
+{
+ idtp.limit = sizeof(idt) - 1;
+ idtp.base = (uint32_t)&idt;
+
+ memset(&idt, 0, sizeof(idt));
+
+ idt_load(&idtp);
+}
new file mode 100644
@@ -0,0 +1,112 @@
+#include <kernel.h>
+#include <lib.h>
+
+/* This defines what the stack looks like after an ISR was running */
+struct regs
+{
+ unsigned int gs, fs, es, ds; /* pushed the segs last */
+ unsigned int edi, esi, ebp, esp, ebx, edx, ecx, eax; /* pushed by 'pusha' */
+ unsigned int int_no, err_code; /* our 'push byte #' and ecodes do this */
+ unsigned int eip, cs, eflags, useresp, ss; /* pushed by the processor automatically */
+};
+
+extern void isr0();
+extern void isr1();
+extern void isr2();
+extern void isr3();
+extern void isr4();
+extern void isr5();
+extern void isr6();
+extern void isr7();
+extern void isr8();
+extern void isr9();
+extern void isr10();
+extern void isr11();
+extern void isr12();
+extern void isr13();
+extern void isr14();
+extern void isr15();
+extern void isr16();
+extern void isr17();
+extern void isr18();
+extern void isr19();
+extern void isr20();
+extern void isr21();
+extern void isr22();
+extern void isr23();
+extern void isr24();
+extern void isr25();
+extern void isr26();
+extern void isr27();
+extern void isr28();
+extern void isr29();
+extern void isr30();
+extern void isr31();
+
+void (*isrs[32])() = {isr0, isr1, isr2, isr3, isr4, isr5, isr6, isr7, isr8,
+ isr9, isr10, isr11, isr12, isr13, isr14, isr15, isr16,
+ isr17, isr18, isr19, isr20, isr21, isr22, isr23, isr24,
+ isr25, isr26, isr27, isr28, isr29, isr30, isr31};
+
+void isrs_install_one(uint8_t isr)
+{
+ idt_set_gate(isr, (uint32_t)isrs[isr], 0x08, 0x8E);
+}
+
+void isrs_install(void)
+{
+ int i;
+
+ for (i = 0; i < 32; i++)
+ isrs_install_one(i);
+}
+
+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(struct regs *r)
+{
+ if (r->int_no < 32)
+ {
+ printk(0, exception_messages[r->int_no]);
+ printk(0, "Exception. System Halted!\n");
+ for (;;)
+ asm volatile ("hlt");
+ }
+}
new file mode 100644
@@ -0,0 +1,194 @@
+/* kernel.c - the C part of the kernel */
+/* 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., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <kernel.h>
+#include <multiboot.h>
+#include <lib.h>
+#include <memory.h>
+
+/* Macros. */
+
+/* Check if the bit BIT in FLAGS is set. */
+#define CHECK_FLAG(flags,bit) ((flags) & (1 << (bit)))
+
+/* Forward declarations. */
+void cmain (unsigned long magic, unsigned long addr);
+
+#if 0
+/* Check if MAGIC is valid and print the Multiboot information structure
+ pointed by ADDR. */
+static void print_boot_info(unsigned long magic, unsigned long addr)
+{
+ multiboot_info_t *mbi;
+
+ /* Am I booted by a Multiboot-compliant boot loader? */
+ if (magic != MULTIBOOT_BOOTLOADER_MAGIC)
+ {
+ printk ("Invalid magic number: 0x%x\n", (unsigned) magic);
+ return;
+ }
+
+ /* Set MBI to the address of the Multiboot information structure. */
+ mbi = (multiboot_info_t *) addr;
+
+ /* Print out the flags. */
+ printk ("flags = 0x%x\n", (unsigned) mbi->flags);
+
+ /* Are mem_* valid? */
+ if (CHECK_FLAG (mbi->flags, 0))
+ printk ("mem_lower = %uKB, mem_upper = %uKB\n",
+ (unsigned) mbi->mem_lower, (unsigned) mbi->mem_upper);
+
+ /* Is boot_device valid? */
+ if (CHECK_FLAG (mbi->flags, 1))
+ printk ("boot_device = 0x%x\n", (unsigned) mbi->boot_device);
+
+ /* Is the command line passed? */
+ if (CHECK_FLAG (mbi->flags, 2))
+ printk ("cmdline = %s\n", (char *) mbi->cmdline);
+
+ /* Are mods_* valid? */
+ if (CHECK_FLAG (mbi->flags, 3))
+ {
+ module_t *mod;
+ int i;
+
+ printk ("mods_count = %d, mods_addr = 0x%x\n",
+ (int) mbi->mods_count, (int) mbi->mods_addr);
+ for (i = 0, mod = (module_t *) mbi->mods_addr;
+ i < mbi->mods_count;
+ i++, mod++)
+ printk (" mod_start = 0x%x, mod_end = 0x%x, string = %s\n",
+ (unsigned) mod->mod_start,
+ (unsigned) mod->mod_end,
+ (char *) mod->string);
+ }
+
+ /* Bits 4 and 5 are mutually exclusive! */
+ if (CHECK_FLAG (mbi->flags, 4) && CHECK_FLAG (mbi->flags, 5))
+ {
+ printk ("Both bits 4 and 5 are set.\n");
+ return;
+ }
+
+ /* Is the symbol table of a.out valid? */
+ if (CHECK_FLAG (mbi->flags, 4))
+ {
+ aout_symbol_table_t *aout_sym = &(mbi->u.aout_sym);
+
+ printk ("aout_symbol_table: tabsize = 0x%0x, "
+ "strsize = 0x%x, addr = 0x%x\n",
+ (unsigned) aout_sym->tabsize,
+ (unsigned) aout_sym->strsize,
+ (unsigned) aout_sym->addr);
+ }
+
+ /* Is the section header table of ELF valid? */
+ if (CHECK_FLAG (mbi->flags, 5))
+ {
+ elf_section_header_table_t *elf_sec = &(mbi->u.elf_sec);
+
+ printk ("elf_sec: num = %u, size = 0x%x,"
+ " addr = 0x%x, shndx = 0x%x\n",
+ (unsigned) elf_sec->num, (unsigned) elf_sec->size,
+ (unsigned) elf_sec->addr, (unsigned) elf_sec->shndx);
+ }
+
+ /* Are mmap_* valid? */
+ if (CHECK_FLAG (mbi->flags, 6))
+ {
+ memory_map_t *mmap;
+
+ printk ("mmap_addr = 0x%x, mmap_length = 0x%x\n",
+ (unsigned) mbi->mmap_addr, (unsigned) mbi->mmap_length);
+ for (mmap = (memory_map_t *) mbi->mmap_addr;
+ (unsigned long) mmap < mbi->mmap_addr + mbi->mmap_length;
+ mmap = (memory_map_t *) ((unsigned long) mmap
+ + mmap->size + sizeof (mmap->size)))
+ printk (" size = 0x%x, base_addr = 0x%x%x,"
+ " length = 0x%x%x, type = 0x%x\n",
+ (unsigned) mmap->size,
+ (unsigned) mmap->base_addr_high,
+ (unsigned) mmap->base_addr_low,
+ (unsigned) mmap->length_high,
+ (unsigned) mmap->length_low,
+ (unsigned) mmap->type);
+ }
+}
+#endif
+
+uint16_t verbosity;
+
+static void cmd_parse(multiboot_info_t *mbi)
+{
+ char *v;
+
+ if (!CHECK_FLAG (mbi->flags, 2))
+ return;
+
+ v = strstr((char*)mbi->cmdline, "v=");
+
+ if (!v)
+ return;
+
+ verbosity = atoi(v + 2);
+ printk(3, "cmdline = %s\n", (char *) mbi->cmdline);
+}
+
+extern struct test_desc __tests_start[], __tests_end[];
+
+static void run_tests(void)
+{
+ struct test_desc *test;
+
+ for (test = __tests_start; test < __tests_end; test++) {
+ int r;
+ printk(0, "Start test: %s\n", test->name);
+ r = test->fn();
+ if (r < 0) {
+ printk(0, "Critical failure. Exiting.\n");
+ return;
+ } else if (r == TEST_FAIL)
+ printk(0, "Test fails\n");
+ else
+ printk(0, "Test Succeeds\n");
+ }
+}
+
+void cmain(unsigned long magic, unsigned long addr)
+{
+ cmd_parse((multiboot_info_t*)addr);
+
+ gdt_install();
+ tss_install();
+ uart_init();
+
+ kalloc_init(_kernel_end, KALLOC_SIZE);
+
+ /* Clear the screen. */
+ cls ();
+
+ printk(3, "kernel start=0x%x end=0x%x\n", _kernel_start, _kernel_end);
+ printk(3, "kalloc_size=%d\n", KALLOC_SIZE);
+
+ idt_install();
+ isrs_install();
+
+ run_tests();
+/* print_boot_info(magic, addr); */
+}
+
new file mode 100644
@@ -0,0 +1,68 @@
+#ifndef _KERNEL_H
+#define _KERNEL_H
+#ifndef ASM
+#include <stdint.h>
+
+
+typedef uint32_t size_t;
+#define NULL ((void*)0)
+
+/* 1M for dynamic memory management */
+#define KALLOC_SIZE (1024*1024 - (_kernel_end - _kernel_start))
+#define TSS_COUNT 4
+#define TSS_GDT_OFFSET 3
+
+extern char _kernel_start[], _kernel_end[];
+
+static inline void outb(int addr, int val)
+{
+ asm volatile ("outb %b1, %w0" : : "d" (addr), "a" (val));
+}
+
+static inline uint8_t inb(int addr)
+{
+ uint8_t val;
+ asm volatile ("inb %w1, %b0" : "=a" (val) : "d" (addr));
+ return val;
+}
+
+void gdt_install(void);
+void gdt_set_gate(int num, uint32_t base, uint32_t limit, uint8_t access,
+ uint8_t gran);
+void idt_install(void);
+void idt_set_gate(uint8_t num, uint32_t base, uint16_t sel, uint8_t flags);
+
+void isrs_install(void);
+void isrs_install_one(uint8_t isr);
+
+void tss_install(void);
+void tss_setup(uint8_t gate, uint8_t desc, void (*fn)(void));
+void tss_info(void);
+
+void uart_init(void);
+void serial_outch(char c);
+char serial_inch(void);
+
+#define TEST_FAIL 0
+#define TEST_SUCCEED 1
+#define TEST_FAIL_EXIT -1
+typedef int (*testfn_t)(void);
+
+struct test_desc {
+ char *name;
+ testfn_t fn;
+};
+
+#define define_test(_fn, _name) \
+ static struct test_desc __test_##fn __attribute__((__used__)) \
+ __attribute__((__section__(".tests"))) = {.name = _name, .fn = _fn}
+
+extern uint16_t verbosity;
+
+#endif /* ASM */
+
+/* put stack somewhere in low memory */
+#define STACK_START 0x80000
+#define INT_STACK_START 0x70000
+
+#endif
new file mode 100644
@@ -0,0 +1,28 @@
+OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386")
+OUTPUT_ARCH(i386)
+ENTRY(start)
+SECTIONS
+{
+ . = 0x100000;
+ _kernel_start = .;
+ .text : {
+ *(.text)
+ }
+ .rodata : {
+ *(.rodata)
+ }
+ .data : {
+ *(.data)
+ }
+ .bss : {
+ *(.bss)
+ }
+ __tests_start = .;
+ .tests : {
+ *(.tests)
+ }
+ __tests_end = .;
+ . = ALIGN(4096);
+ _kernel_end = .;
+ /DISCARD/ : { *(.comment) }
+}
\ No newline at end of file
new file mode 100644
@@ -0,0 +1,296 @@
+#include <kernel.h>
+#include <lib.h>
+
+/* Some screen stuff. */
+/* The number of columns. */
+#define COLUMNS 80
+/* The number of lines. */
+#define LINES 24
+/* The attribute of an character. */
+#define ATTRIBUTE 7
+/* The video memory address. */
+#define VIDEO 0xB8000
+
+/* Variables. */
+/* Save the X position. */
+static int xpos;
+/* Save the Y position. */
+static int ypos;
+/* Point to the video memory. */
+static volatile unsigned char *video;
+
+/* Clear the screen and initialize VIDEO, XPOS and YPOS. */
+void cls (void)
+{
+ int i;
+
+ video = (unsigned char *) VIDEO;
+
+ for (i = 0; i < COLUMNS * LINES * 2; i++)
+ *(video + i) = 0;
+
+ xpos = 0;
+ ypos = 0;
+}
+
+/* Convert the integer D to a string and save the string in BUF. If
+ BASE is equal to 'd', interpret that D is decimal, and if BASE is
+ equal to 'x', interpret that D is hexadecimal. */
+void itoa (char *buf, int base, int d)
+{
+ char *p = buf;
+ char *p1, *p2;
+ unsigned long ud = d;
+ int divisor = 10;
+
+ /* If %d is specified and D is minus, put `-' in the head. */
+ if (base == 'd' && d < 0)
+ {
+ *p++ = '-';
+ buf++;
+ ud = -d;
+ }
+ else if (base == 'x')
+ divisor = 16;
+
+ /* Divide UD by DIVISOR until UD == 0. */
+ do
+ {
+ int remainder = ud % divisor;
+
+ *p++ = (remainder < 10) ? remainder + '0' : remainder + 'a' - 10;
+ }
+ while (ud /= divisor);
+
+ /* Terminate BUF. */
+ *p = 0;
+
+ /* Reverse BUF. */
+ p1 = buf;
+ p2 = p - 1;
+ while (p1 < p2)
+ {
+ char tmp = *p1;
+ *p1 = *p2;
+ *p2 = tmp;
+ p1++;
+ p2--;
+ }
+}
+
+int isdigit(int c)
+{
+ return c >= '0' && c <='9';
+}
+
+int isupper(int c)
+{
+ return c >= 'A' && c <= 'Z';
+}
+
+int islower(int c)
+{
+ return c >= 'a' && c <= 'z';
+}
+
+int atoi_base(const char *buf, int base)
+{
+ int minus,val,digit,base_1;
+ char c;
+
+ base_1 = base - 1;
+
+ if((minus = *buf == '-'))
+ buf++;
+
+ val = 0;
+ while ((c = *buf++)) {
+ if (isdigit(c))
+ digit = c - 48;
+ else if (isupper(c))
+ digit = c - 'A' + 10;
+ else if (islower(c))
+ digit = c - 'a' + 10;
+ else
+ break;
+
+ if (digit < 0 || digit > base_1)
+ break;
+
+ val = base*val + digit;
+ }
+
+ return minus ? -val : val;
+}
+
+/* Put the character C on the screen. */
+static void kputchar (int c)
+{
+#ifdef QEMU
+ outb(0x504, c);
+#endif
+ serial_outch(c);
+
+ if (c == '\n' || c == '\r')
+ {
+newline:
+ xpos = 0;
+ ypos++;
+ if (ypos >= LINES)
+ ypos = 0;
+ return;
+ }
+
+ *(video + (xpos + ypos * COLUMNS) * 2) = c & 0xFF;
+ *(video + (xpos + ypos * COLUMNS) * 2 + 1) = ATTRIBUTE;
+
+ xpos++;
+ if (xpos >= COLUMNS)
+ goto newline;
+}
+
+/* Format a string and print it on the screen, just like the libc
+ function printf. */
+void printk (uint16_t level, const char *format, ...)
+{
+ char **arg = (char **) &format;
+ int c;
+ char buf[20];
+
+ if (level > verbosity)
+ return;
+
+ arg++;
+
+ while ((c = *format++) != 0)
+ {
+ if (c != '%')
+ kputchar (c);
+ else
+ {
+ char *p;
+
+ c = *format++;
+ switch (c)
+ {
+ case 'd':
+ case 'u':
+ case 'x':
+ itoa (buf, c, *((int *) arg++));
+ p = buf;
+ goto string;
+ break;
+
+ case 's':
+ p = *arg++;
+ if (! p)
+ p = "(null)";
+
+string:
+ while (*p)
+ kputchar (*p++);
+ break;
+
+ default:
+ kputchar (*((int *) arg++));
+ break;
+ }
+ }
+ }
+}
+
+void *memset(void *s, int c, size_t n)
+{
+ char *ss = s;
+ int i;
+
+ for (i = 0; i < n; i++)
+ ss[i] = c;
+
+ return s;
+}
+
+char *strstr(const char *phaystack, const char *pneedle)
+{
+ const unsigned char *haystack, *needle;
+ char b, c;
+
+ haystack = (const unsigned char *) phaystack;
+ needle = (const unsigned char *) pneedle;
+
+ b = *needle;
+ if (b != '\0')
+ {
+ haystack--; /* possible ANSI violation */
+ do
+ {
+ c = *++haystack;
+ if (c == '\0')
+ goto ret0;
+ }
+ while (c != b);
+
+ c = *++needle;
+ if (c == '\0')
+ goto foundneedle;
+ ++needle;
+ goto jin;
+
+ for (;;)
+ {
+ char a;
+ const unsigned char *rhaystack, *rneedle;
+
+ do
+ {
+ a = *++haystack;
+ if (a == '\0')
+ goto ret0;
+ if (a == b)
+ break;
+ a = *++haystack;
+ if (a == '\0')
+ goto ret0;
+ shloop:;
+ }
+ while (a != b);
+
+ jin:
+ a = *++haystack;
+ if (a == '\0')
+ goto ret0;
+
+ if (a != c)
+ goto shloop;
+
+ rhaystack = haystack-- + 1;
+ rneedle = needle;
+ a = *rneedle;
+
+ if (*rhaystack == a)
+ do
+ {
+ if (a == '\0')
+ goto foundneedle;
+ ++rhaystack;
+ a = *++needle;
+ if (*rhaystack != a)
+ break;
+ if (a == '\0')
+ goto foundneedle;
+ ++rhaystack;
+ a = *++needle;
+ }
+ while (*rhaystack == a);
+
+ needle = rneedle; /* took the register-poor approach */
+
+ if (a == '\0')
+ break;
+ }
+ }
+foundneedle:
+ return (char*) haystack;
+ret0:
+ return 0;
+}
new file mode 100644
@@ -0,0 +1,13 @@
+#ifndef _LIB_H
+#define _LIB_H
+void cls(void);
+void itoa(char *buf, int base, int d);
+void printk(uint16_t level, const char *format, ...);
+void *memset(void *s, int c, size_t n);
+char *strstr(const char *phaystack, const char *pneedle);
+int atoi_base(const char *buf, int base);
+static inline int atoi(const char *buf)
+{
+ return atoi_base(buf, 10);
+}
+#endif
new file mode 100644
@@ -0,0 +1,100 @@
+#include <kernel.h>
+#include <memory.h>
+
+#define ALIGNMASK 1U
+#define ALIGN(s) (((s) + ALIGNMASK) & ~ALIGNMASK)
+#define POFF ALIGN(sizeof(size_t))
+#define MINSIZ ALIGN(sizeof(struct cell *))
+#define C2P(c) ((char *)(c) + POFF)
+#define P2C(p) ((struct cell *)((char *)(p) - POFF))
+#define ISADJ(c1,c2) ((struct cell *)(C2P(c1) + (c1)->size) == (struct cell *)(c2))
+
+struct cell {
+ size_t size;
+ struct cell *next;
+};
+
+static struct mem_pool {
+ struct cell *tail;
+} *kmem;
+
+void kalloc_init(void *mem, size_t size)
+{
+ kmem->tail = mem;
+ kmem->tail->size = size - POFF;
+ kmem->tail->next = kmem->tail;
+}
+
+void *kalloc(size_t size)
+{
+ struct cell *c1, *c2, *c3;
+
+ size = size < MINSIZ ? MINSIZ : ALIGN(size);
+
+ c1 = kmem->tail;
+ while (c1->next->size < size) {
+ if (c1->next == kmem->tail)
+ return NULL;
+ c1 = c1->next;
+ }
+ c2 = c1->next;
+ if (c2->size > (POFF + size)) { /* split new cell */
+ c3 = (struct cell *)(C2P(c2) + size);
+ c3->size = c2->size - (size + POFF);
+ c3->next = c2->next;
+ c2->size = size;
+ c1->next = c3;
+ } else { /* use the entire cell */
+ c1->next = c2->next;
+ if (c2 == kmem->tail) {
+ kmem->tail = c1;
+ }
+ }
+
+ return C2P(c2);
+}
+
+void kfree(void *ptr)
+{
+ struct cell *c1, *c2, *c3;
+ int j1, j2;
+
+/* splice the cell back into the list */
+ c1 = kmem->tail;
+ c2 = P2C(ptr);
+
+ if (c2 > c1) { /* append to end of list */
+ if (ISADJ(c1,c2)) { /* join with last cell */
+ c1->size += POFF + c2->size;
+ return;
+ }
+ c2->next = c1->next;
+ c1->next = c2;
+ kmem->tail = c2;
+ return;
+ }
+
+ while (c1->next < c2) { /* find insertion point */
+ c1 = c1->next;
+ }
+ c3 = c1->next;
+
+ j1 = ISADJ(c1,c2); /* c1 and c2 need to be joined */
+ j2 = ISADJ(c2,c3); /* c2 and c3 need to be joined */
+
+ if (j1) {
+ if (j2) { /* splice all three cells together */
+ c1->next = c3->next;
+ c1->size += POFF + c3->size;
+ }
+ c1->size += POFF + c2->size;
+ } else {
+ c1->next = c2;
+ if (j2) {
+ c2->next = c3->next;
+ c2->size += POFF + c3->size;
+ } else {
+ c2->next = c3;
+ }
+ }
+}
new file mode 100644
@@ -0,0 +1,6 @@
+#ifndef _MEMORY_H
+#define _MEMORY_H
+void kalloc_init(void *mem, size_t size);
+void *kalloc(size_t size);
+void kfree(void *ptr);
+#endif
new file mode 100644
@@ -0,0 +1,109 @@
+/* multiboot.h - the header for Multiboot */
+/* Copyright (C) 1999, 2001 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., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Macros. */
+
+/* The magic number for the Multiboot header. */
+#define MULTIBOOT_HEADER_MAGIC 0x1BADB002
+
+/* The flags for the Multiboot header. */
+#ifdef __ELF__
+# define MULTIBOOT_HEADER_FLAGS 0x00000003
+#else
+# define MULTIBOOT_HEADER_FLAGS 0x00010003
+#endif
+
+/* The magic number passed by a Multiboot-compliant boot loader. */
+#define MULTIBOOT_BOOTLOADER_MAGIC 0x2BADB002
+
+#ifndef ASM
+/* Do not include here in boot.S. */
+
+/* Types. */
+
+/* The Multiboot header. */
+typedef struct multiboot_header
+{
+ unsigned long magic;
+ unsigned long flags;
+ unsigned long checksum;
+ unsigned long header_addr;
+ unsigned long load_addr;
+ unsigned long load_end_addr;
+ unsigned long bss_end_addr;
+ unsigned long entry_addr;
+} multiboot_header_t;
+
+/* The symbol table for a.out. */
+typedef struct aout_symbol_table
+{
+ unsigned long tabsize;
+ unsigned long strsize;
+ unsigned long addr;
+ unsigned long reserved;
+} aout_symbol_table_t;
+
+/* The section header table for ELF. */
+typedef struct elf_section_header_table
+{
+ unsigned long num;
+ unsigned long size;
+ unsigned long addr;
+ unsigned long shndx;
+} elf_section_header_table_t;
+
+/* The Multiboot information. */
+typedef struct multiboot_info
+{
+ unsigned long flags;
+ unsigned long mem_lower;
+ unsigned long mem_upper;
+ unsigned long boot_device;
+ unsigned long cmdline;
+ unsigned long mods_count;
+ unsigned long mods_addr;
+ union
+ {
+ aout_symbol_table_t aout_sym;
+ elf_section_header_table_t elf_sec;
+ } u;
+ unsigned long mmap_length;
+ unsigned long mmap_addr;
+} multiboot_info_t;
+
+/* The module structure. */
+typedef struct module
+{
+ unsigned long mod_start;
+ unsigned long mod_end;
+ unsigned long string;
+ unsigned long reserved;
+} module_t;
+
+/* The memory map. Be careful that the offset 0 is base_addr_low
+ but no size. */
+typedef struct memory_map
+{
+ unsigned long size;
+ unsigned long base_addr_low;
+ unsigned long base_addr_high;
+ unsigned long length_low;
+ unsigned long length_high;
+ unsigned long type;
+} memory_map_t;
+
+#endif /* ! ASM */
new file mode 100644
@@ -0,0 +1,22 @@
+CC=gcc
+AS=gcc
+CFLAGS=-m32 -I. -I.. -O2 -Wall
+ASFLAGS=-m32 -I. -I..
+OBJS=tss_test.o
+
+PHONY := all
+all: tests.o
+
+tests.o: $(OBJS)
+ ld -m elf_i386 -r $(OBJS) -o $@
+
+-include $(OBJS:.o=.d)
+
+# compile and generate dependency info
+%.o: %.c
+ gcc -c $(CFLAGS) $*.c -o $*.o
+ gcc -MM $(CFLAGS) $*.c > $*.d
+
+PHONY += clean
+clean:
+ -rm *.o *~ *.d
new file mode 100644
@@ -0,0 +1,74 @@
+#include <kernel.h>
+#include <lib.h>
+
+
+static void nmi_tss(void)
+{
+start:
+ printk(0, "NMI task is running\n");
+ tss_info();
+ asm volatile ("iret");
+ printk(0, "NMI task restart after iret.\n");
+ goto start;
+}
+
+static int test_divider;
+
+static void de_tss(void)
+{
+start:
+ printk(0, "DE task is running\n");
+ tss_info();
+ test_divider = 10;
+ asm volatile ("iret");
+ goto start;
+}
+
+static void of_tss(void)
+{
+start:
+ printk(0, "OF task is running\n");
+ tss_info();
+ asm volatile ("iret");
+ goto start;
+}
+
+static int tss_test(void)
+{
+ int ret = TEST_SUCCEED, res;
+
+ tss_setup(2, 1, nmi_tss);
+ tss_setup(0, 2, de_tss);
+ tss_setup(4, 3, of_tss);
+
+ printk(0, "Triggering nmi\n");
+ asm volatile ("int $2");
+ printk(0, "Return from nmi 1\n");
+ asm volatile ("int $2");
+ printk(0, "Return from nmi 2\n");
+ printk(0, "Try to devide by 0\n");
+ res = 1500 / test_divider;
+ printk(0, "Result is %d\n", res);
+ if (res != 150) {
+ ret = TEST_FAIL;
+ goto restore_isrs;
+ }
+ printk(0, "Call int 0\n");
+ asm volatile ("int $0");
+ printk(0, "Return from int 0\n");
+ printk(0, "Call into\n");
+ asm volatile ("addb $127, %b0\ninto"::"a"(127));
+ printk(0, "Return from into\n");
+ printk(0, "Calling nmi task by lcall\n");
+ asm volatile("lcall $0x20, $0");
+ printk(0, "Return from call\n");
+
+restore_isrs:
+ isrs_install_one(0);
+ isrs_install_one(2);
+ isrs_install_one(4);
+
+ return ret;
+}
+
+define_test(tss_test, "TSS test");
new file mode 100644
@@ -0,0 +1,108 @@
+#include <kernel.h>
+#include <lib.h>
+
+struct tss_desc {
+ uint16_t link;
+ uint16_t link_h;
+
+ uint32_t esp0;
+ uint16_t ss0;
+ uint16_t ss0_h;
+
+ uint32_t esp1;
+ uint16_t ss1;
+ uint16_t ss1_h;
+
+ uint32_t esp2;
+ uint16_t ss2;
+ uint16_t ss2_h;
+
+ 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;
+
+ uint16_t es;
+ uint16_t es_h;
+
+ uint16_t cs;
+ uint16_t cs_h;
+
+ uint16_t ss;
+ uint16_t ss_h;
+
+ uint16_t ds;
+ uint16_t ds_h;
+
+ uint16_t fs;
+ uint16_t fs_h;
+
+ uint16_t gs;
+ uint16_t gs_h;
+
+ uint16_t ldt;
+ uint16_t ldt_h;
+
+ uint16_t trap;
+ uint16_t iomap;
+} __attribute__ ((packed));
+
+static struct tss_desc tss[TSS_COUNT];
+
+void tss_install(void)
+{
+ uint16_t desc_size = sizeof(struct tss_desc);
+ int i;
+
+ for (i = 0; i < TSS_COUNT; i++) {
+ tss[i].ss0 = tss[i].ss1 = tss[i].ss2 = 0x10;
+ tss[i].esp0 = tss[i].esp1 = tss[i].esp2 = INT_STACK_START;
+ tss[i].cs = 0x08;
+ tss[i].ds = tss[i].es = tss[i].fs = tss[i].gs = tss[i].ss =0x10;
+ tss[i].esp = INT_STACK_START;
+ tss[i].iomap = (uint16_t)desc_size;
+ gdt_set_gate(TSS_GDT_OFFSET + i, (uint32_t)&tss[i],
+ desc_size - 1, 0x89, 0x0f);
+ }
+
+ tss[0].esp0 = tss[0].esp1 = tss[0].esp2 = tss[0].esp = STACK_START;
+
+ asm volatile ( "ltr %%ax" : : "a" ( 0x18 ) );
+}
+
+void tss_setup(uint8_t gate, uint8_t i, void (*fn)(void))
+{
+ if (i >= TSS_COUNT) {
+ printk(0, "Try to setup TSS out if bound %d\n", tss);
+ return;
+ }
+ tss[i].eip = (uint32_t)fn;
+ printk(2, "TSS set gate %d to TSS %x\n", gate,
+ (i + TSS_GDT_OFFSET) << 3);
+ idt_set_gate(gate, 0, (i + TSS_GDT_OFFSET) << 3, 0x85);
+}
+
+void tss_info(void)
+{
+ uint16_t tr, i;
+
+ asm volatile ("str %0":"=r"(tr));
+
+ i = (tr >> 3) - TSS_GDT_OFFSET;
+
+ if (i >= TSS_COUNT)
+ printk(0, "Current TR %x is wrong!\n", tr);
+
+ printk(0, "TR=%x Main TSS back link %x. Current TSS back link %x\n",
+ tr, tss[0]. link, tss[i].link);
+}
new file mode 100644
@@ -0,0 +1,43 @@
+#include <kernel.h>
+
+#define PORT1 0x3F8
+#define PORT2 0x2F8
+#define PORT3 0x3E8
+#define PORT4 0x2E8
+
+#define BAUD38400 0x03
+#define BAUD115200 0x01
+#define BAUD57600 0x02
+#define BAUD19200 0x06
+#define BAUD9600 0x0C
+#define BAUD4800 0x18
+#define BAUD2400 0x30
+
+void uart_init(void)
+{
+ outb(PORT1 + 1 , 0); /* Turn off interrupts */
+
+ outb(PORT1 + 3 , 0x80); /* SET DLAB ON */
+ /* Set Baud rate - Divisor Latch Low Byte */
+ outb(PORT1 + 0 , BAUD38400);
+ /* Set Baud rate - Divisor Latch High Byte */
+ outb(PORT1 + 1 , 0x00);
+ outb(PORT1 + 3 , 0x03); /* 8 Bits, No Parity, 1 Stop Bit */
+ outb(PORT1 + 2 , 0xC7); /* FIFO Control Register */
+ outb(PORT1 + 4 , 0x0B); /* Turn on DTR, RTS, and OUT2 */
+}
+
+void serial_outch(char c)
+{
+ outb(PORT1, c);
+}
+
+char serial_inch(void)
+{
+ char c;
+ do {
+ c = inb(PORT1 + 5);
+ if (c & 1)
+ return inb(PORT1);
+ } while(1);
+}