Initial import of the x86 port

Currently this works only in qemu.
This commit is contained in:
René Kijewski 2014-01-16 07:39:33 +01:00
parent 2b95e7b144
commit 4e4f908379
65 changed files with 7915 additions and 6 deletions

1
.gitignore vendored
View File

@ -11,3 +11,4 @@ cachegrind.out*
.project
.cproject
.settings
/toolchain

11
boards/qemu-i386/Makefile Normal file
View File

@ -0,0 +1,11 @@
MODULE = qemu-i386_base
DIRS = $(RIOTBOARD)/x86-multiboot-common
all: $(BINDIR)$(MODULE).a
@for i in $(DIRS) ; do "$(MAKE)" -C $$i ; done ;
include $(RIOTBASE)/Makefile.base
clean::
@for i in $(DIRS) ; do "$(MAKE)" -C $$i clean ; done ;

View File

@ -0,0 +1,7 @@
include $(RIOTBOARD)/x86-multiboot-common/Makefile.include
CFLAGS += -march=i686 -mtune=i686
CFLAGS += -I$(RIOTBOARD)/qemu-i386/include
TERMPROG = qemu-system-i386 -m 512m -serial stdio -nographic -monitor /dev/null -kernel $(BINDIR)/$(PROJECT).hex
FLASHER = true

View File

@ -0,0 +1,43 @@
/*
* Copyright (C) 2014 René Kijewski <rene.kijewski@fu-berlin.de>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/**
* @ingroup x86
* @{
*
* @file
* @brief Board specific defines for qemu-i386.
*
* @author René Kijewski <rene.kijewski@fu-berlin.de>
*/
#ifndef __RIOT__BOARDS__QEMU_I386__BOARD__H
#define __RIOT__BOARDS__QEMU_I386__BOARD__H
#define UART_PORT (COM1_PORT) /* IO port to use for UART */
#define UART_IRQ (COM1_IRQ) /* IRQ line to use for UART */
#define LED_RED_ON /* not available */
#define LED_RED_OFF /* not available */
#define LED_RED_TOGGLE /* not available */
#endif
/**
* @}
*/

View File

@ -0,0 +1,46 @@
/*
* Copyright (C) 2014 René Kijewski <rene.kijewski@fu-berlin.de>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/**
* CPU-specific defines for qemu-i386.
*
* @ingroup x86
* @{
* @file
* @author René Kijewski <rene.kijewski@fu-berlin.de>
*/
#ifndef __RIOT__BOARDS__QEMU_I386__CPU_CONF__H
#define __RIOT__BOARDS__QEMU_I386__CPU_CONF__H
/* FIXME: This file is just a filler. The numbers are entirely random ... */
#define KERNEL_CONF_STACKSIZE_DEFAULT (8192)
#define KERNEL_CONF_STACKSIZE_IDLE (8192)
#define KERNEL_CONF_STACKSIZE_PRINTF (8192)
#define KERNEL_CONF_STACKSIZE_PRINTF_FLOAT (8192)
#define MINIMUM_STACK_SIZE (8192)
#define UART0_BUFSIZE (16)
#define F_CPU (1000000) /* This value is unused in x86 */
#endif
/** @} */

View File

@ -0,0 +1,55 @@
/*
* Copyright (C) 2014 René Kijewski <rene.kijewski@fu-berlin.de>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/**
* @ingroup x86
* @{
*
* @file
* @brief Initialization code for the qemu-i386 board
*
* @author René Kijewski <rene.kijewski@fu-berlin.de>
*
* @}
*/
#include "cpu.h"
#include "x86_ports.h"
#include "x86_reboot.h"
#define ENABLE_DEBUG (0)
#include "debug.h"
static bool qemu_shutdown(void)
{
unsigned old_state = disableIRQ();
DEBUG("SHUTTING DOWN.\n");
/* (phony) ACPI shutdown (http://forum.osdev.org/viewtopic.php?t=16990) */
/* Works for qemu and bochs. */
outw(0xB004, 0x2000);
restoreIRQ(old_state);
return false;
}
void x86_init_board(void)
{
x86_set_shutdown_fun(qemu_shutdown);
}

View File

@ -0,0 +1,59 @@
/*
* Copyright (C) 2014 René Kijewski <rene.kijewski@fu-berlin.de>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/**
* @ingroup x86
* @{
*
* @file
* @brief Placeholder if someone uses x86-multiboot as a board.
*
* @author René Kijewski <rene.kijewski@fu-berlin.de>
*
* @}
*/
#include <lpm.h>
void lpm_init(void)
{
// void
}
enum lpm_mode lpm_set(enum lpm_mode target)
{
if (target != LPM_ON) {
asm volatile ("hlt");
}
return LPM_UNKNOWN;
}
void lpm_awake(void)
{
// void
}
void lpm_begin_awake(void)
{
// void
}
void lpm_end_awake(void)
{
// void
}

