[BOOT] Print functions for debugging
This commit is contained in:
parent
df86be2973
commit
d149d4d20a
1
Makefile
1
Makefile
@ -25,3 +25,4 @@ clean:
|
||||
$(MAKE) -C kernel clean
|
||||
rm -f mittos64.iso
|
||||
rm -f qemu-error.log
|
||||
rm -f serial.log
|
||||
|
1
activate
1
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}"
|
||||
|
||||
|
7
emul
7
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
|
||||
|
@ -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
|
||||
|
27
kernel/arch/ports.c
Normal file
27
kernel/arch/ports.c
Normal file
@ -0,0 +1,27 @@
|
||||
#include <ports.h>
|
||||
#include <stdint.h>
|
||||
|
||||
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;
|
||||
}
|
132
kernel/boot/debug.c
Normal file
132
kernel/boot/debug.c
Normal file
@ -0,0 +1,132 @@
|
||||
#include <debug.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <vga.h>
|
||||
#include <serial.h>
|
||||
|
||||
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<i)
|
||||
{
|
||||
char t = buf[i];
|
||||
buf[i--] = buf[j];
|
||||
buf[j++] = t;
|
||||
}
|
||||
|
||||
// Zero terminate
|
||||
buf[i+j+1] = '\0';
|
||||
return i+j+1;
|
||||
}
|
||||
|
||||
void debug_putch(char c)
|
||||
{
|
||||
vga_printch(c);
|
||||
serial_write(PORT_COM1, c);
|
||||
}
|
||||
void debug_putsn(char *s, size_t n)
|
||||
{
|
||||
while(n--)
|
||||
debug_putch(*s++);
|
||||
}
|
||||
void debug_puts(char *s)
|
||||
{
|
||||
debug_putsn(s, strlen(s));
|
||||
}
|
||||
|
||||
void debug_vprintf(char *str, va_list args)
|
||||
{
|
||||
size_t len = 0;
|
||||
if(!(*str))
|
||||
return;
|
||||
if(*str != '%')
|
||||
{
|
||||
while(str[len] && str[len] != '%')
|
||||
len++;
|
||||
debug_putsn(str, len);
|
||||
return debug_vprintf(&str[len], args);
|
||||
}
|
||||
str++;
|
||||
char *prefix;
|
||||
uint32_t base = 0;
|
||||
#define DEBUG_VPRINTF_BASE_CHAR 255
|
||||
#define DEBUG_VPRINTF_BASE_STR 1024
|
||||
switch(*str)
|
||||
{
|
||||
case 'b':
|
||||
prefix = "0b";
|
||||
base = 2;
|
||||
break;
|
||||
case 'o':
|
||||
prefix = "0";
|
||||
base = 8;
|
||||
break;
|
||||
case 'd':
|
||||
prefix = "";
|
||||
base = 10;
|
||||
break;
|
||||
case 'x':
|
||||
prefix = "0x";
|
||||
base = 16;
|
||||
break;
|
||||
case 'c':
|
||||
prefix = "";
|
||||
base = DEBUG_VPRINTF_BASE_CHAR;
|
||||
break;
|
||||
case 's':
|
||||
prefix = "";
|
||||
base = DEBUG_VPRINTF_BASE_STR;
|
||||
break;
|
||||
case '%':
|
||||
default:
|
||||
debug_putch('%');
|
||||
break;
|
||||
}
|
||||
if(base == DEBUG_VPRINTF_BASE_STR)
|
||||
debug_puts(va_arg(args, char *));
|
||||
else if(base == DEBUG_VPRINTF_BASE_CHAR)
|
||||
debug_putch((char)va_arg(args, uint64_t));
|
||||
else if(base)
|
||||
{
|
||||
uint64_t num = va_arg(args, uint64_t);
|
||||
char buf[128];
|
||||
num2str(num, base, buf);
|
||||
debug_puts(prefix);
|
||||
debug_puts(buf);
|
||||
}
|
||||
str++;
|
||||
return debug_vprintf(str, args);
|
||||
}
|
||||
|
||||
void debug_printf(char *str, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, str);
|
||||
debug_vprintf(str, args);
|
||||
va_end(args);
|
||||
}
|
@ -1,34 +1,22 @@
|
||||
#include <mem.h>
|
||||
|
||||
#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 <debug.h>
|
||||
|
||||
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");
|
||||
}
|
||||
|
20
kernel/boot/version.c
Normal file
20
kernel/boot/version.c
Normal file
@ -0,0 +1,20 @@
|
||||
#include <debug.h>
|
||||
|
||||
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);
|
||||
}
|
23
kernel/drivers/serial.c
Normal file
23
kernel/drivers/serial.c
Normal file
@ -0,0 +1,23 @@
|
||||
#include <serial.h>
|
||||
#include <ports.h>
|
||||
|
||||
|
||||
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);
|
||||
}
|
77
kernel/drivers/vga.c
Normal file
77
kernel/drivers/vga.c
Normal file
@ -0,0 +1,77 @@
|
||||
#include <vga.h>
|
||||
#include <stdint.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include <mem.h>
|
||||
#include <ports.h>
|
||||
|
||||
|
||||
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();
|
||||
}
|
31
kernel/include/debug.h
Normal file
31
kernel/include/debug.h
Normal file
@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
#include <string.h>
|
||||
|
||||
#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();
|
||||
|
8
kernel/include/ports.h
Normal file
8
kernel/include/ports.h
Normal file
@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
|
||||
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);
|
10
kernel/include/serial.h
Normal file
10
kernel/include/serial.h
Normal file
@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
|
||||
#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);
|
13
kernel/include/string.h
Normal file
13
kernel/include/string.h
Normal file
@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
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);
|
8
kernel/include/vga.h
Normal file
8
kernel/include/vga.h
Normal file
@ -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);
|
46
kernel/mem/string.c
Normal file
46
kernel/mem/string.c
Normal file
@ -0,0 +1,46 @@
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
// 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;
|
||||
}
|
14
util/colorize.sh
Executable file
14
util/colorize.sh
Executable file
@ -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
|
Loading…
x
Reference in New Issue
Block a user