diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..a2cd2d7a --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +ports/x86/build/* +*.swp +*.o +tags +*.iso diff --git a/ports/x86/Makefile b/ports/x86/Makefile new file mode 100644 index 00000000..a6ec2404 --- /dev/null +++ b/ports/x86/Makefile @@ -0,0 +1,123 @@ +############ +# Settings # +############ + +# Build all test applications: +# make +# +# Run all tests communicating via UART +# make tests + +# Location of build tools and atomthreads sources +KERNEL_DIR=../../kernel +TESTS_DIR=../../tests +PORT_DIR=. +CC=i686-elf-gcc +OBJCOPY=i686-elf-objcopy +ARCHIVE=i686-elf-ar + +# Folder delete command (OS-specific) +ifeq ($(OS),Windows_NT) + RMDIR=rd /s /q +else + RMDIR=rm -rf +endif + +# Enable stack-checking. +#STACK_CHECK=true + +# Directory for built objects +BUILD_DIR=build +PLAT_DIR=plat +PORT_UTIL_DIR=utils +# Platform-specific object files +PLAT_OBJECTS = irq_handlers.o pic.o pit.o terminal.o + +# Port-specific object files +PORT_OBJECTS = atomport.o c_traps.o main.o gdt.o +PORT_ASM_OBJECTS = atomport-asm.o boot.o traps.o +PORT_UTIL_OBJECTS = string.o printf.o + +# Kernel object files +KERNEL_OBJECTS = atomkernel.o atomsem.o atommutex.o atomtimer.o atomqueue.o + +# Collection of built objects (excluding test applications) +ALL_OBJECTS = $(PLAT_OBJECTS) $(PLAT_ASM_OBJECTS) $(PORT_OBJECTS) $(PORT_ASM_OBJECTS) $(PORT_UTIL_OBJECTS) $(KERNEL_OBJECTS) +BUILT_OBJECTS = $(patsubst %,$(BUILD_DIR)/%,$(ALL_OBJECTS)) + +# Test object files (dealt with separately as only one per application build) +TEST_OBJECTS = $(notdir $(patsubst %.c,%.o,$(wildcard $(TESTS_DIR)/*.c))) +TEST_ALL = $(patsubst %.o,%,$(TEST_OBJECTS)) + +# Search build/output directory for dependencies +vpath %.o ./$(BUILD_DIR) + +# GCC flags +CFLAGS=-c -s -ffreestanding -std=gnu99 -Wall -Wextra -Werror \ + -Wno-unused-parameter \ + -Wno-unused-but-set-variable \ + -Wno-unused-variable \ + -Wno-sign-compare + #-O2 + +AFLAGS=$(CFLAGS) -x assembler-with-cpp +LFLAGS=-Tlinker.ld -Wall -ffreestanding -nostdlib + +# Enable stack-checking options (disable if not required) +ifeq ($(STACK_CHECK),true) +CFLAGS += -DATOM_STACK_CHECKING +endif +ifeq ($(TESTS_LOG_STACK),true) +CFLAGS += -DTESTS_LOG_STACK_USAGE -I$(PORT_UTIL_DIR) +endif + + +################# +# Build targets # +################# + +main: $(BUILD_DIR) $(TEST_ALL) + +$(TEST_ALL): $(TEST_OBJECTS) $(BUILD_DIR) $(ALL_OBJECTS) + $(CC) $(LFLAGS) $(BUILT_OBJECTS) $(BUILD_DIR)/$(notdir $@.o) --output $(BUILD_DIR)/$@.bin + +# Build archive for linking with external application +libatomthreads.a: $(BUILD_DIR) $(ALL_OBJECTS) Makefile + $(ARCHIVE) cr $(BUILD_DIR)/$@ $(BUILT_OBJECTS) + +# Make build/output directory +$(BUILD_DIR): + mkdir $(BUILD_DIR) + +# Kernel objects builder +$(KERNEL_OBJECTS): %.o: $(KERNEL_DIR)/%.c + $(CC) -c $(CFLAGS) -I. -I$(PORT_DIR) $< -o $(BUILD_DIR)/$(notdir $@) + +# Test objects builder +$(TEST_OBJECTS): %.o: $(TESTS_DIR)/%.c + $(CC) -c $(CFLAGS) -I. -I$(PORT_DIR) -I$(PORT_UTIL_DIR) -I$(KERNEL_DIR) $< -o $(BUILD_DIR)/$(notdir $@) + +# Platform C objects builder +$(PLAT_OBJECTS): %.o: $(PLAT_DIR)/%.c + $(CC) -c $(CFLAGS) -I. -I$(PORT_DIR) -I$(KERNEL_DIR) -I$(PORT_UTIL_DIR) -I$(TESTS_DIR) $< -o $(BUILD_DIR)/$(notdir $@) + +# Platform asm objects builder +$(PLAT_ASM_OBJECTS): %.o: $(PLAT_DIR)/%.s + $(CC) -c $(AFLAGS) -I. -I$(PORT_DIR) -I$(KERNEL_DIR) $< -o $(BUILD_DIR)/$(notdir $@) + +# Port C objects builder +$(PORT_OBJECTS): %.o: $(PORT_DIR)/%.c + $(CC) -c $(CFLAGS) -I. -Imachine/ -I$(PORT_DIR) -I$(KERNEL_DIR) -I$(PORT_UTIL_DIR) -I$(TESTS_DIR) $< -o $(BUILD_DIR)/$(notdir $@) + +# Port asm objects builder +$(PORT_ASM_OBJECTS): %.o: $(PORT_DIR)/%.s + $(CC) -c $(AFLAGS) -I. -I$(PORT_DIR) -I$(KERNEL_DIR) $< -o $(BUILD_DIR)/$(notdir $@) + +# Port C objects builder +$(PORT_UTIL_OBJECTS): %.o: $(PORT_UTIL_DIR)/%.c + $(CC) -c $(CFLAGS) -I../ -I$(PORT_DIR) -I$(KERNEL_DIR) -I$(PORT_UTIL_DIR) -I$(TESTS_DIR) $< -o $(BUILD_DIR)/$(notdir $@) + +# Clean +clean: + $(RMDIR) build/* + diff --git a/ports/x86/atomport-asm.s b/ports/x86/atomport-asm.s new file mode 100644 index 00000000..a082cf9b --- /dev/null +++ b/ports/x86/atomport-asm.s @@ -0,0 +1,45 @@ +/* + * AtomThreads project - Copyright (c) 2010, Kelvin Lawson. All rights reserved. + * + * Please refer to the README file for further details and License information. + * + * x86 port by: ido@mm.st + */ + +.section .text + +.global archContextSwitch +.type archContextSwitch, @function +archContextSwitch: + pushl %ebp + movl %esp,%ebp + + pushf + pusha + + movl 8(%ebp),%eax + movl 12(%ebp),%ebx + + movl %esp,(%eax) + movl (%ebx),%esp + + popa + popf + + popl %ebp + + sti + ret + +.global archFirstThreadRestore +.type archFirstThreadRestore, @function +archFirstThreadRestore: + movl 4(%esp),%eax + movl (%eax),%esp + + addl $32,%esp + popf + popl %ebp + sti + ret + diff --git a/ports/x86/atomport-tests.h b/ports/x86/atomport-tests.h new file mode 100644 index 00000000..3cac73b0 --- /dev/null +++ b/ports/x86/atomport-tests.h @@ -0,0 +1,36 @@ +/* + * AtomThreads project - Copyright (c) 2010, Kelvin Lawson. All rights reserved. + * + * Please refer to the README file for further details and License information. + * + * x86 port by: ido@mm.st + */ + +#ifndef __ATOM_PORT_TESTS_H +#define __ATOM_PORT_TESTS_H + +/* Include Atomthreads kernel API */ +#include "atom.h" +#include "plat.h" +#include "print.h" + +/* Logger macro for viewing test results */ + +#define ATOMLOG kprintf +/* + * String location macro: for platforms which need to place strings in + * alternative locations, e.g. on avr-gcc strings can be placed in + * program space, saving SRAM. On most platforms this can expand to + * empty. + */ +#define _STR(x) x + +/* Default thread stack size (in bytes). Allow plenty for printf(). */ +#define TEST_THREAD_STACK_SIZE 1024*16 // 16K + +/* Uncomment to enable logging of stack usage to UART */ +/* #define TESTS_LOG_STACK_USAGE */ + + +#endif /* __ATOM_PORT_TESTS_H */ + diff --git a/ports/x86/atomport.c b/ports/x86/atomport.c new file mode 100644 index 00000000..2361eac7 --- /dev/null +++ b/ports/x86/atomport.c @@ -0,0 +1,50 @@ +/* + * AtomThreads project - Copyright (c) 2010, Kelvin Lawson. All rights reserved. + * + * Please refer to the README file for further details and License information. + * + * x86 port by: ido@mm.st + */ + +#include "atom.h" +#include "atomport.h" +#include "x86.h" +#include "plat.h" + +static void thread_init(uint32_t entry_param) +{ + ATOM_TCB* tcb; + entry_param = entry_param; + + tcb = atomCurrentContext(); + + x86_enable_int(); + if( tcb && tcb->entry_point) + tcb->entry_point(tcb->entry_param); + + tcb->terminated = TRUE; + atomSched(TRUE); +} + +void archThreadContextInit (ATOM_TCB *tcb_ptr, void *stack_top, void (*entry_point)(uint32_t), uint32_t entry_param) +{ + uint32_t *stack_ptr = stack_top; + + *--stack_ptr = (uint32_t) thread_init; + + stack_ptr -= 9; + (uint16_t*) stack_ptr--; + + tcb_ptr->sp_save_ptr = stack_ptr; +} + +inline uint32_t contextEnterCritical (void){ + uint32_t flags = x86_get_eflags(); + x86_disable_int(); + + return flags; +} + +inline void contextExitCritical(uint32_t flags) { + x86_set_eflags(flags); +} diff --git a/ports/x86/atomport.h b/ports/x86/atomport.h new file mode 100644 index 00000000..8cc0f502 --- /dev/null +++ b/ports/x86/atomport.h @@ -0,0 +1,39 @@ +/* + * AtomThreads project - Copyright (c) 2010, Kelvin Lawson. All rights reserved. + * + * Please refer to the README file for further details and License information. + * + * x86 port by: ido@mm.st + */ + +#ifndef __ATOM_PORT_H +#define __ATOM_PORT_H + +#include +#include + + +/* Required number of system ticks per second (normally 100 for 10ms tick) */ +#define SYSTEM_TICKS_PER_SEC 100 + +/* Size of each stack entry / stack alignment size (32 bits on this platform) */ +#define STACK_ALIGN_SIZE sizeof(uint32_t) + +/** + * Architecture-specific types. + * Most of these are available from stdint.h on this platform, which is + * included above. + */ +#define POINTER void * + +extern uint32_t contextEnterCritical (void) ; +extern void contextExitCritical (uint32_t posture) ; + +#define CRITICAL_STORE uint32_t __atom_critical +#define CRITICAL_START() __atom_critical = contextEnterCritical() +#define CRITICAL_END() contextExitCritical(__atom_critical) + +/* Uncomment to enable stack-checking */ +/* #define ATOM_STACK_CHECKING */ + +#endif /* __ATOM_PORT_H */ diff --git a/ports/x86/boot.s b/ports/x86/boot.s new file mode 100644 index 00000000..21dcb05a --- /dev/null +++ b/ports/x86/boot.s @@ -0,0 +1,62 @@ +/* + * AtomThreads project - Copyright (c) 2010, Kelvin Lawson. All rights reserved. + * + * Please refer to the README file for further details and License information. + * + * x86 port by: ido@mm.st + */ + +# multiboot header + +.set ALIGN, 1<<0 # align loaded modules on page boundaries +.set MEMINFO, 1<<1 # provide memory map +.set FLAGS, ALIGN | MEMINFO # this is the Multiboot 'flag' field +.set MAGIC, 0x1BADB002 # 'magic number' lets bootloader find the header +.set CHECKSUM, -(MAGIC + FLAGS) # checksum of above, to prove we are multiboot + +.section .multiboot +.align 4 +.long MAGIC +.long FLAGS +.long CHECKSUM + +.section .bss + .skip 16384 # 16K stack +sys_stack_top: + +.section .text + +.global _start +.type _start, @function +_start: + + cli + movl $sys_stack_top, %esp + + call kernel_main + + cli + hlt +.Lhang: + jmp .Lhang + +.global gdt_flush +.extern gdt_p + +gdt_flush: + lgdt gdt_p + mov $0x10, %ax + mov %ax, %ds + mov %ax, %es + mov %ax, %fs + mov %ax, %gs + mov %ax, %ss + ljmp $0x08, $1f + +1: ret + +# Set the size of the _start symbol to the current location '.' minus its start. +# This is useful when debugging or when you implement call tracing. +.size _start, . - _start + + diff --git a/ports/x86/c_traps.c b/ports/x86/c_traps.c new file mode 100644 index 00000000..eb492cfc --- /dev/null +++ b/ports/x86/c_traps.c @@ -0,0 +1,73 @@ +/* + * AtomThreads project - Copyright (c) 2010, Kelvin Lawson. All rights reserved. + * + * Please refer to the README file for further details and License information. + * + * x86 port by: ido@mm.st + */ + +#include "plat.h" +#include "print.h" + +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 handle_exception(uint32_t vector) +{ + print("Exception raised: "); + print(exception_messages[vector]); + + x86_halt(); +} + +void handle_unknown(void){ + handle_exception(16); +} + +void x86_exception_handler(x86_iframe_t* iframe){ + + uint32_t vector = iframe->vector; + + if(31 >= vector) + handle_exception(vector); + else if(47 >= vector) + handle_platform_irq(iframe); + else + handle_unknown(); +} diff --git a/ports/x86/gdt.c b/ports/x86/gdt.c new file mode 100644 index 00000000..25093ec2 --- /dev/null +++ b/ports/x86/gdt.c @@ -0,0 +1,50 @@ +/* + * AtomThreads project - Copyright (c) 2010, Kelvin Lawson. All rights reserved. + * + * Please refer to the README file for further details and License information. + * + * x86 port by: ido@mm.st + * */ + +struct gdt_entry +{ + unsigned short limit_low; + unsigned short base_low; + unsigned char base_middle; + unsigned char access; + unsigned char granularity; + unsigned char base_high; +} __attribute__((packed)); + +struct gdt_ptr +{ + unsigned short limit; + unsigned int base; +} __attribute__((packed)); + +struct gdt_entry gdt[3]; +struct gdt_ptr gdt_p; + +extern void gdt_flush(); + +void init_gdt_entry(int index, unsigned long base, unsigned long limit, unsigned char access, unsigned char granularity) +{ + gdt[index].base_low = (base & 0xFFFF); + gdt[index].base_middle = (base >> 16) & 0xFF; + gdt[index].base_high = (base >> 24) & 0xFF; + gdt[index].limit_low = (limit & 0xFFFF); + gdt[index].granularity = ((limit >> 16) & 0x0F); + gdt[index].granularity |= (granularity & 0xF0); + gdt[index].access = access; +} + +void gdt_install_flat() +{ + gdt_p.limit = (sizeof(struct gdt_entry) * 3) - 1; + gdt_p.base = (unsigned int) gdt; + + init_gdt_entry(0, 0, 0, 0, 0); // Null + init_gdt_entry(1, 0, 0xFFFFFFFF, 0x9A, 0xCF); // Code + init_gdt_entry(2, 0, 0xFFFFFFFF, 0x92, 0xCF); // Data + gdt_flush(); +} diff --git a/ports/x86/linker.ld b/ports/x86/linker.ld new file mode 100644 index 00000000..0331cce7 --- /dev/null +++ b/ports/x86/linker.ld @@ -0,0 +1,40 @@ +/* + * AtomThreads project - Copyright (c) 2010, Kelvin Lawson. All rights reserved. + * + * Please refer to the README file for further details and License information. + * + * x86 port by: ido@mm.st + */ + +ENTRY(_start) + +SECTIONS +{ + /* load us at 1 MiB */ + . = 1M; + + .text BLOCK(4K) : ALIGN(4K) + { + *(.multiboot) + *(.text) + } + + /* RO data. */ + .rodata BLOCK(4K) : ALIGN(4K) + { + *(.rodata) + } + + /* RW (initialized) */ + .data BLOCK(4K) : ALIGN(4K) + { + *(.data) + } + + /* RW (uninitialized) data and stack */ + .bss BLOCK(4K) : ALIGN(4K) + { + *(COMMON) + *(.bss) + } +} diff --git a/ports/x86/main.c b/ports/x86/main.c new file mode 100644 index 00000000..d75c11c6 --- /dev/null +++ b/ports/x86/main.c @@ -0,0 +1,118 @@ +/* + * AtomThreads project - Copyright (c) 2010, Kelvin Lawson. All rights reserved. + * + * Please refer to the README file for further details and License information. + * + * x86 port by: ido@mm.st + */ + +#include "atom.h" +#include "atomtests.h" +#include "atomport.h" +#include "x86.h" +#include "plat.h" +#include "print.h" + +#define APP_PRIORITY 16 + +#define IDLE_STACK_SIZE_BYTES 1024*16 +#define APP_STACK_SIZE 1024*16 + +static uint8_t idle_thread_stack[IDLE_STACK_SIZE_BYTES]; +static uint8_t app_stack[APP_STACK_SIZE]; + +static ATOM_TCB app_tcb; + +int x86_pc_init(void) +{ + gdt_install_flat(); + print("GDT installed\n"); + + setup_idt(); + print("IDT installed\n"); + + pit_init(SYSTEM_TICKS_PER_SEC); + print("i8253 (PIT) initialized @%d hz\n", SYSTEM_TICKS_PER_SEC); + + pic_init(); + print("i8259 (PIC) initialized\n"); + + irq_register_handler(0, sys_tick_handler); + irq_register_handler(1, sys_key_handler); + + return ATOM_OK; +} + +static void app_entry(uint32_t data); + +void kernel_main (void) +{ + terminal_init(); + + print("kernel_main()\n"); + + if(ATOM_OK != x86_pc_init()) goto failure; + if(ATOM_OK != atomOSInit(&idle_thread_stack[0], IDLE_STACK_SIZE_BYTES, TRUE)) goto failure; + if(ATOM_OK != atomThreadCreate( + &app_tcb, + APP_PRIORITY, + app_entry, + 1, + &app_stack[0], + APP_STACK_SIZE, + TRUE)) goto failure; + + atomOSStart(); + +failure: + + print("[FAILURE]\n"); + x86_halt(); +} + +static void app_entry (uint32_t data) +{ + uint32_t test_status; + + print("\nstarting test..\n"); + + test_status = test_start(); + + /* Check main thread stack usage (if enabled) */ +#ifdef ATOM_STACK_CHECKING + if (test_status == 0) + { + uint32_t used_bytes, free_bytes; + + /* Check idle thread stack usage */ + if (atomThreadStackCheck (&main_tcb, &used_bytes, &free_bytes) == ATOM_OK) + { + /* Check the thread did not use up to the end of stack */ + if (free_bytes == 0) + { + print ("Main stack overflow\n"); + test_status++; + } + + /* Log the stack usage */ +#ifdef TESTS_LOG_STACK_USAGE + print ("MainUse:%d\n", (int)used_bytes); +#endif + } + + } +#endif + + /* Log final status */ + if (test_status == 0) + { + print_color("\n Test Pass!\n", COLOR_WHITE, COLOR_GREEN); + } + else + { + print_color("\nTets Fail\n", COLOR_WHITE, COLOR_RED ); + kprintf("Total Failures: %d\n", test_status); + } + + print("\n\nPress Q to reboot\n"); +} diff --git a/ports/x86/plat.h b/ports/x86/plat.h new file mode 100644 index 00000000..c79ec0cb --- /dev/null +++ b/ports/x86/plat.h @@ -0,0 +1,39 @@ +/* + * AtomThreads project - Copyright (c) 2010, Kelvin Lawson. All rights reserved. + * + * Please refer to the README file for further details and License information. + * + * x86 port by: ido@mm.st + */ + +#ifndef PLAT_H +#define PLAT_H + +#define IRQ_PIT 0 +#define IRQ_PS2 1 + +#include "x86.h" + +void pic_init(void); +void pic_send_EOI(uint32_t); +void pic_disable(void); + +void pit_init(uint32_t frequency); +void irq_install_handler(int irq, void (*handler)(void)); + +#define plat_reboot() out8(0x64, 0xFE) +#define plat_hide_cursor() out16(0x3D4,0x200A) + +void irq_register_handler(int irq, void (*handler)(x86_iframe_t*)); +void irq_unregister_handler(int irq); + +void handle_platform_irq(x86_iframe_t* frame); +void sys_key_handler(x86_iframe_t* frame); +void sys_tick_handler(x86_iframe_t* frame); + + +extern void terminal_init(); +extern void terminal_putchar(char c); +extern void terminal_putchar_color(char c, uint8_t text_color, uint8_t background_color); + +#endif diff --git a/ports/x86/plat/irq_handlers.c b/ports/x86/plat/irq_handlers.c new file mode 100644 index 00000000..0ebf215f --- /dev/null +++ b/ports/x86/plat/irq_handlers.c @@ -0,0 +1,51 @@ +/* + * AtomThreads project - Copyright (c) 2010, Kelvin Lawson. All rights reserved. + * + * Please refer to the README file for further details and License information. + * + * x86 port by: ido@mm.st + */ + +#include "atom.h" +#include "plat.h" +#include "print.h" + +static void *irq_routines[16] = {0}; + +void irq_register_handler(int irq, void (*handler)(x86_iframe_t*)){ + if((NULL == handler) || (irq<0) || (irq>15)) return; + irq_routines[irq] = handler; +} + +void irq_unregister_handler(int irq){ + irq_routines[irq] = 0; +} + +void handle_platform_irq(x86_iframe_t* frame){ + void (*handler)(x86_iframe_t* frame); + uint32_t irq = frame->vector -32; + + handler = irq_routines[irq]; + + if (handler){ + handler(frame); + if(irq==IRQ_PIT) return; + } + + pic_send_EOI(irq); +} + +void sys_tick_handler(x86_iframe_t* frame){ + atomIntEnter(); + atomTimerTick(); + pic_send_EOI(IRQ_PIT); + atomIntExit(TRUE); +} + +void sys_key_handler(x86_iframe_t* frame){ + uint8_t scan_code = in8(0x60); + if(0x90 == scan_code) // Q - pressed + plat_reboot(); + else if(0x81 <= scan_code || 0xd3 <= scan_code) + print("click @%d\n" , atomTimeGet()); +} diff --git a/ports/x86/plat/pic.c b/ports/x86/plat/pic.c new file mode 100644 index 00000000..79ce1114 --- /dev/null +++ b/ports/x86/plat/pic.c @@ -0,0 +1,49 @@ +/* + * AtomThreads project - Copyright (c) 2010, Kelvin Lawson. All rights reserved. + * + * Please refer to the README file for further details and License information. + * + * x86 port by: ido@mm.st + */ + +#include "x86.h" + +#define PIC_MASTER_REG 0x20 +#define PIC_MASTER_IMR 0x21 +#define PIC_SLAVE_REG 0xA0 +#define PIC_SLAVE_IMR 0xA1 + +#define PIC_ICW_1 0x11 + +void pic_init(void){ + // ICW #1 + out8(PIC_MASTER_REG, PIC_ICW_1); + out8(PIC_SLAVE_REG, PIC_ICW_1); + + // ICW #2 + out8(PIC_MASTER_IMR, 0x20); // remapping IRQ0-IRQ7 to start from 0x20, i.e 32'th ISR + out8(PIC_SLAVE_IMR, 0x28); // remapping IRQ8-IRQ15 to start from 0x28, i.e 32'th ISR + + // ICW #3 - MASTER/SLAVE coordination + out8(PIC_MASTER_IMR, 0x4); + out8(PIC_SLAVE_IMR, 0x2); + + // ICW #4 - set 80x86 mode + out8(PIC_MASTER_IMR, 0x01); + out8(PIC_SLAVE_IMR, 0x01); + + out8(PIC_MASTER_IMR, 0x0); + out8(PIC_SLAVE_IMR, 0x0); +} + +void pic_disable(void){ + out8(PIC_MASTER_IMR, 0xff); + out8(PIC_SLAVE_IMR, 0xff); +} + +void pic_send_EOI(uint32_t irq){ + if (40 <= irq) + out8(PIC_SLAVE_REG, 0x20); + + out8(PIC_MASTER_REG, 0x20); +} diff --git a/ports/x86/plat/pit.c b/ports/x86/plat/pit.c new file mode 100644 index 00000000..e7a907c6 --- /dev/null +++ b/ports/x86/plat/pit.c @@ -0,0 +1,53 @@ +/* + * AtomThreads project - Copyright (c) 2010, Kelvin Lawson. All rights reserved. + * + * Please refer to the README file for further details and License information. + * + * x86 port by: ido@mm.st + */ + +#include "x86.h" + +static uint64_t timer_delta_time; +static uint16_t divisor; + +/* PIT (i8253) registers */ +#define I8253_CONTROL_REG 0x43 +#define I8253_DATA_REG 0x40 + +#define INTERNAL_FREQ 1193182ULL +#define INTERNAL_FREQ_3X 3579546ULL + +void pit_init(uint32_t frequency) +{ + uint32_t count, remainder; + + /* figure out the correct divisor for the desired frequency */ + if (frequency <= 18) { + count = 0xffff; + } else if (frequency >= INTERNAL_FREQ) { + count = 1; + } else { + count = INTERNAL_FREQ_3X / frequency; + remainder = INTERNAL_FREQ_3X % frequency; + + if (remainder >= INTERNAL_FREQ_3X / 2) { + count += 1; + } + + count /= 3; + remainder = count % 3; + + if (remainder >= 1) { + count += 1; + } + } + + divisor = count & 0xffff; + + timer_delta_time = (3685982306ULL * count) >> 10; + + out8(I8253_CONTROL_REG, 0x34); + out8(I8253_DATA_REG, divisor & 0xff); // LSB + out8(I8253_DATA_REG, divisor >> 8); // MSB +} diff --git a/ports/x86/plat/terminal.c b/ports/x86/plat/terminal.c new file mode 100644 index 00000000..17681d39 --- /dev/null +++ b/ports/x86/plat/terminal.c @@ -0,0 +1,83 @@ +/* + * AtomThreads project - Copyright (c) 2010, Kelvin Lawson. All rights reserved. + * + * Please refer to the README file for further details and License information. + * + * x86 port by: ido@mm.st + */ + +#include +#include +#include "string.h" +#include "plat.h" +#include "print.h" + +#define PC_VGA_BUFFER 0xB8000 + +static const size_t VGA_WIDTH = 80; +static const size_t VGA_HEIGHT = 25; + +static size_t terminal_row; +static size_t terminal_column; +static uint8_t terminal_color; +static uint16_t* terminal_buffer; + +static uint8_t make_color(enum vga_color fg, enum vga_color bg) { + return fg | bg << 4; +} + +static uint16_t vga_entry(char c, uint8_t color) { + uint16_t c16 = c; + uint16_t color16 = color; + return c16 | color16 << 8; +} +void terminal_init() { + terminal_row = 0; + terminal_column = 0; + terminal_color = make_color(COLOR_LIGHT_RED, COLOR_WHITE); + terminal_buffer = (uint16_t*) PC_VGA_BUFFER; + for (size_t y = 0; y < VGA_HEIGHT; y++) { + for (size_t x = 0; x < VGA_WIDTH; x++) { + const size_t index = y * VGA_WIDTH + x; + terminal_buffer[index] = vga_entry(' ', terminal_color); + } + } + plat_hide_cursor(); +} + +static void scroll(void) +{ + memcpyw( terminal_buffer, terminal_buffer + VGA_WIDTH, (VGA_HEIGHT-1) * VGA_WIDTH *2); + uint16_t c = vga_entry(' ', terminal_color); + memsetw(terminal_buffer + (VGA_HEIGHT-1)*VGA_WIDTH , c , VGA_WIDTH); + --terminal_row; +} + +void terminal_putchar_color(char c, uint8_t text_color, uint8_t background_color) { + + uint8_t color = terminal_color; + + if(VGA_HEIGHT <= terminal_row) + scroll(); + + if( COLOR_DEFAULT != text_color && COLOR_DEFAULT != background_color) + color = make_color(text_color, background_color); + + if('\n' == c) { + terminal_column = 0; + ++terminal_row; + }else{ + const size_t index = terminal_row * VGA_WIDTH + terminal_column; + terminal_buffer[index] = vga_entry(c, color); + } + + if (++terminal_column == VGA_WIDTH) { + terminal_column = 0; + if (++terminal_row >= VGA_HEIGHT) + terminal_row = 0; + } +} + +void terminal_putchar(char c){ + terminal_putchar_color(c,COLOR_DEFAULT, COLOR_DEFAULT); +} diff --git a/ports/x86/string.h b/ports/x86/string.h new file mode 100644 index 00000000..285b3aa9 --- /dev/null +++ b/ports/x86/string.h @@ -0,0 +1,19 @@ +/* + * AtomThreads project - Copyright (c) 2010, Kelvin Lawson. All rights reserved. + * + * Please refer to the README file for further details and License information. + * + * x86 port by: ido@mm.st + */ + +#ifndef _STRING_H +#define _STRING_H + +#include +#include + +void *memcpyw(uint16_t* restrict dest, const uint16_t* restrict src, size_t n); +uint16_t *memsetw(uint16_t *dest, uint16_t val, size_t count); +void *memcpy(void *restrict dest, const void *restrict src, size_t n); + +#endif diff --git a/ports/x86/traps.s b/ports/x86/traps.s new file mode 100644 index 00000000..285564e0 --- /dev/null +++ b/ports/x86/traps.s @@ -0,0 +1,124 @@ +/* + * AtomThreads project - Copyright (c) 2010, Kelvin Lawson. All rights reserved. + * + * Please refer to the README file for further details and License information. + * + * x86 port by: ido@mm.st + * + * This file was taken from the LK project, please see: + * + * https://github.com/littlekernel/lk/blob/master/LICENSE + */ + +#define NUM_INT 0x31 +#define NUM_EXC 0x14 + +#define CODE_SELECTOR 0x08 +#define DATA_SELECTOR 0x10 + +.text + +/* interrupt service routine stubs */ +_isr: + +.set i, 0 +.rept NUM_INT + +.set isr_stub_start, . + +.if i == 8 || (i >= 10 && i <= 14) || i == 17 + nop /* error code pushed by exception */ + nop /* 2 nops are the same length as push byte */ + pushl $i /* interrupt number */ + jmp interrupt_common +.else + pushl $0 /* fill in error code in iframe */ + pushl $i /* interrupt number */ + jmp interrupt_common +.endif + +/* figure out the length of a single isr stub (usually 6 or 9 bytes) */ +.set isr_stub_len, . - isr_stub_start + +.set i, i + 1 +.endr + +/* annoying, but force AS to use the same (longer) encoding of jmp for all of the stubs */ +.fill 256 + +interrupt_common: + pushl %gs /* save segment registers */ + pushl %fs + pushl %es + pushl %ds + pusha /* save general purpose registers */ + movl $DATA_SELECTOR, %eax /* put known good value in segment registers */ + movl %eax, %gs + movl %eax, %fs + movl %eax, %es + movl %eax, %ds + + movl %esp, %eax /* store pointer to iframe */ + pushl %eax + + call x86_exception_handler + + popl %eax /* drop pointer to iframe */ + + popa /* restore general purpose registers */ + popl %ds /* restore segment registers */ + popl %es + popl %fs + popl %gs + addl $8, %esp /* drop exception number and error code */ + iret + +.global setup_idt +setup_idt: + /* setup isr stub descriptors in the idt */ + movl $_isr, %esi + movl $_idt, %edi + movl $NUM_INT, %ecx + +.Lloop: + movl %esi, %ebx + movw %bx, (%edi) /* low word in IDT(n).low */ + shrl $16, %ebx + movw %bx, 6(%edi) /* high word in IDT(n).high */ + + addl $isr_stub_len, %esi/* index the next ISR stub */ + addl $8, %edi /* index the next IDT entry */ + + loop .Lloop + + lidt _idtr + + ret + +.data + +.align 8 +.global _idtr +_idtr: + .short _idt_end - _idt - 1 /* IDT limit */ + .int _idt + +/* interrupt descriptor table (IDT) */ +.global _idt +_idt: + +.set i, 0 +.rept NUM_INT-1 + .short 0 /* low 16 bits of ISR offset (_isr#i & 0FFFFh) */ + .short CODE_SELECTOR /* selector */ + .byte 0 + .byte 0x8e /* present, ring 0, 32-bit interrupt gate */ + .short 0 /* high 16 bits of ISR offset (_isr#i / 65536) */ + +.set i, i + 1 +.endr + +.global _idt_end +_idt_end: + + diff --git a/ports/x86/utils/print.h b/ports/x86/utils/print.h new file mode 100644 index 00000000..3d438c03 --- /dev/null +++ b/ports/x86/utils/print.h @@ -0,0 +1,40 @@ +/* + * AtomThreads project - Copyright (c) 2010, Kelvin Lawson. All rights reserved. + * + * Please refer to the README file for further details and License information. + * + * x86 port by: ido@mm.st + */ + +#ifndef PRINTF_H +#define PRINTF_H + +#include "atomport.h" + +/* Hardware text mode color constants. */ +enum vga_color { + COLOR_BLACK = 0, + COLOR_BLUE = 1, + COLOR_GREEN = 2, + COLOR_CYAN = 3, + COLOR_RED = 4, + COLOR_MAGENTA = 5, + COLOR_BROWN = 6, + COLOR_LIGHT_GREY = 7, + COLOR_DARK_GREY = 8, + COLOR_LIGHT_BLUE = 9, + COLOR_LIGHT_GREEN = 10, + COLOR_LIGHT_CYAN = 11, + COLOR_LIGHT_RED = 12, + COLOR_LIGHT_MAGENTA = 13, + COLOR_LIGHT_BROWN = 14, + COLOR_WHITE = 15, + COLOR_DEFAULT = 0xff +}; + +#define print kprintf + +uint32_t kprintf(const char *format, ...); +void print_color(char* s, uint8_t text_color, uint8_t background_color); + +#endif diff --git a/ports/x86/utils/printf.c b/ports/x86/utils/printf.c new file mode 100644 index 00000000..56988863 --- /dev/null +++ b/ports/x86/utils/printf.c @@ -0,0 +1,259 @@ +/* + * AtomThreads project - Copyright (c) 2010, Kelvin Lawson. All rights reserved. + * + * Please refer to the README file for further details and License information. + * + * x86 port by: ido@mm.st + */ + +#include +#include "plat.h" + +#define kernel_putchar terminal_putchar + +typedef unsigned long word_t; + +static unsigned int +print_string(const char *s) +{ + unsigned int n; + + for (n = 0; *s; s++, n++) { + kernel_putchar(*s); + } + + return n; +} + +static unsigned long +xdiv(unsigned long x, unsigned int denom) +{ + switch (denom) { + case 16: + return x / 16; + case 10: + return x / 10; + default: + return 0; + } +} + +static unsigned long +xmod(unsigned long x, unsigned int denom) +{ + switch (denom) { + case 16: + return x % 16; + case 10: + return x % 10; + default: + return 0; + } +} + +word_t +print_unsigned_long(unsigned long x, word_t ui_base) +{ + char out[sizeof(unsigned long) * 2 + 3]; + word_t i, j; + unsigned int d; + + if (ui_base != 10 && ui_base != 16) { + return 0; + } + + if (x == 0) { + kernel_putchar('0'); + return 1; + } + + for (i = 0; x; x = xdiv(x, ui_base), i++) { + d = xmod(x, ui_base); + + if (d >= 10) { + out[i] = 'a' + d - 10; + } else { + out[i] = '0' + d; + } + } + + for (j = i; j > 0; j--) { + kernel_putchar(out[j - 1]); + } + + return i; +} + +static unsigned int +print_unsigned_long_long(unsigned long long x, unsigned int ui_base) +{ + unsigned int upper, lower; + unsigned int n = 0; + unsigned int mask = 0xF0000000u; + unsigned int shifts = 0; + + if (ui_base != 16) { + return 0; + } + + upper = (unsigned int) (x >> 32llu); + lower = (unsigned int) x & 0xffffffff; + + if (upper > 0) { + n += print_unsigned_long(upper, ui_base); + /* print leading 0s */ + while (!(mask & lower)) { + kernel_putchar('0'); + n++; + mask = mask >> 4; + shifts++; + if (shifts == 8) { + break; + } + } + } + /* print last 32 bits */ + n += print_unsigned_long(lower, ui_base); + + return n; +} + + +static int +vprintf(const char *format, va_list ap) +{ + unsigned int n; + unsigned int formatting; + + if (!format) { + return 0; + } + + n = 0; + formatting = 0; + while (*format) { + if (formatting) { + switch (*format) { + case '%': + kernel_putchar('%'); + n++; + format++; + break; + + case 'd': { + int x = va_arg(ap, int); + + if (x < 0) { + kernel_putchar('-'); + n++; + x = -x; + } + + n += print_unsigned_long(x, 10); + format++; + break; + } + + case 'u': + n += print_unsigned_long(va_arg(ap, unsigned int), 10); + format++; + break; + + case 'x': + n += print_unsigned_long(va_arg(ap, unsigned int), 16); + format++; + break; + + case 'p': { + unsigned long p = va_arg(ap, unsigned long); + if (p == 0) { + n += print_string("(nil)"); + } else { + n += print_string("0x"); + n += print_unsigned_long(p, 16); + } + format++; + break; + } + + case 's': + n += print_string(va_arg(ap, char *)); + format++; + break; + + case 'l': + format++; + switch (*format) { + case 'd': { + long x = va_arg(ap, long); + + if (x < 0) { + kernel_putchar('-'); + n++; + x = -x; + } + + n += print_unsigned_long((unsigned long)x, 10); + format++; + } + break; + case 'l': + if (*(format + 1) == 'x') { + n += print_unsigned_long_long(va_arg(ap, unsigned long long), 16); + } + format += 2; + break; + case 'u': + n += print_unsigned_long(va_arg(ap, unsigned long), 10); + format++; + break; + case 'x': + n += print_unsigned_long(va_arg(ap, unsigned long), 16); + format++; + break; + + default: + /* format not supported */ + return -1; + } + break; + default: + /* format not supported */ + return -1; + } + + formatting = 0; + } else { + switch (*format) { + case '%': + formatting = 1; + format++; + break; + + default: + kernel_putchar(*format); + n++; + format++; + break; + } + } + } + + return n; +} + +uint32_t kprintf(const char *format, ...) +{ + va_list args; + word_t i; + + va_start(args, format); + i = vprintf(format, args); + va_end(args); + return i; +} + +void print_color(char* s, uint8_t text_color, uint8_t background_color){ + for(;*s;s++) + terminal_putchar_color(*s,text_color,background_color); +} diff --git a/ports/x86/utils/string.c b/ports/x86/utils/string.c new file mode 100644 index 00000000..7bfdb381 --- /dev/null +++ b/ports/x86/utils/string.c @@ -0,0 +1,33 @@ +/* + * AtomThreads project - Copyright (c) 2010, Kelvin Lawson. All rights reserved. + * + * Please refer to the README file for further details and License information. + * + * x86 port by: ido@mm.st + */ + +#include "string.h" + +uint16_t *memsetw(uint16_t *dest, uint16_t val, size_t count) +{ + uint16_t *temp = dest; + while(count--) *temp++ = val; + return dest; +} + +void *memcpyw(uint16_t* restrict dest, const uint16_t* restrict src, size_t n) +{ + uint8_t *d = (uint8_t*) dest; + const uint8_t *s = (const uint8_t*) src; + while(n--) *dest++ = *src++; + return dest; +} + +void *memcpy(void *restrict dest, const void *restrict src, size_t n) +{ + uint8_t *d = dest; + const uint8_t *s = src; + while(n--) *d++=*s++; + return dest; +} + diff --git a/ports/x86/x86.h b/ports/x86/x86.h new file mode 100644 index 00000000..2346d70a --- /dev/null +++ b/ports/x86/x86.h @@ -0,0 +1,78 @@ +/* + * AtomThreads project - Copyright (c) 2010, Kelvin Lawson. All rights reserved. + * + * Please refer to the README file for further details and License information. + * + * x86 port by: ido@mm.st + */ + +#ifndef X86_H +#define X86_H + +#include +#include + +typedef struct x86_32_iframe { + uint32_t di, si, bp, sp, bx, dx, cx, ax; + uint32_t ds, es, fs, gs; + uint32_t vector; + uint32_t err_code; + uint32_t ip, cs, flags; + uint32_t user_sp, user_ss; +} x86_iframe_t ; + +void gdt_install_flat(void); +void setup_idt(void); + +static inline void out8(uint16_t port, uint8_t value) +{ + __asm__ volatile("outb %[value], %[port]" :: [port] "d"(port), [value] "a"(value)); +} + +static inline void out16(uint16_t port, uint16_t value) +{ + __asm__ volatile("outw %[value], %[port]" :: [port] "d"(port), [value] "a"(value)); +} + +static inline uint8_t in8(uint16_t port) +{ + uint8_t value; + __asm__ volatile("inb %[port], %[value]" : [value] "=a"(value) : [port] "d" (port)); + return value; +} + +static inline uint32_t x86_get_eflags(void){ + + uint32_t flags; + + __asm__ volatile( + "pushfl;" + "popl %0" + : "=rm" (flags) + :: "memory"); + + return flags; +} + +static inline void x86_set_eflags(uint32_t flags){ + __asm__ volatile( + "pushl %0;" + "popfl" + :: "g" (flags) + : "memory", "cc"); +} + +static inline void x86_enable_int(void){ + __asm__ volatile("sti"); +} + +static inline void x86_disable_int(void){ + __asm__ volatile("cli"); +} + +static inline void x86_halt(void){ + __asm__ volatile( + "cli;" + "hlt"); +} +#endif diff --git a/tests/commit-tests-x86_32.sh b/tests/commit-tests-x86_32.sh new file mode 100755 index 00000000..7c0a0b4d --- /dev/null +++ b/tests/commit-tests-x86_32.sh @@ -0,0 +1,28 @@ +#!/bin/bash + +# Quit on any error +set -e +cd ../ports/x86 + +BIN_DIR=build/run/iso/boot +GRUB_DIR=$BIN_DIR/grub +GRUB_CFG=$GRUB_DIR/grub.cfg +ISO=all_tests_multiboot.iso + +make clean + +mkdir -p $GRUB_DIR + +make + +cp build/*.bin $BIN_DIR + +for fn in $BIN_DIR/*.bin; do + echo "menuentry \"$(basename $fn)\" { multiboot /boot/$(basename $fn) }" >> $GRUB_CFG +done + +if [ $(ls $BIN_DIR/*.bin | wc -l) -gt 0 ];then + grub-mkrescue -o $ISO build/run/iso + qemu-system-i386 -cdrom $ISO -monitor stdio +fi +