View File

@ -0,0 +1,3 @@
MODULE = x86-multiboot-common_base
include $(RIOTBASE)/Makefile.base

View File

@ -0,0 +1,56 @@
ifeq (, $(NEWLIB_BASE))
NEWLIB_BASE := $(RIOTBASE)/toolchain/x86/i586-none-elf
ifneq (0, $(shell test -e "$(NEWLIB_BASE)/lib/libc.a" && echo $$?))
NEWLIB_PRECOMPILED_NAME := i586-newlib_2.1.0_tlsf.txz
NEWLIB_PRECOMPILED := http://download.riot-os.org/$(NEWLIB_PRECOMPILED_NAME)
$(warning "Precompiled newlib is missing in $(NEWLIB_BASE)")
$(warning "Downloading from $(NEWLIB_PRECOMPILED)")
$(shell cd $(RIOTBASE) && wget -qO- "$(NEWLIB_PRECOMPILED)" | tar xJ)
endif
endif
ifeq (,$(BUILD_INCLUDE_BASE))
GCC_BUILD_TRIPLET ?= $(shell gcc -dumpmachine)
GCC_BUILD_VERSION ?= $(shell gcc -dumpversion)
BUILD_INCLUDE_BASE = /usr/lib/gcc/$(GCC_BUILD_TRIPLET)/$(GCC_BUILD_VERSION)
ifeq (,$(shell echo $(GCC_BUILD_TRIPLET) | sed -e 's,-.*,,' | grep -e '\(x\|i[3-7]\)86'))
$(warning Your build machine is a(n) $(GCC_BUILD_TRIPLET).)
$(warning Since this is not IA32 compatible, you must set BUILD_INCLUDE_BASE explicitly!)
endif
endif
export INCLUDES += -isystem $(BUILD_INCLUDE_BASE)/include \
-isystem $(NEWLIB_BASE)/include \
-isystem $(NEWLIB_BASE)/sys-include \
-isystem $(BUILD_INCLUDE_BASE)/include-fixed \
-I$(RIOTBOARD)/x86-multiboot-common/include
export CPU = x86
# toolchain config
export CC ?= $(PREFIX)gcc
export AR ?= $(PREFIX)ar
export AS ?= $(PREFIX)as
export RANLIB ?= $(PREFIX)ranlib
export LINK ?= $(RIOTBASE)/boards/x86-multiboot-common/dist/link $(PREFIX)gcc
export SIZE ?= $(PREFIX)size
export OBJCOPY ?= $(PREFIX)objcopy
export CFLAGS += -m32 -mfpmath=387 -ffreestanding -nostdlib -nostdinc -fno-builtin
export OFLAGS = -O binary
LINKFLAGS += -m32 -nostdlib -nostdinc -nostartfiles -nodefaultlibs \
--prefix=$(NEWLIB_BASE) \
-Wl,-rpath,$(NEWLIB_BASE)/lib \
-T $(RIOTBASE)/boards/x86-multiboot-common/linker.ld
UNDEF += $(BINDIR)x86-multiboot-common_base/startup.o
#CFLAGS += -ffunction-sections -fdata-sections
#LINKFLAGS += -Wl,--gc-sections
#CFLAGS += -Wall -Wextra -Werror -pedantic -pedantic-errors \
BASELIBS += $(NEWLIB_BASE)/lib/libc.a \
$(NEWLIB_BASE)/lib/libm.a

14
boards/x86-multiboot-common/dist/link vendored Executable file
View File

