diff --git a/.gitignore b/.gitignore index c2c6bd86cc..2fd4ca1d1e 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,4 @@ cachegrind.out* .project .cproject .settings +/toolchain diff --git a/boards/qemu-i386/Makefile b/boards/qemu-i386/Makefile new file mode 100644 index 0000000000..2fbf158dbd --- /dev/null +++ b/boards/qemu-i386/Makefile @@ -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 ; diff --git a/boards/qemu-i386/Makefile.include b/boards/qemu-i386/Makefile.include new file mode 100644 index 0000000000..5e3d17ce02 --- /dev/null +++ b/boards/qemu-i386/Makefile.include @@ -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 diff --git a/boards/qemu-i386/include/board.h b/boards/qemu-i386/include/board.h new file mode 100644 index 0000000000..846115a7b2 --- /dev/null +++ b/boards/qemu-i386/include/board.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2014 René Kijewski + * + * 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 + */ + +#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 + +/** + * @} + */ diff --git a/boards/qemu-i386/include/cpu-conf.h b/boards/qemu-i386/include/cpu-conf.h new file mode 100644 index 0000000000..d43449895d --- /dev/null +++ b/boards/qemu-i386/include/cpu-conf.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2014 René Kijewski + * + * 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 + */ + +#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 + +/** @} */ diff --git a/boards/qemu-i386/x86_board_init.c b/boards/qemu-i386/x86_board_init.c new file mode 100644 index 0000000000..f35b39e9b7 --- /dev/null +++ b/boards/qemu-i386/x86_board_init.c @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2014 René Kijewski + * + * 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 + * + * @} + */ + +#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); +} diff --git a/boards/qemu-i386/x86_board_lpm.c b/boards/qemu-i386/x86_board_lpm.c new file mode 100644 index 0000000000..0df2580107 --- /dev/null +++ b/boards/qemu-i386/x86_board_lpm.c @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2014 René Kijewski + * + * 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 + * + * @} + */ + +#include + +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 +} diff --git a/boards/x86-multiboot-common/Makefile b/boards/x86-multiboot-common/Makefile new file mode 100644 index 0000000000..dee7269a50 --- /dev/null +++ b/boards/x86-multiboot-common/Makefile @@ -0,0 +1,3 @@ +MODULE = x86-multiboot-common_base + +include $(RIOTBASE)/Makefile.base diff --git a/boards/x86-multiboot-common/Makefile.include b/boards/x86-multiboot-common/Makefile.include new file mode 100644 index 0000000000..d4c20343a5 --- /dev/null +++ b/boards/x86-multiboot-common/Makefile.include @@ -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 diff --git a/boards/x86-multiboot-common/dist/link b/boards/x86-multiboot-common/dist/link new file mode 100755 index 0000000000..06bda0ce80 --- /dev/null +++ b/boards/x86-multiboot-common/dist/link @@ -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[@]}" diff --git a/boards/x86-multiboot-common/include/multiboot.h b/boards/x86-multiboot-common/include/multiboot.h new file mode 100644 index 0000000000..fa6ab02b61 --- /dev/null +++ b/boards/x86-multiboot-common/include/multiboot.h @@ -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, +}; + +/** @} */ diff --git a/boards/x86-multiboot-common/linker.ld b/boards/x86-multiboot-common/linker.ld new file mode 100644 index 0000000000..2a9f0a2a85 --- /dev/null +++ b/boards/x86-multiboot-common/linker.ld @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2014 René Kijewski + * + * 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. */ +} diff --git a/boards/x86-multiboot-common/startup.c b/boards/x86-multiboot-common/startup.c new file mode 100644 index 0000000000..2596473c2b --- /dev/null +++ b/boards/x86-multiboot-common/startup.c @@ -0,0 +1,146 @@ +/* + * Copyright (C) 2014 René Kijewski + * + * 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 + * + * @} + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#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(); +} diff --git a/cpu/x86/Makefile b/cpu/x86/Makefile new file mode 100644 index 0000000000..08e25a221a --- /dev/null +++ b/cpu/x86/Makefile @@ -0,0 +1,3 @@ +MODULE = cpu + +include $(RIOTBASE)/Makefile.base diff --git a/cpu/x86/Makefile.include b/cpu/x86/Makefile.include new file mode 100644 index 0000000000..ab760c38e1 --- /dev/null +++ b/cpu/x86/Makefile.include @@ -0,0 +1,4 @@ +export INCLUDES += -I$(RIOTCPU)/x86/include + +export USEMODULE += quad_math +export USEPKG += tlsf diff --git a/cpu/x86/include/cpu.h b/cpu/x86/include/cpu.h new file mode 100644 index 0000000000..c2c78c6201 --- /dev/null +++ b/cpu/x86/include/cpu.h @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2014 René Kijewski + * + * 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 + */ + +#ifndef CPU__X86__CPU__H__ +#define CPU__X86__CPU__H__ + +#include "attributes.h" +#include "irq.h" +#include "ucontext.h" +#include "cpu-conf.h" + +#include +#include +#include +#include + +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. + *
  • The .bss has to be cleared before. + *
  • The stack has to be set up, probably somewhere in the low memory. + *
  • The A20 line has to be activated, because all the code is beyong 1MB. + *
  • Paging must be disabled. + *
  • The SS, DS, and CS must span the whole 4GB of RAM. + *
  • 32 bit protected mode must be entered. + *
  • 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(): + *
  • You must not spawn thread_create() in this function. + *
  • 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 + +/** @} */ diff --git a/cpu/x86/include/hwtimer_cpu.h b/cpu/x86/include/hwtimer_cpu.h new file mode 100644 index 0000000000..f1872ab092 --- /dev/null +++ b/cpu/x86/include/hwtimer_cpu.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2014 René Kijewski + * + * 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 + */ + +#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 + +/** @} */ diff --git a/cpu/x86/include/ucontext.h b/cpu/x86/include/ucontext.h new file mode 100644 index 0000000000..c9d572cb0d --- /dev/null +++ b/cpu/x86/include/ucontext.h @@ -0,0 +1,166 @@ +/* + * Copyright (C) 2014 René Kijewski + * + * 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 + */ + +#ifndef CPU__X86__UCONTEXT__HPP__ +#define CPU__X86__UCONTEXT__HPP__ + +#include +#include + +/** + * @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 + +/** @} */ diff --git a/cpu/x86/include/x86_cmos.h b/cpu/x86/include/x86_cmos.h new file mode 100644 index 0000000000..7aef69de29 --- /dev/null +++ b/cpu/x86/include/x86_cmos.h @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2014 René Kijewski + * + * 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 + */ + +#ifndef CPU__X86__CMOS__H__ +#define CPU__X86__CMOS__H__ + +#include "x86_ports.h" + +#include + +#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 + +/** @} */ diff --git a/cpu/x86/include/x86_hwtimer.h b/cpu/x86/include/x86_hwtimer.h new file mode 100644 index 0000000000..138a28461f --- /dev/null +++ b/cpu/x86/include/x86_hwtimer.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2014 René Kijewski + * + * 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 + */ + +#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 + +/** @} */ diff --git a/cpu/x86/include/x86_interrupts.h b/cpu/x86/include/x86_interrupts.h new file mode 100644 index 0000000000..7a1ae7a2f2 --- /dev/null +++ b/cpu/x86/include/x86_interrupts.h @@ -0,0 +1,157 @@ +/* + * Copyright (C) 2014 René Kijewski + * + * 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 + */ + +#ifndef CPU__X86__INTERRUPTS__H__ +#define CPU__X86__INTERRUPTS__H__ + +#include "ucontext.h" + +#include + +/** + * @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 + +/** @} */ diff --git a/cpu/x86/include/x86_kernel_memory.h b/cpu/x86/include/x86_kernel_memory.h new file mode 100644 index 0000000000..b9cf1032b3 --- /dev/null +++ b/cpu/x86/include/x86_kernel_memory.h @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2014 René Kijewski + * + * 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 + */ + +#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 + +/** @} */ diff --git a/cpu/x86/include/x86_memory.h b/cpu/x86/include/x86_memory.h new file mode 100644 index 0000000000..c92ccf51f9 --- /dev/null +++ b/cpu/x86/include/x86_memory.h @@ -0,0 +1,171 @@ +/* + * Copyright (C) 2014 René Kijewski + * + * 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 + */ + +#ifndef CPU__X86__MEMORY__H__ +#define CPU__X86__MEMORY__H__ + +#include +#include + +/** + * @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 + +/** @} */ diff --git a/cpu/x86/include/x86_pci.h b/cpu/x86/include/x86_pci.h new file mode 100644 index 0000000000..146ddf7dd5 --- /dev/null +++ b/cpu/x86/include/x86_pci.h @@ -0,0 +1,415 @@ +/* + * Copyright (C) 2014 René Kijewski + * + * 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 + */ + +#ifndef CPU__X86__PCI__H__ +#define CPU__X86__PCI__H__ + +#include "x86_pci_init.h" +#include "x86_pic.h" + +#include +#include +#include + +/** + * @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 + +/** @} */ diff --git a/cpu/x86/include/x86_pci_init.h b/cpu/x86/include/x86_pci_init.h new file mode 100644 index 0000000000..d9078f9b31 --- /dev/null +++ b/cpu/x86/include/x86_pci_init.h @@ -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 diff --git a/cpu/x86/include/x86_pci_strings.h b/cpu/x86/include/x86_pci_strings.h new file mode 100644 index 0000000000..f0b4efa38e --- /dev/null +++ b/cpu/x86/include/x86_pci_strings.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2014 René Kijewski + * + * 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 + */ + +#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 + +/** @} */ diff --git a/cpu/x86/include/x86_pic.h b/cpu/x86/include/x86_pic.h new file mode 100644 index 0000000000..d381a46475 --- /dev/null +++ b/cpu/x86/include/x86_pic.h @@ -0,0 +1,168 @@ +/* + * Copyright (C) 2014 René Kijewski + * + * 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 + */ + +#ifndef CPU__X86__PIC__H__ +#define CPU__X86__PIC__H__ + +#include + +/** + * @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 + +/** @} */ diff --git a/cpu/x86/include/x86_pit.h b/cpu/x86/include/x86_pit.h new file mode 100644 index 0000000000..a2d88dee77 --- /dev/null +++ b/cpu/x86/include/x86_pit.h @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2014 René Kijewski + * + * 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 + */ + +#ifndef CPU__X86__PIT__H__ +#define CPU__X86__PIT__H__ + +#include +#include + +#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 + +/** @} */ diff --git a/cpu/x86/include/x86_ports.h b/cpu/x86/include/x86_ports.h new file mode 100644 index 0000000000..364b89f503 --- /dev/null +++ b/cpu/x86/include/x86_ports.h @@ -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 +#include + +/** + * @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 diff --git a/cpu/x86/include/x86_reboot.h b/cpu/x86/include/x86_reboot.h new file mode 100644 index 0000000000..ae7077888a --- /dev/null +++ b/cpu/x86/include/x86_reboot.h @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2014 René Kijewski + * + * 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 + */ + +#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 + +/** @} */ diff --git a/cpu/x86/include/x86_registers.h b/cpu/x86/include/x86_registers.h new file mode 100644 index 0000000000..f84b2b18fe --- /dev/null +++ b/cpu/x86/include/x86_registers.h @@ -0,0 +1,252 @@ +/* + * Copyright (C) 2014 René Kijewski + * + * 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 + */ + +#ifndef CPU__X86__REGISTERS__H__ +#define CPU__X86__REGISTERS__H__ + +#include + +/* see "Intel® Quark SoC X1000 Core Developer’s 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 + +/** @} */ diff --git a/cpu/x86/include/x86_rtc.h b/cpu/x86/include/x86_rtc.h new file mode 100644 index 0000000000..0ef3445b95 --- /dev/null +++ b/cpu/x86/include/x86_rtc.h @@ -0,0 +1,211 @@ +/* + * Copyright (C) 2014 René Kijewski + * + * 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 + */ + +#ifndef CPU__X86__RTC__H__ +#define CPU__X86__RTC__H__ + +#include "msg.h" +#include "x86_cmos.h" + +#include + +/** + * @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 + *
  • there was a power loss, + *
  • the stored time is invalid (i.e. February 30), or + *
  • 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 + +/** @} */ diff --git a/cpu/x86/include/x86_threading.h b/cpu/x86/include/x86_threading.h new file mode 100644 index 0000000000..9c6c1da89f --- /dev/null +++ b/cpu/x86/include/x86_threading.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2014 René Kijewski + * + * 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 + */ + +#ifndef CPU__X86__THREADING__H__ +#define CPU__X86__THREADING__H__ + +#include + +/** + * @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 + +/** @} */ diff --git a/cpu/x86/include/x86_uart.h b/cpu/x86/include/x86_uart.h new file mode 100644 index 0000000000..48be9d10da --- /dev/null +++ b/cpu/x86/include/x86_uart.h @@ -0,0 +1,176 @@ +/* + * Copyright (C) 2014 René Kijewski + * + * 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 + */ + +#ifndef CPU__X86__UART__H__ +#define CPU__X86__UART__H__ + +#include "x86_pic.h" + +#include +#include +#include +#include + +/** + * @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 + +/** @} */ diff --git a/cpu/x86/include/x86_videoram.h b/cpu/x86/include/x86_videoram.h new file mode 100644 index 0000000000..371f41026e --- /dev/null +++ b/cpu/x86/include/x86_videoram.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2014 René Kijewski + * + * 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 + */ + +#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 + +/** @} */ diff --git a/cpu/x86/nop_nop_nop.inc b/cpu/x86/nop_nop_nop.inc new file mode 100644 index 0000000000..49b9ec1f2e --- /dev/null +++ b/cpu/x86/nop_nop_nop.inc @@ -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"); diff --git a/cpu/x86/x86_atomic.c b/cpu/x86/x86_atomic.c new file mode 100644 index 0000000000..988340410f --- /dev/null +++ b/cpu/x86/x86_atomic.c @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2014 René Kijewski + * + * 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 + * + * @} + */ + +#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; +} diff --git a/cpu/x86/x86_cmos.c b/cpu/x86/x86_cmos.c new file mode 100644 index 0000000000..e35b769a26 --- /dev/null +++ b/cpu/x86/x86_cmos.c @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2014 René Kijewski + * + * 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 + * + * @} + */ + +#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); + } +} diff --git a/cpu/x86/x86_glue.c b/cpu/x86/x86_glue.c new file mode 100644 index 0000000000..4f4697be69 --- /dev/null +++ b/cpu/x86/x86_glue.c @@ -0,0 +1,137 @@ +/* + * Copyright (C) 2014 René Kijewski + * + * 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 + * + * @} + */ + +#include "attributes.h" +#include "cpu.h" +#include "kernel_internal.h" +#include "sched.h" +#include "x86_uart.h" + +#include +#include +#include + +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; +} + diff --git a/cpu/x86/x86_hwtimer.c b/cpu/x86/x86_hwtimer.c new file mode 100644 index 0000000000..92c1acebec --- /dev/null +++ b/cpu/x86/x86_hwtimer.c @@ -0,0 +1,540 @@ +/* + * Copyright (C) 2014 René Kijewski + * + * 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 + * + * @} + */ + +#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 +#include + +#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(); + } +} diff --git a/cpu/x86/x86_interrupts.c b/cpu/x86/x86_interrupts.c new file mode 100644 index 0000000000..8c98629e7c --- /dev/null +++ b/cpu/x86/x86_interrupts.c @@ -0,0 +1,431 @@ +/* + * Copyright (C) 2014 René Kijewski + * + * 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 + * + * @} + */ + +#include "x86_interrupts.h" +#include "x86_memory.h" +#include "x86_ports.h" +#include "x86_registers.h" +#include "x86_threading.h" + +#include +#include +#include +#include +#include + +#include +#include + +#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(""); + 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(""); +} + +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; +} diff --git a/cpu/x86/x86_memory.c b/cpu/x86/x86_memory.c new file mode 100644 index 0000000000..ed461777bf --- /dev/null +++ b/cpu/x86/x86_memory.c @@ -0,0 +1,457 @@ +/* + * Copyright (C) 2014 René Kijewski + * + * 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 + * + * @} + */ + +#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 +#include +#include +#include +#include + +/* Compare Figure 44 (p. 99) of "Intel® Quark SoC X1000 Core Developer’s 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); +} diff --git a/cpu/x86/x86_pci.c b/cpu/x86/x86_pci.c new file mode 100644 index 0000000000..08f20983db --- /dev/null +++ b/cpu/x86/x86_pci.c @@ -0,0 +1,331 @@ +/* + * Copyright (C) 2014 René Kijewski + * + * 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 + * + * @} + */ + +#include "x86_memory.h" +#include "x86_pci.h" +#include "x86_pci_init.h" +#include "x86_pci_strings.h" +#include "x86_ports.h" + +#include +#include +#include +#include + +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; +} diff --git a/cpu/x86/x86_pci_init.c b/cpu/x86/x86_pci_init.c new file mode 100644 index 0000000000..8d828fa4b7 --- /dev/null +++ b/cpu/x86/x86_pci_init.c @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2014 René Kijewski + * + * 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 + * + * @} + */ + +#include "x86_pci.h" +#include "x86_pci_init.h" + +void x86_init_pci_devices(void) +{ + /* no PCI devices are implemented, yet */ +} diff --git a/cpu/x86/x86_pci_strings.c b/cpu/x86/x86_pci_strings.c new file mode 100644 index 0000000000..436c4b319e --- /dev/null +++ b/cpu/x86/x86_pci_strings.c @@ -0,0 +1,409 @@ +/* + * Copyright (C) 2014 René Kijewski + * + * 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 + * + * @} + */ + +#include "x86_pci_strings.h" + +#include + +/* 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; +} diff --git a/cpu/x86/x86_pic.c b/cpu/x86/x86_pic.c new file mode 100644 index 0000000000..80c1fabc93 --- /dev/null +++ b/cpu/x86/x86_pic.c @@ -0,0 +1,192 @@ +/* + * Copyright (C) 2014 René Kijewski + * + * 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 + * + * @} + */ + +#include "x86_interrupts.h" +#include "x86_pic.h" +#include "x86_ports.h" +#include "cpu.h" +#include "irq.h" + +#include + +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; +} diff --git a/cpu/x86/x86_pit.c b/cpu/x86/x86_pit.c new file mode 100644 index 0000000000..cd89f617b7 --- /dev/null +++ b/cpu/x86/x86_pit.c @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2014 René Kijewski + * + * 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 + * + * @} + */ + +#include "x86_interrupts.h" +#include "x86_pit.h" +#include "x86_ports.h" +#include "cpu.h" +#include "irq.h" + +#include + +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; +} diff --git a/cpu/x86/x86_reboot.c b/cpu/x86/x86_reboot.c new file mode 100644 index 0000000000..90d2eef502 --- /dev/null +++ b/cpu/x86/x86_reboot.c @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2014 René Kijewski + * + * 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 + * + * @} + */ + +#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; +} diff --git a/cpu/x86/x86_rtc.c b/cpu/x86/x86_rtc.c new file mode 100644 index 0000000000..f0cd78d436 --- /dev/null +++ b/cpu/x86/x86_rtc.c @@ -0,0 +1,297 @@ +/* + * Copyright (C) 2014 René Kijewski + * + * 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 + * + * @} + */ + +#include "x86_pic.h" +#include "x86_rtc.h" +#include "irq.h" + +#include + +#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; +} diff --git a/cpu/x86/x86_startup.c b/cpu/x86/x86_startup.c new file mode 100644 index 0000000000..92ae3da9a6 --- /dev/null +++ b/cpu/x86/x86_startup.c @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2014 René Kijewski + * + * 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 + * + * @} + */ + +#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 +#include +#include + +#include + +/* 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(); +} diff --git a/cpu/x86/x86_threading.c b/cpu/x86/x86_threading.c new file mode 100644 index 0000000000..152f517fae --- /dev/null +++ b/cpu/x86/x86_threading.c @@ -0,0 +1,223 @@ +/* + * Copyright (C) 2014 René Kijewski + * + * 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 + * + * @} + */ + +#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"); +} diff --git a/cpu/x86/x86_uart.c b/cpu/x86/x86_uart.c new file mode 100644 index 0000000000..4231bbb58d --- /dev/null +++ b/cpu/x86/x86_uart.c @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2014 René Kijewski + * + * 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 + * + * @} + */ + +#include "board.h" +#include "x86_ports.h" +#include "x86_uart.h" + +#include +#include + +#include +#include +#include + +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 +} diff --git a/cpu/x86/x86_ucontext.c b/cpu/x86/x86_ucontext.c new file mode 100644 index 0000000000..99e7027a9b --- /dev/null +++ b/cpu/x86/x86_ucontext.c @@ -0,0 +1,150 @@ +/* + * Copyright (C) 2014 René Kijewski + * + * 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 + * + * @} + */ + +#include + +#include + +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; +} diff --git a/cpu/x86/x86_videoram.c b/cpu/x86/x86_videoram.c new file mode 100644 index 0000000000..fac5354afe --- /dev/null +++ b/cpu/x86/x86_videoram.c @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2014 René Kijewski + * + * 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 + * + * @} + */ + +#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); +} diff --git a/dist/tools/toolchains/build_x86.sh b/dist/tools/toolchains/build_x86.sh new file mode 100755 index 0000000000..33b2f0c802 --- /dev/null +++ b/dist/tools/toolchains/build_x86.sh @@ -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 + +.' + +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 diff --git a/dist/tools/toolchains/build_x86_newlib.sh b/dist/tools/toolchains/build_x86_newlib.sh new file mode 100755 index 0000000000..c0a2a83477 --- /dev/null +++ b/dist/tools/toolchains/build_x86_newlib.sh @@ -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 + +.' + +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 diff --git a/examples/ccn-lite-client/Makefile b/examples/ccn-lite-client/Makefile index 549b353e24..60a4ea55db 100644 --- a/examples/ccn-lite-client/Makefile +++ b/examples/ccn-lite-client/Makefile @@ -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: diff --git a/examples/ccn-lite-relay/Makefile b/examples/ccn-lite-relay/Makefile index cfe811eafa..9dc91a7d4d 100644 --- a/examples/ccn-lite-relay/Makefile +++ b/examples/ccn-lite-relay/Makefile @@ -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: diff --git a/examples/rpl_udp/Makefile b/examples/rpl_udp/Makefile index c56154bc21..8c7e79f4f2 100644 --- a/examples/rpl_udp/Makefile +++ b/examples/rpl_udp/Makefile @@ -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: diff --git a/sys/posix/include/unistd.h b/sys/posix/include/unistd.h index 390cd265da..9deb4deb64 100644 --- a/sys/posix/include/unistd.h +++ b/sys/posix/include/unistd.h @@ -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 diff --git a/sys/shell/commands/Makefile b/sys/shell/commands/Makefile index 0deeb0867a..4ea8d09afd 100644 --- a/sys/shell/commands/Makefile +++ b/sys/shell/commands/Makefile @@ -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 diff --git a/sys/shell/commands/sc_x86_lspci.c b/sys/shell/commands/sc_x86_lspci.c new file mode 100644 index 0000000000..94e03ea60a --- /dev/null +++ b/sys/shell/commands/sc_x86_lspci.c @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2014 René Kijewski + * + * 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 + * + * @} + */ + +#include "x86_pci.h" +#include "x86_pci_strings.h" + +#include + +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); + } +} diff --git a/sys/shell/commands/shell_commands.c b/sys/shell/commands/shell_commands.c index dfc48b8878..30b3c19de7 100644 --- a/sys/shell/commands/shell_commands.c +++ b/sys/shell/commands/shell_commands.c @@ -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} }; diff --git a/tests/test_net_if/Makefile b/tests/test_net_if/Makefile index 742f3aa282..e41b864a39 100644 --- a/tests/test_net_if/Makefile +++ b/tests/test_net_if/Makefile @@ -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 diff --git a/tests/test_pnet/Makefile b/tests/test_pnet/Makefile index 41a00176f0..c8c8ee76a0 100644 --- a/tests/test_pnet/Makefile +++ b/tests/test_pnet/Makefile @@ -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