diff --git a/Makefile b/Makefile index 8d9a638..51ce646 100644 --- a/Makefile +++ b/Makefile @@ -25,3 +25,4 @@ clean: $(MAKE) -C kernel clean rm -f mittos64.iso rm -f qemu-error.log + rm -f serial.log diff --git a/activate b/activate index 2266248..b03366e 100755 --- a/activate +++ b/activate @@ -19,6 +19,7 @@ function decomp() { ${TARGET}-objdump -S $1 | vim - -R } +alias viewlog="cat serial.log | ${BUILDROOT}/util/colorize.sh" export PS1="(mittos64)${PS1}" diff --git a/emul b/emul index 4c8f179..95626fd 100755 --- a/emul +++ b/emul @@ -57,7 +57,8 @@ function main() { make all || exit 1 util/build_iso.sh || exit 1 - emulator="qemu-system-x86_64 -cdrom mittos64.iso ${EMULPARAM[@]}" + echo > serial.log + emulator="qemu-system-x86_64 -cdrom mittos64.iso -serial file:serial.log ${EMULPARAM[@]}" debugger=$(which x86_64-elf-linux-gdb) if [[ (-n ${EMULVNC}) ]]; then @@ -81,9 +82,11 @@ function main() { if [[ -n "${TMUX}" ]]; then emulwindow=`tmux new-window -P -n "osdevemul" "${emulator} 2>qemu-error.log; tmux kill-window -t osdevemul"` - if [[ -z ${EMULDEBUG} ]]; then + if [[ -z ${EMULNDEBUG} ]]; then debugpane=`tmux split-window -P -h -t ${emulwindow} "sleep 1; ${debugger}"` fi + serialpane=`tmux split-window -P -v -t ${emulwindow} "tail -f serial.log | util/colorize.sh"` + tmux select-pane -l else ${emulator} fi diff --git a/kernel/Makefile b/kernel/Makefile index 8c6b86f..13467ec 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -15,6 +15,8 @@ KERNEL_OBJS := $(addprefix obj/,$(patsubst %,%.o,$(basename $(KERNEL_SRC)))) # Kernel object file KERNEL := obj/boot/kernel +# Special file for keeping git information up to date +VERSION_OBJ := obj/boot/version.o # Default compilation flags @@ -41,10 +43,25 @@ $(KERNEL_OBJS): | $(OBJ_DIRS) $(OBJ_DIRS): mkdir -p $@ +# Git status flags for the version file +GITHASH := $(shell git log -1 --pretty="tformat:%h") +GITDATE := $(shell git log -1 --pretty="tformat:%cd") +GITDIRTY := $(shell git status -s >/dev/null 2>/dev/null && echo 1 || echo 0) +GITMESSAGE := $(shell git log -1 --pretty="tformat:%s") +GITBRANCH := $(shell git log -1 --pretty="tformat:%d") +GITFLAGS := -DGITHASH='"$(GITHASH)"' \ + -DGITDATE='"$(GITDATE)"' \ + -DGITDIRTY='$(GITDIRTY)' \ + -DGITMESSAGE='"$(GITMESSAGE)"' \ + -DGITBRANCH='"$(GITBRANCH)"' +$(VERSION_OBJ): CFLAGS += $(GITFLAGS) + # The kernel needs some special flags $(KERNEL): LDFLAGS += -n -nostdlib -T Link.ld $(KERNEL): LDLIBS := -lgcc $(KERNEL): $(KERNEL_OBJS) + rm -rf $(VERSION_OBJ) + $(MAKE) $(VERSION_OBJ) $(LINK.c) $^ -o $@ # Use the default make compilation rules diff --git a/kernel/arch/ports.c b/kernel/arch/ports.c new file mode 100644 index 0000000..0b9af41 --- /dev/null +++ b/kernel/arch/ports.c @@ -0,0 +1,27 @@ +#include +#include + +void outb(uint16_t port, uint8_t value) +{ + // Send byte to port + asm volatile("outb %1, %0" : : "dN" (port), "a" (value)); +} + +uint8_t inb(uint16_t port) +{ + uint8_t ret; + asm volatile("inb %1, %0" : "=a" (ret) : "dN" (port)); + return ret; +} + +void outw(uint16_t port, uint16_t value) +{ + asm volatile("outw %1, %0" : : "dN" (port), "a" (value)); +} + +uint16_t inw(uint16_t port) +{ + uint16_t ret; + asm volatile("inw %1, %0" : "=a" (ret) : "dN" (port)); + return ret; +} diff --git a/kernel/boot/debug.c b/kernel/boot/debug.c new file mode 100644 index 0000000..195e75b --- /dev/null +++ b/kernel/boot/debug.c @@ -0,0 +1,132 @@ +#include +#include +#include +#include +#include +#include + +void debug_init() +{ + vga_init(); + serial_init(PORT_COM1); +} + +uint64_t num2str(uint64_t num, uint32_t base, char *buf) +{ + // Convert a number to string + if(num == 0) + { + buf[0] = '0'; + buf[1] = '\0'; + return 0; + } + uint32_t i=0, j=0; + char chars[] = "0123456789ABCDEF"; + // Build the string starting with the least significant digit + while(num > 0) + { + buf[i++] = chars[num%base]; + num /= base; + } + + // Invert the string to get digits in right order + i--; + j=0; + while(j - -#define VGA_MEMORY 0xb8000 -#define VGA_ROWS 24 -#define VGA_COLS 80 - -void clear_screen() -{ - // Clear the video memory - unsigned char *m = (void *)P2V(VGA_MEMORY); - for(int i = 0; i < VGA_ROWS*VGA_COLS*2; i++) - { - *m++ = 0; - } -} - -void prints(const char *s, unsigned int row, unsigned int col) -{ - // Very simple pure ascii string printing - unsigned char *m = (void *)P2V(VGA_MEMORY); - m += 2*VGA_COLS*row+2*col; - while(*s) - { - *m++ = *s++; - *m++ = 0x7; - } -} +#include int kmain(void) { - clear_screen(); - prints("Hello, world!", 0, 0); + debug_init(); + debug_ok("MITTOS64 kernel booted\n"); + debug_build_time(); + debug_git_info(); + + // Test the printf functions + debug("binary:%b octal:%o dec:%d\n", 0xAA55, 0123, 456); + debug("hex:%x string:%s char:%c\n", 0xabcd, "Hello", 'T'); + debug("pointer:%x\n", kmain); + + debug_info("An information string\n"); + debug_ok("%s prints ok\n", "This string"); + debug_warning("A warning message\n"); + debug_error("%d is less than %x\n", 12, 17); + + debug_info("BOOT COMPLETE\n"); for(;;)asm("hlt"); } diff --git a/kernel/boot/version.c b/kernel/boot/version.c new file mode 100644 index 0000000..5f7020e --- /dev/null +++ b/kernel/boot/version.c @@ -0,0 +1,20 @@ +#include + +char *_kernel_build_date = __DATE__; +char *_kernel_build_time = __TIME__; + +char *_kernel_git_hash = GITHASH; +char *_kernel_git_date = GITDATE; +int _kernel_git_dirty = GITDIRTY; +char *_kernel_git_message = GITMESSAGE; +char *_kernel_git_branch = GITBRANCH; + +void debug_build_time() +{ + debug_info("Kernel built: %s (%s)\n", _kernel_build_date, _kernel_build_time); +} +void debug_git_info() +{ + debug_info("Kernel git: %s%s%s %s\n", _kernel_git_branch, _kernel_git_hash, (_kernel_git_dirty)?" (dirty)":"", _kernel_git_date); + debug_info("Git message: %s\n", _kernel_git_message); +} diff --git a/kernel/drivers/serial.c b/kernel/drivers/serial.c new file mode 100644 index 0000000..b0fa32f --- /dev/null +++ b/kernel/drivers/serial.c @@ -0,0 +1,23 @@ +#include +#include + + +void serial_init(uint16_t port) +{ + // Serial port initialization according to + // http://wiki.osdev.org/Serial_Ports + outb(port + 1, 0x00); + outb(port+ 3, 0x80); + outb(port+ 0, 0x03); + outb(port+ 1, 0x00); + outb(port+ 3, 0x03); + outb(port+ 2, 0xC7); + outb(port+ 4, 0x0B); +} + +void serial_write(uint16_t port, uint8_t c) +{ + while(!(inb(port+ 5)&0x20)); + + outb(port, c); +} diff --git a/kernel/drivers/vga.c b/kernel/drivers/vga.c new file mode 100644 index 0000000..1717d4a --- /dev/null +++ b/kernel/drivers/vga.c @@ -0,0 +1,77 @@ +#include +#include +#include +#include +#include +#include + + +void *vidmem = P2V(0xb8000); +uint16_t buffer[VGA_SIZE]; + +struct { + uint32_t x; + uint32_t y; + uint8_t style; +} cursor; + +void sync() +{ + // Copy the buffer to video memory + memcpy(vidmem, buffer, sizeof(buffer)); +} + +void setcursor() +{ + // Move hardware cursor to correspond to the software one + uint16_t position = cursor.y*VGA_WIDTH + cursor.x; + outb(0x3D4, 0x0F); + outb(0x3D5, (uint8_t)(position & 0xFF)); + outb(0x3D4, 0x0E); + outb(0x3D5, (uint8_t)((position >> 8) & 0xF)); +} + +void scroll() +{ + // Scroll all lines up and clear the bottom one + while(cursor.y >= VGA_LINES) + { + memmove(buffer, &buffer[VGA_WIDTH], sizeof(buffer)-2*VGA_WIDTH); + memset(&buffer[VGA_SIZE-VGA_WIDTH], 0, 2*VGA_WIDTH); + cursor.y--; + } +} + +void vga_printch(char c) +{ + // Print a character to VGA memory + if(c == '\n') + { + // Linebreak + cursor.x = 0; + cursor.y++; + } else { + buffer[cursor.y*VGA_WIDTH + cursor.x] = c | (cursor.style << 8); + cursor.x++; + } + + if(cursor.x >= VGA_WIDTH) + { + // Wrap line + cursor.x = 0; + cursor.y++; + } + scroll(); + sync(); + setcursor(); +} + +void vga_init() +{ + // Set cursor at top left + cursor.x = cursor.y = 0; + cursor.style = 0x07; + // Clear video memory + memset(buffer, 0, sizeof(buffer)); + sync(); +} diff --git a/kernel/include/debug.h b/kernel/include/debug.h new file mode 100644 index 0000000..7173b9d --- /dev/null +++ b/kernel/include/debug.h @@ -0,0 +1,31 @@ +#pragma once +#include + +#ifndef NDEBUG + #define debug(...) debug_printf(__VA_ARGS__) +#else + #define debug(...) ((void)0) +#endif +#define debug_info(...) do{debug("[INFO] ");debug(__VA_ARGS__);}while(0) +#define debug_ok(...) do{debug("[OK] ");debug(__VA_ARGS__);}while(0) +#define debug_warning(...) do{debug("[WARNING] ");debug(__VA_ARGS__);}while(0) +#define debug_error(...) do{debug("[ERROR] ");debug(__VA_ARGS__);}while(0) + +void debug_init(); +void debug_putch(char c); +void debug_putsn(char *s, size_t n); +void debug_puts(char *s); +void debug_printf(char *, ...); + +char *_kernel_build_date; +char *_kernel_build_time; + +char *_kernel_git_hash; +char *_kernel_git_date; +int kernel_git_dirty; +char *_kernel_git_message; +char *_kernel_git_branch; + +void debug_build_time(); +void debug_git_info(); + diff --git a/kernel/include/ports.h b/kernel/include/ports.h new file mode 100644 index 0000000..08f9a4c --- /dev/null +++ b/kernel/include/ports.h @@ -0,0 +1,8 @@ +#pragma once +#include + +void outb(uint16_t port, uint8_t value); +uint8_t inb(uint16_t port); + +void outw(uint16_t port, uint16_t value); +uint16_t inw(uint16_t port); diff --git a/kernel/include/serial.h b/kernel/include/serial.h new file mode 100644 index 0000000..ac0bdd2 --- /dev/null +++ b/kernel/include/serial.h @@ -0,0 +1,10 @@ +#pragma once +#include + +#define PORT_COM1 0x3F8 +#define PORT_COM2 0x2F8 +#define PORT_COM3 0x3E8 +#define PORT_COM4 0x2E8 + +void serial_init(uint16_t port); +void serial_write(uint16_t port, uint8_t c); diff --git a/kernel/include/string.h b/kernel/include/string.h new file mode 100644 index 0000000..f9719b2 --- /dev/null +++ b/kernel/include/string.h @@ -0,0 +1,13 @@ +#pragma once +#include +#include + +void *memcpy(void *dest, const void *src, size_t n); + +void *memset(void *s, int c, size_t n); + +void *memmove(void *dest, const void *src, size_t n); + +int memcmp(const void *s1, const void *s2, size_t n); + +size_t strlen(const char *s); diff --git a/kernel/include/vga.h b/kernel/include/vga.h new file mode 100644 index 0000000..27e58eb --- /dev/null +++ b/kernel/include/vga.h @@ -0,0 +1,8 @@ +#pragma once + +#define VGA_WIDTH 80 +#define VGA_LINES 25 +#define VGA_SIZE (VGA_WIDTH*VGA_LINES) + +void vga_init(); +void vga_printch(char c); diff --git a/kernel/mem/string.c b/kernel/mem/string.c new file mode 100644 index 0000000..132f4ad --- /dev/null +++ b/kernel/mem/string.c @@ -0,0 +1,46 @@ +#include +#include +#include + +// Standard function required by gcc +// Just the naĆ­ve implementations for now + +void *memcpy(void *dest, const void *src, size_t n) +{ + char *dp = dest; + const char *sp = src; + while(n--) *dp++ = *sp++; + return dest; +} + +void *memset(void *s, int c, size_t n) +{ + unsigned char *p = s; + while(n--) *p++ = (unsigned char)c; + return s; +} + +void *memmove(void *dest, const void *src, size_t n) +{ + // Since our memcpy implementation copies one void * at a time, this is safe + memcpy(dest, src, n); + return dest; +} + +int memcmp(const void *s1, const void *s2, size_t n) +{ + const unsigned char *p1 = s1, *p2 = s2; + for(; n--; p1++, p2++) + { + if (*p1 != *p2) + return *p1 - *p2; + } + return 0; +} + +size_t strlen(const char *s) +{ + size_t len = 0; + while(*s++) len++; + return len; +} diff --git a/util/colorize.sh b/util/colorize.sh new file mode 100755 index 0000000..ea5ceca --- /dev/null +++ b/util/colorize.sh @@ -0,0 +1,14 @@ +#!/usr/bin/env bash + +function ce() +{ + echo -e "${@}" +} + +while read line; do + echo "$line" | sed \ + -e "s/^\[\(INFO\)\]/\[$(ce \\033[36m)\1$(ce \\033[0m)\]/" \ + -e "s/^\[\(OK\)\]/\[$(ce \\033[32m)\1$(ce \\033[0m)\]/" \ + -e "s/^\[\(WARNING\)\]/\[$(ce \\033[33m)\1$(ce \\033[0m)\]/" \ + -e "s/^\[\(ERROR\)\]/\[$(ce \\033[31m)\1$(ce \\033[0m)\]/" +done