1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00

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.
*
* 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 virtual memory management of x86 boards.
*
* @ingroup x86
* @{
* @file
* @author René Kijewski <rene.kijewski@fu-berlin.de>
*/
#ifndef CPU__X86__MEMORY__H__
#define CPU__X86__MEMORY__H__
#include <stdbool.h>
#include <stdint.h>
/**
* @brief Maximum static size of the kernel.
*
* Each page table can hold 2MB.
* As the low memory is ignored, 1MB of virtual memory is wasted.
* Bad things will happen if this value is set too small.
* The kernel must not exceed the 10MB mark, unless you are sure that your board has no ISA hole.
* (It is unlikely that you have an ISA hole, though.)
*/
#define X86_NUM_STATIC_PT (4)
#define X86_NUM_STATIC_PD ((X86_NUM_STATIC_PT+511) / 512) /* 1GB=512 PTs each */
/**
* @brief Initialize virtual memory management.
*
* This function is called during initialization by x86_startup().
* You must not call this function on your own accord.
*/
void x86_init_memory(void);
/**
* @brief Datum for the Global Descriptor Table Register.
*/
struct gdtr_t {
uint16_t size_minus_one;
uint32_t offset;
} __attribute__((packed));
/**
* @brief One entry in the Global Descriptor Table.
*/
struct gdt_entry {
uint16_t limit_0_15;
uint16_t base_0_15;
uint8_t base_16_23;
uint8_t access_byte;
uint8_t limit_16_19_and_flags;
uint8_t base_24_31;
} __attribute__((packed));
#define GDT_AB_AC (1u << 0) /**< Access byte (set to one by CPU when accessed) */
#define GDT_AB_RW (1u << 1) /**< If CS: read access allowed. If DS: write access allowed. */
#define GDT_AB_DC (1u << 2) /**< Direction bit */
#define GDT_AB_EX (1u << 3) /**< 1 = segment is CS */
#define GDT_AB_S (1u << 4) /**< 1 = segment is CS or DS; 0 = segment is gate or TSS */
#define GDT_AB_RING_0 (0u << 5) /**< Privilege level: ring 0 */
#define GDT_AB_RING_1 (1u << 5) /**< Privilege level: ring 1 */
#define GDT_AB_RING_2 (2u << 5) /**< Privilege level: ring 2 */
#define GDT_AB_RING_3 (3u << 5) /**< Privilege level: ring 3 */
#define GDT_AB_PR (1u << 7) /**< Segment present */
#define GDT_FLAG_USR4 (1u << 4) /**< bit for free use */
#define GDT_FLAG_L (1u << 5) /**< 1 = long mode */
#define GDT_FLAG_SZ (1u << 6) /**< 0 = 16/64 bit selector; 1 = 32 bit selector */
#define GDT_FLAG_GR (1u << 7) /**< 0 = limit given in 1 b blocks; 1 = limit given in 4k blocks */
typedef uint64_t pae_page_directory_pointer_table_t[512] __attribute__((aligned(0x1000)));
typedef uint64_t pae_page_directory_t[512] __attribute__((aligned(0x1000)));
typedef uint64_t pae_page_table_t[512] __attribute__((aligned(0x1000)));
#define PT_P (1ull << 0) /**< 1 = page present */
#define PT_RW (1ull << 1) /**< 1 = page writable */
#define PT_US (1ull << 2) /**< 1 = don't restrict to ring 0 */
#define PT_PWT (1ull << 3) /**< 1 = use write-through; 0 = use write-back */
#define PT_PCD (1ull << 4) /**< 1 = disable caching */
#define PT_A (1ull << 5) /**< 1 = page was accessed (set by the CPU, used for swapping) */
#define PT_D (1ull << 6) /**< 1 = dirty (set by the CPU when written to) */
#define PT_S (1ull << 7) /**< 1 = 4MB pages */
#define PT_PAT (1ull << 7) /**< ? */
#define PT_G (1ull << 8) /**< 1 = global (ignored for page directory entries) */
#define PT_USR9 (1ull << 9) /**< bit for free use */
#define PT_USR10 (1ull << 10) /**< bit for free use */
#define PT_USR11 (1ull << 11) /**< bit for free use */
#define PT_XD (1ull << 63) /**< 1 = no execute */
#define PT_ADDR_MASK (((1ull << 48) - 1) & ~((1ull << 12) - 1))
#define PF_EC_P (1u << 0) /**< 1 = page protection violation; 0 = page not present */
#define PF_EC_W (1u << 1) /**< 1 = accessed writingly; 0 = readingly */
#define PF_EC_U (1u << 2) /**< 1 = ring 3 */
#define PF_EC_R (1u << 3) /**< ? */
#define PF_EC_I (1u << 4) /**< 1 = exception in CS; 0 = exception in DS */
/**
* @brief Error value for x86_get_pte().
*
* All bits are set but PT_P.
* Most likely you are interested only if the page table is present.
* Since the PTE bits 48 to 62 are restricted, this value cannot occur in an actual PTE.
*/
#define NO_PTE (~PT_P)
/**
* @brief Read page table entry for addr.
* @param addr Does not have to be aligned.
* @returns PTE iff addr has an entry in the PDPT and PD, otherwise NO_PTE.
*/
uint64_t x86_get_pte(uint32_t addr);
/**
* @brief Maps a range of physical pages at a new virtual address.
* @param physical_start Start address of the physical page. Must be aligned.
* @param pages Number of physical pages.
* @param bits Flags of the PTE for each page (do not forget PT_P).
* @returns Start of the virtual address range, aligned of course.
* @returns NULL if the memory was exhausted.
*
* This function is to be used by hardware drivers.
*/
void *x86_map_physical_pages(uint64_t physical_start, unsigned pages, uint64_t bits);
/**
* @brief Allocate a range of pages.
* @param pages Number of pages to allocate.
* @param bits Flags of the PTE for each page (do not forget PT_P).
* @returns Start of the virtual address range, aligned of course.
* Use x86_get_pte() to determine the start of the physical address range.
*/
void *x86_get_virtual_pages(unsigned pages, uint64_t bits);
/**
* @brief Deallocate a range of bytes that were allocated with x86_get_virtual_pages()
* @param virtual_start The return value of x86_get_virtual_pages().
* @param pages Must match the original call to x86_get_virtual_pages().
*/
void x86_release_virtual_pages(uint32_t virtual_start, unsigned pages);
/**
* @brief Intializes the global descriptor table.
* @details This should be the very first call in the startup routine.
* Changing the segmentation confuses the JTAG.
* @details 0x0000 is an invalid descriptor.
* 0x0008 is the code segment, from 0x00000000 to 0xFFFFFFFF, executable, read-only.
* 0x0010 is the data segment, from 0x00000000 to 0xFFFFFFFF, non-executable, writable.
*/
void x86_init_gdt(void);
#endif
/** @} */

415
cpu/x86/include/x86_pci.h Normal file
View File

@ -0,0 +1,415 @@
/*
* 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 PCI subsystem of x86 boards.
*
* @ingroup x86-irq
* @{
* @file
* @author René Kijewski <rene.kijewski@fu-berlin.de>
*/
#ifndef CPU__X86__PCI__H__
#define CPU__X86__PCI__H__
#include "x86_pci_init.h"
#include "x86_pic.h"
#include <machine/endian.h>
#include <stdbool.h>
#include <stdint.h>
/**
* @brief Initialize the Peripheral Component Interconnect.
*
* This function is called during initialization by x86_startup().
* You must not call this function on your own accord.
*/
void x86_init_pci(void);
#define PCI_CONFIG_DATA (0x0cfc)
#define PCI_CONFIG_ADDRESS (0x0cf8)
#define PCI_IO_ENABLE (1u << 31)
#define PCI_IO_SHIFT_BUS (16u)
#define PCI_IO_SHIFT_DEV (11u)
#define PCI_IO_SHIFT_FUN (8u)
#define PCI_IO_SHIFT_REG (0u)
#define PCI_BUS_COUNT (1u << 8)
#define PCI_DEV_COUNT (1u << 5)
#define PCI_FUN_COUNT (1u << 3)
#if BYTE_ORDER == LITTLE_ENDIAN
# define U8_PCItoH(x) (x)
# define U16_PCItoH(x) (x)
# define U32_PCItoH(x) (x)
# define U8_HtoPCI(x) (x)
# define U16_HtoPCI(x) (x)
# define U32_HtoPCI(x) (x)
#else
# error "Only BYTE_ORDER == LITTLE_ENDIAN is supported right now"
#endif
// "PCI LOCAL BUS SPECIFICATION, REV. 3.0"; Appendix 6.1.
/**
* @brief The registers 0x00 through 0x03 of a PCI device.
*/
typedef union pci_reg_0x00 {
__extension__ struct {
/**
* @brief Vendor ID of the device
*
* The values PCI_HEADER_VENDOR_ID_INVALID, PCI_HEADER_VENDOR_ID_UNRESPONSIVE
* and PCI_HEADER_VENDOR_ID_ABSENT denote a special status.
*/
uint16_t vendor_id;
uint16_t device_id; /**< Vendor specific device ID */
} __attribute__((packed));
uint32_t packed;
} __attribute__((packed)) pci_reg_0x00_t;
/**
* @brief The registers 0x04 through 0x07 of a PCI device.
*/
typedef union pci_reg_0x04 {
__extension__ struct {
uint16_t command; /**< Port to tell the PCI device to do things. */
uint16_t status; /**< Reading the current status of a PCI device. */
} __attribute__((packed));
uint32_t packed;
} __attribute__((packed)) pci_reg_0x04_t;
/**
* @brief The registers 0x08 through 0x0b of a PCI device.
*/
typedef union pci_reg_0x08 {
__extension__ struct {
uint8_t revision_id;
uint8_t programming_interface;
uint8_t subclass;
uint8_t baseclass;
} __attribute__((packed));
uint32_t packed;
} __attribute__((packed)) pci_reg_0x08_t;
/**
* @brief The registers 0x0c through 0x0f of a PCI device.
*/
typedef union pci_reg_0x0c {
__extension__ struct {
uint8_t cache_line_size;
uint8_t latency_timer;
uint8_t header_type;
uint8_t builtin_selftest;
} __attribute__((packed));
uint32_t packed;
} __attribute__((packed)) pci_reg_0x0c_t;
/**
* @brief Generic layout of the port space of a PCI device.
*/
typedef struct pci_config {
union pci_reg_0x00 reg_0x00;
union pci_reg_0x04 reg_0x04;
union pci_reg_0x08 reg_0x08;
union pci_reg_0x0c reg_0x0c;
/* reg = 0x10 */
uint8_t header_type_specific1[0x34 - 0x10];
/* reg = 0x34 */
uint8_t capatibilities_pointer;
uint8_t header_type_specific2[0x3c - 0x35];
/* reg = 0x3C */
uint8_t interrupt_line;
uint8_t interrupt_pin;
uint8_t header_type_specific3[0x40 - 0x3e];
} __attribute__((packed)) pci_config_t;
/**
* @brief Layout of the port space for PCI devices with `header_type == PCI_HEADER_TYPE_GENERAL_DEVICE`.
*/
typedef struct pci_config_standard {
union pci_reg_0x00 reg_0x00;
union pci_reg_0x04 reg_0x04;
union pci_reg_0x08 reg_0x08;
union pci_reg_0x0c reg_0x0c;
/* reg = 0x10 */
uint32_t bar[6];
/* reg = 0x28 */
uint32_t cardbus_cis_pointer;
/* reg = 0x2c */
uint16_t subsystem_vendor_id;
uint16_t subsystem_id;
// reg = 0x30 */
uint32_t expansion_rom_address;
/* reg = 0x34 */
uint8_t capatibilities_pointer;
uint8_t header_type_specific2[0x3c - 0x35];
/* reg = 0x3C */
uint8_t interrupt_line;
uint8_t interrupt_pin;
uint8_t header_type_specific3[0x40 - 0x3e];
} __attribute__((packed)) pci_config_standard_t;
/**
* @brief Layout of the port space for PCI devices with `header_type == PCI_HEADER_TYPE_BRIDGE`.
*/
typedef struct pci_config_bridge {
union pci_reg_0x00 reg_0x00;
union pci_reg_0x04 reg_0x04;
union pci_reg_0x08 reg_0x08;
union pci_reg_0x0c reg_0x0c;
/* reg = 0x10 */
uint32_t bar[2];
/* reg = 0x18 */
uint8_t bus_primary;
uint8_t bus_secondary;
uint8_t bus_subordinary;
uint8_t bus_secondary_latency_timer;
/* reg = 0x1c */
uint8_t io_lower_base;
uint8_t io_lower_limit;
uint16_t status_secondary;
/* reg = 0x20 */
uint16_t memory_base;
uint16_t memory_limit;
/* reg = 0x24 */
uint16_t prefetchable_lower_base;
uint16_t prefetchable_lower_limit;
// reg = 0x28
uint32_t prefetchable_upper_base;
// reg = 0x2c
uint32_t prefetchable_upper_limit;
// reg = 0x30
uint16_t io_upper_base;
uint16_t io_upper_limit;
// reg = 0x34
uint8_t capatibilities_pointer;
uint8_t header_type_specific2[0x38 - 0x35];
// reg = 0x38
uint32_t extension_ron_address;
// reg = 0x3C
uint8_t interrupt_line;
uint8_t interrupt_pin;
uint16_t bridge_control;
} __attribute__((packed)) pci_config_bridge_t;
#define PCI_HEADER_VENDOR_ID_INVALID (0x0000u)
#define PCI_HEADER_VENDOR_ID_UNRESPONSIVE (0x0001u)
#define PCI_HEADER_VENDOR_ID_ABSENT (0xffffu)
#define PCI_HEADER_TYPE_GENERAL_DEVICE (0x00u)
#define PCI_HEADER_TYPE_BRIDGE (0x01u)
#define PCI_HEADER_TYPE_CARD_BUS (0x02u)
#define PCI_HEADER_TYPE_MULTI_FUNCTION (0x80u)
#define PCI_HEADER_TYPE_MASK (0x7fu)
#define PCI_COMMAND_IO_SPACE (1u << 0)
#define PCI_COMMAND_MEMORY_SPACE (1u << 1)
#define PCI_COMMAND_BUS_MASTER (1u << 2)
#define PCI_COMMAND_SPECIAL_CYCLES (1u << 3)
#define PCI_COMMAND_MEM_WRITE_AND_INV (1u << 4)
#define PCI_COMMAND_VGA_PALETTE_SNOOP (1u << 5)
#define PCI_COMMAND_PARITY_ERR_RESPONSE (1u << 6)
#define PCI_COMMAND_SERR (1u << 8)
#define PCI_COMMAND_FAST_B2B (1u << 9)
#define PCI_COMMAND_INTERRUPT_DISABLE (1u << 10)
#define PRI_STATUS_INTERRUPT (1u << 3)
#define PRI_STATUS_CAPATIBILITIES (1u << 4)
#define PRI_STATUS_66MHZ (1u << 5)
#define PRI_STATUS_FAST_B2B (1u << 7)
#define PRI_STATUS_MASTER_PARITY_ERROR (1u << 8)
#define PRI_STATUS_DEVSEL_FAST (0u << 9)
#define PRI_STATUS_DEVSEL_MEDIUM (1u << 9)
#define PRI_STATUS_DEVSEL_SLOW (3u << 9)
#define PRI_STATUS_DEVSEL_MASK (3u << 9)
#define PRI_STATUS_SIG_TARGET_ABORT (1u << 11)
#define PRI_STATUS_RECV_TARGET_ABORT (1u << 12)
#define PRI_STATUS_RECV_MASTER_ABORT (1u << 13)
#define PRI_STATUS_SIG_SYSTEM_ERROR (1u << 14)
#define PRI_STATUS_PARITY_ERROR (1u << 15)
#define PCI_CAPS_PTR_MIN (0x40u)
#define PCI_CAPS_PTR_MAX (0xf0u)
#define PCI_INTR_PIN_NONE (0x00u)
#define PCI_INTR_PIN_A (0x01u)
#define PCI_INTR_PIN_B (0x02u)
#define PCI_INTR_PIN_C (0x03u)
#define PCI_INTR_PIN_D (0x04u)
#define PCI_BAR_IO_SPACE (1u << 0)
#define PCI_BAR_SPACE_32 (0u << 1)
#define PCI_BAR_SPACE_1M (1u << 1)
#define PCI_BAR_SPACE_64 (2u << 1)
#define PCI_BAR_SPACE_MASK (3u << 1)
#define PCI_BAR_PREFETCHABLE (1u << 3)
#define PCI_BAR_ADDR_OFFS_MEM (4u)
#define PCI_BAR_ADDR_OFFS_IO (2u)
/**
* @brief Address type of a struct x86_pci_io.
*/
typedef enum {
PCI_IO_INVALID = 0, /**< There was an error while configuring this IO port. */
PCI_IO_MEM, /**< physical memory */
PCI_IO_PORT, /**< port address */
} x86_pci_io_type_t;
/**
* @brief One IO port of a struct x86_known_pci_device.
*/
typedef struct x86_pci_io {
x86_pci_io_type_t type; /**< Type of this IO port. */
unsigned long length; /**< Length (in bytes or port) of this IO port. */
union {
void *ptr; /**< Base **virtual** memory address. */
int port; /**< Base port number. */
} addr; /**< Base address of the IO port. */
uint8_t bar_num;
} x86_pci_io_t;
struct x86_known_pci_device;
/**
* @brief A callback handler if there was an IRQ on the IRQ line of this device.
* @param[in] d Device that (might) need attention.
*
* Because PCI is multiplexer, there might not be an IRQ for this device.
* This callback is called out of the interrupt handler (inISR() == true).
* Lengthy operations should be handled in a dedicated thread; use msg_send_int().
* You must no enable interrupt inside the handler.
*/
typedef void (*x86_pci_irq_handler_t)(struct x86_known_pci_device *d);
/**
* @brief A PCI device.
*/
typedef struct x86_known_pci_device {
uint8_t bus, dev, fun;
pci_reg_0x00_t vendor;
pci_reg_0x08_t class;
unsigned io_count; /**< Number of entries in io. */
struct x86_pci_io **io; /**< Pointer to io_count pointers to configured IO ports. */
bool managed; /**< To be set by the hardware driver if it is going to manage this device. */
uint8_t irq; /**< IRQ line of this device. Change through x86_pci_set_irq(). */
x86_pci_irq_handler_t irq_handler; /**< Callback function if there was an interrupt on `irq`. */
} x86_known_pci_device_t;
/**
* @brief Enumerate unmanaged PCI devices.
* @param index Opaque reentrant pointer for the next iteration. Must be zero at first call.
* @returns Pointer to next unmanaged PCI device, or NULL.
*
* This function shall only be called by hardware drivers.
* x86_init_pci() will call the hardware drivers once it is ready.
* To not call earlier or later on your on accord.
*
* The result is a pointer to a pointer so that you can use realloc() to extend the struct as needed.
* You must only do that if you are going to manage this device, set managed iff so.
*/
struct x86_known_pci_device **x86_enumerate_unmanaged_pci_devices(unsigned *index);
/**
* @brief Enumerate all PCI devices.
* @param index Opaque reentrant pointer for the next iteration. Must be zero at first call.
* @returns Pointer to next PCI device, or NULL.
*/
const struct x86_known_pci_device *x86_enumerate_pci_devices(unsigned *index);
/**
* @brief Read a long word from the PCI configuration space.
* @param reg The register must be aligned on a 4 bytes boundary.
*/
uint32_t x86_pci_read(unsigned bus, unsigned dev, unsigned fun, unsigned reg);
/**
* @brief Read a byte from the PCI configuration space.
* @param reg The register must be aligned on a 4 bytes boundary.
*/
uint8_t x86_pci_read8(unsigned bus, unsigned dev, unsigned fun, unsigned reg);
/**
* @brief Read a short word from the PCI configuration space.
* @param reg The register must be aligned on a 4 bytes boundary.
*/
uint16_t x86_pci_read16(unsigned bus, unsigned dev, unsigned fun, unsigned reg);
/**
* @brief Write a long word to the PCI configuration space.
* @param reg The register must be aligned on a 4 bytes boundary.
*/
void x86_pci_write(unsigned bus, unsigned dev, unsigned fun, unsigned reg, uint32_t datum);
/**
* @brief Write a byte to the PCI configuration space.
* @param reg The register must be aligned on a 4 bytes boundary.
*/
void x86_pci_write8(unsigned bus, unsigned dev, unsigned fun, unsigned reg, uint8_t datum);
/**
* @brief Write a short word to the PCI configuration space.
* @param reg The register must be aligned on a 4 bytes boundary.
*/
void x86_pci_write16(unsigned bus, unsigned dev, unsigned fun, unsigned reg, uint16_t datum);
/**
* @brief Read a long word from the PCI configuration space, and cast the datum accordingly.
* @param REG Either `0x00`, `0x04`, `0x08`, or `0x0c`. Must be a constant token.
* @returns A `union pci_reg_0x00`, `union pci_reg_0x04`, ...
*/
#define x86_pci_read_reg(REG, BUS, DEV, FUN) \
((union pci_reg_##REG) { .packed = x86_pci_read((BUS), (DEV), (FUN), (REG)) })
#define PCI_IRQ_ACPI (PIC_NUM_LPT2) /**< IRQ line that may be used by the ACPI. */
#define PCI_IRQ_NETWORKING (PIC_NUM_9) /**< IRQ line that may be used by networking devices. */
#define PCI_IRQ_DEFAULT (PIC_NUM_ATA_4) /**< IRQ line that may be used by other devices. */
#define PCI_IRQ_USB (PIC_NUM_ATA_3) /**< IRQ line that may be used by USB host controllers. */
/**
* @brief Set the IRQ line if a PCI device.
*
* irq_num must be one of PCI_IRQ_ACPI, PCI_IRQ_NETWORKING, PCI_IRQ_DEFAULT, PCI_IRQ_USB.
* Bad things will happen if you supply any other value.
*/
void x86_pci_set_irq(struct x86_known_pci_device *d, uint8_t irq_num);
#endif
/** @} */

View File

@ -0,0 +1,8 @@
#ifndef __X86__PCI_INIT__H
#define __X86__PCI_INIT__H
/* no PCI devices are implemented, yet */
void x86_init_pci_devices(void);
#endif

View File

@ -0,0 +1,53 @@
/*
* 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 string representation of PCI constants. For humans.
*
* @ingroup x86-irq
* @{
* @file
* @author René Kijewski <rene.kijewski@fu-berlin.de>
*/
#ifndef CPU__X86__PCI_NAMES__H__
#define CPU__X86__PCI_NAMES__H__
/**
* @brief Get a string representation of the device class.
* @param[out] baseclass_name Name of the base class. Supply NULL if you do not care.
* @return A string (lengthy text) that describes the device class.
*
* If the device class is unknown, then the hexadecimal value is returned.
* This function is not thread safe.
*/
const char *x86_pci_subclass_to_string(unsigned baseclass, unsigned subclass, unsigned interface, const char **baseclass_name);
/**
* @brief Get a string representation of the device name.
* @param[out] vendor_name Name of the device vendor. Supply NULL if you do not care.
* @return A string that tells the device name.
*
* If the device ID is unknown, then the hexadecimal value is returned.
* This function is not thread safe.
*/
const char *x86_pci_device_id_to_string(unsigned vendor_id, unsigned device_id, const char **vendor_name);
#endif
/** @} */

168
cpu/x86/include/x86_pic.h Normal file
View File

@ -0,0 +1,168 @@
/*
* 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 IRQ handler of x86 boards.
*
* @ingroup x86-irq
* @{
* @file
* @author René Kijewski <rene.kijewski@fu-berlin.de>
*/
#ifndef CPU__X86__PIC__H__
#define CPU__X86__PIC__H__
#include <stdint.h>
/**
* @brief Initialize the Programmable Interrupt Controller.
*
* This function is called during initialization by x86_startup().
* You must not call this function on your own accord.
*/
void x86_init_pic(void);
#define X86_IRQ_COUNT (0x10) /**< Number of IRQ lines */
/**
* @brief Offset of the IRQ master in the interrupt numbers.
*
* x86_interrupts.c expects this value to be `0x20`. Do not change.
*/
#define PIC_MASTER_INTERRUPT_BASE (0x20)
/**
* @brief Offset of the IRQ slave in the interrupt numbers.
*
* The whole IRQ subsystem expects the IRQ slave numbers to come immediately after the master.
*/
#define PIC_SLAVE_INTERRUPT_BASE (PIC_MASTER_INTERRUPT_BASE + 8)
#define PIC_MASTER (0x20)
#define PIC_SLAVE (0xA0)
#define PIC_COMMAND (0x00)
#define PIC_DATA (0x01)
#define PIC_IMR (0x01)
#define PIC_EOI 0x20 /**< End-of-interrupt command code */
#define PIC_READ_IRR 0x0a /**< OCW3 irq ready next CMD read */
#define PIC_READ_ISR 0x0b /**< OCW3 irq service next CMD read */
#define PIC_ICW1_ICW4 (0x01) /**< ICW4 (not) needed */
#define PIC_ICW1_SINGLE (0x02) /**< Single (cascade) mode */
#define PIC_ICW1_INTERVAL4 (0x04) /**< Call address interval 4 (8) */
#define PIC_ICW1_LEVEL (0x08) /**< Level triggered (edge) mode */
#define PIC_ICW1_INIT (0x10) /**< Initialization - required! */
#define PIC_ICW4_8086 (0x01) /**< 8086/88 (MCS-80/85) mode */
#define PIC_ICW4_AUTO (0x02) /**< Auto (normal) EOI */
#define PIC_ICW4_BUF_SLAVE (0x08) /**< Buffered mode/slave */
#define PIC_ICW4_BUF_MASTER (0x0C) /**< Buffered mode/master */
#define PIC_ICW4_SFNM (0x10) /**< Special fully nested (not) */
#define PIC_NUM_PIT (0x0) /**< IRQ line of the Programmable Interrupt Controller **/
#define PIC_NUM_KEYBOARD_CONTROLLER_1 (0x1) /**< IRQ line of the first PS/2 port **/
#define PIC_NUM_SLAVE (0x2) /**< not a valid IRQ line! */
#define PIC_NUM_RS232_2_4 (0x3) /**< IRQ line of COM 2+4 **/
#define PIC_NUM_RS232_1_3 (0x4) /**< IRQ line of COM 1+2 **/
#define PIC_NUM_LPT2 (0x5) /**< IRQ line of the secondary printer or soundcard (available for PCI) **/
#define PIC_NUM_FLOPPY (0x6) /**< IRQ line of the floppy drive. **/
#define PIC_NUM_LPT1 (0x7) /**< IRQ line of the parallel port (available for PCI) **/
#define PIC_NUM_RTC (0x8) /**< IRQ line of the Real Time Clock **/
#define PIC_NUM_9 (0x9) /**< Free to use IRQ line (available for PCI) **/
#define PIC_NUM_ATA_4 (0xa) /**< Free to use IRQ line (available for PCI) **/
#define PIC_NUM_ATA_3 (0xb) /**< Free to use IRQ line (available for PCI) **/
#define PIC_NUM_KEYBOARD_CONTROLLER_2 (0xc) /**< IRQ line of the second PS/2 port **/
#define PIC_NUM_FPU (0xd) /**< not a valid IRQ line! */
#define PIC_NUM_ATA_1 (0xe) /**< IRQ line of the primary IDE controller **/
#define PIC_NUM_ATA_2 (0xf) /**< IRQ line of the secondary IDQ controller **/
#define PIC_MASK_PIT (1 << PIC_NUM_PIT)
#define PIC_MASK_KEYBOARD_CONTROLLER_1 (1 << PIC_NUM_KEYBOARD_CONTROLLER_1)
#define PIC_MASK_SLAVE (1 << PIC_NUM_SLAVE)
#define PIC_MASK_RS232_2_4 (1 << PIC_NUM_RS232_2_4)
#define PIC_MASK_RS232_1_3 (1 << PIC_NUM_RS232_1_3)
#define PIC_MASK_LPT2 (1 << PIC_NUM_LPT2)
#define PIC_MASK_FLOPPY (1 << PIC_NUM_FLOPPY)
#define PIC_MASK_LPT1 (1 << PIC_NUM_LPT1)
#define PIC_MASK_RTC (1 << PIC_NUM_RTC)
#define PIC_MASK_9 (1 << PIC_NUM_9)
#define PIC_MASK_ATA_4 (1 << PIC_NUM_ATA_4)
#define PIC_MASK_ATA_3 (1 << PIC_NUM_ATA_3)
#define PIC_MASK_KEYBOARD_CONTROLLER_2 (1 << PIC_NUM_KEYBOARD_CONTROLLER_2)
#define PIC_MASK_FPU (1 << PIC_NUM_FPU)
#define PIC_MASK_ATA_1 (1 << PIC_NUM_ATA_1)
#define PIC_MASK_ATA_2 (1 << PIC_NUM_ATA_2)
/**
* @brief Callback handler if there was an interrupt on this IRQ line.
* @param irq_num IRQ line in question.
*
* This callback is called out of the interrupt handler (inISR() == true).
* Lengthy operations should be handled in a dedicated thread; use msg_send_int().
* You must no enable interrupt inside the handler.
*/
typedef void (*x86_irq_handler_t)(uint8_t irq_num);
/**
* @brief Set callback function for an IRQ line.
* @param handler The callback function, or NULL to do nothing.
*
* Setting a handler does not enable the interrupt.
* Use x86_pic_enable_irq() or x86_pic_disable_irq() accordingly.
*
* This function must only be called with interrupts disabled.
*
* Beware on unsetting interrupt handlers:
* The PIC default handler will still send an EOI.
* Especially the keyboard controller does not like it,
* if it is told that everything was done but it wasn't.
* A raised #GP might be the least of your problems.
*/
void x86_pic_set_handler(unsigned irq, x86_irq_handler_t handler);
/**
* @brief Set the enabled IRQs
*
* Beware: this is the exact opposite of masking IRQs.
*
* This function should only be called by other subsystems like the PCI subsystem.
*/
void x86_pic_set_enabled_irqs(uint16_t mask);
/**
* @brief Enable (unmask) an IRQ
*
* This function should only be called by other subsystems like the PCI subsystem.
*/
void x86_pic_enable_irq(unsigned num);
/**
* @brief Disable (mask) an IRQ
*
* This function should only be called by other subsystems like the PCI subsystem.
*/
void x86_pic_disable_irq(unsigned num);
#endif
/** @} */

114
cpu/x86/include/x86_pit.h Normal file
View File

@ -0,0 +1,114 @@
/*
* 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
*/
/**
* @brief Interfacing with the PIT.
*
* The core of the x86 port only uses the RTC for the hwtimer.
* Application developers are free to use this module.
* Be aware of portability issues.
*
* You most likely want to use the vtimer interface instead.
*
* @ingroup x86-irq
* @{
* @file
* @author René Kijewski <rene.kijewski@fu-berlin.de>
*/
#ifndef CPU__X86__PIT__H__
#define CPU__X86__PIT__H__
#include <stdbool.h>
#include <stdint.h>
#define PIT_CHANNEL_0_PORT (0x40) /**< Channel 0 */
#define PIT_CHANNEL_1_PORT (0x41) /**< Channel 1, DO NOT USE */
#define PIT_CHANNEL_2_PORT (0x42) /**< Channel 2, do not use if you can help it */
#define PIT_COMMAND_PORT (0x43)
#define PIT_ACCESS_MODE_LATCH_COUNT (0 << 4)
#define PIT_ACCESS_MODE_LO_BYTE (1 << 4)
#define PIT_ACCESS_MODE_HI_BYTE (2 << 4)
#define PIT_ACCESS_MODE_LO_HI (3 << 4)
#define PIT_INTERRUPT_ONTERMINAL_COUNT (0 << 1) /**< */
#define PIT_ONE_SHOT (1 << 1) /**< */
#define PIT_RATE_GENERATOR (2 << 1) /**< */
#define PIT_SQUARE_WAVE (3 << 1) /**< */
#define PIT_SOFWARE_TRIGGERED_STROBE (4 << 1) /**< */
#define PIT_HARDWARE_TRIGGERED_STROBE (5 << 1) /**< */
#define PIT_MIN_HZ (19)
#define PIT_MAX_HZ (1193181)
/**
* @brief Initialize the Programmable Interval Timer.
*
* This function is called during initialization by x86_startup().
* You must not call this function on your own accord.
*/
void x86_init_pit(void);
/**
* @brief Reads the current value of the of the channel.
* @param channel Channel (1, 2, or 3) to read from.
* @returns Current value of the channel.
*
* Channel 1 is the only channel that you should access.
*
* Channel 2 might be absent on current systems.
* It was never free to use for the OSes.
* Never access this channel!
*
* Channel 3 was originally intended to be used by the sound card.
* It's free to use, but you probably shouldn't.
*/
uint16_t x86_pit_read(unsigned channel);
/**
* @brief Sets the speed and operation mode of a channel.
* @param channel Channel (1, 2, or 3) to set.
* @param mode Action on overflow.
* @param max After how many ticks the timer should overflow.
*
* See x86_pit_read() for considerations about the channel.
*
* The timer has a speed of 1193181hz.
* That value was convinient for hardware vendors,
* but has only the quite useless prime factors 3, 11, 19, and 173.
* That hurts the usefulness severly.
*/
void x86_pit_set2(unsigned channel, unsigned mode, uint16_t max);
/**
* @brief Convenience wrapper for x86_pit_set2().
* @param hz After how how long the timer should overflow.
* @returns true iff the value of hz was sane.
*
* See x86_pit_set2() for a more detailed explanation.
* max will be set to 1193181 / hz.
* That means hz has to be a value between 19 and 1193181.
*
* Beware: the 1,193,163 different values for hz will only render 2,165 different values for max.
*/
bool x86_pit_set(unsigned channel, unsigned mode, unsigned hz);
#endif
/** @} */

186
cpu/x86/include/x86_ports.h Normal file
View File

@ -0,0 +1,186 @@
/* Copyright (C) 2004, 2005, 2006 Board of Trustees, Leland Stanford
* Jr. University. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
// Copy of Pintos' threads/io.h, license header extracted from Pintos' LICENSE file.
#ifndef CPU__X86__PORTS_H__
#define CPU__X86__PORTS_H__
#include <stdlib.h>
#include <stdint.h>
/**
* @brief Reads and returns a byte from PORT.
* @param[in] port Port to read from.
* @returns Read value.
*/
static inline uint8_t __attribute__((always_inline)) inb(uint16_t port)
{
/* See [IA32-v2a] "IN". */
uint8_t data;
asm volatile("inb %w1, %b0" : "=a"(data) : "Nd"(port));
return data;
}
/**
* @brief Reads multiple bytes from a port.
* @param[in] port Port to read from.
* @param[out] addr Buffer to write the read values into.
* @param[in] cnt Number of bytes to read.
*/
static inline void __attribute__((always_inline)) insb(uint16_t port, void *addr, size_t cnt)
{
/* See [IA32-v2a] "INS". */
asm volatile("rep insb" : "+D"(addr), "+c"(cnt) : "d"(port) : "memory");
}
/**
* @brief Reads and returns a word from PORT.
* @param[in] port Port to read from.
* @returns Read value.
*/
static inline uint16_t __attribute__((always_inline)) inw(uint16_t port)
{
uint16_t data;
/* See [IA32-v2a] "IN". */
asm volatile("inw %w1, %w0" : "=a"(data) : "Nd"(port));
return data;
}
/**
* @brief Reads multiple words from a port.
* @param[in] port Port to read from.
* @param[out] addr Buffer to write the read values into.
* @param[in] cnt Number of words to read.
*/
static inline void __attribute__((always_inline)) insw(uint16_t port, void *addr, size_t cnt)
{
/* See [IA32-v2a] "INS". */
asm volatile("rep insw" : "+D"(addr), "+c"(cnt) : "d"(port) : "memory");
}
/**
* @brief Reads and returns a long from PORT.
* @param[in] port Port to read from.
* @returns Read value.
*/
static inline uint32_t __attribute__((always_inline)) inl(uint16_t port)
{
/* See [IA32-v2a] "IN". */
uint32_t data;
asm volatile("inl %w1, %0" : "=a"(data) : "Nd"(port));
return data;
}
/**
* @brief Reads multiple longs from a port.
* @param[in] port Port to read from.
* @param[out] addr Buffer to write the read values into.
* @param[in] cnt Number of words to read.
*/
static inline void __attribute__((always_inline)) insl(uint16_t port, void *addr, size_t cnt)
{
/* See [IA32-v2a] "INS". */
asm volatile("rep insl" : "+D"(addr), "+c"(cnt) : "d"(port) : "memory");
}
/**
* @brief Writes a byte into a port.
* @param[in] port Port to write into.
* @param[in] data Byte to write.
*/
static inline void __attribute__((always_inline)) outb(uint16_t port, uint8_t data)
{
/* See [IA32-v2b] "OUT". */
asm volatile("outb %b0, %w1" : : "a"(data), "Nd"(port));
}
/**
* @brief Writes multiple bytes into a port.
* @param[in] port Port to write into.
* @param[in] addr Buffer to read from.
* @param[in] cnt Number of bytes to write.
*/
static inline void __attribute__((always_inline)) outsb(uint16_t port, const void *addr, size_t cnt)
{
/* See [IA32-v2b] "OUTS". */
asm volatile("rep outsb" : "+S"(addr), "+c"(cnt) : "d"(port));
}
/**
* @brief Writes a word into a port.
* @param[in] port Port to write into.
* @param[in] data Word to write.
*/
static inline void __attribute__((always_inline)) outw(uint16_t port, uint16_t data)
{
/* See [IA32-v2b] "OUT". */
asm volatile("outw %w0, %w1" : : "a"(data), "Nd"(port));
}
/**
* @brief Writes multiple words into a port.
* @param[in] port Port to write into.
* @param[in] addr Buffer to read from.
* @param[in] cnt Number of words to write.
*/
static inline void __attribute__((always_inline)) outsw(uint16_t port, const void *addr, size_t cnt)
{
/* See [IA32-v2b] "OUTS". */
asm volatile("rep outsw" : "+S"(addr), "+c"(cnt) : "d"(port));
}
/**
* @brief Writes a long into a port.
* @param[in] port Port to write into.
* @param[in] data Long to write.
*/
static inline void __attribute__((always_inline)) outl(uint16_t port, uint32_t data)
{
/* See [IA32-v2b] "OUT". */
asm volatile("outl %0, %w1" : : "a"(data), "Nd"(port));
}
/**
* @brief Writes multiple longs into a port.
* @param[in] port Port to write into.
* @param[in] addr Buffer to read from.
* @param[in] cnt Number of longs to write.
*/
static inline void __attribute__((always_inline)) outsl(uint16_t port, const void *addr, size_t cnt)
{
/* See [IA32-v2b] "OUTS". */
asm volatile("rep outsl" : "+S"(addr), "+c"(cnt) : "d"(port));
}
/**
* @brief Make sure a write to a port was already acknowledged.
*/
static inline void __attribute__((always_inline)) io_wait(void)
{
asm volatile(" jmp 1f\n"
"1: jmp 2f\n"
"2:");
}
#endif

View File

@ -0,0 +1,89 @@
/*
* 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
*/
/**
* Rebooting x86 boards.
*
* @ingroup x86
* @{
* @file
* @author René Kijewski <rene.kijewski@fu-berlin.de>
*/
#ifndef CPU__X86__REBOOT__H__
#define CPU__X86__REBOOT__H__
#include "kernel.h"
/**
* @brief Prototype for a x86 reboot function.
*
* The function may return, then a naive reboot is tried.
* It is safe to call x86_kbc_reboot() inside the handler,
* then a naive reboot will be done immediately.
*
* The handler is called with interrupts disabled.
* You should keep the interrupts disabled and use busy loops.
*/
typedef void (*x86_reboot_t)(void);
/**
* @brief Reboots the system.
*
* First the function supplied if x86_set_reboot_fun() is called (iff applies).
* Then a reboot using the keyboard controller is tried.
* And if everything fails, a tripple fault is provoked.
*/
void NORETURN x86_kbc_reboot(void);
/**
* @brief Set specialized reboot function.
*
* This function should be utilized by the ACPI subsystem.
*/
void x86_set_reboot_fun(x86_reboot_t fun);
/**
* @brief Loads an empty interrupt descriptor table.
*
* Any interrupt that will occur causes a tripple fault, i.e. a reboot.
* Must be called with interrupts disabled.
*/
void x86_load_empty_idt(void);
/**
* @brief Prototype of the board specific x86 shutdown function.
* @returns `false` if the shutdown could not be scheduled.
*/
typedef bool (*x86_shutdown_t)(void);
/**
* @brief Shutdown and power off
* @details May return. The shutdown only gets scheduled.
* @returns `false` if shutting down is not possible at this moment.
*/
bool x86_shutdown(void);
/**
* @brief Function to call on x86_shutdown()
*/
void x86_set_shutdown_fun(x86_shutdown_t fun);
#endif
/** @} */

View File

@ -0,0 +1,252 @@
/*
* 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 various non-general purpose registers of x86 CPUs.
*
* @ingroup x86
* @{
* @file
* @author René Kijewski <rene.kijewski@fu-berlin.de>
*/
#ifndef CPU__X86__REGISTERS__H__
#define CPU__X86__REGISTERS__H__
#include <stdint.h>
/* see "Intel® Quark SoC X1000 Core Developers Manual", § 4.4.1.1 (p. 47) */
#define CR0_PE (1u << 0) /**< 1 = protected mode */
#define CR0_MP (1u << 1) /**< 1 = monitor coprocessor (FWAIT causes an interrupt) */
#define CR0_EM (1u << 2) /**< 1 = FPU emulation (x87 instruction cause #NM, SSE causes #UD) */
#define CR0_TS (1u << 3) /**< 1 = task switched flag (causes #NM on x87/SSE instructions, set by CPU on hardware task switch) */
#define CR0_ET (1u << 4) /**< 1 = 80387; 0 = 80287 */
#define CR0_NE (1u << 5) /**< 1 = numeric error */
#define CR0_WP (1u << 16) /**< 1 = write proctected pages aren't writable in ring 0 either */
#define CR0_AM (1u << 18) /**< 1 = alignment mask */
#define CR0_NW (1u << 29) /**< 1 = not write-through */
#define CR0_CD (1u << 30) /**< 1 = disable caching */
#define CR0_PG (1u << 31) /**< 1 = enable paging */
#define CR3_PWT (1u << 3) /**< 1 = page-level writes transparent */
#define CR3_PCD (1u << 4) /**< 1 = page-level cache disabled */
#define CR4_VME (1u << 0) /**< 1 = virtual 8086 mode */
#define CR4_PVI (1u << 1) /**< 1 = protected-mode virtual interrupts */
#define CR4_TSD (1u << 2) /**< 1 = restrict RDTSC instruction to ring 0 */
#define CR4_DE (1u << 3) /**< 1 = debugging extension */
#define CR4_PSE (1u << 4) /**< 1 = page size extension */
#define CR4_PAE (1u << 5) /**< 1 = physical address extension */
#define CR4_MCE (1u << 6) /**< 1 = machine-check enable */
#define CR4_PGE (1u << 7) /**< 1 = enable G flag in PT */
#define CR4_PCE (1u << 8) /**< 1 = allow RDPMC instruction in rings 1-3, too */
#define CR4_OSFXSR (1u << 9) /**< 1 = disable #NM if CR0.TS=1 */
#define CR4_OSXMMEXCPT (1u << 10) /**< 1 = enable unmasked SSE exceptions */
#define CR4_SMEP (1u << 10) /**< 1 = enables supervisor-mode execution prevention */
#define X86_CR_ATTR __attribute__ ((always_inline, no_instrument_function))
/**
* @brief Read the Control Register 0, various control flags for the CPU.
*/
static inline uint32_t X86_CR_ATTR cr0_read(void)
{
uint32_t result;
asm volatile ("mov %%cr0, %%eax" : "=a"(result));
return result;
}
/**
* @brief Set the Control Register 0, various control flags for the CPU.
*
* You must not blank out unknown bits.
* First use cr0_read(), then set and reset the bit you want to manipulate,
* then call this function.
*/
static inline void X86_CR_ATTR cr0_write(uint32_t value)
{
asm volatile ("mov %%eax, %%cr0" :: "a"(value));
}
/**
* @brief Read the Page Fault Linear Address.
*
* The PFLA is the address which was accessed when the page fauled occured,
* i.e. this is not the PC of the #PF!
*/
static inline uint32_t X86_CR_ATTR cr2_read(void)
{
uint32_t result;
asm volatile ("mov %%cr2, %%eax" : "=a"(result));
return result;
}
/**
* @brief Read the (physical) address of the page table.
*
* You are doing it wrong if you ever find you need to call this function ...
*/
static inline uint32_t X86_CR_ATTR cr3_read(void)
{
uint32_t result;
asm volatile ("mov %%cr3, %%eax" : "=a"(result));
return result;
}
/**
* @brief Set the (pyhsical) address of the page table.
*
* This flushes the TLB for all pages that do not have the PT_G flag.
*/
static inline void X86_CR_ATTR cr3_write(uint32_t value)
{
asm volatile ("mov %%eax, %%cr3" :: "a"(value));
}
/**
* @brief Set the Control Register 4, various control flags for the CPU.
*/
static inline uint32_t X86_CR_ATTR cr4_read(void)
{
uint32_t result;
asm volatile ("mov %%cr4, %%eax" : "=a"(result));
return result;
}
/**
* @brief Set the Control Register 4, various control flags for the CPU.
*
* You must not blank out unknown bits.
* First use cr0_read(), then set and reset the bit you want to manipulate,
* then call this function.
*/
static inline void X86_CR_ATTR cr4_write(uint32_t value)
{
asm volatile ("mov %%eax, %%cr4" :: "a"(value));
}
#define EFER_SCE (1u << 0)
#define EFER_LME (1u << 8)
#define EFER_LMA (1u << 10)
#define EFER_NXE (1u << 11)
#define EFER_SVME (1u << 12)
#define EFER_LMSLE (1u << 12)
#define EFER_FFXSR (1u << 14)
#define MSR_EFER (0xC0000080)
/**
* @brief Read a Machine Specific Register.
*/
static inline uint64_t X86_CR_ATTR msr_read(uint32_t msr)
{
uint32_t eax, edx;
asm volatile (
"rdmsr"
: "=a"(eax), "=d"(edx)
: "c"(msr)
);
return (((uint64_t) edx) << 32) | eax;
}
/**
* @brief Set a Machine Specific Register.
*
* You must not blank out unknown bits.
* First use msr_read(), then set and reset the bit you want to manipulate,
* then call this function.
*/
static inline void X86_CR_ATTR msr_set(uint32_t msr, uint64_t value)
{
asm volatile (
"wrmsr"
:: "a"((uint32_t) value), "d"((uint32_t) (value >> 32)), "c"(msr)
);
}
#define CPUID_FPU (1ull << 0)
#define CPUID_VME (1ull << 1)
#define CPUID_DE (1ull << 2)
#define CPUID_PSE (1ull << 3)
#define CPUID_TSC (1ull << 4)
#define CPUID_MSR (1ull << 5)
#define CPUID_PAE (1ull << 6)
#define CPUID_MCE (1ull << 7)
#define CPUID_CX8 (1ull << 8)
#define CPUID_APIC (1ull << 9)
#define CPUID_SEP (1ull << 11)
#define CPUID_MTRR (1ull << 12)
#define CPUID_PGE (1ull << 13)
#define CPUID_MCA (1ull << 14)
#define CPUID_CMOV (1ull << 15)
#define CPUID_PAT (1ull << 16)
#define CPUID_PSE36 (1ull << 17)
#define CPUID_PSN (1ull << 18)
#define CPUID_CLF (1ull << 19)
#define CPUID_DTES (1ull << 21)
#define CPUID_ACPI (1ull << 22)
#define CPUID_MMX (1ull << 23)
#define CPUID_FXSR (1ull << 24)
#define CPUID_SSE (1ull << 25)
#define CPUID_SSE2 (1ull << 26)
#define CPUID_SS (1ull << 27)
#define CPUID_HTT (1ull << 28)
#define CPUID_TM1 (1ull << 29)
#define CPUID_IA64 (1ull << 30)
#define CPUID_PBE (1ull << 31)
#define CPUID_SSE3 (1ull << (32 + 0))
#define CPUID_PCLMUL (1ull << (32 + 1))
#define CPUID_DTES64 (1ull << (32 + 2))
#define CPUID_MONITOR (1ull << (32 + 3))
#define CPUID_DS_CPL (1ull << (32 + 4))
#define CPUID_VMX (1ull << (32 + 5))
#define CPUID_SMX (1ull << (32 + 6))
#define CPUID_EST (1ull << (32 + 7))
#define CPUID_TM2 (1ull << (32 + 8))
#define CPUID_SSSE3 (1ull << (32 + 9))
#define CPUID_CID (1ull << (32 + 10))
#define CPUID_FMA (1ull << (32 + 12))
#define CPUID_CX16 (1ull << (32 + 13))
#define CPUID_ETPRD (1ull << (32 + 14))
#define CPUID_PDCM (1ull << (32 + 15))
#define CPUID_DCA (1ull << (32 + 18))
#define CPUID_SSE4_1 (1ull << (32 + 19))
#define CPUID_SSE4_2 (1ull << (32 + 20))
#define CPUID_x2APIC (1ull << (32 + 21))
#define CPUID_MOVBE (1ull << (32 + 22))
#define CPUID_POPCNT (1ull << (32 + 23))
#define CPUID_AES (1ull << (32 + 25))
#define CPUID_XSAVE (1ull << (32 + 26))
#define CPUID_OSXSAVE (1ull << (32 + 27))
#define CPUID_AVX (1ull << (32 + 28))
/**
* @brief Read the basic features of the CPU.
*
* http://www.sandpile.org/x86/cpuid.htm
*/
static inline uint64_t X86_CR_ATTR cpuid_caps(void)
{
uint32_t edx, ecx;
asm volatile ("cpuid" : "=d"(edx), "=c"(ecx) : "a"(1) : "ebx");
return ((uint64_t) ecx << 32) | edx;
}
#endif
/** @} */

211
cpu/x86/include/x86_rtc.h Normal file
View File

@ -0,0 +1,211 @@
/*
* 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
*/
/**
* @brief Interfacing the realtime clock on x86 boards.
*
* Only use this module to read the current time.
* Using the other functions in applications would break the hwtimer.
*
* @ingroup x86-irq
* @{
* @file
* @author René Kijewski <rene.kijewski@fu-berlin.de>
*/
#ifndef CPU__X86__RTC__H__
#define CPU__X86__RTC__H__
#include "msg.h"
#include "x86_cmos.h"
#include <stdbool.h>
/**
* @brief A timestamp.
*
* The value of the century is unreliable.
*/
typedef union x86_rtc_data {
__extension__ struct {
uint8_t second, minute, hour;
uint8_t day, month, year, century;
};
uint64_t timestamp;
} x86_rtc_data_t;
#define RTC_REG_SECOND (0x00)
#define RTC_REG_ALARM_SECOND (0x01)
#define RTC_REG_MINUTE (0x02)
#define RTC_REG_ALARM_MINUTE (0x03)
#define RTC_REG_HOUR (0x04)
#define RTC_REG_ALARM_HOUR (0x05)
#define RTC_REG_DOW (0x06)
#define RTC_REG_DAY (0x07)
#define RTC_REG_MONTH (0x08)
#define RTC_REG_YEAR (0x09)
#define RTC_REG_A (0x0a)
#define RTC_REG_B (0x0b)
#define RTC_REG_C (0x0c)
#define RTC_REG_D (0x0d)
#define RTC_REG_POST (0x0e)
#define RTC_REG_CENTURY (0x32)
#define RTC_REG_A_HZ_OFF ( 0 << 0)
#define RTC_REG_A_HZ_8192 ( 3 << 0)
#define RTC_REG_A_HZ_4096 ( 4 << 0)
#define RTC_REG_A_HZ_2048 ( 5 << 0)
#define RTC_REG_A_HZ_1024 ( 6 << 0)
#define RTC_REG_A_HZ_512 ( 7 << 0)
#define RTC_REG_A_HZ_256 ( 8 << 0)
#define RTC_REG_A_HZ_128 ( 9 << 0)
#define RTC_REG_A_HZ_64 (10 << 0)
#define RTC_REG_A_HZ_32 (11 << 0)
#define RTC_REG_A_HZ_16 (12 << 0)
#define RTC_REG_A_HZ_8 (13 << 0)
#define RTC_REG_A_HZ_4 (14 << 0)
#define RTC_REG_A_HZ_2 (15 << 0)
#define RTC_REG_A_HZ_MASK (15 << 0)
#define RTC_REG_A_DIVIDER_MASK ( 3 << 4)
#define RTC_REG_A_UPDATING ( 1 << 7)
#define RTC_REG_B_DST (1 << 0)
#define RTC_REG_B_24H (1 << 1)
#define RTC_REG_B_BIN (1 << 2)
#define RTC_REG_B_WAVE (1 << 3)
#define RTC_REG_B_INT_UPDATE (1 << 4)
#define RTC_REG_B_INT_ALARM (1 << 5)
#define RTC_REG_B_INT_PERIODIC (1 << 6)
#define RTC_REG_B_INT_MASK (7 << 4)
#define RTC_REG_B_UPDATE (1 << 7)
#define RTC_REG_C_IRQ_UPDATE (1 << 4)
#define RTC_REG_C_IRQ_ALARM (1 << 5)
#define RTC_REG_C_IRQ_PERIODIC (1 << 6)
#define RTC_REG_C_IRQ (1 << 7)
#define RTC_REG_D_VALID (1 << 7)
#define RTC_REG_POST_TIME_INVALID (1 << 2)
#define RTC_REG_POST_POWER_LOSS (1 << 7)
/**
* @brief Initialize the Real Time Clock.
*
* This function is called during initialization by x86_startup().
* You must not call this function on your own accord.
*
* The RTC subsystem will refuse to work if the CMOS says that
* <li>there was a power loss,
* <li>the stored time is invalid (i.e. February 30), or
* <li>the CMOS had a checksum failure.
*/
void x86_init_rtc(void);
/**
* @brief Read the current time.
* @returns false iff the RTC is unreliable, the value of dest is random is this case.
*
* This reads the CMOS value
* The standard does not tell the timezone of this value.
*/
bool x86_rtc_read(x86_rtc_data_t *dest);
/**
* @brief A custom callback handler for RTC interrupts.
* @param reg_c The value of CMOS register C.
*/
typedef void (*x86_rtc_callback_t)(uint8_t reg_c);
/**
* @brief Set an RTC alarm.
* @param[in] when Time when the RTC you raise an interrupt. The date part is ignored.
* @param msg_content The value for msg_t::content.value.
* @param target_pid The process which shall receive the message, `-1u` to disable.
* @param allow_replace Whether it is allowed to overwrite an existing alarm.
*
* The value of msg_t::type will be `reg_c | (RTC_REG_B_INT_UPDATE << 8)`,
* where `reg_c` is the value of CMOS register C.
*
* You should not call this function directly.
* You should use hwtimer -- or better yet -- vtimer instead.
*/
bool x86_rtc_set_alarm(const x86_rtc_data_t *when, uint32_t msg_content, unsigned int target_pid, bool allow_replace);
/**
* @brief Set up periodic interrupts
* @param hz How often a second the interrupt should fire, e.g. RTC_REG_A_HZ_8192.
* @param msg_content The value for msg_t::content.value.
* @param target_pid The process which shall receive the message, `-1u` to disable.
* @param allow_replace Whether it is allowed to overwrite an existing alarm.
*
* The value of msg_t::type will be `reg_c | (RTC_REG_B_INT_PERIODIC << 8)`,
* where `reg_c` is the value of CMOS register C.
*
* You should not call this function directly.
* You should use hwtimer -- or better yet -- vtimer instead.
*/
bool x86_rtc_set_periodic(uint8_t hz, uint32_t msg_content, unsigned int target_pid, bool allow_replace);
/**
* @brief Set up secondly interrupts.
* @param msg_content The value for msg_t::content.value.
* @param target_pid The process which shall receive the message, `-1u` to disable.
* @param allow_replace Whether it is allowed to overwrite an existing alarm.
*
* The value of msg_t::type will be `reg_c | (RTC_REG_B_INT_UPDATE << 8)`,
* where `reg_c` is the value of CMOS register C.
*
* You should not call this function directly.
* You should use hwtimer -- or better yet -- vtimer instead.
*/
bool x86_rtc_set_update(uint32_t msg_content, unsigned int target_pid, bool allow_replace);
/**
* @brief Set custom alarm interrupt handler.
* @param cb Your custom handler, or NULL to use default.
*
* You must never use this function.
* It is only there for x86_hwtimer.c,
* because the hwtimer subsystem gets set up before the message system works.
*/
void x86_rtc_set_alarm_callback(x86_rtc_callback_t cb);
/**
* @brief Set custom periodic interrupt handler.
* @param cb Your custom handler, or NULL to use default.
*
* You must never use this function.
* It is only there for x86_hwtimer.c,
* because the hwtimer subsystem gets set up before the message system works.
*/
void x86_rtc_set_periodic_callback(x86_rtc_callback_t cb);
/**
* @brief Set custom update interrupt handler.
* @param cb Your custom handler, or NULL to use default.
*
* You must never use this function.
* It is only there for x86_hwtimer.c,
* because the hwtimer subsystem gets set up before the message system works.
*/
void x86_rtc_set_update_callback(x86_rtc_callback_t cb);
#endif
/** @} */

View File

@ -0,0 +1,49 @@
/*
* 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
*/
/**
* Using multiple threads on x86 boards.
*
* @ingroup x86
* @defgroup x86-multithreading i586 multithreading support
* @{
* @file
* @author René Kijewski <rene.kijewski@fu-berlin.de>
*/
#ifndef CPU__X86__THREADING__H__
#define CPU__X86__THREADING__H__
#include <stdbool.h>
/**
* @brief Initialize threading.
*
* This function is called during initialization by x86_startup().
* You must not call this function on your own accord.
*/
void x86_init_threading(void);
/**
* @brief The getter/setter for inISR() for the x86 port.
*/
extern bool x86_in_isr;
#endif
/** @} */

176
cpu/x86/include/x86_uart.h Normal file
View File

@ -0,0 +1,176 @@
/*
* 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
*/
/**
* Using Communations Port 0 (COM0) to interface with a human being on x86 boards.
*
* @ingroup x86-irq
* @{
* @file
* @author René Kijewski <rene.kijewski@fu-berlin.de>
*/
#ifndef CPU__X86__UART__H__
#define CPU__X86__UART__H__
#include "x86_pic.h"
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <unistd.h>
/**
* @brief Early initialization of the UART system, before there are interrupts.
*
* This function is called during initialization by x86_startup().
* You must not call this function on your own accord.
*/
void x86_early_init_uart(void);
/**
* @brief Full initialization of the Universal Asynchronous Receiver Transmitter.
*
* This function is called during initialization by x86_startup().
* You must not call this function on your own accord.
*/
void x86_init_uart(void);
/**
* @brief Write out characters to the UART.
* @param[in] buf Buffer to write.
* @param len Length of the buffer.
* @returns Actual amount of bytes written, always the same as len.
*
* This function blocks with interrupts disabled once the output buffer is full.
*/
ssize_t x86_uart_write(const char *buf, size_t len);
/**
* @brief Read in characters to the UART.
* @param[out] buf Buffer to set.
* @param len Length of the buffer.
* @returns Actual amount of bytes read, always the same as len.
*
* This function blocks with interrupts disabled once the input buffer is full.
*/
ssize_t x86_uart_read(char *buf, size_t len);
#define COM1_PORT (0x03F8)
#define COM2_PORT (0x02F8)
#define COM3_PORT (0x03E8)
#define COM4_PORT (0x02E8)
#define COM1_IRQ (PIC_NUM_RS232_1_3)
#define COM2_IRQ (PIC_NUM_RS232_2_4)
#define COM3_IRQ (PIC_NUM_RS232_1_3)
#define COM4_IRQ (PIC_NUM_RS232_2_4)
/* 115200 baud */
#define BAUD_LL (0x01)
#define BAUD_HL (0x00)
enum uart_port_offs_t {
/* DLAB RW */
THR = 0, /* 0 W Transmitter Holding Buffer */
RBR = 0, /* 0 R Receiver Buffer */
DLL = 0, /* 1 RW Divisor Latch Low Byte */
IER = 1, /* 0 RW Interrupt Enable Register */
DLH = 1, /* 1 RW Divisor Latch High Byte */
IIR = 2, /* - R Interrupt Identification Register */
FCR = 2, /* - RW FIFO Control Register */
LCR = 3, /* - RW Line Control Register */
MCR = 4, /* - RW Modem Control Register */
LSR = 5, /* - R Line Status Register */
MSR = 6, /* - R Modem Status Register */
SR = 7, /* - RW Scratch Register */
};
enum ier_t {
IER_RECV = 1 << 0, /* Enable Received Data Available Interrupt */
IER_SEND = 1 << 1, /* Enable Transmitter Holding Register Empty Interrupt */
IER_LS = 1 << 2, /* Enable Receiver Line Status Interrupt */
IER_MS = 1 << 3, /* Enable Modem Status Interrupt */
IER_SLEEP = 1 << 4, /* Enables Sleep Mode (16750) */
IER_LPM = 1 << 5, /* Enables Low Power Mode (16750) */
};
enum fcr_t {
FCR_ENABLE = 1 << 0, /* Enable FIFOs */
FCR_CLR_RECV = 1 << 1, /* Clear Receive FIFO */
FCR_CLR_SEND = 1 << 2, /* Clear Transmit FIFO */
FCR_MDA = 1 << 3, /* DMA Mode Select */
FCR_64BYTE = 1 << 5, /* Enable 64 Byte FIFO (16750) */
FCR_TRIGGER_1 = 0 << 6, /* 1 byte */
FCR_TRIGGER_16 = 1 << 6, /* 16 bytes */
FCR_TRIGGER_32 = 2 << 6, /* 32 bytes */
FCR_TRIGGER_56 = 3 << 6, /* 56 bytes */
};
enum lcr_t {
LCR_WORD_BITS_5 = (0) << 0, /* Word length: 5 bits */
LCR_WORD_BITS_6 = (1) << 0, /* Word length: 6 bits */
LCR_WORD_BITS_7 = (2) << 0, /* Word length: 7 bits */
LCR_WORD_BITS_8 = (3) << 0, /* Word length: 8 bits */
LCR_STOP_BITS_1 = (0) << 2, /* Stop bits: 1 */
LCR_STOP_BITS_2 = (1) << 2, /* Stop bits: 1.5 or 2 */
LCR_PAR_NONE = (0) << 3, /* no parity */
LCR_PAR_ODD = (1) << 3, /* odd parity */
LCR_PAR_EVEN = (3) << 3, /* even parity */
LCR_PAR_MARK = (5) << 3, /* mark (?) */
LCR_PAR_SPACE = (7) << 3, /* space (?) */
LCR_SET_BREAK = (1) << 6, /* set break enabled */
LCR_DLAB = (1) << 7, /* divisor latch access bit */
};
enum mcr_t {
MCR_DSR = 1 << 0, /* Data Terminal Ready */
MCR_RTS = 1 << 1, /* Request To Send */
MCR_AUX1 = 1 << 2, /* Auxiliary Output 1 */
MCR_AUX2 = 1 << 3, /* Auxiliary Output 2 */
MCR_LOOPBACK = 1 << 4, /* Loopback Mode */
MCR_AUTOFLOW = 1 << 5, /* Autoflow Control Enabled (16750) */
};
enum iir_t {
IIR_IPF = 1 << 0, /* interrupt pending flag */
IIR_INT_MASK = 3 << 1,
IIR_INT_MS = 0 << 1, /* modem status interrupt (reset: read MSR) */
IIR_INT_TH = 1 << 1, /* THR empty interrupt (reset: read IIR or write to THR) */
IIR_INT_BR = 2 << 1, /* recv data available interrupt (reset: read RBR) */
IIR_INT_LS = 3 << 1, /* line status changed (reset: read LSR) */
IIR_INT_TO = 6 << 1, /* timeout interrupt pending (reset: read RBR) */
IIR_FIFO64 = 1 << 5, /* 64 byte FIFO enabled */
IIR_FIFO_MASK = 3 << 6,
IIR_FIFO_NONE = 0 << 6, /* no FIFO available */
IIR_FIFO_RES = 1 << 6, /* reserved condition */
IIR_FIFO_MAL = 2 << 6, /* FIFO enabled, not working */
IIR_FIFO_ENABLED = 3 << 6, /* FIFO enabled */
};
#endif
/** @} */

View File

@ -0,0 +1,62 @@
/*
* 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
*/
/**
* Writing to the videoram.
*
* @ingroup x86
* @{
* @file
* @author René Kijewski <rene.kijewski@fu-berlin.de>
*/
#ifndef CPU__X86__VIDEORAM_H__
#define CPU__X86__VIDEORAM_H__
/**
* @brief Print out a single character on the graphical device.
*
* This method can be used before the UART system was initialized.
*
* Special charactes that are understood: '\n' and '\r'.
* Use DOS newlines.
*/
void videoram_putc(char c);
/**
* @brief Print out multiple characters on the graphical device.
*
* This is the same as calling videoram_putc() repeatedly.
*/
void videoram_put(const char *s);
/**
* @brief Print out multiple characters on the graphical device, then go to the next line.
*
* This is the same as calling `videoram_put(s); videoram_put("\r\n");`.
*/
void videoram_puts(const char *s);
/**
* @brief Print out a hexadecimal number on the graphical device, including "0x" at the start.
*/
void videoram_put_hex(unsigned long v);
#endif
/** @} */

128
cpu/x86/nop_nop_nop.inc Normal file
View File

@ -0,0 +1,128 @@
#if 0
from itertools import cycle
regs = [ ('eax', [1,0,0,0,0,0,0]),
('ecx', [0,1,0,0,0,0,0]),
('edx', [0,0,1,0,0,0,0]),
#('ebx', [0,0,0,1,0,0,0]),
#('ebp', [0,0,0,0,1,0,0]),
('esi', [0,0,0,0,0,1,0]),
('edi', [0,0,0,0,0,0,1]), ]
REGS0 = list(regs)
for cur in cycle(range(len(regs))):
nex = (cur + 1) % len(regs)
print 'asm volatile ("xor %%%s, %%%s");' % (regs[cur][0], regs[nex][0])
val = [a^b for a, b in zip(regs[cur][1], regs[nex][1])]
regs[nex] = (regs[nex][0], val)
if regs == REGS0:
break
#endif
asm volatile ("xor %eax, %ecx");
asm volatile ("xor %ecx, %edx");
asm volatile ("xor %edx, %esi");
asm volatile ("xor %esi, %edi");
asm volatile ("xor %edi, %eax");
asm volatile ("xor %eax, %ecx");
asm volatile ("xor %ecx, %edx");
asm volatile ("xor %edx, %esi");
asm volatile ("xor %esi, %edi");
asm volatile ("xor %edi, %eax");
asm volatile ("xor %eax, %ecx");
asm volatile ("xor %ecx, %edx");
asm volatile ("xor %edx, %esi");
asm volatile ("xor %esi, %edi");
asm volatile ("xor %edi, %eax");
asm volatile ("xor %eax, %ecx");
asm volatile ("xor %ecx, %edx");
asm volatile ("xor %edx, %esi");
asm volatile ("xor %esi, %edi");
asm volatile ("xor %edi, %eax");
asm volatile ("xor %eax, %ecx");
asm volatile ("xor %ecx, %edx");
asm volatile ("xor %edx, %esi");
asm volatile ("xor %esi, %edi");
asm volatile ("xor %edi, %eax");
asm volatile ("xor %eax, %ecx");
asm volatile ("xor %ecx, %edx");
asm volatile ("xor %edx, %esi");
asm volatile ("xor %esi, %edi");
asm volatile ("xor %edi, %eax");
asm volatile ("xor %eax, %ecx");
asm volatile ("xor %ecx, %edx");
asm volatile ("xor %edx, %esi");
asm volatile ("xor %esi, %edi");
asm volatile ("xor %edi, %eax");
asm volatile ("xor %eax, %ecx");
asm volatile ("xor %ecx, %edx");
asm volatile ("xor %edx, %esi");
asm volatile ("xor %esi, %edi");
asm volatile ("xor %edi, %eax");
asm volatile ("xor %eax, %ecx");
asm volatile ("xor %ecx, %edx");
asm volatile ("xor %edx, %esi");
asm volatile ("xor %esi, %edi");
asm volatile ("xor %edi, %eax");
asm volatile ("xor %eax, %ecx");
asm volatile ("xor %ecx, %edx");
asm volatile ("xor %edx, %esi");
asm volatile ("xor %esi, %edi");
asm volatile ("xor %edi, %eax");
asm volatile ("xor %eax, %ecx");
asm volatile ("xor %ecx, %edx");
asm volatile ("xor %edx, %esi");
asm volatile ("xor %esi, %edi");
asm volatile ("xor %edi, %eax");
asm volatile ("xor %eax, %ecx");
asm volatile ("xor %ecx, %edx");
asm volatile ("xor %edx, %esi");
asm volatile ("xor %esi, %edi");
asm volatile ("xor %edi, %eax");
asm volatile ("xor %eax, %ecx");
asm volatile ("xor %ecx, %edx");
asm volatile ("xor %edx, %esi");
asm volatile ("xor %esi, %edi");
asm volatile ("xor %edi, %eax");
asm volatile ("xor %eax, %ecx");
asm volatile ("xor %ecx, %edx");
asm volatile ("xor %edx, %esi");
asm volatile ("xor %esi, %edi");
asm volatile ("xor %edi, %eax");
asm volatile ("xor %eax, %ecx");
asm volatile ("xor %ecx, %edx");
asm volatile ("xor %edx, %esi");
asm volatile ("xor %esi, %edi");
asm volatile ("xor %edi, %eax");
asm volatile ("xor %eax, %ecx");
asm volatile ("xor %ecx, %edx");
asm volatile ("xor %edx, %esi");
asm volatile ("xor %esi, %edi");
asm volatile ("xor %edi, %eax");
asm volatile ("xor %eax, %ecx");
asm volatile ("xor %ecx, %edx");
asm volatile ("xor %edx, %esi");
asm volatile ("xor %esi, %edi");
asm volatile ("xor %edi, %eax");
asm volatile ("xor %eax, %ecx");
asm volatile ("xor %ecx, %edx");
asm volatile ("xor %edx, %esi");
asm volatile ("xor %esi, %edi");
asm volatile ("xor %edi, %eax");
asm volatile ("xor %eax, %ecx");
asm volatile ("xor %ecx, %edx");
asm volatile ("xor %edx, %esi");
asm volatile ("xor %esi, %edi");
asm volatile ("xor %edi, %eax");
asm volatile ("xor %eax, %ecx");
asm volatile ("xor %ecx, %edx");
asm volatile ("xor %edx, %esi");
asm volatile ("xor %esi, %edi");
asm volatile ("xor %edi, %eax");
asm volatile ("xor %eax, %ecx");
asm volatile ("xor %ecx, %edx");
asm volatile ("xor %edx, %esi");
asm volatile ("xor %esi, %edi");
asm volatile ("xor %edi, %eax");

37
cpu/x86/x86_atomic.c Normal file
View File

@ -0,0 +1,37 @@
/*
* 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-multithreading
* @{
*
* @file
* @brief Atomic arithmetic for the x86 port.
*
* @author René Kijewski <rene.kijewski@fu-berlin.de>
*
* @}
*/
#include "atomic.h"
unsigned int atomic_set_return(unsigned int *val, unsigned int set)
{
asm volatile ("lock xchg %0, %1" : "+m"(*val), "+r"(set));
return set;
}

52
cpu/x86/x86_cmos.c Normal file
View File

@ -0,0 +1,52 @@
/*
* 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 Accessing the CMOS space of the BIOS.
*
* @author René Kijewski <rene.kijewski@fu-berlin.de>
*
* @}
*/
#include "x86_cmos.h"
uint8_t x86_cmos_read(int reg)
{
outb(CMOS_ADDRESS, reg);
io_wait();
return inb(CMOS_DATA);
}
void x86_cmos_write(int reg, uint8_t value)
{
outb(CMOS_ADDRESS, reg);
io_wait();
outb(CMOS_DATA, value);
}
void x86_cmos_serial(uint8_t (*serial)[CMOS_SERIAL_LEN])
{
for (unsigned i = 0; i < CMOS_SERIAL_LEN; ++i) {
(*serial)[i] = x86_cmos_read(CMOS_SERIAL_OFFS + i);
}
}

137
cpu/x86/x86_glue.c Normal file
View File

@ -0,0 +1,137 @@
/*
* 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 The interface between newlib and kernel functions.
*
* @author René Kijewski <rene.kijewski@fu-berlin.de>
*
* @}
*/
#include "attributes.h"
#include "cpu.h"
#include "kernel_internal.h"
#include "sched.h"
#include "x86_uart.h"
#include <signal.h>
#include <sys/stat.h>
#include <unistd.h>
void _exit(int status)
{
(void) status;
sched_task_exit();
UNREACHABLE();
}
#ifndef MODULE_POSIX
/* Already defined in /sys/posix/unistd.c */
int close(int fildes)
{
/* TODO */
(void) fildes;
return -1;
}
#endif
pid_t getpid(void)
{
return sched_active_pid;
}
int fstat(int fildes, struct stat *buf)
{
/* TODO */
(void) fildes;
(void) buf;
return -1;
}
int isatty(int fildes)
{
/* TODO */
(void) fildes;
return 0; /* sic */
}
int kill(pid_t pid, int sig)
{
/* TODO */
(void) pid;
(void) sig;
return -1;
}
off_t lseek(int fildes, off_t offset, int whence)
{
/* TODO */
(void) fildes;
(void) offset;
(void) whence;
return (off_t) -1;
}
ssize_t read(int fildes, void *buf, size_t nbyte)
{
if (nbyte == 0) {
/* allow reading nothing from every FD ... */
return 0;
}
else if (fildes == STDOUT_FILENO || fildes == STDERR_FILENO) {
/* cannot read from STDOUT or STDERR */
return -1;
}
else if (fildes == STDIN_FILENO) {
return x86_uart_read(buf, nbyte);
}
/* TODO: find appropriate FILE */
(void) fildes;
(void) buf;
(void) nbyte;
return -1;
}
ssize_t write(int fildes, const void *buf, size_t nbyte)
{
if (nbyte == 0) {
/* allow writing nothing to every FD ... */
return 0;
}
else if (fildes == STDIN_FILENO) {
/* cannot write to STDIN */
return -1;
}
else if (fildes == STDOUT_FILENO || fildes == STDERR_FILENO) {
return x86_uart_write(buf, nbyte);
}
/* TODO: find appropriate FILE */
(void) fildes;
(void) buf;
(void) nbyte;
return -1;
}

540
cpu/x86/x86_hwtimer.c Normal file
View File

@ -0,0 +1,540 @@
/*
* 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-irq
* @{
*
* @file
* @brief Longterm and shortterm timeout handler using callbacks.
*
* @author René Kijewski <rene.kijewski@fu-berlin.de>
*
* @}
*/
#include "hwtimer_cpu.h"
#include "x86_hwtimer.h"
#include "x86_rtc.h"
#include "x86_threading.h"
#include "hwtimer_arch.h"
#include "irq.h"
#include "thread.h"
#include <stdint.h>
#include <stdio.h>
#define ENABLE_DEBUG (0)
#include "debug.h"
#define US_PER_SECOND (1000l * 1000l)
#define START_MAX_US_PREMATURELY (US_PER_SECOND / 2048)
#define NNN_TS_ITERATIONS (1024)
#define NNN_TICK_ITERATIONS (16)
#define TICK_HZ_VAL (256)
#define TICK_HZ_REG_A (RTC_REG_A_HZ_256)
#define ASM_FUN_ATTRIBUTES \
__attribute__((noinline)) \
__attribute__((no_instrument_function)) \
__attribute__((optimize("Os", "omit-frame-pointer")))
static bool set_next_alarm(bool may_call);
static uint64_t ts_base;
static x86_rtc_data_t rtc_base;
static uint64_t ts_per_nop_nop_nop;
static uint64_t nop_nop_nops_per_tick;
static uint64_t nop_nop_nops_per_second;
static uint64_t instructions_per_second;
static void ASM_FUN_ATTRIBUTES nop_nop_nop(void)
{
# include "nop_nop_nop.inc"
}
static uint64_t ASM_FUN_ATTRIBUTES rdtsc(void)
{
uint64_t result;
asm volatile ("cpuid" :: "a"(0) : "ebx", "ecx", "edx");
asm volatile ("rdtsc" : "=A"(result));
return result;
}
static volatile bool periodic_interrupt_called;
static void flip_periodic_interrupt_called(uint8_t reg_c)
{
(void) reg_c;
periodic_interrupt_called = !periodic_interrupt_called;
}
static void measure_nop_nop_nops_per_tick(void)
{
x86_rtc_set_periodic_callback(flip_periodic_interrupt_called);
x86_rtc_set_periodic(TICK_HZ_REG_A, 0, 0, true);
for (unsigned i = 0; i < NNN_TICK_ITERATIONS; ++i) {
periodic_interrupt_called = false;
eINT();
while (!periodic_interrupt_called) {
nop_nop_nop();
}
while (periodic_interrupt_called) {
nop_nop_nop();
}
uint64_t counting_start = rdtsc();
while (!periodic_interrupt_called) {
++nop_nop_nops_per_tick;
nop_nop_nop();
}
dINT();
uint64_t counting_end = rdtsc();
ts_per_nop_nop_nop += counting_end - counting_start;
}
x86_rtc_set_periodic_callback(NULL);
x86_rtc_set_periodic(RTC_REG_A_HZ_OFF, 0, -1, false);
/* instructions_per_second = nop_nop_nops_per_second * ts_per_nop_nop_nop: */
instructions_per_second = nop_nop_nops_per_tick * TICK_HZ_VAL * ts_per_nop_nop_nop / nop_nop_nops_per_tick / NNN_TICK_ITERATIONS;
/* nop_nop_nops_per_second = nop_nop_nops_per_tick / TICK_HZ_VAL: */
nop_nop_nops_per_second = (nop_nop_nops_per_tick * TICK_HZ_VAL) / NNN_TICK_ITERATIONS;
ts_per_nop_nop_nop /= nop_nop_nops_per_tick;
nop_nop_nops_per_tick /= NNN_TICK_ITERATIONS;
}
static void update_cb(uint8_t reg_c)
{
periodic_interrupt_called = reg_c & RTC_REG_C_IRQ_UPDATE;
DEBUG("DEBUG update_cb(0x%02hhx): periodic_interrupt_called = %u\n", reg_c, periodic_interrupt_called);
}
static void init_bases(void)
{
x86_rtc_set_periodic_callback(update_cb);
x86_rtc_set_periodic(RTC_REG_A_HZ_2, 0, 0, true);
eINT();
periodic_interrupt_called = false;
while (!periodic_interrupt_called) {
asm volatile ("hlt");
}
dINT();
x86_rtc_read(&rtc_base);
ts_base = rdtsc();
printf(" %02hhu:%02hhu:%02hhu, %04u-%02hhu-%02hhu is %llu\n",
rtc_base.hour, rtc_base.minute, rtc_base.second,
rtc_base.century * 100 + rtc_base.year, rtc_base.month, rtc_base.day,
ts_base);
x86_rtc_set_periodic_callback(NULL);
x86_rtc_set_periodic(RTC_REG_A_HZ_OFF, 0, -1, false);
}
void x86_init_hwtimer(void)
{
puts("Measuring CPU speed:");
measure_nop_nop_nops_per_tick();
printf(" CPU speed = %.3f MHz\n", instructions_per_second / (1024.f * 1024));
init_bases();
}
static void (*hwtimer_callback)(int timer);
static char hwtimer_stack[KERNEL_CONF_STACKSIZE_DEFAULT];
static int hwtimer_pid = -1;
struct alarm_time {
uint64_t ts_absolute_alarm;
bool enabled;
struct alarm_time *next, *prev;
};
static struct alarm_time timers[HWTIMER_MAXTIMERS];
static struct alarm_time *timers_start;
static bool hwtimer_ie;
static unsigned rtc_alarm_ie, rtc_update_ie, rtc_periodic_ie;
static bool timer_unlink(unsigned i)
{
bool do_yield = false;
bool was_start = timers_start == &timers[i];
DEBUG("DEBUG timer_unlink(%u): was_start=%u\n", i, was_start);
if (timers[i].next) {
timers[i].next->prev = timers[i].prev;
}
if (timers[i].prev) {
timers[i].prev->next = timers[i].next;
}
if (was_start) {
timers_start = timers[i].next;
}
timers[i].next = timers[i].prev = NULL;
if (was_start && timers[i].enabled) {
do_yield = set_next_alarm(false);
}
timers[i].enabled = false;
return do_yield;
}
static void hwtimer_tick_handler(void)
{
msg_t msg_array[2];
msg_init_queue(msg_array, sizeof (msg_array) / sizeof (*msg_array));
while (1) {
msg_t m;
msg_receive(&m);
dINT();
set_next_alarm(true);
eINT();
}
}
static void stop_alarms(void)
{
DEBUG("DEBUG stop_alarms(): AIE=%u, UIE=%u, PIE=%u\n",
rtc_alarm_ie, rtc_update_ie, rtc_periodic_ie);
if (rtc_alarm_ie) {
rtc_alarm_ie = 0;
x86_rtc_set_alarm(NULL, 0, -1u, false);
}
if (rtc_update_ie) {
rtc_update_ie = 0;
x86_rtc_set_update(0, -1u, false);
}
if (rtc_periodic_ie) {
rtc_periodic_ie = 0;
x86_rtc_set_periodic(RTC_REG_A_HZ_OFF, 0, -1u, false);
}
}
static bool set_next_alarm(bool may_call)
{
if (!hwtimer_ie) {
return false;
}
while (timers_start) {
uint64_t ts_now = rdtsc();
int64_t ts_future = (int64_t) timers_start->ts_absolute_alarm - (int64_t) ts_now;
/* prevent overflows */
int64_t us_future = ts_future;
us_future *= (int64_t) (US_PER_SECOND / 0x1000);
us_future /= (int64_t) (instructions_per_second / 0x1000);
unsigned timer_i = timers_start - timers;
DEBUG("DEBUG set_next_alarm(): timers_start=%u, us_future=%lli, ts_future=%lli\n",
timer_i, us_future, ts_future);
if (us_future <= START_MAX_US_PREMATURELY) {
DEBUG(" callback(%u) (%lli µs prematurely), may_call=%u\n",
timer_i, us_future, may_call);
if (!may_call) {
msg_t m;
msg_send_int(&m, hwtimer_pid);
return true;
}
else {
bool do_yield = timer_unlink(timer_i);
eINT();
hwtimer_callback(timer_i);
if (do_yield) {
thread_yield();
}
dINT();
continue;
}
}
us_future -= START_MAX_US_PREMATURELY / 2;
if (us_future > 5 * US_PER_SECOND) {
us_future -= US_PER_SECOND;
int8_t seconds = (us_future / US_PER_SECOND) % 60;
int8_t minutes = (us_future / US_PER_SECOND) / 60 % 60;
int8_t hours = (us_future / US_PER_SECOND) / 60 / 60 % 24;
DEBUG(" setting AIE %02hhu:%02hhu:%02hhu\n", hours, minutes, seconds);
x86_rtc_data_t rtc_now;
x86_rtc_read(&rtc_now);
rtc_now.second += seconds;
if (rtc_now.second >= 60) {
rtc_now.second -= 60;
++minutes;
}
rtc_now.minute += minutes;
if (rtc_now.minute >= 60) {
rtc_now.minute -= 60;
++hours;
}
rtc_now.hour += hours;
if (rtc_now.hour > 24) {
rtc_now.hour -= 24;
}
rtc_alarm_ie = false;
stop_alarms();
x86_rtc_set_alarm(&rtc_now, 0, hwtimer_pid, true);
rtc_alarm_ie = true;
}
else if (us_future > 1 * US_PER_SECOND) {
DEBUG(" setting UIE\n");
rtc_update_ie = false;
stop_alarms();
x86_rtc_set_update(0, hwtimer_pid, true);
rtc_update_ie = true;
}
else {
/* TODO: this has to work without an epic if-else construct */
unsigned hz;
if ((unsigned long) us_future >= 1 * US_PER_SECOND / 2) {
hz = RTC_REG_A_HZ_2;
}
else if ((unsigned long) us_future >= 1 * US_PER_SECOND / 4) {
hz = RTC_REG_A_HZ_4;
}
else if ((unsigned long) us_future >= 1 * US_PER_SECOND / 8) {
hz = RTC_REG_A_HZ_8;
}
else if ((unsigned long) us_future >= 1 * US_PER_SECOND / 16) {
hz = RTC_REG_A_HZ_16;
}
else if ((unsigned long) us_future >= 1 * US_PER_SECOND / 32) {
hz = RTC_REG_A_HZ_32;
}
else if ((unsigned long) us_future >= 1 * US_PER_SECOND / 64) {
hz = RTC_REG_A_HZ_64;
}
else if ((unsigned long) us_future >= 1 * US_PER_SECOND / 128) {
hz = RTC_REG_A_HZ_128;
}
else if ((unsigned long) us_future >= 1 * US_PER_SECOND / 256) {
hz = RTC_REG_A_HZ_256;
}
else if ((unsigned long) us_future >= 1 * US_PER_SECOND / 512) {
hz = RTC_REG_A_HZ_512;
}
else if ((unsigned long) us_future >= 1 * US_PER_SECOND / 1024) {
hz = RTC_REG_A_HZ_1024;
}
else if ((unsigned long) us_future >= 1 * US_PER_SECOND / 2048) {
hz = RTC_REG_A_HZ_2048;
}
else if ((unsigned long) us_future < 1 * US_PER_SECOND / 4096) {
hz = RTC_REG_A_HZ_8192;
}
else {
hz = RTC_REG_A_HZ_4096;
}
if (hz != rtc_periodic_ie) {
DEBUG(" setting PIE reg_c |= 0x%02x\n", hz);
rtc_periodic_ie = false;
stop_alarms();
x86_rtc_set_periodic(hz, 0, hwtimer_pid, true);
rtc_periodic_ie = hz;
}
}
return false;
}
stop_alarms();
return false;
}
void hwtimer_arch_init(void (*handler)(int), uint32_t fcpu)
{
(void) fcpu;
hwtimer_callback = handler;
hwtimer_pid = thread_create(hwtimer_stack,
sizeof (hwtimer_stack),
1,
CREATE_STACKTEST,
hwtimer_tick_handler,
"x86-hwtimer");
hwtimer_ie = true;
}
/* µs since x86_init_hwtimer() */
unsigned long hwtimer_arch_now(void)
{
unsigned old_state = disableIRQ();
uint64_t result = rdtsc();
restoreIRQ(old_state);
return (result - ts_base) * US_PER_SECOND / instructions_per_second;
}
void hwtimer_arch_set(unsigned long offset_us, short timer)
{
unsigned old_state = disableIRQ();
hwtimer_arch_set_absolute(offset_us + hwtimer_arch_now(), timer);
restoreIRQ(old_state);
}
void hwtimer_arch_enable_interrupt(void)
{
bool do_yield = false;
unsigned old_state = disableIRQ();
if (!hwtimer_ie) {
hwtimer_ie = true;
do_yield = set_next_alarm(false);
}
restoreIRQ(old_state);
if (do_yield) {
thread_yield();
}
}
void hwtimer_arch_disable_interrupt(void)
{
unsigned old_state = disableIRQ();
if (hwtimer_ie) {
stop_alarms();
hwtimer_ie = false;
}
restoreIRQ(old_state);
}
void hwtimer_arch_set_absolute(unsigned long value_us_, short timer)
{
unsigned old_state = disableIRQ();
DEBUG("DEBUG hwtimer_arch_set_absolute(%lu, %hu)\n", value_us_, timer);
bool new_top = false;
if (timers[timer].enabled) {
DEBUG(" overwriting timers[%u]\n", timer);
timers[timer].enabled = false;
timer_unlink(timer);
if (timers_start == &timers[timer]) {
new_top = true;
}
}
uint64_t now_ts = rdtsc();
uint64_t now_us = ((now_ts - ts_base) * US_PER_SECOND) / instructions_per_second;
uint64_t future_us = value_us_;
if (value_us_ < now_us) {
future_us += 0x10000ull * 0x10000ull;
}
future_us -= now_us;
uint64_t future_ts = (future_us * instructions_per_second) / US_PER_SECOND;
DEBUG(" future_us=%llu, future_ts=%llu, now_ts=%llu\n", future_us, future_ts, now_ts);
timers[timer].ts_absolute_alarm = future_ts + now_ts;
timers[timer].enabled = true;
if (timers_start) {
struct alarm_time *prev = timers_start;
while (1) {
if (prev->ts_absolute_alarm < timers[timer].ts_absolute_alarm) {
if (prev->next) {
prev = prev->next;
}
else {
prev->next = &timers[timer];
timers[timer].prev = prev;
break;
}
}
else {
timers[timer].next = prev;
timers[timer].prev = prev->prev;
if (timers[timer].prev) {
timers[timer].prev->next = &timers[timer];
}
else {
timers_start = &timers[timer];
new_top = true;
}
prev->prev = &timers[timer];
break;
}
}
}
else {
timers_start = &timers[timer];
new_top = true;
}
bool do_yield = false;
if (new_top) {
do_yield = set_next_alarm(false);
}
restoreIRQ(old_state);
if (do_yield) {
thread_yield();
}
}
void hwtimer_arch_unset(short timer)
{
bool do_yield = false;
unsigned old_state = disableIRQ();
if (timers[timer].enabled) {
bool new_top = timers[timer].prev == NULL;
timers[timer].enabled = false;
timer_unlink(timer);
if (new_top) {
do_yield = set_next_alarm(false);
}
}
restoreIRQ(old_state);
if (do_yield) {
thread_yield();
}
}

431
cpu/x86/x86_interrupts.c Normal file
View File

@ -0,0 +1,431 @@
/*
* 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-irq
* @{
*
* @file
* @brief Interrupt entry points for x86.
*
* @author René Kijewski <rene.kijewski@fu-berlin.de>
*
* @}
*/
#include "x86_interrupts.h"
#include "x86_memory.h"
#include "x86_ports.h"
#include "x86_registers.h"
#include "x86_threading.h"
#include <attributes.h>
#include <cpu.h>
#include <sched.h>
#include <thread.h>
#include <ucontext.h>
#include <stdint.h>
#include <stdio.h>
#define ASM_FUN_ATTRIBUTES \
__attribute__((noinline)) \
__attribute__((no_instrument_function)) \
__attribute__((optimize("Os", "omit-frame-pointer")))
static const char *const exception_name[X86_MAX_INTERRUPTS] = {
[0x00] = "Divide by zero",
[0x01] = "Debug",
[0x02] = "Non-maskable Interrupt",
[0x03] = "Breakpoint",
[0x04] = "Overflow",
[0x05] = "Bound range",
[0x06] = "Invalid Opcode",
[0x07] = "Device not available",
[0x08] = "Double Fault",
[0x09] = NULL,
[0x0a] = NULL,
[0x0b] = NULL,
[0x0c] = NULL,
[0x0d] = "General Protection Fault",
[0x0e] = "Page Fault",
[0x0f] = NULL,
[0x10] = "x87 Floating-Point Exception",
[0x11] = "Alignment Check",
[0x12] = "Machine Check",
[0x13] = NULL,
[0x14] = NULL,
[0x15] = NULL,
[0x16] = NULL,
[0x17] = NULL,
[0x18] = NULL,
[0x19] = NULL,
[0x1a] = NULL,
[0x1b] = NULL,
[0x1c] = NULL,
[0x1d] = NULL,
[0x1e] = NULL,
[0x1f] = NULL,
[0x20] = "PIC PIT",
[0x21] = "PIC KBC1",
[0x22] = NULL,
[0x23] = "PIC RS232 2/4",
[0x24] = "PIC RS232 1/3",
[0x25] = "PIC LPT2",
[0x26] = "PIC floppy",
[0x27] = "PIC LPT1",
[0x28] = "PIC RTC",
[0x29] = "PIC #9",
[0x2a] = "PIC ATA4",
[0x2b] = "PIC ATA3",
[0x2c] = "PIC KBC2",
[0x2d] = NULL,
[0x2e] = "PIC ATA1",
[0x2f] = "PIC ATA2",
};
uint8_t x86_current_interrupt;
unsigned long x86_current_interrupt_error_code;
struct x86_pushad x86_interrupted_ctx;
char x86_interrupt_handler_stack[2048];
static x86_intr_handler_t x86_registered_interrupts[X86_MAX_INTERRUPTS];
static void print_stacktrace(unsigned long bp, unsigned long ip)
{
puts("<stack trace>");
printf(" %08lx\n", ip);
for (unsigned max_depth = 0; max_depth < 30; ++max_depth) {
uint64_t pte = x86_get_pte(bp) & x86_get_pte(bp + 4);
if (!(pte & PT_P) || !(pte & PT_PWT)) {
puts(" ???");
break;
}
unsigned long *bp_ = (void *) bp;
ip = bp_[1];
if (ip == 0) {
break;
}
printf(" %08lx\n", ip);
bp = bp_[0];
if (bp == 0) {
break;
}
}
puts("</stack trace>");
}
void x86_print_registers(struct x86_pushad *orig_ctx, unsigned long error_code)
{
unsigned long *sp = (void *) orig_ctx->sp; /* ip, cs, flags */
printf("EAX=%08lx ECX=%08lx EDX=%08lx EBX=%08lx\n", orig_ctx->ax, orig_ctx->cx, orig_ctx->dx, orig_ctx->bx);
printf("ESP=%08lx EBP=%08lx ESI=%08lx EDI=%08lx\n", orig_ctx->sp, orig_ctx->bp, orig_ctx->si, orig_ctx->di);
printf("Error code=%08lx\n", error_code);
printf("CR0=%08x CR2=%08x CR3=%08x CR4=%08x\n", cr0_read(), cr2_read(), cr3_read(), cr4_read());
printf("EIP=%04lx:%08lx EFLAGS=%08lx\n", sp[1], sp[0], sp[2]);
print_stacktrace(orig_ctx->bp, sp[0]);
}
static void intr_handler_default(uint8_t intr_num, struct x86_pushad *orig_ctx, unsigned long error_code)
{
printf("Unhandled interrupt 0x%02x (%s)\n", intr_num, exception_name[intr_num]);
x86_print_registers(orig_ctx, error_code);
puts("Halting.");
x86_hlt();
}
void x86_int_exit(void) NORETURN;
static void continue_after_intr(void)
{
ucontext_t *ctx = (ucontext_t *) sched_active_thread->sp;
x86_interrupted_ctx = ctx->uc_context.registers;
asm volatile (
"push %0\n" /* flags */
"push $0x0008\n" /* cs */
"push %1\n" /* ip */
"jmp x86_int_exit"
:: "g"(ctx->__intr.flags), "g"(ctx->__intr.ip)
);
__builtin_unreachable();
}
static unsigned in_intr_handler = 0, old_intr;
void x86_int_handler(void)
{
switch (in_intr_handler++) {
case 0:
break;
case 1:
printf("Interrupt 0x%02x (%s) while handling 0x%02x (%s)\n",
x86_current_interrupt, exception_name[x86_current_interrupt],
old_intr, exception_name[old_intr]);
x86_print_registers(&x86_interrupted_ctx, x86_current_interrupt_error_code);
puts("Halting.");
x86_hlt();
default:
x86_hlt();
}
old_intr = x86_current_interrupt;
bool old_in_isr = x86_in_isr;
x86_in_isr = true;
x86_intr_handler_t intr_handler = x86_registered_interrupts[x86_current_interrupt];
(intr_handler ? intr_handler : intr_handler_default)(x86_current_interrupt, &x86_interrupted_ctx, x86_current_interrupt_error_code);
--in_intr_handler;
unsigned long *sp = (void *) x86_interrupted_ctx.sp; /* ip, cs, flags */
if (!sched_context_switch_request || !(sp[2] & X86_IF)) {
x86_in_isr = old_in_isr;
return;
}
ucontext_t *ctx = (ucontext_t *) sched_active_thread->sp;
ctx->uc_context.registers = x86_interrupted_ctx;
ctx->uc_stack.ss_sp = x86_interrupt_handler_stack;
ctx->uc_stack.ss_size = sizeof x86_interrupt_handler_stack;
asm volatile ("pushf; pop %0" : "=g"(ctx->uc_context.flags));
ctx->uc_context.ip = (void *) (uintptr_t) &continue_after_intr;
ctx->__intr.ip = sp[0];
ctx->__intr.flags = sp[2];
thread_yield();
__builtin_unreachable();
}
void ASM_FUN_ATTRIBUTES NORETURN x86_int_entry(void)
{
asm volatile ("mov %eax, (4*0 + x86_interrupted_ctx)");
asm volatile ("mov %ecx, (4*1 + x86_interrupted_ctx)");
asm volatile ("mov %edx, (4*2 + x86_interrupted_ctx)");
asm volatile ("mov %ebx, (4*3 + x86_interrupted_ctx)");
asm volatile ("mov %ebp, (4*5 + x86_interrupted_ctx)");
asm volatile ("mov %esi, (4*6 + x86_interrupted_ctx)");
asm volatile ("mov %edi, (4*7 + x86_interrupted_ctx)");
asm volatile ("jnc 1f");
asm volatile (" mov (%esp), %eax");
asm volatile (" add $4, %esp");
asm volatile (" jmp 2f");
asm volatile ("1:");
asm volatile (" xor %eax, %eax");
asm volatile ("2:");
asm volatile (" mov %eax, x86_current_interrupt_error_code");
asm volatile ("mov %esp, (4*4 + x86_interrupted_ctx)");
asm volatile ("mov %0, %%esp" :: "g"(&x86_interrupt_handler_stack[sizeof x86_interrupt_handler_stack]));
asm volatile ("call x86_int_handler");
asm volatile ("jmp x86_int_exit");
__builtin_unreachable();
}
void ASM_FUN_ATTRIBUTES NORETURN x86_int_exit(void)
{
asm volatile ("mov (4*0 + x86_interrupted_ctx), %eax");
asm volatile ("mov (4*1 + x86_interrupted_ctx), %ecx");
asm volatile ("mov (4*2 + x86_interrupted_ctx), %edx");
asm volatile ("mov (4*3 + x86_interrupted_ctx), %ebx");
asm volatile ("mov (4*5 + x86_interrupted_ctx), %ebp");
asm volatile ("mov (4*6 + x86_interrupted_ctx), %esi");
asm volatile ("mov (4*7 + x86_interrupted_ctx), %edi");
asm volatile ("mov (4*4 + x86_interrupted_ctx), %esp");
asm volatile ("iret");
__builtin_unreachable();
}
#define DECLARE_INT(NUM, HAS_ERROR_CODE, MNEMONIC) \
static void ASM_FUN_ATTRIBUTES NORETURN x86_int_entry_##NUM##h(void) \
{ \
asm volatile ("movb %0, x86_current_interrupt" :: "n"(0x##NUM)); \
if ((HAS_ERROR_CODE)) { \
asm volatile ("stc"); \
} \
else { \
asm volatile ("clc"); \
}\
asm volatile ("jmp x86_int_entry"); \
__builtin_unreachable(); \
}
DECLARE_INT(00, 0, "#DE")
DECLARE_INT(01, 0, "#DB")
DECLARE_INT(02, 0, "NMI")
DECLARE_INT(03, 0, "#BP")
DECLARE_INT(04, 0, "#OF")
DECLARE_INT(05, 0, "#BR")
DECLARE_INT(06, 0, "#UD")
DECLARE_INT(07, 0, "#NM")
DECLARE_INT(08, 1, "#DF")
DECLARE_INT(0d, 1, "#GP")
DECLARE_INT(0e, 1, "#PF")
DECLARE_INT(10, 0, "#MF")
DECLARE_INT(11, 1, "#AC")
DECLARE_INT(12, 0, "#MC")
DECLARE_INT(20, 0, "PIC PIT")
DECLARE_INT(21, 1, "PIC KBC1")
DECLARE_INT(23, 0, "PIC RS232 2/4")
DECLARE_INT(24, 0, "PIC RS232 1/3")
DECLARE_INT(25, 0, "PIC LPT2")
DECLARE_INT(26, 0, "PIC floppy")
DECLARE_INT(27, 0, "PIC LPT1")
DECLARE_INT(28, 0, "PIC RPC")
DECLARE_INT(29, 0, "PIC #9")
DECLARE_INT(2a, 0, "PIC ATA4")
DECLARE_INT(2b, 0, "PIC ATA3")
DECLARE_INT(2c, 0, "PIC KBC2")
DECLARE_INT(2e, 0, "PIC ATA1")
DECLARE_INT(2f, 0, "PIC ATA2")
static struct idt_desc X86_IDT_ENTRIES[X86_MAX_INTERRUPTS];
static struct idtr_t idtr = {
.limit = sizeof X86_IDT_ENTRIES,
.base = &X86_IDT_ENTRIES[0],
};
#define INTR_TEST_REG_AX (0xAA00AA00ul)
#define INTR_TEST_REG_DX (0x00DD00DDul)
#define INTR_TEST_REG_CX (0xC0C0C0C0ul)
#define INTR_TEST_REG_BX (0x0B0B0B0Bul)
#define INTR_TEST_REG_SI (0x00666600ul)
#define INTR_TEST_REG_DI (0x33000033ul)
static void intr_handler_test_int_bp(uint8_t intr_num, struct x86_pushad *orig_ctx, unsigned long error_code)
{
(void) error_code;
if (intr_num != X86_INT_BP ||
orig_ctx->ax != INTR_TEST_REG_AX ||
orig_ctx->dx != INTR_TEST_REG_DX ||
orig_ctx->cx != INTR_TEST_REG_CX ||
orig_ctx->bx != INTR_TEST_REG_BX ||
orig_ctx->si != INTR_TEST_REG_SI ||
orig_ctx->di != INTR_TEST_REG_DI
) {
puts("Interrupt handler test failed (int 3, capture registers).");
x86_hlt();
}
orig_ctx->ax ^= -1;
orig_ctx->dx ^= -2;
orig_ctx->cx ^= -3;
orig_ctx->bx ^= -4;
orig_ctx->si ^= -5;
orig_ctx->di ^= -6;
}
static void test_int_bp(void)
{
x86_registered_interrupts[X86_INT_BP] = intr_handler_test_int_bp;
unsigned long ax = INTR_TEST_REG_AX;
unsigned long dx = INTR_TEST_REG_DX;
unsigned long cx = INTR_TEST_REG_CX;
unsigned long bx = INTR_TEST_REG_BX;
unsigned long si;
unsigned long di;
unsigned long eflags_before, eflags_after;
asm volatile (
"mov %8, %%esi\n"
"mov %9, %%edi\n"
"pushf; pop %6\n"
"int3\n"
"pushf; pop %7\n"
"mov %%esi, %4\n"
"mov %%edi, %5\n"
: "+a"(ax), "+d"(dx), "+c"(cx), "+b"(bx), "=g"(si), "=g"(di), "=g"(eflags_before), "=g"(eflags_after)
: "n"(INTR_TEST_REG_SI), "n"(INTR_TEST_REG_DI)
: "esi", "edi"
);
if (ax != (INTR_TEST_REG_AX ^ -1) ||
dx != (INTR_TEST_REG_DX ^ -2) ||
cx != (INTR_TEST_REG_CX ^ -3) ||
bx != (INTR_TEST_REG_BX ^ -4) ||
si != (INTR_TEST_REG_SI ^ -5) ||
di != (INTR_TEST_REG_DI ^ -6) ||
/* ignore EFLAGS.TF, hurts debugging */
((eflags_before != eflags_after) && ((eflags_before ^ eflags_after) != X86_TF))
) {
puts("Interrupt handler test failed (int 3, return code).");
x86_hlt();
}
x86_registered_interrupts[X86_INT_BP] = NULL;
}
static inline void __attribute__((always_inline)) set_idt_desc(void (*fun_)(void), unsigned num, unsigned pl)
{
uintptr_t fun = (uintptr_t) fun_;
X86_IDT_ENTRIES[num] = (struct idt_desc) {
.offset_1 = fun & 0xffff,
.selector = 8,
.zero = 0,
.type_attr = 14 | (0 << 4) | (pl << 5) | (1 << 7),
.offset_2 = fun >> 16,
};
}
#define SET_IDT_DESC(NUM, HAS_ERROR_CODE, MNEMONIC, PL) \
(set_idt_desc(x86_int_entry_##NUM##h, 0x##NUM, PL));
static void load_interrupt_descriptor_table(void)
{
SET_IDT_DESC(00, 0, "#DE", 0)
SET_IDT_DESC(01, 0, "#DB", 0)
SET_IDT_DESC(02, 0, "NMI", 0)
SET_IDT_DESC(03, 0, "#BP", 3)
SET_IDT_DESC(04, 0, "#OF", 0)
SET_IDT_DESC(05, 0, "#BR", 0)
SET_IDT_DESC(06, 0, "#UD", 0)
SET_IDT_DESC(07, 0, "#NM", 0)
SET_IDT_DESC(08, 1, "#DF", 0)
SET_IDT_DESC(0d, 1, "#GP", 0)
SET_IDT_DESC(0e, 1, "#PF", 0)
SET_IDT_DESC(10, 0, "#MF", 0)
SET_IDT_DESC(11, 1, "#AC", 0)
SET_IDT_DESC(12, 0, "#MC", 0)
SET_IDT_DESC(20, 0, "PIC PIT", 0)
SET_IDT_DESC(21, 1, "PIC KBC1", 0)
SET_IDT_DESC(23, 0, "PIC RS232 2/4", 0)
SET_IDT_DESC(24, 0, "PIC RS232 1/3", 0)
SET_IDT_DESC(25, 0, "PIC LPT2", 0)
SET_IDT_DESC(26, 0, "PIC floppy", 0)
SET_IDT_DESC(27, 0, "PIC LPT1", 0)
SET_IDT_DESC(28, 0, "PIC RPC", 0)
SET_IDT_DESC(29, 0, "PIC #9", 0)
SET_IDT_DESC(2a, 0, "PIC ATA4", 0)
SET_IDT_DESC(2b, 0, "PIC ATA3", 0)
SET_IDT_DESC(2c, 0, "PIC KBC2", 0)
SET_IDT_DESC(2e, 0, "PIC ATA1", 0)
SET_IDT_DESC(2f, 0, "PIC ATA2", 0)
asm volatile ("lidt %0" :: "m"(idtr));
}
void x86_init_interrupts(void)
{
load_interrupt_descriptor_table();
test_int_bp();
puts("Interrupt handling initialized");
}
void x86_interrupt_handler_set(unsigned num, x86_intr_handler_t handler)
{
x86_registered_interrupts[num] = handler;
}

457
cpu/x86/x86_memory.c Normal file
View File

@ -0,0 +1,457 @@
/*
* 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 Virtual memory management.
*
* @author René Kijewski <rene.kijewski@fu-berlin.de>
*
* @}
*/
#include "x86_kernel_memory.h"
#include "x86_interrupts.h"
#include "x86_memory.h"
#include "x86_registers.h"
#include "cpu.h"
#include "irq.h"
#include "tlsf-malloc.h"
#include <malloc.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
/* Compare Figure 44 (p. 99) of "Intel® Quark SoC X1000 Core Developers Manual" */
#define PT_CR3_BITS (0)
#define PT_PDPT_BITS (PT_P)
#define PT_PD_BITS (PT_P | PT_RW | PT_US)
#define PT_HEAP_BITS (PT_P | PT_RW | PT_US | pt_xd)
static uint64_t pt_xd = PT_XD;
typedef union page {
char content[4096];
uint64_t next_page;
uint64_t indices[512];
} __attribute__((aligned(0x1000))) page_t;
static volatile page_t TEMP_PAGE;
#define TEMP_PAGE_PT (((uintptr_t) &TEMP_PAGE / 0x1000) / 512)
#define TEMP_PAGE_PTE (((uintptr_t) &TEMP_PAGE / 0x1000) % 512)
void x86_init_gdt(void)
{
static const struct gdt_entry gdt_entries[3] = {
[0x0000 / 8] = {
.limit_0_15 = 0,
.base_0_15 = 0,
.base_16_23 = 0,
.access_byte = 0,
.limit_16_19_and_flags = 0,
.base_24_31 = 0,
},
[0x0008 / 8] = {
.limit_0_15 = 0xFFFF,
.base_0_15 = 0,
.base_16_23 = 0,
.access_byte = GDT_AB_EX | GDT_AB_S | GDT_AB_RING_0 | GDT_AB_PR,
.limit_16_19_and_flags = 0xF | GDT_FLAG_SZ | GDT_FLAG_GR,
.base_24_31 = 0,
},
[0x0010 / 8] = {
.limit_0_15 = 0xFFFF,
.base_0_15 = 0,
.base_16_23 = 0,
.access_byte = GDT_AB_RW | GDT_AB_S | GDT_AB_RING_0 | GDT_AB_PR,
.limit_16_19_and_flags = 0xF | GDT_FLAG_SZ | GDT_FLAG_GR,
.base_24_31 = 0,
},
};
static const struct gdtr_t gdt = {
.size_minus_one = sizeof gdt_entries - 1,
.offset = (unsigned long) &gdt_entries[0],
};
asm volatile ("" :: "a"(0x0010));
asm volatile ("lgdt %0" :: "m"(gdt));
asm volatile ("ljmp $0x0008, $1f\n"
"1:");
asm volatile ("mov %ax, %ds");
asm volatile ("mov %ax, %es");
asm volatile ("mov %ax, %fs");
asm volatile ("mov %ax, %gs");
asm volatile ("mov %ax, %ss");
}
/* Addresses in PDPT, PD, and PT are linear addresses. */
/* TEMP_PAGE is used to to access these pages. */
static pae_page_table_t static_pts[X86_NUM_STATIC_PT];
static pae_page_directory_t static_pds[X86_NUM_STATIC_PD];
static pae_page_directory_pointer_table_t pdpt;
static void init_elf_static_section(uint64_t bits, void *start_, void *end_)
{
unsigned long start = ((unsigned long) start_) / 0x1000;
unsigned long end = (((unsigned long) end_) + 0x1000 - 1) / 0x1000;
for (unsigned i = start; i < end; ++i) {
unsigned pt_num = i / 512;
unsigned pte_num = i % 512;
static_pts[pt_num][pte_num] = (i * 0x1000) | PT_P | PT_G | bits;
}
}
static void check_requirements(void)
{
uint64_t cpuid = cpuid_caps();
if ((cpuid & CPUID_PAE) == 0) {
puts("Your CPU does not support PAE! Halting.");
x86_hlt();
}
if ((cpuid & CPUID_PGE) == 0) {
puts("Your CPU does not support PGE! Halting.");
x86_hlt();
}
if ((cpuid & CPUID_MSR) == 0) {
puts("Warning: Your CPU does not support MSR!\n"
" Setting PT_XD = 0.");
pt_xd = 0;
}
else {
/* enable NX bit (if possible) */
uint64_t efer = msr_read(MSR_EFER);
efer |= EFER_NXE;
msr_set(MSR_EFER, efer);
if (!(msr_read(MSR_EFER) & EFER_NXE)) {
puts("Warning: Your hardware does not support the NX bit!\n"
" Setting PT_XD = 0.");
pt_xd = 0;
}
}
}
static void init_pagetable(void)
{
/* identity map tables */
for (unsigned i = 0; i < X86_NUM_STATIC_PD; ++i) {
pdpt[i] = ((uintptr_t) &static_pds[i]) | PT_PDPT_BITS;
}
for (unsigned i = 0; i < X86_NUM_STATIC_PT; ++i) {
unsigned pd_num = i / 512;
unsigned pt_num = i % 512;
static_pds[pd_num][pt_num] = ((uintptr_t) &static_pts[i]) | PT_PD_BITS;
}
init_elf_static_section(PT_RW | pt_xd, (void *) 0, (void *) 0x100000);
init_elf_static_section(PT_US, &_section_text_start, &_section_text_end);
init_elf_static_section(PT_US | pt_xd, &_section_rodata_start, &_section_rodata_end);
init_elf_static_section(PT_US | PT_RW | pt_xd, &_section_data_start, &_section_bss_end);
/* activate PAE */
/* FIXME: add x86_init_cr4() */
uint32_t cr4 = cr4_read();
cr4 |= CR4_PAE | CR4_MCE | CR4_PGE | CR4_PCE | CR4_OSXMMEXCPT;
cr4 &= ~(CR4_VME | CR4_PVI | CR4_TSD | CR4_DE | CR4_PSE | CR4_OSFXSR | CR4_SMEP);
cr4_write(cr4);
/* load page table */
cr3_write((uint32_t) &pdpt | PT_CR3_BITS);
/* activate paging */
uint32_t cr0 = cr0_read();
cr0 |= CR0_PE | CR0_NE | CR0_WP | CR0_PG;
cr0 &= ~(CR0_MP | CR0_EM | CR0_TS | CR0_AM | CR0_NW | CR0_CD);
cr0_write(cr0);
}
static void set_temp_page(uint64_t addr)
{
static_pts[TEMP_PAGE_PT][TEMP_PAGE_PTE] = addr != -1ull ? addr | PT_P | PT_RW | pt_xd : 0;
asm volatile ("invlpg (%0)" :: "r"(&TEMP_PAGE));
}
static inline uint64_t min64(uint64_t a, uint64_t b)
{
return a <= b ? a : b;
}
static inline uint64_t max64(uint64_t a, uint64_t b)
{
return a >= b ? a : b;
}
static uint32_t init_free_pages_heap_position = (uintptr_t) &_heap_start;
static uint64_t init_free_pages_sub(uint64_t table, uint64_t bits, unsigned index, uint64_t *start, uint64_t *pos)
{
set_temp_page(table);
if (TEMP_PAGE.indices[index] & PT_P) {
return TEMP_PAGE.indices[index] & PT_ADDR_MASK;
}
TEMP_PAGE.indices[index] = *start | bits;
uint64_t result = *start;
*start += 0x1000;
*pos = max64(*start, *pos);
init_free_pages_heap_position += 0x1000;
return result;
}
static bool add_pages_to_pool(uint64_t start, uint64_t end)
{
start += 0xFFF;
start &= ~0xFFF;
end &= ~0xFFF;
start = max64(start, (uintptr_t) &_kernel_memory_end);
uint64_t pos = start;
uint32_t addr = init_free_pages_heap_position >> 12;
unsigned pte_i = addr % 512;
addr >>= 9;
unsigned pt_i = addr % 512;
addr >>= 9;
unsigned pd_i = addr;
if (pd_i >= 4) {
return false;
}
while (pos < end) {
uint64_t table = (uintptr_t) &pdpt;
table = init_free_pages_sub(table, PT_PDPT_BITS, pd_i, &start, &pos);
if (pos >= end) {
break;
}
table = init_free_pages_sub(table, PT_PD_BITS, pt_i, &start, &pos);
if (pos >= end) {
break;
}
set_temp_page(table);
TEMP_PAGE.indices[pte_i] = pos | PT_HEAP_BITS;
pos += 0x1000;
if (++pte_i >= 512) {
pte_i = 0;
if (++pt_i >= 512) {
pt_i = 0;
if (++pd_i >= 4) {
break;
}
}
}
}
if (start < end) {
cr3_write((uint32_t) &pdpt | PT_CR3_BITS); /* flush tlb */
tlsf_add_pool((void *) init_free_pages_heap_position, end - start);
init_free_pages_heap_position += end - start;
}
return true;
}
static void init_free_pages(void)
{
printf("Kernel memory: %p - %p\r\n", &_kernel_memory_start, &_kernel_memory_end);
printf(" .text: %p - %p\r\n", &_section_text_start, &_section_text_end);
printf(" .rodata: %p - %p\r\n", &_section_rodata_start, &_section_rodata_end);
printf(" .data: %p - %p\r\n", &_section_data_start, &_section_data_end);
printf(" .bss: %p - %p\r\n", &_section_bss_start, &_section_bss_end);
printf("Unmapped memory: %p - %p\r\n", &_kernel_memory_end, &_heap_start);
printf("Heap start: %p\r\n", &_heap_start);
unsigned long cnt = 0;
uint64_t start, len;
while (x86_get_memory_region(&start, &len, &cnt)) {
uint64_t end = start + len;
if (!add_pages_to_pool(start, end)) {
break;
}
}
unsigned long free_pages_count = (init_free_pages_heap_position - (uintptr_t) &_heap_start) / 4096;
float mem_amount = free_pages_count * (4096 / 1024);
const char *mem_unit = "kB";
if (mem_amount >= 2 * 1024) {
mem_amount /= 1024;
mem_unit = "MB";
}
if (mem_amount >= 2 * 1024) {
mem_amount /= 1024;
mem_unit = "GB";
}
printf("There are %lu free pages (%.3f %s) available for the heap.\n", free_pages_count, mem_amount, mem_unit);
}
static unsigned handling_pf;
static void pagefault_handler(uint8_t intr_num, struct x86_pushad *orig_ctx, unsigned long error_code)
{
(void) intr_num; /* intr_num == X86_INT_PF */
++handling_pf;
switch (handling_pf) {
case 1:
break; /* first #PF */
case 2: /* pagefault while handing a page fault. */
puts("A page fault occured while handling a page fault!");
x86_print_registers(orig_ctx, error_code);
puts("Halting.");
/* fall through */
default: /* pagefault while printing #PF message, everything is lost */
x86_hlt();
}
if (error_code & PF_EC_I) {
puts("Page fault while fetching instruction.");
x86_print_registers(orig_ctx, error_code);
puts("Halting.");
x86_hlt();
}
else if (error_code & PF_EC_P) {
printf("Page fault: access violation while %s present page.\n", error_code & PF_EC_W ? "writing to" : "reading from");
x86_print_registers(orig_ctx, error_code);
puts("Halting.");
x86_hlt();
}
else {
printf("Page fault: access violation while %s non-present page.\n", error_code & PF_EC_W ? "writing to" : "reading from");
x86_print_registers(orig_ctx, error_code);
puts("Halting.");
x86_hlt();
}
--handling_pf;
}
static void init_pagefault_handler(void)
{
x86_interrupt_handler_set(X86_INT_PF, &pagefault_handler);
}
void x86_init_memory(void)
{
check_requirements();
init_pagetable();
init_free_pages();
init_pagefault_handler();
puts("Virtual memory initialized");
}
uint64_t x86_get_pte(uint32_t addr)
{
addr >>= 12;
unsigned pte_i = addr % 512;
addr >>= 9;
unsigned pt_i = addr % 512;
addr >>= 9;
unsigned pd_i = addr;
if (pdpt[pd_i] & PT_P) {
set_temp_page(pdpt[pd_i] & PT_ADDR_MASK);
if (TEMP_PAGE.indices[pt_i] & PT_P) {
set_temp_page(TEMP_PAGE.indices[pt_i] & PT_ADDR_MASK);
return TEMP_PAGE.indices[pte_i];
}
}
return NO_PTE;
}
static void virtual_pages_set_bits(uint32_t virtual_addr, unsigned pages, uint64_t bits)
{
while (pages-- > 0) {
unsigned pte_i = (virtual_addr >> 12) % 512;
uint64_t old_physical_addr = x86_get_pte(virtual_addr) & PT_ADDR_MASK;
TEMP_PAGE.indices[pte_i] = old_physical_addr | bits;
asm volatile ("invlpg (%0)" :: "r"(virtual_addr));
virtual_addr += 0x1000;
}
}
void *x86_map_physical_pages(uint64_t physical_start, unsigned pages, uint64_t bits)
{
if (bits & PT_XD) {
bits &= ~PT_XD;
bits |= pt_xd;
}
/* We use an already set up space, so we are sure that the upper level page tables are allocated. */
/* We cut out a slice and re-add the physical pages. */
char *result = memalign(0x1000, pages * 0x1000);
if (!result) {
return NULL;
}
for (unsigned page = 0; page < pages; ++page) {
uint64_t physical_addr = physical_start + page * 0x1000;
uint32_t virtual_addr = (uintptr_t) result + page * 0x1000;
unsigned pte_i = (virtual_addr >> 12) % 512;
uint64_t old_pte = x86_get_pte(virtual_addr);
TEMP_PAGE.indices[pte_i] = physical_addr | bits;
if (page == 0) {
uint64_t old_physical_addr = old_pte & PT_ADDR_MASK;
/* FIXME: does this work? Won't work if TLSF joins different buffers. */
add_pages_to_pool(old_physical_addr, old_physical_addr + 0x1000 * pages);
}
}
cr3_write((uint32_t) &pdpt | PT_CR3_BITS); /* flush tlb */
return result;
}
void *x86_get_virtual_pages(unsigned pages, uint64_t bits)
{
if (bits & PT_XD) {
bits &= ~PT_XD;
bits |= pt_xd;
}
char *result = memalign(0x1000, pages * 0x1000);
if (!result) {
return (void *) -1ul;
}
virtual_pages_set_bits((uintptr_t) result, pages, bits);
return result;
}
void x86_release_virtual_pages(uint32_t virtual_start, unsigned pages)
{
virtual_pages_set_bits(virtual_start, pages, PT_HEAP_BITS);
free((void *) virtual_start);
}

331
cpu/x86/x86_pci.c Normal file
View File

@ -0,0 +1,331 @@
/*
* 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-irq
* @{
*
* @file
* @brief PCI configuration and accessing.
*
* @author René Kijewski <rene.kijewski@fu-berlin.de>
*
* @}
*/
#include "x86_memory.h"
#include "x86_pci.h"
#include "x86_pci_init.h"
#include "x86_pci_strings.h"
#include "x86_ports.h"
#include <malloc.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static struct x86_known_pci_device **known_pci_devices = NULL;
static unsigned num_known_pci_devices;
static void set_addr(unsigned bus, unsigned dev, unsigned fun, unsigned reg)
{
unsigned addr = PCI_IO_ENABLE
| (bus << PCI_IO_SHIFT_BUS)
| (dev << PCI_IO_SHIFT_DEV)
| (fun << PCI_IO_SHIFT_FUN)
| (reg << PCI_IO_SHIFT_REG);
outl(PCI_CONFIG_ADDRESS, addr);
}
uint32_t x86_pci_read(unsigned bus, unsigned dev, unsigned fun, unsigned reg)
{
set_addr(bus, dev, fun, reg);
return U32_PCItoH(inl(PCI_CONFIG_DATA));
}
uint8_t x86_pci_read8(unsigned bus, unsigned dev, unsigned fun, unsigned reg)
{
set_addr(bus, dev, fun, reg);
return U8_PCItoH(inb(PCI_CONFIG_DATA));
}
uint16_t x86_pci_read16(unsigned bus, unsigned dev, unsigned fun, unsigned reg)
{
set_addr(bus, dev, fun, reg);
return U16_PCItoH(inw(PCI_CONFIG_DATA));
}
static bool pci_vendor_id_valid(uint16_t vendor_id)
{
return vendor_id != PCI_HEADER_VENDOR_ID_INVALID &&
vendor_id != PCI_HEADER_VENDOR_ID_UNRESPONSIVE &&
vendor_id != PCI_HEADER_VENDOR_ID_ABSENT;
}
void x86_pci_write(unsigned bus, unsigned dev, unsigned fun, unsigned reg, uint32_t datum)
{
set_addr(bus, dev, fun, reg);
outl(PCI_CONFIG_DATA, U32_HtoPCI(datum));
}
void x86_pci_write8(unsigned bus, unsigned dev, unsigned fun, unsigned reg, uint8_t datum)
{
set_addr(bus, dev, fun, reg);
outb(PCI_CONFIG_DATA, U8_HtoPCI(datum));
}
void x86_pci_write16(unsigned bus, unsigned dev, unsigned fun, unsigned reg, uint16_t datum)
{
set_addr(bus, dev, fun, reg);
outw(PCI_CONFIG_DATA, U16_HtoPCI(datum));
}
static unsigned pci_init_secondary_bus(unsigned bus, unsigned dev, unsigned fun)
{
known_pci_devices[num_known_pci_devices - 1]->managed = true;
/* TODO */
printf(" TODO: pci_init_secondary_bus(0x%x, 0x%x, 0x%x)\n", bus, dev, fun);
(void) bus;
(void) dev;
(void) fun;
return 0;
}
static void pci_setup_ios(struct x86_known_pci_device *dev)
{
/* § 6.2.5. (pp. 224) */
unsigned bar_count = 0;
unsigned bar_base;
unsigned header_type = x86_pci_read_reg(0x0c, dev->bus, dev->dev, dev->fun).header_type & PCI_HEADER_TYPE_MASK;
switch (header_type) {
case PCI_HEADER_TYPE_GENERAL_DEVICE:
bar_count = 6;
bar_base = 0x10;
break;
case PCI_HEADER_TYPE_BRIDGE:
bar_count = 2;
bar_base = 0x10;
break;
default:
printf(" Cannot configure header_type == 0x%02x, yet.\n", header_type);
return;
}
for (unsigned bar_num = 0; bar_num < bar_count; ++bar_num) {
uint32_t old_bar = x86_pci_read(dev->bus, dev->dev, dev->fun, bar_base + 4 * bar_num);
if (old_bar == 0) {
continue;
}
x86_pci_write(dev->bus, dev->dev, dev->fun, bar_base + 4 * bar_num, -1ul);
uint32_t tmp_bar = x86_pci_read(dev->bus, dev->dev, dev->fun, bar_base + 4 * bar_num);
x86_pci_write(dev->bus, dev->dev, dev->fun, bar_base + 4 * bar_num, old_bar);
if ((old_bar & PCI_BAR_IO_SPACE) != (tmp_bar & PCI_BAR_IO_SPACE)) {
/* cannot happen (?) */
continue;
}
dev->io = realloc(dev->io, sizeof (*dev->io) * (dev->io_count + 1));
struct x86_pci_io *io = calloc(1, sizeof *io);
dev->io[dev->io_count] = io;
io->bar_num = bar_num;
++dev->io_count;
unsigned addr_offs = tmp_bar & PCI_BAR_IO_SPACE ? PCI_BAR_ADDR_OFFS_IO : PCI_BAR_ADDR_OFFS_MEM;
uint32_t length_tmp = tmp_bar >> addr_offs;
uint32_t length = 1 << addr_offs;
while ((length_tmp & 1) == 0) {
length <<= 1;
length_tmp >>= 1;
}
io->length = length;
if (tmp_bar & PCI_BAR_IO_SPACE) {
io->type = PCI_IO_PORT;
io->addr.port = old_bar & ~((1 << PCI_BAR_ADDR_OFFS_IO) - 1);
printf(" BAR %u: I/O space, ports 0x%04x-0x%04x\n",
bar_num, io->addr.port, io->addr.port + length - 1);
}
else if ((old_bar & PCI_BAR_IO_SPACE) != PCI_BAR_SPACE_32 && (old_bar & PCI_BAR_IO_SPACE) != PCI_BAR_SPACE_64) {
printf(" BAR %u: memory with unknown location 0x%x, ERROR!\n", bar_num, (old_bar >> 1) & 3);
}
else {
uint32_t physical_start = old_bar & ~0xfff;
void *ptr = x86_map_physical_pages(physical_start, (length + 0xfff) / 0x1000, PT_P | PT_G | PT_RW | PT_PWT | PT_PCD | PT_XD);
if (!ptr) {
io->type = PCI_IO_INVALID;
printf(" BAR %u: memory, physical = 0x%08x-0x%08x, ERROR!\n",
bar_num, (unsigned) physical_start, physical_start + length - 1);
}
else {
io->type = PCI_IO_MEM;
io->addr.ptr = (char *) ptr + (old_bar & ~0xfff & ~((1 << PCI_BAR_ADDR_OFFS_MEM) - 1));
printf(" BAR %u: memory, physical = 0x%08x-0x%08x, virtual = 0x%08x-0x%08x\n",
bar_num,
physical_start, physical_start + length - 1,
(unsigned) ptr, (uintptr_t) ptr + length - 1);
}
}
}
}
static void pci_find_on_bus(unsigned bus);
void x86_pci_set_irq(struct x86_known_pci_device *d, uint8_t irq_num)
{
if (d->irq == irq_num) {
return;
}
d->irq = irq_num;
uint32_t old_3c = x86_pci_read(d->bus, d->dev, d->fun, 0x3c);
x86_pci_write(d->bus, d->dev, d->fun, 0x3c, (old_3c & ~0xff) | d->irq);
printf(" IRQ: new = %u, old = %u\n", d->irq, old_3c & 0xff);
}
static void pci_find_function(unsigned bus, unsigned dev, unsigned fun)
{
union pci_reg_0x00 vendor = x86_pci_read_reg(0x00, bus, dev, fun);
if (!pci_vendor_id_valid(vendor.vendor_id)) {
return;
}
union pci_reg_0x08 class = x86_pci_read_reg(0x08, bus, dev, fun);
const char *baseclass_name, *subclass_name = x86_pci_subclass_to_string(class.baseclass,
class.subclass,
class.programming_interface,
&baseclass_name);
const char *vendor_name, *device_name = x86_pci_device_id_to_string(vendor.vendor_id, vendor.device_id, &vendor_name);
printf(" %02x:%02x.%x \"%s\": \"%s\" (%s: %s, rev: %02hhx)\n",
bus, dev, fun, vendor_name, device_name, baseclass_name, subclass_name, class.revision_id);
known_pci_devices = realloc(known_pci_devices, sizeof (*known_pci_devices) * (num_known_pci_devices + 1));
struct x86_known_pci_device *d = calloc(1, sizeof *d);
known_pci_devices[num_known_pci_devices] = d;
++num_known_pci_devices;
d->bus = bus;
d->dev = dev;
d->fun = fun;
d->vendor = vendor;
d->class = class;
d->managed = false;
uint32_t old_3c = x86_pci_read(bus, dev, fun, 0x3c);
if (old_3c & 0xff) {
d->irq = PCI_IRQ_DEFAULT;
x86_pci_write(bus, dev, fun, 0x3c, (old_3c & ~0xff) | d->irq);
printf(" IRQ: new = %u, old = %u\n", d->irq, old_3c & 0xff);
}
pci_setup_ios(d);
if (class.baseclass == 0x06 && class.subclass == 0x04) {
unsigned secondary_bus = pci_init_secondary_bus(bus, dev, fun);
if (secondary_bus != 0) {
pci_find_on_bus(secondary_bus);
}
}
}
static void pci_find_on_bus(unsigned bus)
{
for (unsigned dev = 0; dev < PCI_DEV_COUNT; ++dev) {
if (!pci_vendor_id_valid(x86_pci_read_reg(0x00, bus, dev, 0).vendor_id)) {
continue;
}
if (x86_pci_read_reg(0x0c, bus, dev, 0).header_type & PCI_HEADER_TYPE_MULTI_FUNCTION) {
for (unsigned fun = 0; fun < PCI_FUN_COUNT; ++fun) {
pci_find_function(bus, dev, fun);
}
}
else {
pci_find_function(bus, dev, 0);
}
}
}
static void pci_find(void)
{
if (x86_pci_read_reg(0x0c, 0, 0, 0).header_type & PCI_HEADER_TYPE_MULTI_FUNCTION) {
for (unsigned fun = 0; fun < PCI_FUN_COUNT; ++fun) {
if (pci_vendor_id_valid(x86_pci_read_reg(0x00, 0, 0, fun).vendor_id)) {
pci_find_on_bus(fun);
}
}
}
else {
pci_find_on_bus(0);
}
}
static void irq_handler(uint8_t irq_num)
{
for (unsigned i = 0; i < num_known_pci_devices; ++i) {
struct x86_known_pci_device *d = known_pci_devices[i];
if (d->managed && d->irq_handler && d->irq == irq_num) {
d->irq_handler(d);
}
}
}
void x86_init_pci(void)
{
x86_pic_set_handler(PCI_IRQ_ACPI, irq_handler);
x86_pic_enable_irq(PCI_IRQ_ACPI);
x86_pic_set_handler(PCI_IRQ_NETWORKING, irq_handler);
x86_pic_enable_irq(PCI_IRQ_NETWORKING);
x86_pic_set_handler(PCI_IRQ_DEFAULT, irq_handler);
x86_pic_enable_irq(PCI_IRQ_DEFAULT);
x86_pic_set_handler(PCI_IRQ_USB, irq_handler);
x86_pic_enable_irq(PCI_IRQ_USB);
puts("Looking up PCI devices");
pci_find();
x86_init_pci_devices();
}
struct x86_known_pci_device **x86_enumerate_unmanaged_pci_devices(unsigned *index)
{
while (*index < num_known_pci_devices) {
struct x86_known_pci_device **result = &known_pci_devices[*index];
++*index;
if (*result && !(**result).managed) {
return result;
}
}
return NULL;
}
const struct x86_known_pci_device *x86_enumerate_pci_devices(unsigned *index)
{
while (*index < num_known_pci_devices) {
struct x86_known_pci_device *result = known_pci_devices[*index];
++*index;
if (result) {
return result;
}
}
return NULL;
}

37
cpu/x86/x86_pci_init.c Normal file
View File

@ -0,0 +1,37 @@
/*
* 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-irq
* @{
*
* @file
* @brief PCI device setup
*
* @author René Kijewski <rene.kijewski@fu-berlin.de>
*
* @}
*/
#include "x86_pci.h"
#include "x86_pci_init.h"
void x86_init_pci_devices(void)
{
/* no PCI devices are implemented, yet */
}

409
cpu/x86/x86_pci_strings.c Normal file
View File

@ -0,0 +1,409 @@
/*
* 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-irq
* @{
*
* @file
* @brief String names of PCI classes, vendors, and devices.
*
* @author René Kijewski <rene.kijewski@fu-berlin.de>
*
* @}
*/
#include "x86_pci_strings.h"
#include <stdio.h>
/* TODO: don't always include all strings */
const char *x86_pci_subclass_to_string(unsigned baseclass, unsigned subclass, unsigned interface, const char **baseclass_name)
{
static char temp_baseclass_number[7], temp_subclass_number[7];
const char *baseclass_name_null;
if (!baseclass_name) {
baseclass_name = &baseclass_name_null;
}
// "PCI LOCAL BUS SPECIFICATION, REV. 3.0"; Appendix D.
switch (baseclass) {
case 0x00:
*baseclass_name = "Device was built before Class Code definitions were finalized";
switch (subclass) {
case 0x00: return "Non-VGA";
case 0x01: return "VGA";
}
break;
case 0x01:
*baseclass_name = "Mass storage controller";
switch (subclass) {
case 0x00: return "SCSI bus controller";
case 0x01: return "IDE controller";
case 0x02: return "Floppy disk controller";
case 0x03: return "IPI bus controller";
case 0x04: return "RAID controller";
case 0x05: return "ATA controller";
case 0x06: return "Serial ATA Direct Port Access";
case 0x80: return "Other mass storage controller";
}
break;
case 0x02:
*baseclass_name = "Network controller";
switch (subclass) {
case 0x00: return "Ethernet controller";
case 0x01: return "Token Ring controller";
case 0x02: return "FDDI controller";
case 0x03: return "ATM controller";
case 0x04: return "ISDN controller";
case 0x05: return "WorldFip controller";
case 0x06: return "PICMG 2.14 Multi Computing";
case 0x80: return "Other network controller";
}
break;
case 0x03:
*baseclass_name = "Display controller";
switch (subclass) {
case 0x00: switch (interface) {
case 0x00: return "VGA-compatible controller";
case 0x01: return "8514-compatible controller";
default: return "[UNDEFINED IN ACPI 3.0]";
}
case 0x01: return "XGA coltroller";
case 0x02: return "3D controller";
case 0x80: return "Other display controller";
}
break;
case 0x04:
*baseclass_name = "Multimedia device";
switch (subclass) {
case 0x00: return "Video device";
case 0x01: return "Audio device";
case 0x02: return "Computer telephony device";
case 0x80: return "Other multimedia device";
}
break;
case 0x05:
*baseclass_name = "Memory controller";
switch (subclass) {
case 0x00: return "RAM";
case 0x01: return "Flash";
case 0x80: return "Other memory controller";
}
break;
case 0x06:
*baseclass_name = "Bridge device";
switch (subclass) {
case 0x00: return "Host bridge";
case 0x01: return "ISA bridge";
case 0x02: return "EISA bridge";
case 0x03: return "MCA bridge";
case 0x04: return "PCI-to-PCI bridge";
case 0x05: return "PCMCIA bridge";
case 0x06: return "NuBus bridge";
case 0x07: return "CardBus bridge";
case 0x08: return "RACEway bridge";
case 0x09: return "Semi-transparent PCI-to-PCI bridge";
case 0x0a: return "InfiniBand-to-PCI host bridge";
case 0x80: return "Other bridge device";
}
break;
case 0x07:
*baseclass_name = "Simple communication controller";
switch (subclass) {
case 0x00:
switch (interface) {
case 0x00: return "Generic XT-compatible serial controller";
case 0x01: return "16450-compatible serial controller";
case 0x02: return "16550-compatible serial controller";
case 0x03: return "16650-compatible serial controller";
case 0x04: return "16750-compatible serial controller";
case 0x05: return "16850-compatible serial controller";
case 0x06: return "16950-compatible serial controller";
}
break;
case 0x01:
switch (interface) {
case 0x00: return "Parallel port";
case 0x01: return "Bi-directional parallel port";
case 0x02: return "ECP 1.X compliant parallel port";
case 0x03: return "IEEE1284 controller";
case 0xef: return "IEEE1284 target device";
}
break;
case 0x02: return "Multiport serial controller";
case 0x03:
switch (interface) {
case 0x00: return "Generic modem";
case 0x01: return "Hayes compatible modem, 16450-compatible interface";
case 0x02: return "Hayes compatible modem, 16550-compatible interface";
case 0x03: return "Hayes compatible modem, 16650-compatible interface";
case 0x04: return "Hayes compatible modem, 16750-compatible interface";
}
break;
case 0x04: return "GPIB (IEEE 488.1/2) controller";
case 0x05: return "Smart Card";
case 0x80: return "Other communications device";
}
break;
case 0x08:
*baseclass_name = "Base system peripheral";
switch (subclass) {
case 0x00:
switch (interface) {
case 0x00: return "Generic 8259 PIC";
case 0x01: return "ISA PIC";
case 0x02: return "EISA PIC";
case 0x10: return "I/O APIC interrupt controller (see below)";
case 0x20: return "I/O(x) APIC interrupt controller";
}
break;
case 0x01:
switch (interface) {
case 0x00: return "Generic 8237 DMA controller";
case 0x01: return "ISA DMA controller";
case 0x02: return "EISA DMA controller";
}
break;
case 0x02:
switch (interface) {
case 0x00: return "Generic 8254 system timer";
case 0x01: return "ISA system timer";
case 0x02: return "EISA system timers (two timers)";
}
break;
case 0x03:
switch (interface) {
case 0x00: return "Generic RTC controller";
case 0x01: return "ISA RTC controller";
}
break;
case 0x04: return "Generic PCI Hot-Plug controller";
case 0x80: return "Other system peripheral";
}
break;
case 0x09:
*baseclass_name = "Input device";
switch (subclass) {
case 0x00: return "Keyboard controller";
case 0x01: return "Digitizer (pen)";
case 0x02: return "Mouse controller";
case 0x03: return "Scanner controller";
case 0x04: return "Gameport controller";
case 0x80: return "Other input controller";
}
break;
case 0x0a:
*baseclass_name = "Docking station";
switch (subclass) {
case 0x00: return "Generic docking station";
case 0x80: return "Other type of docking station";
}
break;
case 0x0b:
*baseclass_name = "Processor";
switch (subclass) {
case 0x00: return "386";
case 0x01: return "486";
case 0x02: return "Pentium";
case 0x10: return "Alpha";
case 0x20: return "PowerPC";
case 0x30: return "MIPS";
case 0x40: return "Co-processor";
}
break;
case 0x0c:
*baseclass_name = "Serial bus controller";
switch (subclass) {
case 0x00:
switch (interface) {
case 0x00: return "IEEE 1394 (FireWire)";
case 0x01: return "IEEE 1394 following the 1394 OpenHCI specification";
}
break;
case 0x01: return "ACCESS.bus";
case 0x02: return "SSA";
case 0x03:
switch (interface) {
case 0x00: return "Universal Serial Bus (USB) following the Universal Host Controller Specification";
case 0x10: return "Universal Serial Bus (USB) following the Open Host Controller Specification";
case 0x20: return "USB2 host controller following the Intel Enhanced Host Controller Interface";
case 0x80: return "Universal Serial Bus with no specific programming interface";
case 0xfe: return "USB device";
}
break;
case 0x04: return "Fibre Channel";
case 0x05: return "SMBus";
case 0x06: return "InfiniBand";
case 0x07:
switch (interface) {
case 0x00: return "IPMI SMIC Interface";
case 0x01: return "IPMI Kybd Controller Style Interface";
case 0x02: return "IPMI Block Transfer Interface";
}
break;
case 0x08: return "SERCOS Interface Standard (IEC 61491)";
case 0x09: return "CANbus";
}
break;
case 0x0d:
*baseclass_name = "Wireless controller";
switch (subclass) {
case 0x00: return "iRDA compatible controller";
case 0x01: return "Consumer IR controller";
case 0x10: return "RF controller";
case 0x11: return "Bluetooth";
case 0x12: return "Broadband";
case 0x20: return "Ethernet (802.11a)";
case 0x21: return "Ethernet (802.11b)";
case 0x80: return "Other type of wireless controller";
}
break;
case 0x0e:
*baseclass_name = "Intelligent I/O controller";
switch (subclass) {
case 0x00:
switch (interface) {
case 0x00: return "Message FIFO at offset 040h";
default: return "Intelligent I/O (I2O) Architecture Specification 1.0";
}
break;
}
break;
case 0x0f:
*baseclass_name = "Satellite communication controller";
switch (subclass) {
case 0x01: return "TV";
case 0x02: return "Audio";
case 0x03: return "Voice";
case 0x04: return "Data";
}
break;
case 0x10:
*baseclass_name = "Encryption/Decryption controller";
switch (subclass) {
case 0x00: return "Network and computing en/decryption";
case 0x10: return "Entertainment en/decryption";
case 0x80: return "Other en/decryption";
}
break;
case 0x11:
*baseclass_name = "Data acquisition and signal processing controller";
switch (subclass) {
case 0x00: return "DPIO modules";
case 0x01: return "Performance counters";
case 0x10: return "Communications synchronization plus time and frequency test/measurement";
case 0x20: return "Management card";
case 0x80: return "Other data acquisition/signal processing controllers";
}
break;
case 0xff:
*baseclass_name = "Other device";
break;
default:
snprintf(temp_baseclass_number, sizeof temp_baseclass_number, "0x%04x", baseclass);
*baseclass_name = temp_baseclass_number;
}
snprintf(temp_subclass_number, sizeof temp_subclass_number, "0x%04x", subclass);
return temp_subclass_number;
}
const char *x86_pci_device_id_to_string(unsigned vendor_id, unsigned device_id, const char **vendor_name)
{
static char temp_vendor_name[7], temp_device_name[7];
const char *vendor_name_null;
if (!vendor_name) {
vendor_name = &vendor_name_null;
}
// http://pci-ids.ucw.cz/read/PC/
// http://www.pcidatabase.com/
switch (vendor_id) {
case 0x1013:
*vendor_name = "Cirrus Logic";
switch (device_id) {
case 0x00b8: return "GD 5446";
}
break;
case 0x8086:
*vendor_name = "Intel Corporation";
switch (device_id) {
case 0x100e: return "82540EM Gigabit Ethernet Controller";
case 0x1237: return "440FX - 82441FX PMC [Natoma]";
case 0x2415: return "82801AA AC'97 Audio Controller";
case 0x2668: return "82801FB/FBM/FR/FW/FRW (ICH6 Family) HDA Controller";
case 0x7000: return "82371SB PIIX3 ISA [Natoma/Triton II]";
case 0x7010: return "82371SB PIIX3 IDE [Natoma/Triton II]";
case 0x7020: return "82371SB PIIX3 USB [Natoma/Triton II]";
case 0x7113: return "82371AB/EB/MB PIIX4 ACPI";
/* intel galileo */
/* FIXME: find out the actual names ... */
case 0x0958: return "Host Bridge";
case 0x095E: return "Legacy Bridge";
case 0x11C3: return "PCIe* Root Port 0";
case 0x11C4: return "PCIe* Root Port 1";
case 0x08A7: return "SDIO / eMMC Controller";
case 0x0936: return "HS-UART";
case 0x0939: return "USB 2.0 Device";
case 0x093A: return "USB OHCI Host Controller";
case 0x0937: return "10/100 Ethernet";
case 0x0935: return "SPI Controller";
case 0x0934: return "I2C* Controller and GPIO Controller";
}
break;
default:
snprintf(temp_vendor_name, sizeof temp_vendor_name, "0x%04x", vendor_id);
*vendor_name = temp_vendor_name;
}
snprintf(temp_device_name, sizeof temp_device_name, "0x%04x", device_id);
return temp_device_name;
}

192
cpu/x86/x86_pic.c Normal file
View File

@ -0,0 +1,192 @@
/*
* 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-irq
* @{
*
* @file
* @brief Configuration and interrupt handling for the Programmable Interrupt Controller (PIC).
*
* @author René Kijewski <rene.kijewski@fu-berlin.de>
*
* @}
*/
#include "x86_interrupts.h"
#include "x86_pic.h"
#include "x86_ports.h"
#include "cpu.h"
#include "irq.h"
#include <stdio.h>
static x86_irq_handler_t x86_registered_irqs[X86_IRQ_COUNT];
static const char *const IRQ_NAMES[X86_IRQ_COUNT] __attribute__((unused)) = {
"PIT",
"KBC 1",
NULL,
"COM 2/4",
"COM 1/3",
"LPT 2",
"FLOPPY",
"LPT 1",
"RTC",
"#9",
"ATA 4",
"ATA 3",
"KBC 2",
NULL,
"ATA 1",
"ATA 2",
};
static bool spurious_irq(uint8_t irq_num)
{
if (irq_num < 8) {
outb(PIC_MASTER + PIC_COMMAND, PIC_READ_ISR);
return (inb(PIC_MASTER + PIC_COMMAND) & (1 << irq_num)) == 0;
}
return false; // TODO: does not work
irq_num -= 8;
outb(PIC_SLAVE + PIC_COMMAND, PIC_READ_ISR);
if (inb(PIC_SLAVE + PIC_COMMAND) & (1 << irq_num)) {
outb(PIC_MASTER + PIC_COMMAND, PIC_EOI);
return true;
}
return false;
}
static void pic_interrupt_entry(uint8_t intr_num, struct x86_pushad *orig_ctx, unsigned long error_code)
{
(void) error_code;
(void) orig_ctx;
uint8_t irq_num = intr_num - PIC_MASTER_INTERRUPT_BASE;
if (spurious_irq(irq_num)) {
return;
}
x86_irq_handler_t handler = x86_registered_irqs[irq_num];
if (handler) {
handler(irq_num);
}
outb(PIC_MASTER + PIC_COMMAND, PIC_EOI);
if (irq_num > 7) {
outb(PIC_SLAVE + PIC_COMMAND, PIC_EOI);
}
}
static void pic_remap(void)
{
/* tell both PICs we want to initialize them */
outb(PIC_MASTER + PIC_COMMAND, PIC_ICW1_INIT + PIC_ICW1_ICW4);
io_wait();
outb(PIC_SLAVE + PIC_COMMAND, PIC_ICW1_INIT + PIC_ICW1_ICW4);
io_wait();
/* which ISR should be called if an IRQ occurs */
outb(PIC_MASTER + PIC_DATA, PIC_MASTER_INTERRUPT_BASE);
io_wait();
outb(PIC_SLAVE + PIC_DATA, PIC_SLAVE_INTERRUPT_BASE);
io_wait();
/* IRQ of the master the slave talks to */
outb(PIC_MASTER + PIC_DATA, 1 << PIC_NUM_SLAVE);
io_wait();
outb(PIC_SLAVE + PIC_DATA, PIC_NUM_SLAVE);
io_wait();
/* use PC mode */
outb(PIC_MASTER + PIC_DATA, PIC_ICW4_8086);
io_wait();
outb(PIC_SLAVE + PIC_DATA, PIC_ICW4_8086);
}
static void pic_register_handler(void)
{
for (unsigned i = 0; i < X86_IRQ_COUNT; ++i) {
x86_interrupt_handler_set(PIC_MASTER_INTERRUPT_BASE + i, &pic_interrupt_entry);
}
}
void x86_pic_set_enabled_irqs(uint16_t mask)
{
unsigned old_status = disableIRQ();
mask |= PIC_MASK_SLAVE;
mask &= ~PIC_MASK_FPU;
outb(PIC_MASTER + PIC_IMR, ~(uint8_t) mask);
io_wait();
outb(PIC_SLAVE + PIC_IMR, ~(uint8_t) (mask >> 8));
restoreIRQ(old_status);
}
void x86_pic_enable_irq(unsigned num)
{
unsigned old_status = disableIRQ();
uint16_t port;
if (num < 8) {
port = PIC_MASTER;
}
else {
num -= 8;
port = PIC_SLAVE;
}
uint8_t cur = inb(port + PIC_IMR);
outb(port + PIC_IMR, cur & ~(1 << num));
restoreIRQ(old_status);
}
void x86_pic_disable_irq(unsigned num)
{
unsigned old_status = disableIRQ();
uint16_t port;
if (num < 8) {
port = PIC_MASTER;
}
else {
num -= 8;
port = PIC_SLAVE;
}
uint8_t cur = inb(port + PIC_IMR);
outb(port + PIC_IMR, cur | (1 << num));
restoreIRQ(old_status);
}
void x86_init_pic(void)
{
pic_register_handler();
pic_remap();
x86_pic_set_enabled_irqs(0);
puts("PIC initialized");
}
void x86_pic_set_handler(unsigned irq, x86_irq_handler_t handler)
{
x86_registered_irqs[irq] = handler;
}

72
cpu/x86/x86_pit.c Normal file
View File

@ -0,0 +1,72 @@
/*
* 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-irq
* @{
*
* @file
* @brief Configuration and interrupt handling for the Programmable Interval Timer (PIT).
*
* @author René Kijewski <rene.kijewski@fu-berlin.de>
*
* @}
*/
#include "x86_interrupts.h"
#include "x86_pit.h"
#include "x86_ports.h"
#include "cpu.h"
#include "irq.h"
#include <stdio.h>
void x86_init_pit(void)
{
puts("PIT initialized");
}
uint16_t x86_pit_read(unsigned channel)
{
unsigned old_flags = disableIRQ();
outb(PIT_COMMAND_PORT, (channel - 1) << 6 | PIT_ACCESS_MODE_LATCH_COUNT);
uint16_t lohi = inb(PIT_CHANNEL_0_PORT + channel - 1);
lohi += inb(PIT_CHANNEL_0_PORT + channel - 1) << 8;
restoreIRQ(old_flags);
return lohi;
}
void x86_pit_set2(unsigned channel, unsigned mode, uint16_t max)
{
unsigned old_flags = disableIRQ();
outb(PIT_COMMAND_PORT, ((channel - 1) << 6) | mode | PIT_ACCESS_MODE_LO_HI);
outb(PIT_CHANNEL_0_PORT + channel - 1, max && 0xff);
outb(PIT_CHANNEL_0_PORT + channel - 1, max >> 8);
restoreIRQ(old_flags);
}
bool x86_pit_set(unsigned channel, unsigned mode, unsigned hz)
{
if (PIT_MIN_HZ > hz || hz > PIT_MAX_HZ) {
return false;
}
uint16_t max = PIT_MAX_HZ / hz;
x86_pit_set2(channel, mode, max);
return true;
}

123
cpu/x86/x86_reboot.c Normal file
View File

@ -0,0 +1,123 @@
/*
* 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 Configurable reboot handler for x86.
*
* @author René Kijewski <rene.kijewski@fu-berlin.de>
*
* @}
*/
#include "x86_interrupts.h"
#include "x86_ports.h"
#include "x86_reboot.h"
#define KBC_DATA (0x60)
#define KBC_STATUS (0x64)
#define KBC_COMMAND (0x64)
#define KBC_OUTPUT_FULL (1u << 0)
#define KBC_INPUT_FULL (1u << 1)
#define KBC_RESET ((uint8_t) ~(1u << 0))
static const struct idtr_t EMPTY_IDT = {
.limit = 0,
.base = NULL,
};
void x86_load_empty_idt(void)
{
asm volatile ("lidt %0" :: "m"(EMPTY_IDT));
}
static bool fail_violently;
void NORETURN x86_kbc_reboot(void)
{
/* First load an empty IDT. Any interrupt will cause a tripple fault. */
x86_load_empty_idt();
while (1) {
if (fail_violently) {
asm volatile ("int3"); /* Cause a tripple fault. Won't return. */
}
fail_violently = true;
for (unsigned i = 0; i < 0x100; ++i) {
for (unsigned j = 0; j < 0x10000; ++j) {
uint8_t c = inb(KBC_STATUS);
if (c & KBC_OUTPUT_FULL) {
(void) inb(KBC_DATA);
}
else if (!(c & KBC_OUTPUT_FULL)) {
outb(KBC_COMMAND, KBC_RESET);
break;
}
}
}
asm volatile ("int3"); /* Cause a tripple fault. Won't return. */
}
}
static x86_reboot_t reboot_fun;
static bool reboot_twice;
int reboot_arch(int mode)
{
switch (mode) {
case RB_AUTOBOOT:
asm volatile ("cli");
if (!reboot_twice) {
reboot_twice = true;
if (reboot_fun) {
reboot_fun();
}
}
x86_kbc_reboot();
default:
return -1;
}
}
void x86_set_reboot_fun(x86_reboot_t fun)
{
reboot_fun = fun;
}
static x86_shutdown_t shutdown_fun;
bool x86_shutdown(void)
{
if (!shutdown_fun) {
return false;
}
return shutdown_fun();
}
void x86_set_shutdown_fun(x86_shutdown_t fun)
{
shutdown_fun = fun;
}

297
cpu/x86/x86_rtc.c Normal file
View File

@ -0,0 +1,297 @@
/*
* 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-irq
* @{
*
* @file
* @brief Reading and interrupt handling for the Real Time Clock (RTC).
*
* @author René Kijewski <rene.kijewski@fu-berlin.de>
*
* @}
*/
#include "x86_pic.h"
#include "x86_rtc.h"
#include "irq.h"
#include <stdio.h>
#define ENABLE_DEBUG (0)
#include "debug.h"
static bool valid;
static int32_t alarm_msg_content, periodic_msg_content, update_msg_content;
static unsigned alarm_pid = -1u, periodic_pid = -1u, update_pid = -1u;
static void alarm_callback_default(uint8_t reg_c)
{
if (alarm_pid != -1u) {
msg_t m;
m.type = reg_c | (RTC_REG_B_INT_ALARM << 8);
m.content.value = alarm_msg_content;
msg_send_int(&m, alarm_pid);
}
}
static void periodic_callback_default(uint8_t reg_c)
{
if (periodic_pid != -1u) {
msg_t m;
m.type = reg_c | (RTC_REG_B_INT_PERIODIC << 8);
m.content.value = periodic_msg_content;
msg_send_int(&m, periodic_pid);
}
}
static void update_callback_default(uint8_t reg_c)
{
if (update_pid != -1u) {
msg_t m;
m.type = reg_c | (RTC_REG_B_INT_UPDATE << 8);
m.content.value = update_msg_content;
msg_send_int(&m, update_pid);
}
}
static x86_rtc_callback_t alarm_callback = alarm_callback_default;
static x86_rtc_callback_t periodic_callback = periodic_callback_default;
static x86_rtc_callback_t update_callback = update_callback_default;
void x86_rtc_set_alarm_callback(x86_rtc_callback_t cb)
{
alarm_callback = cb ? cb : alarm_callback_default;
}
void x86_rtc_set_periodic_callback(x86_rtc_callback_t cb)
{
periodic_callback = cb ? cb : periodic_callback_default;
}
void x86_rtc_set_update_callback(x86_rtc_callback_t cb)
{
update_callback = cb ? cb : update_callback_default;
}
static void rtc_irq_handler(uint8_t irq_num)
{
(void) irq_num; /* == PIC_NUM_RTC */
uint8_t c = x86_cmos_read(RTC_REG_C);
DEBUG("RTC: c = 0x%02x, IRQ=%u, A=%u, P=%u, U=%u\n", c, c & RTC_REG_C_IRQ ? 1 : 0,
c & RTC_REG_C_IRQ_ALARM ? 1 : 0,
c & RTC_REG_C_IRQ_PERIODIC ? 1 : 0,
c & RTC_REG_C_IRQ_UPDATE ? 1 : 0);
if (!(c & RTC_REG_C_IRQ)) {
return;
}
if (c & RTC_REG_C_IRQ_ALARM) {
alarm_callback(c);
}
if (c & RTC_REG_C_IRQ_PERIODIC) {
periodic_callback(c);
}
if (c & RTC_REG_C_IRQ_UPDATE) {
update_callback(c);
}
}
void x86_init_rtc(void)
{
uint8_t d = x86_cmos_read(RTC_REG_D);
valid = (d & RTC_REG_D_VALID) != 0;
if (!valid) {
puts("Warning: RTC does not work.");
return;
}
x86_cmos_write(RTC_REG_B, x86_cmos_read(RTC_REG_B) & ~RTC_REG_B_INT_MASK);
rtc_irq_handler(0);
x86_pic_set_handler(PIC_NUM_RTC, &rtc_irq_handler);
x86_pic_enable_irq(PIC_NUM_RTC);
x86_rtc_data_t now;
x86_rtc_read(&now);
printf("RTC initialized [%02hhu:%02hhu:%02hhu, %04u-%02hhu-%02hhu]\n",
now.hour, now.minute, now.second,
now.century * 100 + now.year, now.month, now.day);
if (x86_cmos_read(RTC_REG_POST) & (RTC_REG_POST_POWER_LOSS | RTC_REG_POST_TIME_INVALID)) {
puts("Warning: RTC time is invalid (power loss?)");
}
}
static inline bool is_update_in_progress(void)
{
return (x86_cmos_read(RTC_REG_A) & RTC_REG_A_UPDATING) != 0;
}
static uint8_t bcd2binary(uint8_t datum)
{
return (datum / 16) * 10 + (datum % 16);
}
static uint8_t binary2bcd(uint8_t datum)
{
return (datum / 10) * 16 + (datum % 10);
}
bool x86_rtc_read(x86_rtc_data_t *dest)
{
if (!valid) {
return false;
}
unsigned old_status = disableIRQ();
while (is_update_in_progress()) {
asm volatile ("pause");
}
uint8_t b = x86_cmos_read(RTC_REG_B);
do {
dest->second = x86_cmos_read(RTC_REG_SECOND);
dest->minute = x86_cmos_read(RTC_REG_MINUTE);
dest->hour = x86_cmos_read(RTC_REG_HOUR);
dest->day = x86_cmos_read(RTC_REG_DAY);
dest->month = x86_cmos_read(RTC_REG_MONTH);
dest->year = x86_cmos_read(RTC_REG_YEAR);
dest->century = bcd2binary(x86_cmos_read(RTC_REG_CENTURY));
} while (dest->second != x86_cmos_read(RTC_REG_SECOND));
if (dest->century == 0) {
dest->century = 20; // safe guess
}
if (!(b & RTC_REG_B_BIN)) {
dest->second = bcd2binary(dest->second);
dest->minute = bcd2binary(dest->minute);
dest->hour = ((dest->hour & 0x0F) + (((dest->hour & 0x70) / 16) * 10)) | (dest->hour & 0x80);
dest->day = bcd2binary(dest->day);
dest->month = bcd2binary(dest->month);
dest->year = bcd2binary(dest->year);
}
if (!(b & RTC_REG_B_24H) && (dest->hour & 0x80)) {
dest->hour = ((dest->hour & 0x7F) + 12) % 24;
}
restoreIRQ(old_status);
return true;
}
bool x86_rtc_set_alarm(const x86_rtc_data_t *when, uint32_t msg_content, unsigned int target_pid, bool allow_replace)
{
if (!valid) {
return false;
}
unsigned old_status = disableIRQ();
bool result;
if (target_pid == -1u) {
result = true;
alarm_pid = -1u;
uint8_t b = x86_cmos_read(RTC_REG_B);
x86_cmos_write(RTC_REG_B, b & ~RTC_REG_B_INT_ALARM);
}
else {
result = allow_replace || alarm_pid == -1u;
if (result) {
alarm_msg_content = msg_content;
alarm_pid = target_pid;
uint8_t b = x86_cmos_read(RTC_REG_B);
if (b & RTC_REG_B_BIN) {
x86_cmos_write(RTC_REG_ALARM_SECOND, when->second);
x86_cmos_write(RTC_REG_ALARM_MINUTE, when->minute);
x86_cmos_write(RTC_REG_ALARM_HOUR, when->hour);
}
else {
x86_cmos_write(RTC_REG_ALARM_SECOND, binary2bcd(when->second));
x86_cmos_write(RTC_REG_ALARM_MINUTE, binary2bcd(when->minute));
x86_cmos_write(RTC_REG_ALARM_HOUR, binary2bcd(when->hour));
}
x86_cmos_write(RTC_REG_B, b | RTC_REG_B_INT_ALARM);
}
}
rtc_irq_handler(0);
restoreIRQ(old_status);
return result;
}
bool x86_rtc_set_periodic(uint8_t hz, uint32_t msg_content, unsigned int target_pid, bool allow_replace)
{
if (!valid) {
return false;
}
unsigned old_status = disableIRQ();
bool result;
if (target_pid == -1u || hz == RTC_REG_A_HZ_OFF) {
result = true;
periodic_pid = -1u;
uint8_t old_divider = x86_cmos_read(RTC_REG_A) & ~RTC_REG_A_HZ_MASK;
x86_cmos_write(RTC_REG_A, old_divider | RTC_REG_A_HZ_OFF);
x86_cmos_write(RTC_REG_B, x86_cmos_read(RTC_REG_B) & ~RTC_REG_B_INT_PERIODIC);
}
else {
result = allow_replace || periodic_pid == -1u;
if (result) {
periodic_msg_content = msg_content;
periodic_pid = target_pid;
uint8_t old_divider = x86_cmos_read(RTC_REG_A) & ~RTC_REG_A_HZ_MASK;
x86_cmos_write(RTC_REG_A, old_divider | hz);
x86_cmos_write(RTC_REG_B, x86_cmos_read(RTC_REG_B) | RTC_REG_B_INT_PERIODIC);
}
}
rtc_irq_handler(0);
restoreIRQ(old_status);
return result;
}
bool x86_rtc_set_update(uint32_t msg_content, unsigned int target_pid, bool allow_replace)
{
if (!valid) {
return false;
}
unsigned old_status = disableIRQ();
bool result;
if (target_pid == -1u) {
result = true;
update_pid = -1u;
x86_cmos_write(RTC_REG_B, x86_cmos_read(RTC_REG_B) & ~RTC_REG_B_INT_UPDATE);
}
else {
result = allow_replace || update_pid == -1u;
if (result) {
update_msg_content = msg_content;
update_pid = target_pid;
x86_cmos_write(RTC_REG_B, x86_cmos_read(RTC_REG_B) | RTC_REG_B_INT_UPDATE);
}
}
rtc_irq_handler(0);
restoreIRQ(old_status);
return result;
}

75
cpu/x86/x86_startup.c Normal file
View File

@ -0,0 +1,75 @@
/*
* 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 The common startup code for the x86 port.
*
* @author René Kijewski <rene.kijewski@fu-berlin.de>
*
* @}
*/
#include "x86_hwtimer.h"
#include "x86_interrupts.h"
#include "x86_memory.h"
#include "x86_pci.h"
#include "x86_pic.h"
#include "x86_pit.h"
#include "x86_rtc.h"
#include "x86_threading.h"
#include "x86_uart.h"
#include <cpu.h>
#include <kernel_internal.h>
#include <tlsf-malloc.h>
#include <stdio.h>
/* Must be <= 0x1000 because otherwise x86_map_physical_pages() might get a page out of this pool.
* Because static memory has the PT_G flag, flushing the TLB would cause a stale PT
* after calling add_pages_to_pool() in x86_map_physical_pages().
*/
static char early_malloc_pool[0x1000] __attribute__((aligned(4)));
void x86_startup(void)
{
tlsf_add_pool(early_malloc_pool, sizeof early_malloc_pool);
x86_early_init_uart();
x86_init_threading();
x86_init_interrupts();
x86_init_pic();
x86_init_uart();
x86_init_memory();
x86_init_rtc();
x86_init_pit();
x86_init_hwtimer();
x86_init_pci();
puts("RIOT x86 hardware initialization complete.");
x86_init_board();
puts("RIOT board initialization complete.");
kernel_init(); /* should not return */
puts("kernel_init returned");
x86_hlt();
}

223
cpu/x86/x86_threading.c Normal file
View File

@ -0,0 +1,223 @@
/*
* 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-multithreading
* @{
*
* @file
* @brief Multi-thread management for x86.
*
* @author René Kijewski <rene.kijewski@fu-berlin.de>
*
* @}
*/
#include "x86_interrupts.h"
#include "x86_reboot.h"
#include "x86_registers.h"
#include "x86_threading.h"
#include "cpu.h"
#include "irq.h"
#include "kernel_internal.h"
#include "ucontext.h"
#include "sched.h"
#include "stdbool.h"
#include "thread.h"
#define ENABLE_DEBUG (0)
#include "debug.h"
static char isr_stack[SIGSTKSZ];
static ucontext_t isr_context;
static char end_stack[SIGSTKSZ];
static ucontext_t end_context;
bool x86_in_isr = true;
static long fpu_owner = -1;
//static ucontext_t *cur_ctx, *isr_ctx;
int inISR(void)
{
return x86_in_isr;
}
unsigned disableIRQ(void)
{
unsigned long eflags = x86_pushf_cli();
return (eflags & X86_IF) != 0;
}
unsigned enableIRQ(void)
{
unsigned long eflags;
asm volatile ("pushf; pop %0; sti" : "=g"(eflags));
return (eflags & X86_IF) != 0;
}
void restoreIRQ(unsigned state)
{
if (state) {
asm volatile ("sti");
}
else {
asm volatile ("cli");
}
}
int inISR(void);
static void __attribute__((noreturn)) isr_thread_yield(void)
{
sched_run();
ucontext_t *ctx = (ucontext_t *) sched_active_thread->sp;
DEBUG("isr_thread_yield(): switching to (%s, %p)\n\n", sched_active_thread->name, ctx->uc_context.ip);
uint32_t cr0 = cr0_read();
cr0 |= CR0_TS;
cr0_write(cr0);
x86_in_isr = false;
setcontext(ctx);
}
void thread_yield(void)
{
if (x86_in_isr) {
isr_thread_yield();
}
unsigned old_intr = disableIRQ();
x86_in_isr = true;
isr_context.uc_stack.ss_sp = isr_stack;
isr_context.uc_stack.ss_size = sizeof isr_stack;
makecontext(&isr_context, isr_thread_yield, 0);
swapcontext((ucontext_t *) sched_active_thread->sp, &isr_context);
restoreIRQ(old_intr);
}
void isr_cpu_switch_context_exit(void)
{
DEBUG("XXX: cpu_switch_context_exit(), num_tasks = %d\n", sched_num_threads);
if (sched_num_threads <= 2) {
/* there is always "idle" and "x86-hwtimer" */
DEBUG("cpu_switch_context_exit(): last task has ended. Shutting down.\n");
x86_shutdown();
}
if ((sched_context_switch_request == 1) || (sched_active_thread == NULL)) {
sched_run();
}
ucontext_t *ctx = (ucontext_t *)(sched_active_thread->sp);
DEBUG("XXX: cpu_switch_context_exit(): calling setcontext(%s, %p)\n\n", sched_active_thread->name, ctx->uc_context.ip);
x86_in_isr = false;
setcontext(ctx);
}
void cpu_switch_context_exit(void)
{
dINT();
if (!x86_in_isr) {
x86_in_isr = true;
isr_context.uc_stack.ss_sp = isr_stack;
isr_context.uc_stack.ss_size = sizeof isr_stack;
makecontext(&isr_context, isr_cpu_switch_context_exit, 0);
setcontext(&isr_context);
}
else {
isr_cpu_switch_context_exit();
}
__builtin_unreachable();
}
char *thread_stack_init(void (*task_func)(void), void *stack_start, int stacksize)
{
DEBUG("thread_stack_init()\n");
unsigned int *stk = stack_start;
ucontext_t *p = (ucontext_t *)(stk + ((stacksize - sizeof(ucontext_t)) / sizeof(void *)));
stacksize -= sizeof(ucontext_t);
getcontext(p);
p->uc_stack.ss_sp = stk;
p->uc_stack.ss_size = stacksize;
p->uc_link = &end_context;
p->uc_context.flags |= X86_IF;
makecontext(p, task_func, 0);
return (char *) p;
}
static void fpu_used_interrupt(uint8_t intr_num, struct x86_pushad *orig_ctx, unsigned long error_code)
{
static volatile struct x86_fxsave fpu_data;
(void) intr_num;
(void) orig_ctx;
(void) error_code;
asm volatile ("clts"); /* clear task switch flag */
if (fpu_owner == sched_active_pid) {
return;
}
if (fpu_owner != -1) {
ucontext_t *ctx_owner = (ucontext_t *) sched_threads[fpu_owner]->sp;
asm volatile ("fxsave (%0)" :: "r"(&fpu_data));
ctx_owner->__fxsave = fpu_data;
}
ucontext_t *ctx_active = (ucontext_t *) sched_active_thread->sp;
fpu_data = ctx_active->__fxsave;
asm volatile ("fxrstor (%0)" :: "r"(&fpu_data));
fpu_owner = sched_active_pid;
}
static void x86_thread_exit(void)
{
dINT();
if (fpu_owner == sched_active_pid) {
fpu_owner = -1;
}
sched_task_exit();
}
void x86_init_threading(void)
{
getcontext(&end_context);
end_context.uc_stack.ss_sp = end_stack;
end_context.uc_stack.ss_size = sizeof end_stack;
makecontext(&end_context, x86_thread_exit, 0);
x86_interrupt_handler_set(X86_INT_NM, fpu_used_interrupt);
DEBUG("Threading initialized\n");
}

136
cpu/x86/x86_uart.c Normal file
View File

@ -0,0 +1,136 @@
/*
* 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-irq
* @{
*
* @file
* @brief UART reading and writing.
*
* @author René Kijewski <rene.kijewski@fu-berlin.de>
*
* @}
*/
#include "board.h"
#include "x86_ports.h"
#include "x86_uart.h"
#include <cpu.h>
#include <board_uart0.h>
#include <inttypes.h>
#include <stdint.h>
#include <stdio.h>
void x86_early_init_uart(void)
{
outb(UART_PORT + IER, 0x00); /* Disable all interrupts */
outb(UART_PORT + LCR, LCR_DLAB);
outb(UART_PORT + DLL, BAUD_LL); /* Set divisor (lo byte) */
outb(UART_PORT + DLH, BAUD_HL); /* (hi byte) */
outb(UART_PORT + LCR, LCR_WORD_BITS_8 | LCR_PAR_NONE | LCR_STOP_BITS_1);
outb(UART_PORT + FCR, FCR_ENABLE | FCR_CLR_RECV | FCR_CLR_SEND | FCR_TRIGGER_16);
outb(UART_PORT + MCR, MCR_DSR | MCR_RTS | MCR_AUX2);
}
static bool is_output_empty(void)
{
uint8_t data = inb(UART_PORT + LSR);
return (data & 0x20) != 0;
}
static bool is_input_empty(void)
{
uint8_t data = inb(UART_PORT + LSR);
return (data & 0x01) == 0;
}
ssize_t x86_uart_write(const char *buf, size_t len)
{
if (!UART_PORT) {
return -1;
}
(void) buf;
size_t written = 0;
while (written < len) {
while (!is_output_empty()) {
asm volatile ("pause");
}
outb(UART_PORT + THR, buf[written]);
++written;
}
return written;
}
ssize_t x86_uart_read(char *buf, size_t len)
{
if (!UART_PORT) {
return -1;
}
size_t read = 0;
while (read < len) {
while (!is_input_empty()) {
asm volatile ("pause");
}
buf[read] = inb(UART_PORT + RBR);
++read;
}
return read;
}
#ifdef MODULE_UART0
static void com_handler(uint8_t irq_num)
{
(void) irq_num; /* == UART_IRQ */
switch (inb(UART_PORT + IIR) & IIR_INT_MASK) {
case IIR_INT_BR: {
while (is_input_empty()) {
asm volatile ("pause");
}
do {
uint8_t c = inb(UART_PORT + RBR);
uart0_handle_incoming(c);
} while (!is_input_empty());
uart0_notify_thread();
break;
}
case IIR_INT_TH:
case IIR_INT_LS:
case IIR_INT_TO:
default:
break;
}
}
#endif /* ifdef MODULE_UART0 */
void x86_init_uart(void)
{
#ifdef MODULE_UART0
x86_pic_set_handler(UART_IRQ, com_handler);
x86_pic_enable_irq(UART_IRQ);
outb(UART_PORT + IER, IER_RECV); /* Enable receive interrupt */
puts("UART initialized");
#endif
}

150
cpu/x86/x86_ucontext.c Normal file
View File

@ -0,0 +1,150 @@
/*
* 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-multithreading
* @{
*
* @file
* @brief Coroutine helper functions.
*
* @author René Kijewski <rene.kijewski@fu-berlin.de>
*
* @}
*/
#include <ucontext.h>
#include <stdarg.h>
int __attribute__((optimize("omit-frame-pointer"), no_instrument_function)) getcontext(ucontext_t *ucp)
{
asm volatile ("pushf\n" :: "a"(ucp));
asm volatile ("pop 4*2(%eax)\n");
asm volatile ("mov %eax, 4*3(%eax)\n");
asm volatile ("mov %ecx, 4*4(%eax)\n");
asm volatile ("mov %edx, 4*5(%eax)\n");
asm volatile ("mov %ebx, 4*6(%eax)\n");
/* asm volatile ("mov %esp, 4*7(%eax)\n"); omitted */
asm volatile ("mov %ebp, 4*8(%eax)\n");
asm volatile ("mov %esi, 4*9(%eax)\n");
asm volatile ("mov %edi, 4*10(%eax)\n");
asm volatile ("lea 4(%esp), %edx\n");
asm volatile ("mov %edx, 4*0(%eax)\n");
asm volatile ("xor %edx, %edx\n");
asm volatile ("mov %edx, 4*1(%eax)\n");
asm volatile ("mov (%esp), %edx\n");
asm volatile ("mov %edx, 4*11(%eax)\n");
return 0;
}
int __attribute__((optimize("omit-frame-pointer"), no_instrument_function)) setcontext(const ucontext_t *ucp)
{
asm volatile ("1:\n" :: "a"(ucp));
/* asm volatile ("mov 4*3(%eax), %eax\n");, omitted */
asm volatile ("mov 4*4(%eax), %ecx\n");
/* asm volatile ("mov 4*5(%eax), %edx\n");, omitted */
asm volatile ("mov 4*6(%eax), %ebx\n");
/* asm volatile ("mov 4*7(%eax), %esp\n");, omitted */
asm volatile ("mov 4*8(%eax), %ebp\n");
asm volatile ("mov 4*9(%eax), %esi\n");
asm volatile ("mov 4*10(%eax), %edi\n");
asm volatile ("mov 4*0(%eax), %esp\n");
asm volatile ("add 4*1(%eax), %esp\n");
asm volatile ("mov 4*11(%eax), %edx\n");
asm volatile ("mov %eax, %ebx\n");
asm volatile ("push 4*2(%eax)\n");
asm volatile ("popf\n");
asm volatile ("call *%edx\n");
asm volatile ("mov 4*12(%ebx), %eax\n");
asm volatile ("jmp 1b\n");
__builtin_unreachable();
}
static void __attribute__((optimize("omit-frame-pointer"), noreturn, no_instrument_function)) makecontext_entrypoint(void)
{
/* ebx = ucp, ecx = argc, ebp = arg[0], esi = arg[1], edi = arg[2] */
asm volatile ("mov 4*3(%ebx), %eax\n"); /* eax = func */
asm volatile ("jecxz 0f\n");
asm volatile ("cmpb $1, %cl; je 1f\n");
asm volatile ("cmpb $2, %cl; je 2f\n");
asm volatile ("cmpb $3, %cl; je 3f\n");
asm volatile ("cmpb $4, %cl; je 4f\n");
asm volatile (" mov 4*7(%ebx), %edx; push %edx\n");
asm volatile ("4: mov 4*5(%ebx), %edx; push %edx\n");
asm volatile ("3: push %edi\n");
asm volatile ("2: push %esi\n");
asm volatile ("1: push %ebp\n");
asm volatile ("0: call *%eax\n"); /* call func(...), preserves ebx */
asm volatile ("mov 4*12(%ebx), %eax\n");
asm volatile ("push %eax\n");
asm volatile ("call setcontext\n");
__builtin_unreachable();
}
void makecontext(ucontext_t *ucp, makecontext_fun_t func, int argc, ...)
{
ucp->uc_context.ip = (void *) (unsigned long) &makecontext_entrypoint;
ucp->uc_context.registers.bx = (unsigned long) ucp;
ucp->uc_context.registers.ax = (unsigned long) func;
ucp->uc_context.registers.cx = argc > 0 ? argc : 0;
if (argc <= 0) {
return;
}
unsigned long *arg_registers[5] = {
&ucp->uc_context.registers.bp,
&ucp->uc_context.registers.si,
&ucp->uc_context.registers.di,
&ucp->uc_context.registers.dx,
&ucp->uc_context.registers.sp,
};
va_list ap;
va_start(ap, argc);
for (int i = 0; i < argc; ++i) {
*arg_registers[i] = va_arg(ap, unsigned long);
}
va_end(ap);
}
int swapcontext(ucontext_t *oucp, const ucontext_t *ucp)
{
/* Valid sentinel: an address in .text section is not valid for a stack pointer. */
oucp->uc_context.registers.sp = (unsigned long) &swapcontext;
getcontext(oucp);
if (oucp->uc_context.registers.sp == (unsigned long) &swapcontext) {
oucp->uc_context.registers.sp = 0;
setcontext(ucp);
}
return 0;
}

82
cpu/x86/x86_videoram.c Normal file
View File

@ -0,0 +1,82 @@
/*
* 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 Trivial functions to write to the videoram.
*
* @author René Kijewski <rene.kijewski@fu-berlin.de>
*
* @}
*/
#include "x86_videoram.h"
#define VIDEORAM ((char *) 0xB8000)
void videoram_putc(char c)
{
static unsigned pos = 0;
if (c == '\n') {
pos += 80;
}
else if (c == '\r') {
pos = (pos / 80) * 80;
}
else {
VIDEORAM[2 * pos + 0] = c;
VIDEORAM[2 * pos + 1] = (char) 0x9E; /* light blue bg, light yellow fg */
++pos;
}
if (pos >= 25 * 80) {
pos = 0;
}
}
void videoram_put(const char *s)
{
for (; *s; ++s) {
videoram_putc(*s);
}
}
void videoram_puts(const char *s)
{
videoram_put(s);
videoram_put("\r\n");
}
void videoram_put_hex(unsigned long v)
{
char s[2 + 2 * sizeof v + 1];
char *p = &s[sizeof s];
*--p = 0;
do {
char c = v % 16;
v /= 16;
*--p = c + (c < 10 ? '0' : 'A' - 10);
} while (v > 0);
*--p = 'x';
*--p = '0';
videoram_put(p);
}

386
dist/tools/toolchains/build_x86.sh vendored Executable file
View File

@ -0,0 +1,386 @@
#!/bin/bash
# directory to download source files and store intermediates
[[ -z $TARGET ]] && TARGET=i586-none-elf
[[ -z $TMP_DIR ]] && TMP_DIR=/var/tmp/RIOT-toolchain-${USER}
[[ -z $BUILDDIR ]] && BUILDDIR=${TMP_DIR}/${TARGET}
[[ -z $PREFIX ]] && PREFIX=${PWD}/toolchain/${TARGET}
[[ -z $MAKE_THREADS ]] && MAKE_THREADS=
NEWLIB_VER=2.1.0
NEWLIB_MD5=c6559d83ecce4728a52f0ce7ec80de97
NEWLIB_CONFIGURE_FLAGS=(
--target=${TARGET}
--prefix=${PREFIX}
--with-gmp=${PREFIX}
--with-mpfr=${PREFIX}
--with-mpc=${PREFIX}
--enable-interwork
--enable-multilib
--enable-target-optspace
--enable-lto
--disable-newlib-supplied-syscalls
--enable-newlib-reent-small
--enable-newlib-io-long-long
--enable-newlib-io-float
--enable-newlib-io-pos-args
--enable-newlib-io-c99-formats
--enable-newlib-multithread
)
NEWLIB_PATCHES=(
libgloss/i386/cygmon-gmon.c
'64d
59a
#include <string.h>
.'
libgloss/arm/_exit.c
'14a
__builtin_unreachable ();
.'
newlib/libc/stdlib/mallocr.c
'3700d
3434,3440d
3422,3424d
3400,3404d
3357,3370d
3278,3355d
1764,1898d
388d
384,386d
1,3d'
newlib/libc/stdlib/mlock.c
'63,64d
1d'
)
NEWLIB_TARGET_CFLAGS=(
-DREENTRANT_SYSCALLS_PROVIDED
-DMALLOC_PROVIDED
-DSIGNAL_PROVIDED
-ggdb3
-Os
-flto
-fomit-frame-pointer
-ffunction-sections
-fdata-sections
)
GCC_VER=4.8.2
GCC_MD5=a3d7d63b9cb6b6ea049469a0c4a43c9d
GCC_CONFIGURE_FLAGS=(
--target=${TARGET}
--prefix=${PREFIX}
--with-gmp=${PREFIX}
--with-mpfr=${PREFIX}
--with-mpc=${PREFIX}
--enable-interwork
--enable-multilib
--enable-languages="c,c++"
--with-newlib
#--enable-lto
--disable-libssp
--with-headers=${BUILDDIR}/newlib-${NEWLIB_VER}/newlib/libc/include
--enable-obsolete
--enable-target-optspace
--disable-nls
)
BINUTILS_VER=2.24
BINUTILS_MD5=e0f71a7b2ddab0f8612336ac81d9636b
BINUTILS_CONFIGURE_FLAGS=(
--prefix=${PREFIX}
--enable-interwork
--enable-multilib
#--enable-lto
--disable-nls
)
GDB_VER=7.6
GDB_MD5=fda57170e4d11cdde74259ca575412a8
GDB_CONFIGURE_FLAGS=(
--target=${TARGET}
--prefix=${PREFIX}
--with-gmp=${PREFIX}
--with-mpfr=${PREFIX}
--with-mpc=${PREFIX}
--enable-interwork
--enable-multilib
--disable-nls
)
GMP_VER=5.1.3
GMP_MD5=e5fe367801ff067b923d1e6a126448aa
GMP_CONFIGURE_FLAGS=(
--prefix=${PREFIX}
--enable-cxx
--disable-nls
)
MPFR_VER=3.1.2
MPFR_MD5=e3d203d188b8fe60bb6578dd3152e05c
MPFR_CONFIGURE_FLAGS=(
--prefix=${PREFIX}
--with-gmp=${PREFIX}
--disable-nls
)
MPC_VER=1.0.2
MPC_MD5=68fadff3358fb3e7976c7a398a0af4c3
MPC_CONFIGURE_FLAGS=(
--prefix=${PREFIX}
--with-gmp=${PREFIX}
--with-mpfr=${PREFIX}
--disable-nls
)
DOWNLOADER=wget
DOWNLOADER_OPTS="-nv -c"
SPACE_NEEDED=3264960
build_binutils() {
cd ${BUILDDIR}
echo
[[ -e .binutils_built ]] && return
local TARGETS=(${TARGET} i386-efi-pe)
for i in 0 1; do
echo "Building binutils (${BINUTILS_VER}) for ${TARGETS[$i]} ..."
rm -rf binutils-${TARGETS[$i]}-build &&
mkdir -p binutils-${TARGETS[$i]}-build &&
cd binutils-${TARGETS[$i]}-build &&
../binutils-${BINUTILS_VER}/configure "${BINUTILS_CONFIGURE_FLAGS[@]}" --target=${TARGETS[$i]} --enable-targets=${TARGETS[$i]},${TARGETS[1-$i]} &&
make ${MAKE_THREADS} all &&
make install &&
cd ${BUILDDIR} ||
return $?
done
date -uIns > .binutils_built
}
build_gcc() {
cd ${BUILDDIR}
echo
echo "Building gcc (${GCC_VER}) ..."
[[ -e .gcc_built ]] && return
rm -rf gcc-build && mkdir -p gcc-build && cd gcc-build &&
../gcc-${GCC_VER}/configure "${GCC_CONFIGURE_FLAGS[@]}" &&
make ${MAKE_THREADS} all &&
make install &&
cd ${BUILDDIR} &&
date -uIns > .gcc_built
}
build_newlib() {
cd ${BUILDDIR}
echo
echo "Building newlib (${NEWLIB_VER}) ..."
[[ -e .newlib_built ]] && return
rm -rf newlib-build && mkdir -p newlib-build && cd newlib-build &&
CFLAGS="${CFLAGS} -DREENTRANT_SYSCALLS_PROVIDED -DMALLOC_PROVIDED" \
../newlib-${NEWLIB_VER}/configure "${NEWLIB_CONFIGURE_FLAGS[@]}"
make ${MAKE_THREADS} TARGET_CFLAGS="${NEWLIB_TARGET_CFLAGS[*]}" all &&
make install &&
cd ${BUILDDIR} &&
date -uIns > .newlib_built
}
build_gdb() {
cd ${BUILDDIR}
echo
echo "Building gdb (${GDB_VER}) ..."
[[ -e .gdb_built ]] && return
rm -rf gdb-build && mkdir -p gdb-build && cd gdb-build &&
../gdb-${GDB_VER}/configure "${GDB_CONFIGURE_FLAGS[@]}" &&
make ${MAKE_THREADS} all CFLAGS="-U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=0" &&
make install &&
cd ${BUILDDIR} &&
date -uIns > .gdb_built
}
build_gmp() {
cd ${BUILDDIR}
echo
echo "Building gmp (${GMP_VER}) ..."
[[ -e .gmp_built ]] && return
rm -rf gmp-build && mkdir -p gmp-build && cd gmp-build &&
../gmp-${GMP_VER}/configure "${GMP_CONFIGURE_FLAGS[@]}" &&
make ${MAKE_THREADS} all &&
make install &&
cd ${BUILDDIR} &&
date -uIns > .gmp_built
}
build_mpfr() {
cd ${BUILDDIR}
echo
echo "Building mpfr (${MPFR_VER}) ..."
[[ -e .mpfr_built ]] && return
rm -rf mpfr-build && mkdir -p mpfr-build && cd mpfr-build &&
../mpfr-${MPFR_VER}/configure "${MPFR_CONFIGURE_FLAGS[@]}" &&
make ${MAKE_THREADS} all &&
make install &&
cd ${BUILDDIR} &&
date -uIns > .mpfr_built
}
build_mpc() {
cd ${BUILDDIR}
echo
echo "Building mpc (${MPC_VER}) ..."
[[ -e .mpc_built ]] && return
rm -rf mpc-build && mkdir -p mpc-build && cd mpc-build &&
../mpc-${MPC_VER}/configure "${MPC_CONFIGURE_FLAGS[@]}" &&
make ${MAKE_THREADS} all &&
make install &&
cd ${BUILDDIR} &&
date -uIns > .mpc_built
}
clean() {
echo
echo "Cleaning up..."
for F in binutils gcc newlib gdb gmp mpfr mpc; do
rm -rf ./.${F}_extracted ./.${F}_built ./${F}_build || return $?
done
}
export PATH=$PATH:${PREFIX}/bin
download() {
echo
echo "Downloading TAR files."
download_file ftp://ftp.cs.tu-berlin.de/pub/gnu/binutils binutils-${BINUTILS_VER}.tar.bz2 ${BINUTILS_MD5} &&
download_file ftp://ftp.cs.tu-berlin.de/pub/gnu/gcc/gcc-${GCC_VER} gcc-${GCC_VER}.tar.bz2 ${GCC_MD5} &&
download_file ftp://ftp.cs.tu-berlin.de/pub/gnu/gdb gdb-${GDB_VER}.tar.bz2 ${GDB_MD5} &&
download_file ftp://ftp.cs.tu-berlin.de/pub/gnu/gmp gmp-${GMP_VER}.tar.xz ${GMP_MD5} &&
download_file ftp://ftp.cs.tu-berlin.de/pub/gnu/mpfr mpfr-${MPFR_VER}.tar.xz ${MPFR_MD5} &&
download_file ftp://ftp.cs.tu-berlin.de/pub/gnu/mpc mpc-${MPC_VER}.tar.gz ${MPC_MD5} &&
download_file ftp://sources.redhat.com/pub/newlib newlib-${NEWLIB_VER}.tar.gz ${NEWLIB_MD5}
}
extract() {
echo
echo "Extracting TAR files."
if [[ ! -x ./binutils-${BINUTILS_VER}/configure ]]; then
tar xjf ${TMP_DIR}/binutils-${BINUTILS_VER}.tar.bz2 || return $?
fi
if [[ ! -x ./gcc-${GCC_VER}/configure ]]; then
tar xjf ${TMP_DIR}/gcc-${GCC_VER}.tar.bz2 || return $?
fi
if [[ ! -x ./gdb-${GDB_VER}/configure ]]; then
tar xjf ${TMP_DIR}/gdb-${GDB_VER}.tar.bz2 || return $?
fi
if [[ ! -x ./gmp-${GMP_VER}/configure ]]; then
tar xJf ${TMP_DIR}/gmp-${GMP_VER}.tar.xz || return $?
fi
if [[ ! -x ./mpfr-${MPFR_VER}/configure ]]; then
tar xJf ${TMP_DIR}/mpfr-${MPFR_VER}.tar.xz || return $?
fi
if [[ ! -x ./mpc-${MPC_VER}/configure ]]; then
tar xzf ${TMP_DIR}/mpc-${MPC_VER}.tar.gz || return $?
fi
if [[ ! -x ./newlib-${NEWLIB_VER}/configure ]]; then
tar xzf ${TMP_DIR}/newlib-${NEWLIB_VER}.tar.gz &&
for (( I=0; I < ${#NEWLIB_PATCHES[@]}; I+=2 )); do
echo "Applying Newlib patch $((${I} / 2 + 1))"
echo "${NEWLIB_PATCHES[$I+1]}" | patch -e ./newlib-${NEWLIB_VER}/"${NEWLIB_PATCHES[$I]}" || return $?
done
fi
}
download_file() {
echo "Downloading ${1}/${2}..."
cd ${TMP_DIR} &&
${DOWNLOADER} ${DOWNLOADER_OPTS} $1/$2 &&
echo -n "Checking MD5 of " &&
echo "${3} ${2}" | md5sum -c - &&
cd ${BUILDDIR}
}
check_space() {
echo
echo "Checking disk space in ${TMP_DIR}"
FREETMP=$(df ${BUILDDIR} | awk '{ if (NR == 2) print $4}')
if [[ $FREETMP -lt $SPACE_NEEDED ]]; then
echo "Not enough available space in ${TMP_DIR}."
echo "Minimum ${SPACE_NEEDED} free bytes required."
return 1
fi
}
all() {
echo
echo "Starting in ${BUILDDIR}. Installing to ${PREFIX}."
check_space &&
download &&
extract &&
build_binutils &&
build_gmp &&
build_mpfr &&
build_mpc &&
build_gcc &&
build_newlib &&
build_gdb &&
echo &&
echo 'Success!' &&
echo "Insert \"export PATH=${PREFIX}/bin:\$PATH\" to your .bashrc"
}
usage() {
echo "usage: ${0} [all | download | extract | clean | build_[binutils|gcc|newlib|gdb|gmp|mpfr|mpc]]"
echo "example: ${0} all"
echo ""
echo "Builds a(n) \"${TARGET}\" toolchain,"
echo "installs into \"${PREFIX}\", and"
echo "uses \"${TMP_DIR}\" as temp."
echo ""
echo "Use PREFIX=... and TMP_DIR=... to change these directories."
echo "Use TARGET=... to change the target (affects PREFIX)."
echo ""
echo "Run like \"MAKE_THREADS=-j$(nproc) ${0} all\" to speed up on multicore systems."
}
if [[ -z $1 ]]; then
usage
exit 1
fi
mkdir -p ${PREFIX} &&
mkdir -p ${TMP_DIR} &&
mkdir -p ${BUILDDIR} &&
cd ${BUILDDIR} &&
for arg; do
${arg} || exit $?
done

167
dist/tools/toolchains/build_x86_newlib.sh vendored Executable file
View File

@ -0,0 +1,167 @@
#!/bin/bash
# directory to download source files and store intermediates
[[ -z $TARGET ]] && TARGET=i586-none-elf
[[ -z $TMP_DIR ]] && TMP_DIR=/var/tmp/RIOT-toolchain-${USER}
[[ -z $BUILDDIR ]] && BUILDDIR=${TMP_DIR}/x86
[[ -z $PREFIX ]] && PREFIX=${PWD}/toolchain/x86
[[ -z $MAKE_THREADS ]] && MAKE_THREADS=
NEWLIB_VER=2.1.0
NEWLIB_MD5=c6559d83ecce4728a52f0ce7ec80de97
NEWLIB_CONFIGURE_FLAGS=(
--target=${TARGET}
--prefix=${PREFIX}
--with-gmp=${PREFIX}
--with-mpfr=${PREFIX}
--with-mpc=${PREFIX}
--enable-interwork
--enable-multilib
--enable-target-optspace
--enable-lto
--disable-newlib-supplied-syscalls
--enable-newlib-reent-small
--enable-newlib-io-long-long
--enable-newlib-io-float
--enable-newlib-io-pos-args
--enable-newlib-io-c99-formats
--enable-newlib-multithread
)
NEWLIB_PATCHES=(
libgloss/i386/cygmon-gmon.c
'64d
59a
#include <string.h>
.'
libgloss/arm/_exit.c
'14a
__builtin_unreachable ();
.'
newlib/libc/stdlib/mallocr.c
'3700d
3434,3440d
3422,3424d
3400,3404d
3357,3370d
3278,3355d
1764,1898d
388d
384,386d
1,3d'
newlib/libc/stdlib/mlock.c
'63,64d
1d'
)
NEWLIB_TARGET_CFLAGS=(
-DREENTRANT_SYSCALLS_PROVIDED
-DMALLOC_PROVIDED
-DSIGNAL_PROVIDED
-ggdb3
-Os
-flto
-fomit-frame-pointer
-ffunction-sections
-fdata-sections
)
DOWNLOADER=wget
DOWNLOADER_OPTS="-nv -c"
build_newlib() {
cd ${BUILDDIR}
echo
echo "Building newlib (${NEWLIB_VER}) ..."
[[ -e .newlib_built ]] && return
rm -rf newlib-build && mkdir -p newlib-build && cd newlib-build &&
CFLAGS="${CFLAGS} -DREENTRANT_SYSCALLS_PROVIDED -DMALLOC_PROVIDED" \
../newlib-${NEWLIB_VER}/configure "${NEWLIB_CONFIGURE_FLAGS[@]}"
make ${MAKE_THREADS} TARGET_CFLAGS="${NEWLIB_TARGET_CFLAGS[*]}" all &&
make install &&
cd ${BUILDDIR} &&
date -uIns > .newlib_built
}
clean() {
echo
echo "Cleaning up..."
for F in newlib; do
rm -rf ./.${F}_extracted ./.${F}_built ./${F}_build || return $?
done
}
download() {
echo
echo "Downloading TAR files."
download_file ftp://sources.redhat.com/pub/newlib newlib-${NEWLIB_VER}.tar.gz ${NEWLIB_MD5}
}
extract() {
echo
echo "Extracting TAR files."
if [[ ! -x ./newlib-${NEWLIB_VER}/configure ]]; then
tar xzf ${TMP_DIR}/newlib-${NEWLIB_VER}.tar.gz &&
for (( I=0; I < ${#NEWLIB_PATCHES[@]}; I+=2 )); do
echo "Applying Newlib patch $((${I} / 2 + 1))"
echo "${NEWLIB_PATCHES[$I+1]}" | patch -e ./newlib-${NEWLIB_VER}/"${NEWLIB_PATCHES[$I]}" || return $?
done
fi
}
download_file() {
echo "Downloading ${1}/${2}..."
cd ${TMP_DIR} &&
${DOWNLOADER} ${DOWNLOADER_OPTS} $1/$2 &&
echo -n "Checking MD5 of " &&
echo "${3} ${2}" | md5sum -c - &&
cd ${BUILDDIR}
}
all() {
echo
echo "Starting in ${BUILDDIR}. Installing to ${PREFIX}."
download &&
extract &&
build_newlib &&
echo &&
echo 'Success!' &&
echo "Insert \"export PATH=${PREFIX}/bin:\$PATH\" to your .bashrc"
}
usage() {
echo "usage: ${0} [all | download | extract | clean | build_newlib]"
echo "example: ${0} all"
echo ""
echo "Builds a(n) \"${TARGET}\" toolchain,"
echo "installs into \"${PREFIX}\", and"
echo "uses \"${TMP_DIR}\" as temp."
echo ""
echo "Use PREFIX=... and TMP_DIR=... to change these directories."
echo "Use TARGET=... to change the target (affects PREFIX)."
echo ""
echo "Run like \"MAKE_THREADS=-j$(nproc) ${0} all\" to speed up on multicore systems."
}
if [[ -z $1 ]]; then
usage
exit 1
fi
mkdir -p ${PREFIX} &&
mkdir -p ${TMP_DIR} &&
mkdir -p ${BUILDDIR} &&
cd ${BUILDDIR} &&
for arg; do
${arg} || exit $?
done

View File

@ -28,12 +28,13 @@ export RIOTBASE ?= $(CURDIR)/../..
export QUIET ?= 1
BOARD_INSUFFICIENT_RAM := chronos msb-430h telosb wsn430-v1_3b wsn430-v1_4
BOARD_BLACKLIST := arduino-due mbed_lpc1768 msb-430 pttu redbee-econotag udoo z1
BOARD_BLACKLIST := arduino-due mbed_lpc1768 msb-430 pttu redbee-econotag udoo z1 qemu-i386
# mbed_lpc1768: see https://github.com/RIOT-OS/RIOT/issues/675
# msb-430: see https://github.com/RIOT-OS/RIOT/issues/658
# pttu: see https://github.com/RIOT-OS/RIOT/issues/659
# redbee-econotag: see https://github.com/RIOT-OS/RIOT/issues/676
# z1: lacks RTC features
# qemu-i386: no tranceiver, yet
# Modules to include:

View File

@ -28,12 +28,13 @@ export RIOTBASE ?= $(CURDIR)/../..
export QUIET ?= 1
BOARD_INSUFFICIENT_RAM := chronos msb-430h telosb wsn430-v1_3b wsn430-v1_4
BOARD_BLACKLIST := arduino-due mbed_lpc1768 msb-430 pttu redbee-econotag udoo z1
BOARD_BLACKLIST := arduino-due mbed_lpc1768 msb-430 pttu redbee-econotag udoo z1 qemu-i386
# mbed_lpc1768: see https://github.com/RIOT-OS/RIOT/issues/675
# msb-430: see https://github.com/RIOT-OS/RIOT/issues/658
# pttu: see https://github.com/RIOT-OS/RIOT/issues/659
# redbee-econotag: see https://github.com/RIOT-OS/RIOT/issues/676
# z1: lacks RTC features
# qemu-i386: no tranceiver, yet
# Modules to include:

View File

@ -35,10 +35,11 @@ ifeq ($(shell $(CC) -Wno-cpp -E - 2>/dev/null >/dev/null dev/null ; echo $$?),0)
endif
BOARD_INSUFFICIENT_RAM := chronos msb-430h redbee-econotag telosb wsn430-v1_3b wsn430-v1_4 z1
BOARD_BLACKLIST := arduino-due mbed_lpc1768 msb-430 pttu udoo
BOARD_BLACKLIST := arduino-due mbed_lpc1768 msb-430 pttu udoo qemu-i386
# mbed_lpc1768: see https://github.com/RIOT-OS/RIOT/issues/675
# msb-430: see https://github.com/RIOT-OS/RIOT/issues/658
# pttu: see https://github.com/RIOT-OS/RIOT/issues/659
# qemu-i386: no tranceiver, yet
# Modules to include:

View File

@ -52,7 +52,11 @@
*/
int close(int fildes);
typedef uint32_t useconds_t;
#ifndef __USECONDS_T_TYPE
typedef unsigned long __USECONDS_T_TYPE;
#endif
typedef __USECONDS_T_TYPE __useconds_t;
typedef __useconds_t useconds_t;
/**
* @brief the caller will sleep for given amount of micro seconds

View File

@ -40,5 +40,8 @@ endif
ifneq (,$(filter random,$(USEMODULE)))
SRC += sc_mersenne.c
endif
ifeq ($(CPU),x86)
SRC += sc_x86_lspci.c
endif
include $(RIOTBASE)/Makefile.base

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
*/
/**
* @ingroup shell_commands
* @ingroup x86-irq
* @{
*
* @file
* @brief List PCI devices
*
* @author René Kijewski <rene.kijewski@fu-berlin.de>
*
* @}
*/
#include "x86_pci.h"
#include "x86_pci_strings.h"
#include <stdio.h>
extern void _x86_lspci(int argc, char **argv)
{
(void) argc;
(void) argv;
unsigned index = 0;
while (1) {
const char *baseclass_name, *subclass_name, *vendor_name, *device_name;
const x86_known_pci_device_t *d = x86_enumerate_pci_devices(&index);
if (!d) {
break;
}
subclass_name = x86_pci_subclass_to_string(d->class.baseclass,
d->class.subclass,
d->class.programming_interface,
&baseclass_name);
vendor_name = x86_pci_device_id_to_string(d->vendor.vendor_id,
d->vendor.device_id,
&device_name);
printf("%02x:%02x.%x \"%s\": \"%s\" (%s: %s, rev: %02hhx)\n",
d->bus, d->dev, d->fun,
vendor_name, device_name,
baseclass_name, subclass_name, d->class.revision_id);
}
}

View File

@ -53,6 +53,9 @@ extern void _get_current_handler(int argc, char **argv);
extern void _reset_current_handler(int argc, char **argv);
#endif
#ifdef CPU_X86
extern void _x86_lspci(int argc, char **argv);
#endif
/* configure available commands for each transceiver device: */
#ifdef MODULE_TRANSCEIVER
@ -189,6 +192,9 @@ const shell_command_t _shell_command_list[] = {
#ifdef MODULE_RANDOM
{ "mersenne_init", "initializes the PRNG", _mersenne_init },
{ "mersenne_get", "returns 32 bit of pseudo randomness", _mersenne_get },
#endif
#ifdef CPU_X86
{"lspci", "Lists PCI devices", _x86_lspci},
#endif
{NULL, NULL, NULL}
};

View File

@ -1,6 +1,7 @@
export PROJECT = test_net_if
BOARD_BLACKLIST = mbed_lpc1768 arduino-due udoo
BOARD_BLACKLIST = mbed_lpc1768 arduino-due udoo qemu-i386
# qemu-i386: no tranceiver, yet
include ../Makefile.tests_common

View File

@ -2,9 +2,10 @@ export PROJECT = test_pnet
include ../Makefile.tests_common
BOARD_INSUFFICIENT_RAM := chronos msb-430h redbee-econotag telosb wsn430-v1_3b wsn430-v1_4 z1
BOARD_BLACKLIST := arduino-due mbed_lpc1768 msb-430 udoo
BOARD_BLACKLIST := arduino-due mbed_lpc1768 msb-430 udoo qemu-i386
# mbed_lpc1768: see https://github.com/RIOT-OS/RIOT/issues/675
# msb-430: see https://github.com/RIOT-OS/RIOT/issues/658
# qemu-i386: no tranceiver, yet
USEMODULE += posix
USEMODULE += pnet