@ -0,0 +1,14 @@
#!/bin/bash
args=($@)
for ((i = 0; i < ${#args[@]}; ++i))
do
if [[ "${args[i]}" == '-lm' ]]
then
unset args[i]
break
fi
done
exec "${args[@]}"

View File

@ -0,0 +1,124 @@
/* 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. */
/**
* Architecture specific definitions for multiboot enabled kernels.
*
* @ingroup x86-multiboot
* @{
* @file
*/
#define MULTIBOOT_HEADER_MAGIC 0x1BADB002 /**< The magic number for the Multiboot header. */
#define MULTIBOOT_HEADER_FLAGS 0x00010003 /**< The flags for the Multiboot header. */
#define MULTIBOOT_HEADER_CHECKSUM (-(MULTIBOOT_HEADER_MAGIC + MULTIBOOT_HEADER_FLAGS)) /**< The checksum for the Multiboot header. */
#define MULTIBOOT_BOOTLOADER_MAGIC 0x2BADB002 /**< The magic number passed by a Multiboot-compliant boot loader. */
/**
* @brief The Multiboot header.
*
* A multiboot enabled kernel needs to contain this header in the first 8kB of the binary file.
*/
typedef struct multiboot_header {
unsigned long magic; /**< Set to MULTIBOOT_HEADER_MAGIC. */
unsigned long flags; /**< Set to MULTIBOOT_HEADER_FLAGS. */
unsigned long checksum; /**< Set to MULTIBOOT_HEADER_CHECKSUM. */
unsigned long header_addr; /**< The address of the datum. */
unsigned long load_addr; /**< Set to `(uintptr_t) &_kernel_memory_start`. */
unsigned long load_end_addr; /**< Set to `(uintptr_t) &_section_data_end`. */
unsigned long bss_end_addr; /**< Set to `(uintptr_t) &_section_bss_end` */
unsigned long entry_addr; /**< Your entry function. */
} multiboot_header_t;
/**
* @brief The symbol table for a.out.
*
* Do not use!
*/
typedef struct aout_symbol_table {
unsigned long tabsize;
unsigned long strsize;
unsigned long addr;
unsigned long reserved;
} aout_symbol_table_t;
/**
* @brief The section header table for ELF.
*
* Do not use!
*/
typedef struct elf_section_header_table {
unsigned long num;
unsigned long size;
unsigned long addr;
unsigned long shndx;
} elf_section_header_table_t;
/**
* @brief The multiboot information provided by the bootloader (e.g. Grub).
*/
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;
/**
* @brief The module structure.
*
* Do not use!
*/
typedef struct module {
unsigned long mod_start;
unsigned long mod_end;
unsigned long string;
unsigned long reserved;
} module_t;
/**
* @brief An entry in the memory map table.
*/
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;
enum multiboot_info_flags {
MULTIBOOT_INFO_FLAGS_MEM = 0x01,
MULTIBOOT_INFO_FLAGS_BOOT = 0x02,
MULTIBOOT_INFO_FLAGS_CMDLINE = 0x04,
MULTIBOOT_INFO_FLAGS_MODS = 0x08,
MULTIBOOT_INFO_FLAGS_SYMS1 = 0x10,
MULTIBOOT_INFO_FLAGS_SYMS2 = 0x20,
MULTIBOOT_INFO_FLAGS_MMAP = 0x40,
};
/** @} */

View File

@ -0,0 +1,117 @@
/*
* Copyright (C) 2014 René Kijewski <rene.kijewski@fu-berlin.de>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/* The bootloader will look at this image and start execution at the symbol
designated as the entry point. */
ENTRY(_start)
/* Tell where the various sections of the object files will be put in the final
kernel image. */
SECTIONS
{
/* Begin putting sections at 1 MiB, a conventional place for kernels to be
loaded at by the bootloader. */
. = 1M;
_kernel_memory_start = .;
/* First put the multiboot header, as it is required to be put very early
early in the image or the bootloader won't recognize the file format.
Next we'll put the .text section. */
._multiboot_header :
{
*(._multiboot_header)
}
.note.gnu.build-id :
{
*(.note.gnu.build-id)
}
.text :
{
. = ALIGN(4);
_section_text_start = .;
*(.text)
*(.text.*)
*(.gnu.linkonce.t)
*(.gnu.linkonce.t.*)
_section_text_end = .;
}
. = ALIGN(0x1000);
/* Read-only data. */
.rodata :
{
_section_rodata_start = .;
*(.rodata)
*(.rodata.*)
*(.gnu.linkonce.r)
*(.gnu.linkonce.r.*)
_section_rodata_end = .;
}
. = ALIGN(0x1000);
/* Read-write data (initialized) */
.data :
{
_section_data_start = .;
*(.data)
*(.data.*)
*(.gnu.linkonce.d)
*(.gnu.linkonce.d.*)
. = ALIGN(4);
_section_data_end = .;
/* no need to align between .data and .bss */
}
/* Read-write data (uninitialized) and stack */
.bss :
{
_section_bss_start = .;
*(COMMON)
*(COMMON.*)
*(.bss)
*(.bss.*)
*(.gnu.linkonce.b)
*(.gnu.linkonce.b.*)
_section_bss_end = .;
}
. = ALIGN(0x1000);
_kernel_memory_end = .;
. += 0x1000;
. = ALIGN(0x10000);
_heap_start = .;
/* The compiler may produce other sections, by default it will put them in
a segment with the same name. Simply add stuff here as needed. */
}

View File

@ -0,0 +1,146 @@
/*
* Copyright (C) 2014 René Kijewski <rene.kijewski@fu-berlin.de>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/**
* @ingroup x86
* @ingroup boards
* @defgroup x86-multiboot i586 multiboot common
* @{
*
* @file
* @brief Startup code to be loaded by multiboot enabled bootloaders.
*
* @author René Kijewski <rene.kijewski@fu-berlin.de>
*
* @}
*/
#include <cpu.h>
#include <multiboot.h>
#include <x86_kernel_memory.h>
#include <x86_memory.h>
#include <x86_reboot.h>
#include <x86_videoram.h>
#include <inttypes.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifndef BREAK_STARTUP
# define BREAK_STARTUP (0)
#endif
static void __attribute__((noreturn)) startup(uint32_t multiboot_magic, const multiboot_info_t *multiboot_info);
void __attribute__((noreturn, optimize("Os", "omit-frame-pointer"), no_instrument_function)) _start(void);
extern const multiboot_header_t multiboot_header __attribute__((section("._multiboot_header")));
const multiboot_header_t multiboot_header = {
.magic = MULTIBOOT_HEADER_MAGIC,
.flags = MULTIBOOT_HEADER_FLAGS,
.checksum = MULTIBOOT_HEADER_CHECKSUM,
.header_addr = (uintptr_t) &multiboot_header,
.load_addr = (uintptr_t) &_kernel_memory_start,
.load_end_addr = (uintptr_t) &_section_data_end,
.bss_end_addr = (uintptr_t) &_section_bss_end,
.entry_addr = (uintptr_t) &_start,
};
void __attribute__((noreturn, optimize("Os", "omit-frame-pointer"), no_instrument_function)) _start(void)
{
asm volatile ("xor %ebp, %ebp");
asm volatile ("push %ebp");
asm volatile ("push %ebx");
asm volatile ("push %eax");
asm volatile ("push %ebp");
asm volatile ("jmp *%0" :: "r"(&startup));
__builtin_unreachable();
}
static const multiboot_info_t *multiboot_info;
bool x86_get_memory_region(uint64_t *start, uint64_t *len, unsigned long *pos)
{
while (1) {
if (*pos >= multiboot_info->mmap_length) {
return false;
}
const memory_map_t *mmap = (void *)(multiboot_info->mmap_addr + *pos);
*pos += mmap->size + 4;
*start = mmap->base_addr_low + ((uint64_t) mmap->base_addr_high << 32);
*len = mmap->length_low + ((uint64_t) mmap->length_high << 32);
uint64_t end = *start + *len;
printf(" %08lx%08lx - %08lx%08lx ", (long) (*start >> 32), (long) *start, (long) (end >> 32), (long) end);
if (mmap->type != 1) {
// not free (ignore reclaimable RAM)
const char *msg;
switch (mmap->type) {
case 2: msg = "reseved"; break;
case 3: msg = "ACPI [reclaimable]"; break;
case 4: msg = "ACPI [NVS]"; break;
case 5: msg = "bad memory"; break;
default: msg = "unknown";
}
printf("(unusable: %s)\r\n", msg);
}
else {
puts("(usable)");
return true;
}
}
}
static void have_a_break(void)
{
volatile bool cnt = false;
while (!cnt) {
asm volatile ("pause");
}
}
static void __attribute__((noreturn)) startup(uint32_t multiboot_magic, const multiboot_info_t *multiboot_info_)
{
x86_init_gdt(); /* load GDT early */
x86_load_empty_idt(); /* just a safeguard to cause a tripple fault, not really needed */
#if BREAK_STARTUP
have_a_break();
#else
(void) have_a_break;
#endif
memset(&_section_bss_start, 0, &_section_bss_end - &_section_bss_start + 1);
videoram_puts(" Booting RIOT \r\n");
if (multiboot_magic != MULTIBOOT_BOOTLOADER_MAGIC) {
videoram_puts(" Multiboot magic is wrong \r\n");
x86_hlt();
}
else if (!(multiboot_info->flags && MULTIBOOT_INFO_FLAGS_MMAP)) {
videoram_puts(" Multiboot is lacking memory map information \r\n");
x86_hlt();
}
multiboot_info = multiboot_info_;
x86_startup();
}

3
cpu/x86/Makefile Normal file
View File

@ -0,0 +1,3 @@
MODULE = cpu
include $(RIOTBASE)/Makefile.base

4
cpu/x86/Makefile.include Normal file
View File

@ -0,0 +1,4 @@
export INCLUDES += -I$(RIOTCPU)/x86/include
export USEMODULE += quad_math
export USEPKG += tlsf

121
cpu/x86/include/cpu.h Normal file
View File

@ -0,0 +1,121 @@
/*
* Copyright (C) 2014 René Kijewski <rene.kijewski@fu-berlin.de>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/**
* Basic definitions and forwards for x86 boards.
*
* @defgroup x86 i586
* @ingroup cpu
* @{
* @file
* @author René Kijewski <rene.kijewski@fu-berlin.de>
*/
#ifndef CPU__X86__CPU__H__
#define CPU__X86__CPU__H__
#include "attributes.h"
#include "irq.h"
#include "ucontext.h"
#include "cpu-conf.h"
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
static inline void __attribute__((always_inline)) dINT(void)
{
asm volatile ("cli");
}
static inline void __attribute__((always_inline)) eINT(void)
{
asm volatile ("sti");
}
/**
* @brief Disable interrupts and halt forever.
*
* This function is the last resort in case of an unrecoverable error.
* No message will be printed, no nothing.
*/
static inline void __attribute__((always_inline, noreturn)) x86_hlt(void)
{
while (1) {
asm volatile ("cli");
asm volatile ("hlt");
}
}
/**
* @brief Initialize subsystems and run kernel.
*
* Called by the board specific startup code.
* <li>The .bss has to be cleared before.
* <li>The stack has to be set up, probably somewhere in the low memory.
* <li>The A20 line has to be activated, because all the code is beyong 1MB.
* <li>Paging must be disabled.
* <li>The SS, DS, and CS must span the whole 4GB of RAM.
* <li>32 bit protected mode must be entered.
* <li>Interrupts must be disabled.
*/
void x86_startup(void) NORETURN;
/**
* @brief Setup board specific hardware and such.
*
* Called by x86_startup() after all generic subsystems are activated,
* and before kernel_init() gets called.
* Interrupts are disabled before, and are expected to be disabled after calling this function.
* Interrupts may be enabled during the course of this function call.
*
* Probably most of the board specific initialization should be done in auto_init():
* <li>You must not spawn thread_create() in this function.
* <li>The hwtimer is not set up properly at this point of executation.
*/
void x86_init_board(void);
/**
* @brief Returns a range of usable physical memory.
* @param[out] start Start address of the physical memory region.
* @param[out] len Total length of the physical memory region.
* One page are 0x1000 bytes.
* @param cnt Reentrant pointer. Must be zero on first call.
* The value is entirely opaque.
* @returns true iff there was a memory region set in start and len.
* false iff there are no more memory regions.
* The function must not get called again in the latter case.
*
* Called by x86_startup() before x86_init_board() gets called.
*
* The memory management is one of the earliest subsystems to be set up.
* The uart is already configured and usable.
* The interrupt handling is already set up and you may call x86_interrupt_handler_set().
*
* The function may return unaligned memory.
* In this case the begin of the region gets rounded up the the next page,
* and the end gets rounded down.
* The function may supply low memory regions, which will be ignored.
* The regions are expected to contain memory that lies inside the elf sections.
*/
bool x86_get_memory_region(uint64_t *start, uint64_t *len, unsigned long *cnt);
#endif
/** @} */

View File

@ -0,0 +1,58 @@
/*
* Copyright (C) 2014 René Kijewski <rene.kijewski@fu-berlin.de>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/**
* Basic definitions for the hwtimer.
*
* @ingroup x86
* @ingroup core_hwtimer
* @{
* @file
* @author René Kijewski <rene.kijewski@fu-berlin.de>
*/
#ifndef CPU__X86__HWTIMER_CPU__H__
#define CPU__X86__HWTIMER_CPU__H__
/**
* @brief Number of configured hardware timers.
*
* This value may be set as high as you need it.
* The x86_hwtimer.c can emulate a lot of timers, since the real timers aren't used. :-)
*/
#ifndef HWTIMER_MAXTIMERS
# define HWTIMER_MAXTIMERS 4
#endif
/**
* @brief How many "ticks" there are in one second of wallclock time.
*
* Statically configured as `1 tick = 1 µs`.
*/
#define HWTIMER_SPEED (1000000L)
/**
* @brief Biggest value that can specify a valid number of hardware ticks.
*
* Since 1 tick = 1 µs, this number will flow over after 4295 seconds (1h11m34s).
*/
#define HWTIMER_MAXTICKS (0xFFFFFFFFu)
#endif
/** @} */

166
cpu/x86/include/ucontext.h Normal file
View File

@ -0,0 +1,166 @@
/*
* Copyright (C) 2014 René Kijewski <rene.kijewski@fu-berlin.de>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/**
* Coroutine helper functions. The base of the multi-threading system.
*
* @ingroup x86-multithreading
* @{
* @file
* @author René Kijewski <rene.kijewski@fu-berlin.de>
*/
#ifndef CPU__X86__UCONTEXT__HPP__
#define CPU__X86__UCONTEXT__HPP__
#include <stdlib.h>
#include <unistd.h>
/**
* @brief Common stacksize for a signal handler.
*
* Do not use this variable.
* The standard wants us to define this variable, but you are better off the the RIOT specific macros.
*/
#define SIGSTKSZ (2048)
/**
* @brief General purpose registers of an x86 CPU.
*
* Used by ucontext_t.
* Application developers should not use this type directly.
*/
struct x86_pushad {
unsigned long ax, cx, dx, bx; /* field in ucontext_t: 3*4 -> 6*4 */
unsigned long sp, bp, si, di; /* field in ucontext_t: 7*4 -> 10*4 */
} __attribute__((packed));
/**
* @brief Opaque memory needed to store the x87 FPU state.
*
* Used by x86_threading.c.
* Application developers should not use this type directly.
* There is only enough room for the basic x87 state, not the multimedia extensions.
*/
struct x86_fxsave {
char opaque[512];
} __attribute__((packed, aligned(0x10)));
/**
* @brief Machine specific part of the corouting state.
*
* Used by ucontext_t.
* Application developers should not use this type directly.
*/
typedef struct mcontext {
unsigned long flags; /* field in ucontext_t: 2*4 */
struct x86_pushad registers; /* field in ucontext_t: 3*4 -> 10*4 */
void *ip; /* field in ucontext_t: 11*4 */
} __attribute__((packed)) mcontext_t;
/**
* @brief Stack assigned to a coroutine.
*
* Used by ucontext_t.
* Application developers need to set this values to make coroutines working.
*/
typedef struct stack {
void *ss_sp; /* field in ucontext_t: 0*4 */
//int ss_flags;
size_t ss_size; /* field in ucontext_t: 1*4 */
} __attribute__((packed)) stack_t;
/**
* @brief Extra data to perform an `iret`.
*
* Used by x86_interrupts.c to continue a thread
* if sched_context_switch_request was set set by an ISR.
* Application developers should not use this type directly.
*/
struct x86_interrupted_interrupt {
unsigned long ip;
unsigned long flags;
} __attribute__((packed));
/**
* @brief Datatype to enable coroutines in userspace.
*
* This datatype stores the machine state of a suspended coroutine.
*/
typedef struct ucontext {
stack_t uc_stack; /* field in ucontext_t: 0*4 - 1*4 */
mcontext_t uc_context; /* field in ucontext_t: 2*4 -> 11*4 */
struct ucontext *uc_link; /* field in ucontext_t: 12*4 */
//sigset_t uc_sigmask;
struct x86_interrupted_interrupt __intr;
struct x86_fxsave __fxsave;
} __attribute__((packed)) ucontext_t;
/**
* @brief Store current coroutine state.
*
* With setcontext() you can jump to this point of execution again.
* Take care about your stack, though.
* After your thread of execution left the function calling getcontext() there be dragons.
*
* The FPU registers are not stored!
*/
int getcontext(ucontext_t *ucp);
/**
* @brief Restore a coroutine state.
*
* The state must be set up by calling getcontext() or makecontext() previously.
* The FPU registers won't be restored.
*/
int setcontext(const ucontext_t *ucp) __attribute__((noreturn));
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wstrict-prototypes"
typedef void (*makecontext_fun_t)();
#pragma GCC diagnostic pop
/**
* @brief Create a coroutine / trampoline.
* @param[in] func Function to call.
* @param[in] argc Number of arguments for func. Must not exceed 5.
*
* The function arguments shall be passed after argc.
* The arguments have to be type punnable as an int,
* this includes integral types up to 4 bytes,
* but excludes int64_t, float and so on.
*
* Different from what the POSIX standard states,
* you do not have to getcontext() on ucp before calling makecontext().
* This function creates a whole new coroutine state / trampoline.
*/
void makecontext(ucontext_t *ucp, makecontext_fun_t func, int argc, ...);
/**
* @brief Swap coroutine.
* @param[out] oucp Memory to preserve current coroutine state in.
* @param[in] ucp Coroutine to execute next.
*
* This function is an atomic variant of getcontext() and setcontext().
* Same restrictions apply.
*/
int swapcontext(ucontext_t *oucp, const ucontext_t *ucp);
#endif
/** @} */

View File

@ -0,0 +1,63 @@
/*
* Copyright (C) 2014 René Kijewski <rene.kijewski@fu-berlin.de>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/**
* Functions to interact with the CMOS memory of the BIOS.
*
* @ingroup x86
* @{
* @file
* @author René Kijewski <rene.kijewski@fu-berlin.de>
*/
#ifndef CPU__X86__CMOS__H__
#define CPU__X86__CMOS__H__
#include "x86_ports.h"
#include <stdint.h>
#define CMOS_ADDRESS (0x70)
#define CMOS_DATA (0x71)
#define CMOS_SERIAL_OFFS (0x41)
#define CMOS_SERIAL_LEN (6)
/**
* @brief Read a CMOS register.
* @returns The value of the CMOS register.
*/
uint8_t x86_cmos_read(int reg);
/**
* @brief Write a CMOS register.
*/
void x86_cmos_write(int reg, uint8_t value);
/**
* @brief Read serial number of the BIOS.
*
* Most -- if not all current -- BIOSes do not seem to store any serial number here.
* This function won't cause an error, but the read serial might be some more or less random data.
* The implementor of the board specific code should know whether the BIOS contains a serial number.
*/
void x86_cmos_serial(uint8_t (*serial)[CMOS_SERIAL_LEN]);
#endif
/** @} */

View File

@ -0,0 +1,42 @@
/*
* Copyright (C) 2014 René Kijewski <rene.kijewski@fu-berlin.de>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/**
* The interface of the architecture specific part of the hwtimer module.
*
* @ingroup x86
* @ingroup core_hwtimer
* @{
* @file
* @author René Kijewski <rene.kijewski@fu-berlin.de>
*/
#ifndef CPU__X86__HWTIMER__H__
#define CPU__X86__HWTIMER__H__
/**
* @brief Do timestamping to enable the use of the hwtimer module.
*
* This function is called during initialization by x86_startup().
* You must not call this function on your own accord.
*/
void x86_init_hwtimer(void);
#endif
/** @} */

View File

@ -0,0 +1,157 @@
/*
* Copyright (C) 2014 René Kijewski <rene.kijewski@fu-berlin.de>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/**
* The entry points for all interrupts on x86 boards.
*
* @ingroup x86
* @defgroup x86-irq Interrupt handling for i586 boards
* @{
* @file
* @author René Kijewski <rene.kijewski@fu-berlin.de>
*/
#ifndef CPU__X86__INTERRUPTS__H__
#define CPU__X86__INTERRUPTS__H__
#include "ucontext.h"
#include <stdint.h>
/**
* @brief Initialize interrupts.
*
* This function is called during initialization by x86_startup().
* You must not call this function on your own accord.
*/
void x86_init_interrupts(void);
#define X86_MAX_INTERRUPTS (0x30)
/**
* @brief The exceptions in an x86 system.
*
* http://www.sandpile.org/x86/except.htm
*/
enum x86_interrupt_numbers {
X86_INT_DE = 0x00, /**< divide error */
X86_INT_DB = 0x01, /**< debug (thrown after each instruction if X86_TF is activated) */
X86_INT_BP = 0x03, /**< breakpoint (`int3`) */
X86_INT_OF = 0x04, /**< overflow (`into`) */
X86_INT_BR = 0x05, /**< boundary range exceeded (`bound`) */
X86_INT_UD = 0x06, /**< undefined opcode */
X86_INT_NM = 0x07, /**< device not available (raised on FPU accessing if CR0.TS is set) */
X86_INT_DF = 0x08, /**< double fault (exceptions during exception handler invocation) */
X86_INT_GP = 0x0d, /**< general protection */
X86_INT_PF = 0x0e, /**< page fault */
X86_INT_MF = 0x10, /**< math fault */
X86_INT_AC = 0x11, /**< alignment checking */
X86_INT_MC = 0x12, /**< machine check */
};
/**
* @brief Bits in the EFLAGS register.
*
* http://www.sandpile.org/x86/flags.htm
*/
enum x86_eflags {
X86_CF = 1 << 0, /**< carry */
X86_PF = 1 << 2, /**< parity */
X86_AF = 1 << 4, /**< adjust */
X86_ZF = 1 << 6, /**< zero */
X86_SF = 1 << 7, /**< signed */
X86_TF = 1 << 8, /**< singled step (throw #DB after each instruction) */
X86_IF = 1 << 9, /**< interrupts enabled */
X86_DF = 1 << 10, /**< direction (0 = movs increses addresses, 1 = movs decreases addresses) */
X86_OF = 1 << 11, /**< overflow */
X86_IOPL_0 = 0 << 12, /**< priority level 0 (?) */
X86_IOPL_1 = 1 << 12, /**< priority level 1 (?) */
X86_IOPL_2 = 2 << 12, /**< priority level 2 (?) */
X86_IOPL_3 = 3 << 12, /**< priority level 3 (?) */
X86_NT = 1 << 14, /**< nested task */
X86_RF = 1 << 16, /**< resume */
X86_VM = 1 << 17, /**< virtual 8086 mode */
X86_AC = 1 << 18, /**< alignment check */
X86_VIF = 1 << 19, /**< virtual interrupts enabled */
X86_VIP = 1 << 20, /**< virtual interrupts pendigng */
X86_ID = 1 << 21, /**< able to use CPUID */
};
/**
* @brief Datum for the Interrupt Descriptor Table Register.
*/
struct idtr_t {
uint16_t limit; /**< `sizeof (struct idt_desc) * count` */
void *base; /**< physical address of the start of the IDT */
} __attribute__((packed));
/**
* @brief One entry in the IDT
*/
struct idt_desc {
uint16_t offset_1; /**< offset bits 0..15 */
uint16_t selector; /**< a code segment selector in GDT or LDT */
uint8_t zero; /**< unused, set to 0 */
uint8_t type_attr; /**< type and attributes, see below */
uint16_t offset_2; /**< offset bits 16..31 */
} __attribute__((packed));
/**
* @brief Callback on interrupt.
* @param intr_num Number of interrupt -- not the number of the IRQ
* @param orig_ctx (Changable) register set of the calling thread
* @param error_code Related error code (if applies, otherwise 0)
*/
typedef void (*x86_intr_handler_t)(uint8_t intr_num, struct x86_pushad *orig_ctx, unsigned long error_code);
/**
* @brief Set callback function for interrupt.
* @param num Number of interrupt. Must be less than X86_MAX_INTERRUPTS.
* @param handler Function to call, or `NULL` to use default interrupt handler.
*
* This function is meant to be called by other subsystems (x86_pic.c and x86_threading.c).
* Any upper level system should use x86_pic_set_handler() or even higher level functions.
*/
void x86_interrupt_handler_set(unsigned num, x86_intr_handler_t handler);
/**
* @brief Disable interrupts and return previous EFLAGS.
*/
static inline unsigned long __attribute__((always_inline)) x86_pushf_cli(void)
{
unsigned long result;
asm volatile("pushf; cli; pop %0" : "=g"(result));
return result;
}
/**
* @brief Set EFLAGS.
*/
static inline void __attribute__((always_inline)) x86_restore_flags(unsigned long stored_value)
{
asm volatile("push %0; popf" :: "g"(stored_value));
}
/**
* @brief Print saved general purposed registers.
*/
void x86_print_registers(struct x86_pushad *orig_ctx, unsigned long error_code);
#endif
/** @} */

View File

@ -0,0 +1,116 @@
/*
* Copyright (C) 2014 René Kijewski <rene.kijewski@fu-berlin.de>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/**
* Layout of the kernel memory. The symbols are set in the linker.ld.
*
* @ingroup x86
* @{
* @file
* @author René Kijewski <rene.kijewski@fu-berlin.de>
*/
#ifndef KERNEL_MEMORY_H__
#define KERNEL_MEMORY_H__
/**
* @brief First byte inside the elf segments.
*
* Expected to be the start of the high memory, i.e. 1MB.
*/
extern char _kernel_memory_start;
/**
* @brief Start of first byte of code inside the .text segment.
*
* This symbol does not have to be aligned.
*/
extern char _section_text_start;
/**
* @brief First byte after the code.
*
* This symbol does not have to be aligned.
*/
extern char _section_text_end;
/**
* @brief First byte inside the .rodata segment.
*
* This symbol is expected to be aligned.
*/
extern char _section_rodata_start;
/**
* @brief First byte after the .rodata segment.
*
* This symbol does not have to be aligned.
*/
extern char _section_rodata_end;
/**
* @brief First byte inside the .data segment.
*
* This symbol is expected to be aligned.
*/
extern char _section_data_start;
/**
* @brief First byte after the .data segment.
*
* This symbol does not have to be aligned, since it shares the same previleges as the .bss segment.
*/
extern char _section_data_end;
/**
* @brief Start of the .bss segment.
*
* Probably unaligned.
* The .bss segment must follow the .data segment with no other segment inbetween.
* It's likely that `&_section_data_end == &_section_bss_start`.
*/
extern char _section_bss_start;
/**
* @brief First byte after the .bss segment.
*
* This symbol should not to be aligned, because RAM cycles would be wasted to clear unused bytes otherwise.
*/
extern char _section_bss_end;
/**
* @brief First byte after the last of the common elf segments.
*
* This symbol is expected to be aligned.
* The "common elf segments" are .text, .rodata, .data and .bss.
* All the (physical) memory beyond this point will be overwritten at the pleasure of the kernel.
*/
extern char _kernel_memory_end;
/**
* @brief First byte that may be mapped as the heap
*
* This symbol is expected to be aligned.
* Must be bigger than _kernel_memory_end.
* There should be a space between _kernel_memory_end and _heap_start, but it does not have to.
*/
extern char _heap_start;
#endif
/** @} */

View File

@ -0,0 +1,171 @@
/*
* Copyright (C) 2014 René Kijewski <rene.kijewski@fu-berlin.de>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*