mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
Initial import of the x86 port
Currently this works only in qemu.
This commit is contained in:
parent
2b95e7b144
commit
4e4f908379
1
.gitignore
vendored
1
.gitignore
vendored
@ -11,3 +11,4 @@ cachegrind.out*
|
||||
.project
|
||||
.cproject
|
||||
.settings
|
||||
/toolchain
|
||||
|
11
boards/qemu-i386/Makefile
Normal file
11
boards/qemu-i386/Makefile
Normal file
@ -0,0 +1,11 @@
|
||||
MODULE = qemu-i386_base
|
||||
|
||||
DIRS = $(RIOTBOARD)/x86-multiboot-common
|
||||
|
||||
all: $(BINDIR)$(MODULE).a
|
||||
@for i in $(DIRS) ; do "$(MAKE)" -C $$i ; done ;
|
||||
|
||||
include $(RIOTBASE)/Makefile.base
|
||||
|
||||
clean::
|
||||
@for i in $(DIRS) ; do "$(MAKE)" -C $$i clean ; done ;
|
7
boards/qemu-i386/Makefile.include
Normal file
7
boards/qemu-i386/Makefile.include
Normal file
@ -0,0 +1,7 @@
|
||||
include $(RIOTBOARD)/x86-multiboot-common/Makefile.include
|
||||
|
||||
CFLAGS += -march=i686 -mtune=i686
|
||||
CFLAGS += -I$(RIOTBOARD)/qemu-i386/include
|
||||
|
||||
TERMPROG = qemu-system-i386 -m 512m -serial stdio -nographic -monitor /dev/null -kernel $(BINDIR)/$(PROJECT).hex
|
||||
FLASHER = true
|
43
boards/qemu-i386/include/board.h
Normal file
43
boards/qemu-i386/include/board.h
Normal file
@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright (C) 2014 René Kijewski <rene.kijewski@fu-berlin.de>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ingroup x86
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Board specific defines for qemu-i386.
|
||||
*
|
||||
* @author René Kijewski <rene.kijewski@fu-berlin.de>
|
||||
*/
|
||||
|
||||
#ifndef __RIOT__BOARDS__QEMU_I386__BOARD__H
|
||||
#define __RIOT__BOARDS__QEMU_I386__BOARD__H
|
||||
|
||||
#define UART_PORT (COM1_PORT) /* IO port to use for UART */
|
||||
#define UART_IRQ (COM1_IRQ) /* IRQ line to use for UART */
|
||||
|
||||
#define LED_RED_ON /* not available */
|
||||
#define LED_RED_OFF /* not available */
|
||||
#define LED_RED_TOGGLE /* not available */
|
||||
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
46
boards/qemu-i386/include/cpu-conf.h
Normal file
46
boards/qemu-i386/include/cpu-conf.h
Normal file
@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright (C) 2014 René Kijewski <rene.kijewski@fu-berlin.de>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
/**
|
||||
* CPU-specific defines for qemu-i386.
|
||||
*
|
||||
* @ingroup x86
|
||||
* @{
|
||||
* @file
|
||||
* @author René Kijewski <rene.kijewski@fu-berlin.de>
|
||||
*/
|
||||
|
||||
#ifndef __RIOT__BOARDS__QEMU_I386__CPU_CONF__H
|
||||
#define __RIOT__BOARDS__QEMU_I386__CPU_CONF__H
|
||||
|
||||
/* FIXME: This file is just a filler. The numbers are entirely random ... */
|
||||
|
||||
#define KERNEL_CONF_STACKSIZE_DEFAULT (8192)
|
||||
#define KERNEL_CONF_STACKSIZE_IDLE (8192)
|
||||
#define KERNEL_CONF_STACKSIZE_PRINTF (8192)
|
||||
#define KERNEL_CONF_STACKSIZE_PRINTF_FLOAT (8192)
|
||||
|
||||
#define MINIMUM_STACK_SIZE (8192)
|
||||
|
||||
#define UART0_BUFSIZE (16)
|
||||
|
||||
#define F_CPU (1000000) /* This value is unused in x86 */
|
||||
|
||||
#endif
|
||||
|
||||
/** @} */
|
55
boards/qemu-i386/x86_board_init.c
Normal file
55
boards/qemu-i386/x86_board_init.c
Normal file
@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright (C) 2014 René Kijewski <rene.kijewski@fu-berlin.de>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ingroup x86
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Initialization code for the qemu-i386 board
|
||||
*
|
||||
* @author René Kijewski <rene.kijewski@fu-berlin.de>
|
||||
*
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include "cpu.h"
|
||||
#include "x86_ports.h"
|
||||
#include "x86_reboot.h"
|
||||
|
||||
#define ENABLE_DEBUG (0)
|
||||
#include "debug.h"
|
||||
|
||||
static bool qemu_shutdown(void)
|
||||
{
|
||||
unsigned old_state = disableIRQ();
|
||||
|
||||
DEBUG("SHUTTING DOWN.\n");
|
||||
|
||||
/* (phony) ACPI shutdown (http://forum.osdev.org/viewtopic.php?t=16990) */
|
||||
/* Works for qemu and bochs. */
|
||||
outw(0xB004, 0x2000);
|
||||
|
||||
restoreIRQ(old_state);
|
||||
return false;
|
||||
}
|
||||
|
||||
void x86_init_board(void)
|
||||
{
|
||||
x86_set_shutdown_fun(qemu_shutdown);
|
||||
}
|
59
boards/qemu-i386/x86_board_lpm.c
Normal file
59
boards/qemu-i386/x86_board_lpm.c
Normal file
@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright (C) 2014 René Kijewski <rene.kijewski@fu-berlin.de>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ingroup x86
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Placeholder if someone uses x86-multiboot as a board.
|
||||
*
|
||||
* @author René Kijewski <rene.kijewski@fu-berlin.de>
|
||||
*
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include <lpm.h>
|
||||
|
||||
void lpm_init(void)
|
||||
{
|
||||
// void
|
||||
}
|
||||
|
||||
enum lpm_mode lpm_set(enum lpm_mode target)
|
||||
{
|
||||
if (target != LPM_ON) {
|
||||
asm volatile ("hlt");
|
||||
}
|
||||
return LPM_UNKNOWN;
|
||||
}
|
||||
|
||||
void lpm_awake(void)
|
||||
{
|
||||
// void
|
||||
}
|
||||
|
||||
void lpm_begin_awake(void)
|
||||
{
|
||||
// void
|
||||
}
|
||||
|
||||
void lpm_end_awake(void)
|
||||
{
|
||||
// void
|
||||
}
|
3
boards/x86-multiboot-common/Makefile
Normal file
3
boards/x86-multiboot-common/Makefile
Normal file
@ -0,0 +1,3 @@
|
||||
MODULE = x86-multiboot-common_base
|
||||
|
||||
include $(RIOTBASE)/Makefile.base
|
56
boards/x86-multiboot-common/Makefile.include
Normal file
56
boards/x86-multiboot-common/Makefile.include
Normal file
@ -0,0 +1,56 @@
|
||||
ifeq (, $(NEWLIB_BASE))
|
||||
NEWLIB_BASE := $(RIOTBASE)/toolchain/x86/i586-none-elf
|
||||
|
||||
ifneq (0, $(shell test -e "$(NEWLIB_BASE)/lib/libc.a" && echo $$?))
|
||||
NEWLIB_PRECOMPILED_NAME := i586-newlib_2.1.0_tlsf.txz
|
||||
NEWLIB_PRECOMPILED := http://download.riot-os.org/$(NEWLIB_PRECOMPILED_NAME)
|
||||
|
||||
$(warning "Precompiled newlib is missing in $(NEWLIB_BASE)")
|
||||
$(warning "Downloading from $(NEWLIB_PRECOMPILED)")
|
||||
|
||||
$(shell cd $(RIOTBASE) && wget -qO- "$(NEWLIB_PRECOMPILED)" | tar xJ)
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq (,$(BUILD_INCLUDE_BASE))
|
||||
GCC_BUILD_TRIPLET ?= $(shell gcc -dumpmachine)
|
||||
GCC_BUILD_VERSION ?= $(shell gcc -dumpversion)
|
||||
BUILD_INCLUDE_BASE = /usr/lib/gcc/$(GCC_BUILD_TRIPLET)/$(GCC_BUILD_VERSION)
|
||||
|
||||
ifeq (,$(shell echo $(GCC_BUILD_TRIPLET) | sed -e 's,-.*,,' | grep -e '\(x\|i[3-7]\)86'))
|
||||
$(warning Your build machine is a(n) $(GCC_BUILD_TRIPLET).)
|
||||
$(warning Since this is not IA32 compatible, you must set BUILD_INCLUDE_BASE explicitly!)
|
||||
endif
|
||||
endif
|
||||
|
||||
export INCLUDES += -isystem $(BUILD_INCLUDE_BASE)/include \
|
||||
-isystem $(NEWLIB_BASE)/include \
|
||||
-isystem $(NEWLIB_BASE)/sys-include \
|
||||
-isystem $(BUILD_INCLUDE_BASE)/include-fixed \
|
||||
-I$(RIOTBOARD)/x86-multiboot-common/include
|
||||
export CPU = x86
|
||||
|
||||
# toolchain config
|
||||
export CC ?= $(PREFIX)gcc
|
||||
export AR ?= $(PREFIX)ar
|
||||
export AS ?= $(PREFIX)as
|
||||
export RANLIB ?= $(PREFIX)ranlib
|
||||
export LINK ?= $(RIOTBASE)/boards/x86-multiboot-common/dist/link $(PREFIX)gcc
|
||||
export SIZE ?= $(PREFIX)size
|
||||
export OBJCOPY ?= $(PREFIX)objcopy
|
||||
|
||||
export CFLAGS += -m32 -mfpmath=387 -ffreestanding -nostdlib -nostdinc -fno-builtin
|
||||
export OFLAGS = -O binary
|
||||
|
||||
LINKFLAGS += -m32 -nostdlib -nostdinc -nostartfiles -nodefaultlibs \
|
||||
--prefix=$(NEWLIB_BASE) \
|
||||
-Wl,-rpath,$(NEWLIB_BASE)/lib \
|
||||
-T $(RIOTBASE)/boards/x86-multiboot-common/linker.ld
|
||||
UNDEF += $(BINDIR)x86-multiboot-common_base/startup.o
|
||||
|
||||
#CFLAGS += -ffunction-sections -fdata-sections
|
||||
#LINKFLAGS += -Wl,--gc-sections
|
||||
#CFLAGS += -Wall -Wextra -Werror -pedantic -pedantic-errors \
|
||||
|
||||
BASELIBS += $(NEWLIB_BASE)/lib/libc.a \
|
||||
$(NEWLIB_BASE)/lib/libm.a
|
14
boards/x86-multiboot-common/dist/link
vendored
Executable file
14
boards/x86-multiboot-common/dist/link
vendored
Executable file
@ -0,0 +1,14 @@
|
||||
#!/bin/bash
|
||||
|
||||
args=($@)
|
||||
|
||||
for ((i = 0; i < ${#args[@]}; ++i))
|
||||
do
|
||||
if [[ "${args[i]}" == '-lm' ]]
|
||||
then
|
||||
unset args[i]
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
exec "${args[@]}"
|
124
boards/x86-multiboot-common/include/multiboot.h
Normal file
124
boards/x86-multiboot-common/include/multiboot.h
Normal file
@ -0,0 +1,124 @@
|
||||
/* multiboot.h - the header for Multiboot */
|
||||
/* Copyright (C) 1999, 2001 Free Software Foundation, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
|
||||
|
||||
/**
|
||||
* Architecture specific definitions for multiboot enabled kernels.
|
||||
*
|
||||
* @ingroup x86-multiboot
|
||||
* @{
|
||||
* @file
|
||||
*/
|
||||
|
||||
#define MULTIBOOT_HEADER_MAGIC 0x1BADB002 /**< The magic number for the Multiboot header. */
|
||||
#define MULTIBOOT_HEADER_FLAGS 0x00010003 /**< The flags for the Multiboot header. */
|
||||
#define MULTIBOOT_HEADER_CHECKSUM (-(MULTIBOOT_HEADER_MAGIC + MULTIBOOT_HEADER_FLAGS)) /**< The checksum for the Multiboot header. */
|
||||
#define MULTIBOOT_BOOTLOADER_MAGIC 0x2BADB002 /**< The magic number passed by a Multiboot-compliant boot loader. */
|
||||
|
||||
/**
|
||||
* @brief The Multiboot header.
|
||||
*
|
||||
* A multiboot enabled kernel needs to contain this header in the first 8kB of the binary file.
|
||||
*/
|
||||
typedef struct multiboot_header {
|
||||
unsigned long magic; /**< Set to MULTIBOOT_HEADER_MAGIC. */
|
||||
unsigned long flags; /**< Set to MULTIBOOT_HEADER_FLAGS. */
|
||||
unsigned long checksum; /**< Set to MULTIBOOT_HEADER_CHECKSUM. */
|
||||
unsigned long header_addr; /**< The address of the datum. */
|
||||
unsigned long load_addr; /**< Set to `(uintptr_t) &_kernel_memory_start`. */
|
||||
unsigned long load_end_addr; /**< Set to `(uintptr_t) &_section_data_end`. */
|
||||
unsigned long bss_end_addr; /**< Set to `(uintptr_t) &_section_bss_end` */
|
||||
unsigned long entry_addr; /**< Your entry function. */
|
||||
} multiboot_header_t;
|
||||
|
||||
/**
|
||||
* @brief The symbol table for a.out.
|
||||
*
|
||||
* Do not use!
|
||||
*/
|
||||
typedef struct aout_symbol_table {
|
||||
unsigned long tabsize;
|
||||
unsigned long strsize;
|
||||
unsigned long addr;
|
||||
unsigned long reserved;
|
||||
} aout_symbol_table_t;
|
||||
|
||||
/**
|
||||
* @brief The section header table for ELF.
|
||||
*
|
||||
* Do not use!
|
||||
*/
|
||||
typedef struct elf_section_header_table {
|
||||
unsigned long num;
|
||||
unsigned long size;
|
||||
unsigned long addr;
|
||||
unsigned long shndx;
|
||||
} elf_section_header_table_t;
|
||||
|
||||
/**
|
||||
* @brief The multiboot information provided by the bootloader (e.g. Grub).
|
||||
*/
|
||||
typedef struct multiboot_info {
|
||||
unsigned long flags;
|
||||
unsigned long mem_lower;
|
||||
unsigned long mem_upper;
|
||||
unsigned long boot_device;
|
||||
unsigned long cmdline;
|
||||
unsigned long mods_count;
|
||||
unsigned long mods_addr;
|
||||
union {
|
||||
aout_symbol_table_t aout_sym;
|
||||
elf_section_header_table_t elf_sec;
|
||||
} u;
|
||||
unsigned long mmap_length;
|
||||
unsigned long mmap_addr;
|
||||
} multiboot_info_t;
|
||||
|
||||
/**
|
||||
* @brief The module structure.
|
||||
*
|
||||
* Do not use!
|
||||
*/
|
||||
typedef struct module {
|
||||
unsigned long mod_start;
|
||||
unsigned long mod_end;
|
||||
unsigned long string;
|
||||
unsigned long reserved;
|
||||
} module_t;
|
||||
|
||||
/**
|
||||
* @brief An entry in the memory map table.
|
||||
*/
|
||||
typedef struct memory_map {
|
||||
unsigned long size;
|
||||
unsigned long base_addr_low;
|
||||
unsigned long base_addr_high;
|
||||
unsigned long length_low;
|
||||
unsigned long length_high;
|
||||
unsigned long type;
|
||||
} memory_map_t;
|
||||
|
||||
enum multiboot_info_flags {
|
||||
MULTIBOOT_INFO_FLAGS_MEM = 0x01,
|
||||
MULTIBOOT_INFO_FLAGS_BOOT = 0x02,
|
||||
MULTIBOOT_INFO_FLAGS_CMDLINE = 0x04,
|
||||
MULTIBOOT_INFO_FLAGS_MODS = 0x08,
|
||||
MULTIBOOT_INFO_FLAGS_SYMS1 = 0x10,
|
||||
MULTIBOOT_INFO_FLAGS_SYMS2 = 0x20,
|
||||
MULTIBOOT_INFO_FLAGS_MMAP = 0x40,
|
||||
};
|
||||
|
||||
/** @} */
|
117
boards/x86-multiboot-common/linker.ld
Normal file
117
boards/x86-multiboot-common/linker.ld
Normal file
@ -0,0 +1,117 @@
|
||||
/*
|
||||
* Copyright (C) 2014 René Kijewski <rene.kijewski@fu-berlin.de>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
/* The bootloader will look at this image and start execution at the symbol
|
||||
designated as the entry point. */
|
||||
ENTRY(_start)
|
||||
|
||||
/* Tell where the various sections of the object files will be put in the final
|
||||
kernel image. */
|
||||
SECTIONS
|
||||
{
|
||||
/* Begin putting sections at 1 MiB, a conventional place for kernels to be
|
||||
loaded at by the bootloader. */
|
||||
. = 1M;
|
||||
_kernel_memory_start = .;
|
||||
|
||||
/* First put the multiboot header, as it is required to be put very early
|
||||
early in the image or the bootloader won't recognize the file format.
|
||||
Next we'll put the .text section. */
|
||||
._multiboot_header :
|
||||
{
|
||||
*(._multiboot_header)
|
||||
}
|
||||
|
||||
.note.gnu.build-id :
|
||||
{
|
||||
*(.note.gnu.build-id)
|
||||
}
|
||||
|
||||
.text :
|
||||
{
|
||||
. = ALIGN(4);
|
||||
_section_text_start = .;
|
||||
|
||||
*(.text)
|
||||
*(.text.*)
|
||||
|
||||
*(.gnu.linkonce.t)
|
||||
*(.gnu.linkonce.t.*)
|
||||
|
||||
_section_text_end = .;
|
||||
}
|
||||
. = ALIGN(0x1000);
|
||||
|
||||
/* Read-only data. */
|
||||
.rodata :
|
||||
{
|
||||
_section_rodata_start = .;
|
||||
|
||||
*(.rodata)
|
||||
*(.rodata.*)
|
||||
|
||||
*(.gnu.linkonce.r)
|
||||
*(.gnu.linkonce.r.*)
|
||||
|
||||
_section_rodata_end = .;
|
||||
}
|
||||
. = ALIGN(0x1000);
|
||||
|
||||
/* Read-write data (initialized) */
|
||||
.data :
|
||||
{
|
||||
_section_data_start = .;
|
||||
|
||||
*(.data)
|
||||
*(.data.*)
|
||||
|
||||
*(.gnu.linkonce.d)
|
||||
*(.gnu.linkonce.d.*)
|
||||
|
||||
. = ALIGN(4);
|
||||
_section_data_end = .;
|
||||
/* no need to align between .data and .bss */
|
||||
}
|
||||
|
||||
/* Read-write data (uninitialized) and stack */
|
||||
.bss :
|
||||
{
|
||||
_section_bss_start = .;
|
||||
|
||||
*(COMMON)
|
||||
*(COMMON.*)
|
||||
|
||||
*(.bss)
|
||||
*(.bss.*)
|
||||
|
||||
*(.gnu.linkonce.b)
|
||||
*(.gnu.linkonce.b.*)
|
||||
|
||||
_section_bss_end = .;
|
||||
}
|
||||
|
||||
. = ALIGN(0x1000);
|
||||
_kernel_memory_end = .;
|
||||
|
||||
. += 0x1000;
|
||||
. = ALIGN(0x10000);
|
||||
_heap_start = .;
|
||||
|
||||
/* The compiler may produce other sections, by default it will put them in
|
||||
a segment with the same name. Simply add stuff here as needed. */
|
||||
}
|
146
boards/x86-multiboot-common/startup.c
Normal file
146
boards/x86-multiboot-common/startup.c
Normal file
@ -0,0 +1,146 @@
|
||||
/*
|
||||
* Copyright (C) 2014 René Kijewski <rene.kijewski@fu-berlin.de>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ingroup x86
|
||||
* @ingroup boards
|
||||
* @defgroup x86-multiboot i586 multiboot common
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Startup code to be loaded by multiboot enabled bootloaders.
|
||||
*
|
||||
* @author René Kijewski <rene.kijewski@fu-berlin.de>
|
||||
*
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include <cpu.h>
|
||||
#include <multiboot.h>
|
||||
#include <x86_kernel_memory.h>
|
||||
#include <x86_memory.h>
|
||||
#include <x86_reboot.h>
|
||||
#include <x86_videoram.h>
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifndef BREAK_STARTUP
|
||||
# define BREAK_STARTUP (0)
|
||||
#endif
|
||||
|
||||
static void __attribute__((noreturn)) startup(uint32_t multiboot_magic, const multiboot_info_t *multiboot_info);
|
||||
void __attribute__((noreturn, optimize("Os", "omit-frame-pointer"), no_instrument_function)) _start(void);
|
||||
|
||||
extern const multiboot_header_t multiboot_header __attribute__((section("._multiboot_header")));
|
||||
const multiboot_header_t multiboot_header = {
|
||||
.magic = MULTIBOOT_HEADER_MAGIC,
|
||||
.flags = MULTIBOOT_HEADER_FLAGS,
|
||||
.checksum = MULTIBOOT_HEADER_CHECKSUM,
|
||||
.header_addr = (uintptr_t) &multiboot_header,
|
||||
.load_addr = (uintptr_t) &_kernel_memory_start,
|
||||
.load_end_addr = (uintptr_t) &_section_data_end,
|
||||
.bss_end_addr = (uintptr_t) &_section_bss_end,
|
||||
.entry_addr = (uintptr_t) &_start,
|
||||
};
|
||||
|
||||
void __attribute__((noreturn, optimize("Os", "omit-frame-pointer"), no_instrument_function)) _start(void)
|
||||
{
|
||||
asm volatile ("xor %ebp, %ebp");
|
||||
asm volatile ("push %ebp");
|
||||
asm volatile ("push %ebx");
|
||||
asm volatile ("push %eax");
|
||||
asm volatile ("push %ebp");
|
||||
asm volatile ("jmp *%0" :: "r"(&startup));
|
||||
__builtin_unreachable();
|
||||
}
|
||||
|
||||
static const multiboot_info_t *multiboot_info;
|
||||
|
||||
bool x86_get_memory_region(uint64_t *start, uint64_t *len, unsigned long *pos)
|
||||
{
|
||||
while (1) {
|
||||
if (*pos >= multiboot_info->mmap_length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const memory_map_t *mmap = (void *)(multiboot_info->mmap_addr + *pos);
|
||||
*pos += mmap->size + 4;
|
||||
|
||||
*start = mmap->base_addr_low + ((uint64_t) mmap->base_addr_high << 32);
|
||||
*len = mmap->length_low + ((uint64_t) mmap->length_high << 32);
|
||||
|
||||
uint64_t end = *start + *len;
|
||||
printf(" %08lx%08lx - %08lx%08lx ", (long) (*start >> 32), (long) *start, (long) (end >> 32), (long) end);
|
||||
|
||||
if (mmap->type != 1) {
|
||||
// not free (ignore reclaimable RAM)
|
||||
const char *msg;
|
||||
switch (mmap->type) {
|
||||
case 2: msg = "reseved"; break;
|
||||
case 3: msg = "ACPI [reclaimable]"; break;
|
||||
case 4: msg = "ACPI [NVS]"; break;
|
||||
case 5: msg = "bad memory"; break;
|
||||
default: msg = "unknown";
|
||||
}
|
||||
printf("(unusable: %s)\r\n", msg);
|
||||
}
|
||||
else {
|
||||
puts("(usable)");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void have_a_break(void)
|
||||
{
|
||||
volatile bool cnt = false;
|
||||
while (!cnt) {
|
||||
asm volatile ("pause");
|
||||
}
|
||||
}
|
||||
|
||||
static void __attribute__((noreturn)) startup(uint32_t multiboot_magic, const multiboot_info_t *multiboot_info_)
|
||||
{
|
||||
x86_init_gdt(); /* load GDT early */
|
||||
x86_load_empty_idt(); /* just a safeguard to cause a tripple fault, not really needed */
|
||||
|
||||
#if BREAK_STARTUP
|
||||
have_a_break();
|
||||
#else
|
||||
(void) have_a_break;
|
||||
#endif
|
||||
|
||||
memset(&_section_bss_start, 0, &_section_bss_end - &_section_bss_start + 1);
|
||||
|
||||
videoram_puts(" Booting RIOT \r\n");
|
||||
if (multiboot_magic != MULTIBOOT_BOOTLOADER_MAGIC) {
|
||||
videoram_puts(" Multiboot magic is wrong \r\n");
|
||||
x86_hlt();
|
||||
}
|
||||
else if (!(multiboot_info->flags && MULTIBOOT_INFO_FLAGS_MMAP)) {
|
||||
videoram_puts(" Multiboot is lacking memory map information \r\n");
|
||||
x86_hlt();
|
||||
}
|
||||
|
||||
multiboot_info = multiboot_info_;
|
||||
x86_startup();
|
||||
}
|
3
cpu/x86/Makefile
Normal file
3
cpu/x86/Makefile
Normal file
@ -0,0 +1,3 @@
|
||||
MODULE = cpu
|
||||
|
||||
include $(RIOTBASE)/Makefile.base
|
4
cpu/x86/Makefile.include
Normal file
4
cpu/x86/Makefile.include
Normal file
@ -0,0 +1,4 @@
|
||||
export INCLUDES += -I$(RIOTCPU)/x86/include
|
||||
|
||||
export USEMODULE += quad_math
|
||||
export USEPKG += tlsf
|
121
cpu/x86/include/cpu.h
Normal file
121
cpu/x86/include/cpu.h
Normal file
@ -0,0 +1,121 @@
|
||||
/*
|
||||
* Copyright (C) 2014 René Kijewski <rene.kijewski@fu-berlin.de>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
/**
|
||||
* Basic definitions and forwards for x86 boards.
|
||||
*
|
||||
* @defgroup x86 i586
|
||||
* @ingroup cpu
|
||||
* @{
|
||||
* @file
|
||||
* @author René Kijewski <rene.kijewski@fu-berlin.de>
|
||||
*/
|
||||
|
||||
#ifndef CPU__X86__CPU__H__
|
||||
#define CPU__X86__CPU__H__
|
||||
|
||||
#include "attributes.h"
|
||||
#include "irq.h"
|
||||
#include "ucontext.h"
|
||||
#include "cpu-conf.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
static inline void __attribute__((always_inline)) dINT(void)
|
||||
{
|
||||
asm volatile ("cli");
|
||||
}
|
||||
|
||||
static inline void __attribute__((always_inline)) eINT(void)
|
||||
{
|
||||
asm volatile ("sti");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Disable interrupts and halt forever.
|
||||
*
|
||||
* This function is the last resort in case of an unrecoverable error.
|
||||
* No message will be printed, no nothing.
|
||||
*/
|
||||
static inline void __attribute__((always_inline, noreturn)) x86_hlt(void)
|
||||
{
|
||||
while (1) {
|
||||
asm volatile ("cli");
|
||||
asm volatile ("hlt");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Initialize subsystems and run kernel.
|
||||
*
|
||||
* Called by the board specific startup code.
|
||||
* <li>The .bss has to be cleared before.
|
||||
* <li>The stack has to be set up, probably somewhere in the low memory.
|
||||
* <li>The A20 line has to be activated, because all the code is beyong 1MB.
|
||||
* <li>Paging must be disabled.
|
||||
* <li>The SS, DS, and CS must span the whole 4GB of RAM.
|
||||
* <li>32 bit protected mode must be entered.
|
||||
* <li>Interrupts must be disabled.
|
||||
*/
|
||||
void x86_startup(void) NORETURN;
|
||||
|
||||
/**
|
||||
* @brief Setup board specific hardware and such.
|
||||
*
|
||||
* Called by x86_startup() after all generic subsystems are activated,
|
||||
* and before kernel_init() gets called.
|
||||
* Interrupts are disabled before, and are expected to be disabled after calling this function.
|
||||
* Interrupts may be enabled during the course of this function call.
|
||||
*
|
||||
* Probably most of the board specific initialization should be done in auto_init():
|
||||
* <li>You must not spawn thread_create() in this function.
|
||||
* <li>The hwtimer is not set up properly at this point of executation.
|
||||
*/
|
||||
void x86_init_board(void);
|
||||
|
||||
/**
|
||||
* @brief Returns a range of usable physical memory.
|
||||
* @param[out] start Start address of the physical memory region.
|
||||
* @param[out] len Total length of the physical memory region.
|
||||
* One page are 0x1000 bytes.
|
||||
* @param cnt Reentrant pointer. Must be zero on first call.
|
||||
* The value is entirely opaque.
|
||||
* @returns true iff there was a memory region set in start and len.
|
||||
* false iff there are no more memory regions.
|
||||
* The function must not get called again in the latter case.
|
||||
*
|
||||
* Called by x86_startup() before x86_init_board() gets called.
|
||||
*
|
||||
* The memory management is one of the earliest subsystems to be set up.
|
||||
* The uart is already configured and usable.
|
||||
* The interrupt handling is already set up and you may call x86_interrupt_handler_set().
|
||||
*
|
||||
* The function may return unaligned memory.
|
||||
* In this case the begin of the region gets rounded up the the next page,
|
||||
* and the end gets rounded down.
|
||||
* The function may supply low memory regions, which will be ignored.
|
||||
* The regions are expected to contain memory that lies inside the elf sections.
|
||||
*/
|
||||
bool x86_get_memory_region(uint64_t *start, uint64_t *len, unsigned long *cnt);
|
||||
|
||||
#endif
|
||||
|
||||
/** @} */
|
58
cpu/x86/include/hwtimer_cpu.h
Normal file
58
cpu/x86/include/hwtimer_cpu.h
Normal file
@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright (C) 2014 René Kijewski <rene.kijewski@fu-berlin.de>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
/**
|
||||
* Basic definitions for the hwtimer.
|
||||
*
|
||||
* @ingroup x86
|
||||
* @ingroup core_hwtimer
|
||||
* @{
|
||||
* @file
|
||||
* @author René Kijewski <rene.kijewski@fu-berlin.de>
|
||||
*/
|
||||
|
||||
#ifndef CPU__X86__HWTIMER_CPU__H__
|
||||
#define CPU__X86__HWTIMER_CPU__H__
|
||||
|
||||
/**
|
||||
* @brief Number of configured hardware timers.
|
||||
*
|
||||
* This value may be set as high as you need it.
|
||||
* The x86_hwtimer.c can emulate a lot of timers, since the real timers aren't used. :-)
|
||||
*/
|
||||
#ifndef HWTIMER_MAXTIMERS
|
||||
# define HWTIMER_MAXTIMERS 4
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief How many "ticks" there are in one second of wallclock time.
|
||||
*
|
||||
* Statically configured as `1 tick = 1 µs`.
|
||||
*/
|
||||
#define HWTIMER_SPEED (1000000L)
|
||||
|
||||
/**
|
||||
* @brief Biggest value that can specify a valid number of hardware ticks.
|
||||
*
|
||||
* Since 1 tick = 1 µs, this number will flow over after 4295 seconds (1h11m34s).
|
||||
*/
|
||||
#define HWTIMER_MAXTICKS (0xFFFFFFFFu)
|
||||
|
||||
#endif
|
||||
|
||||
/** @} */
|
166
cpu/x86/include/ucontext.h
Normal file
166
cpu/x86/include/ucontext.h
Normal file
@ -0,0 +1,166 @@
|
||||
/*
|
||||
* Copyright (C) 2014 René Kijewski <rene.kijewski@fu-berlin.de>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
/**
|
||||
* Coroutine helper functions. The base of the multi-threading system.
|
||||
*
|
||||
* @ingroup x86-multithreading
|
||||
* @{
|
||||
* @file
|
||||
* @author René Kijewski <rene.kijewski@fu-berlin.de>
|
||||
*/
|
||||
|
||||
#ifndef CPU__X86__UCONTEXT__HPP__
|
||||
#define CPU__X86__UCONTEXT__HPP__
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
/**
|
||||
* @brief Common stacksize for a signal handler.
|
||||
*
|
||||
* Do not use this variable.
|
||||
* The standard wants us to define this variable, but you are better off the the RIOT specific macros.
|
||||
*/
|
||||
#define SIGSTKSZ (2048)
|
||||
|
||||
/**
|
||||
* @brief General purpose registers of an x86 CPU.
|
||||
*
|
||||
* Used by ucontext_t.
|
||||
* Application developers should not use this type directly.
|
||||
*/
|
||||
struct x86_pushad {
|
||||
unsigned long ax, cx, dx, bx; /* field in ucontext_t: 3*4 -> 6*4 */
|
||||
unsigned long sp, bp, si, di; /* field in ucontext_t: 7*4 -> 10*4 */
|
||||
} __attribute__((packed));
|
||||
|
||||
/**
|
||||
* @brief Opaque memory needed to store the x87 FPU state.
|
||||
*
|
||||
* Used by x86_threading.c.
|
||||
* Application developers should not use this type directly.
|
||||
* There is only enough room for the basic x87 state, not the multimedia extensions.
|
||||
*/
|
||||
struct x86_fxsave {
|
||||
char opaque[512];
|
||||
} __attribute__((packed, aligned(0x10)));
|
||||
|
||||
/**
|
||||
* @brief Machine specific part of the corouting state.
|
||||
*
|
||||
* Used by ucontext_t.
|
||||
* Application developers should not use this type directly.
|
||||
*/
|
||||
typedef struct mcontext {
|
||||
unsigned long flags; /* field in ucontext_t: 2*4 */
|
||||
struct x86_pushad registers; /* field in ucontext_t: 3*4 -> 10*4 */
|
||||
void *ip; /* field in ucontext_t: 11*4 */
|
||||
} __attribute__((packed)) mcontext_t;
|
||||
|
||||
/**
|
||||
* @brief Stack assigned to a coroutine.
|
||||
*
|
||||
* Used by ucontext_t.
|
||||
* Application developers need to set this values to make coroutines working.
|
||||
*/
|
||||
typedef struct stack {
|
||||
void *ss_sp; /* field in ucontext_t: 0*4 */
|
||||
//int ss_flags;
|
||||
size_t ss_size; /* field in ucontext_t: 1*4 */
|
||||
} __attribute__((packed)) stack_t;
|
||||
|
||||
/**
|
||||
* @brief Extra data to perform an `iret`.
|
||||
*
|
||||
* Used by x86_interrupts.c to continue a thread
|
||||
* if sched_context_switch_request was set set by an ISR.
|
||||
* Application developers should not use this type directly.
|
||||
*/
|
||||
struct x86_interrupted_interrupt {
|
||||
unsigned long ip;
|
||||
unsigned long flags;
|
||||
} __attribute__((packed));
|
||||
|
||||
/**
|
||||
* @brief Datatype to enable coroutines in userspace.
|
||||
*
|
||||
* This datatype stores the machine state of a suspended coroutine.
|
||||
*/
|
||||
typedef struct ucontext {
|
||||
stack_t uc_stack; /* field in ucontext_t: 0*4 - 1*4 */
|
||||
mcontext_t uc_context; /* field in ucontext_t: 2*4 -> 11*4 */
|
||||
struct ucontext *uc_link; /* field in ucontext_t: 12*4 */
|
||||
//sigset_t uc_sigmask;
|
||||
struct x86_interrupted_interrupt __intr;
|
||||
struct x86_fxsave __fxsave;
|
||||
} __attribute__((packed)) ucontext_t;
|
||||
|
||||
/**
|
||||
* @brief Store current coroutine state.
|
||||
*
|
||||
* With setcontext() you can jump to this point of execution again.
|
||||
* Take care about your stack, though.
|
||||
* After your thread of execution left the function calling getcontext() there be dragons.
|
||||
*
|
||||
* The FPU registers are not stored!
|
||||
*/
|
||||
int getcontext(ucontext_t *ucp);
|
||||
|
||||
/**
|
||||
* @brief Restore a coroutine state.
|
||||
*
|
||||
* The state must be set up by calling getcontext() or makecontext() previously.
|
||||
* The FPU registers won't be restored.
|
||||
*/
|
||||
int setcontext(const ucontext_t *ucp) __attribute__((noreturn));
|
||||
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wstrict-prototypes"
|
||||
typedef void (*makecontext_fun_t)();
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
/**
|
||||
* @brief Create a coroutine / trampoline.
|
||||
* @param[in] func Function to call.
|
||||
* @param[in] argc Number of arguments for func. Must not exceed 5.
|
||||
*
|
||||
* The function arguments shall be passed after argc.
|
||||
* The arguments have to be type punnable as an int,
|
||||
* this includes integral types up to 4 bytes,
|
||||
* but excludes int64_t, float and so on.
|
||||
*
|
||||
* Different from what the POSIX standard states,
|
||||
* you do not have to getcontext() on ucp before calling makecontext().
|
||||
* This function creates a whole new coroutine state / trampoline.
|
||||
*/
|
||||
void makecontext(ucontext_t *ucp, makecontext_fun_t func, int argc, ...);
|
||||
|
||||
/**
|
||||
* @brief Swap coroutine.
|
||||
* @param[out] oucp Memory to preserve current coroutine state in.
|
||||
* @param[in] ucp Coroutine to execute next.
|
||||
*
|
||||
* This function is an atomic variant of getcontext() and setcontext().
|
||||
* Same restrictions apply.
|
||||
*/
|
||||
int swapcontext(ucontext_t *oucp, const ucontext_t *ucp);
|
||||
|
||||
#endif
|
||||
|
||||
/** @} */
|
63
cpu/x86/include/x86_cmos.h
Normal file
63
cpu/x86/include/x86_cmos.h
Normal file
@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright (C) 2014 René Kijewski <rene.kijewski@fu-berlin.de>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
/**
|
||||
* Functions to interact with the CMOS memory of the BIOS.
|
||||
*
|
||||
* @ingroup x86
|
||||
* @{
|
||||
* @file
|
||||
* @author René Kijewski <rene.kijewski@fu-berlin.de>
|
||||
*/
|
||||
|
||||
#ifndef CPU__X86__CMOS__H__
|
||||
#define CPU__X86__CMOS__H__
|
||||
|
||||
#include "x86_ports.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define CMOS_ADDRESS (0x70)
|
||||
#define CMOS_DATA (0x71)
|
||||
|
||||
#define CMOS_SERIAL_OFFS (0x41)
|
||||
#define CMOS_SERIAL_LEN (6)
|
||||
|
||||
/**
|
||||
* @brief Read a CMOS register.
|
||||
* @returns The value of the CMOS register.
|
||||
*/
|
||||
uint8_t x86_cmos_read(int reg);
|
||||
|
||||
/**
|
||||
* @brief Write a CMOS register.
|
||||
*/
|
||||
void x86_cmos_write(int reg, uint8_t value);
|
||||
|
||||
/**
|
||||
* @brief Read serial number of the BIOS.
|
||||
*
|
||||
* Most -- if not all current -- BIOSes do not seem to store any serial number here.
|
||||
* This function won't cause an error, but the read serial might be some more or less random data.
|
||||
* The implementor of the board specific code should know whether the BIOS contains a serial number.
|
||||
*/
|
||||
void x86_cmos_serial(uint8_t (*serial)[CMOS_SERIAL_LEN]);
|
||||
|
||||
#endif
|
||||
|
||||
/** @} */
|
42
cpu/x86/include/x86_hwtimer.h
Normal file
42
cpu/x86/include/x86_hwtimer.h
Normal file
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright (C) 2014 René Kijewski <rene.kijewski@fu-berlin.de>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
/**
|
||||
* The interface of the architecture specific part of the hwtimer module.
|
||||
*
|
||||
* @ingroup x86
|
||||
* @ingroup core_hwtimer
|
||||
* @{
|
||||
* @file
|
||||
* @author René Kijewski <rene.kijewski@fu-berlin.de>
|
||||
*/
|
||||
|
||||
#ifndef CPU__X86__HWTIMER__H__
|
||||
#define CPU__X86__HWTIMER__H__
|
||||
|
||||
/**
|
||||
* @brief Do timestamping to enable the use of the hwtimer module.
|
||||
*
|
||||
* This function is called during initialization by x86_startup().
|
||||
* You must not call this function on your own accord.
|
||||
*/
|
||||
void x86_init_hwtimer(void);
|
||||
|
||||
#endif
|
||||
|
||||
/** @} */
|
157
cpu/x86/include/x86_interrupts.h
Normal file
157
cpu/x86/include/x86_interrupts.h
Normal file
@ -0,0 +1,157 @@
|
||||
/*
|
||||
* Copyright (C) 2014 René Kijewski <rene.kijewski@fu-berlin.de>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
/**
|
||||
* The entry points for all interrupts on x86 boards.
|
||||
*
|
||||
* @ingroup x86
|
||||
* @defgroup x86-irq Interrupt handling for i586 boards
|
||||
* @{
|
||||
* @file
|
||||
* @author René Kijewski <rene.kijewski@fu-berlin.de>
|
||||
*/
|
||||
|
||||
#ifndef CPU__X86__INTERRUPTS__H__
|
||||
#define CPU__X86__INTERRUPTS__H__
|
||||
|
||||
#include "ucontext.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/**
|
||||
* @brief Initialize interrupts.
|
||||
*
|
||||
* This function is called during initialization by x86_startup().
|
||||
* You must not call this function on your own accord.
|
||||
*/
|
||||
void x86_init_interrupts(void);
|
||||
|
||||
#define X86_MAX_INTERRUPTS (0x30)
|
||||
|
||||
/**
|
||||
* @brief The exceptions in an x86 system.
|
||||
*
|
||||
* http://www.sandpile.org/x86/except.htm
|
||||
*/
|
||||
enum x86_interrupt_numbers {
|
||||
X86_INT_DE = 0x00, /**< divide error */
|
||||
X86_INT_DB = 0x01, /**< debug (thrown after each instruction if X86_TF is activated) */
|
||||
X86_INT_BP = 0x03, /**< breakpoint (`int3`) */
|
||||
X86_INT_OF = 0x04, /**< overflow (`into`) */
|
||||
X86_INT_BR = 0x05, /**< boundary range exceeded (`bound`) */
|
||||
X86_INT_UD = 0x06, /**< undefined opcode */
|
||||
X86_INT_NM = 0x07, /**< device not available (raised on FPU accessing if CR0.TS is set) */
|
||||
X86_INT_DF = 0x08, /**< double fault (exceptions during exception handler invocation) */
|
||||
X86_INT_GP = 0x0d, /**< general protection */
|
||||
X86_INT_PF = 0x0e, /**< page fault */
|
||||
X86_INT_MF = 0x10, /**< math fault */
|
||||
X86_INT_AC = 0x11, /**< alignment checking */
|
||||
X86_INT_MC = 0x12, /**< machine check */
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Bits in the EFLAGS register.
|
||||
*
|
||||
* http://www.sandpile.org/x86/flags.htm
|
||||
*/
|
||||
enum x86_eflags {
|
||||
X86_CF = 1 << 0, /**< carry */
|
||||
X86_PF = 1 << 2, /**< parity */
|
||||
X86_AF = 1 << 4, /**< adjust */
|
||||
X86_ZF = 1 << 6, /**< zero */
|
||||
X86_SF = 1 << 7, /**< signed */
|
||||
X86_TF = 1 << 8, /**< singled step (throw #DB after each instruction) */
|
||||
X86_IF = 1 << 9, /**< interrupts enabled */
|
||||
X86_DF = 1 << 10, /**< direction (0 = movs increses addresses, 1 = movs decreases addresses) */
|
||||
X86_OF = 1 << 11, /**< overflow */
|
||||
X86_IOPL_0 = 0 << 12, /**< priority level 0 (?) */
|
||||
X86_IOPL_1 = 1 << 12, /**< priority level 1 (?) */
|
||||
X86_IOPL_2 = 2 << 12, /**< priority level 2 (?) */
|
||||
X86_IOPL_3 = 3 << 12, /**< priority level 3 (?) */
|
||||
X86_NT = 1 << 14, /**< nested task */
|
||||
X86_RF = 1 << 16, /**< resume */
|
||||
X86_VM = 1 << 17, /**< virtual 8086 mode */
|
||||
X86_AC = 1 << 18, /**< alignment check */
|
||||
X86_VIF = 1 << 19, /**< virtual interrupts enabled */
|
||||
X86_VIP = 1 << 20, /**< virtual interrupts pendigng */
|
||||
X86_ID = 1 << 21, /**< able to use CPUID */
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Datum for the Interrupt Descriptor Table Register.
|
||||
*/
|
||||
struct idtr_t {
|
||||
uint16_t limit; /**< `sizeof (struct idt_desc) * count` */
|
||||
void *base; /**< physical address of the start of the IDT */
|
||||
} __attribute__((packed));
|
||||
|
||||
/**
|
||||
* @brief One entry in the IDT
|
||||
*/
|
||||
struct idt_desc {
|
||||
uint16_t offset_1; /**< offset bits 0..15 */
|
||||
uint16_t selector; /**< a code segment selector in GDT or LDT */
|
||||
uint8_t zero; /**< unused, set to 0 */
|
||||
uint8_t type_attr; /**< type and attributes, see below */
|
||||
uint16_t offset_2; /**< offset bits 16..31 */
|
||||
} __attribute__((packed));
|
||||
|
||||
/**
|
||||
* @brief Callback on interrupt.
|
||||
* @param intr_num Number of interrupt -- not the number of the IRQ
|
||||
* @param orig_ctx (Changable) register set of the calling thread
|
||||
* @param error_code Related error code (if applies, otherwise 0)
|
||||
*/
|
||||
typedef void (*x86_intr_handler_t)(uint8_t intr_num, struct x86_pushad *orig_ctx, unsigned long error_code);
|
||||
|
||||
/**
|
||||
* @brief Set callback function for interrupt.
|
||||
* @param num Number of interrupt. Must be less than X86_MAX_INTERRUPTS.
|
||||
* @param handler Function to call, or `NULL` to use default interrupt handler.
|
||||
*
|
||||
* This function is meant to be called by other subsystems (x86_pic.c and x86_threading.c).
|
||||
* Any upper level system should use x86_pic_set_handler() or even higher level functions.
|
||||
*/
|
||||
void x86_interrupt_handler_set(unsigned num, x86_intr_handler_t handler);
|
||||
|
||||
/**
|
||||
* @brief Disable interrupts and return previous EFLAGS.
|
||||
*/
|
||||
static inline unsigned long __attribute__((always_inline)) x86_pushf_cli(void)
|
||||
{
|
||||
unsigned long result;
|
||||
asm volatile("pushf; cli; pop %0" : "=g"(result));
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set EFLAGS.
|
||||
*/
|
||||
static inline void __attribute__((always_inline)) x86_restore_flags(unsigned long stored_value)
|
||||
{
|
||||
asm volatile("push %0; popf" :: "g"(stored_value));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Print saved general purposed registers.
|
||||
*/
|
||||
void x86_print_registers(struct x86_pushad *orig_ctx, unsigned long error_code);
|
||||
|
||||
#endif
|
||||
|
||||
/** @} */
|
116
cpu/x86/include/x86_kernel_memory.h
Normal file
116
cpu/x86/include/x86_kernel_memory.h
Normal file
@ -0,0 +1,116 @@
|
||||
/*
|
||||
* Copyright (C) 2014 René Kijewski <rene.kijewski@fu-berlin.de>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
/**
|
||||
* Layout of the kernel memory. The symbols are set in the linker.ld.
|
||||
*
|
||||
* @ingroup x86
|
||||
* @{
|
||||
* @file
|
||||
* @author René Kijewski <rene.kijewski@fu-berlin.de>
|
||||
*/
|
||||
|
||||
#ifndef KERNEL_MEMORY_H__
|
||||
#define KERNEL_MEMORY_H__
|
||||
|
||||
/**
|
||||
* @brief First byte inside the elf segments.
|
||||
*
|
||||
* Expected to be the start of the high memory, i.e. 1MB.
|
||||
*/
|
||||
extern char _kernel_memory_start;
|
||||
|
||||
/**
|
||||
* @brief Start of first byte of code inside the .text segment.
|
||||
*
|
||||
* This symbol does not have to be aligned.
|
||||
*/
|
||||
extern char _section_text_start;
|
||||
|
||||
/**
|
||||
* @brief First byte after the code.
|
||||
*
|
||||
* This symbol does not have to be aligned.
|
||||
*/
|
||||
extern char _section_text_end;
|
||||
|
||||
/**
|
||||
* @brief First byte inside the .rodata segment.
|
||||
*
|
||||
* This symbol is expected to be aligned.
|
||||
*/
|
||||
extern char _section_rodata_start;
|
||||
|
||||
/**
|
||||
* @brief First byte after the .rodata segment.
|
||||
*
|
||||
* This symbol does not have to be aligned.
|
||||
*/
|
||||
extern char _section_rodata_end;
|
||||
|
||||
/**
|
||||
* @brief First byte inside the .data segment.
|
||||
*
|
||||
* This symbol is expected to be aligned.
|
||||
*/
|
||||
extern char _section_data_start;
|
||||
|
||||
/**
|
||||
* @brief First byte after the .data segment.
|
||||
*
|
||||
* This symbol does not have to be aligned, since it shares the same previleges as the .bss segment.
|
||||
*/
|
||||
extern char _section_data_end;
|
||||
|
||||
/**
|
||||
* @brief Start of the .bss segment.
|
||||
*
|
||||
* Probably unaligned.
|
||||
* The .bss segment must follow the .data segment with no other segment inbetween.
|
||||
* It's likely that `&_section_data_end == &_section_bss_start`.
|
||||
*/
|
||||
extern char _section_bss_start;
|
||||
|
||||
/**
|
||||
* @brief First byte after the .bss segment.
|
||||
*
|
||||
* This symbol should not to be aligned, because RAM cycles would be wasted to clear unused bytes otherwise.
|
||||
*/
|
||||
extern char _section_bss_end;
|
||||
|
||||
/**
|
||||
* @brief First byte after the last of the common elf segments.
|
||||
*
|
||||
* This symbol is expected to be aligned.
|
||||
* The "common elf segments" are .text, .rodata, .data and .bss.
|
||||
* All the (physical) memory beyond this point will be overwritten at the pleasure of the kernel.
|
||||
*/
|
||||
extern char _kernel_memory_end;
|
||||
|
||||
/**
|
||||
* @brief First byte that may be mapped as the heap
|
||||
*
|
||||
* This symbol is expected to be aligned.
|
||||
* Must be bigger than _kernel_memory_end.
|
||||
* There should be a space between _kernel_memory_end and _heap_start, but it does not have to.
|
||||
*/
|
||||
extern char _heap_start;
|
||||
|
||||
#endif
|
||||
|
||||
/** @} */
|
171
cpu/x86/include/x86_memory.h
Normal file
171
cpu/x86/include/x86_memory.h
Normal file
@ -0,0 +1,171 @@
|
||||
/*
|
||||
* Copyright (C) 2014 René Kijewski <rene.kijewski@fu-berlin.de>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
/**
|
||||
* The virtual memory management of x86 boards.
|
||||
*
|
||||
* @ingroup x86
|
||||
* @{
|
||||
* @file
|
||||
* @author René Kijewski <rene.kijewski@fu-berlin.de>
|
||||
*/
|
||||
|
||||
#ifndef CPU__X86__MEMORY__H__
|
||||
#define CPU__X86__MEMORY__H__
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
/**
|
||||
* @brief Maximum static size of the kernel.
|
||||
*
|
||||
* Each page table can hold 2MB.
|
||||
* As the low memory is ignored, 1MB of virtual memory is wasted.
|
||||
* Bad things will happen if this value is set too small.
|
||||
* The kernel must not exceed the 10MB mark, unless you are sure that your board has no ISA hole.
|
||||
* (It is unlikely that you have an ISA hole, though.)
|
||||
*/
|
||||
#define X86_NUM_STATIC_PT (4)
|
||||
#define X86_NUM_STATIC_PD ((X86_NUM_STATIC_PT+511) / 512) /* 1GB=512 PTs each */
|
||||
|
||||
/**
|
||||
* @brief Initialize virtual memory management.
|
||||
*
|
||||
* This function is called during initialization by x86_startup().
|
||||
* You must not call this function on your own accord.
|
||||
*/
|
||||
void x86_init_memory(void);
|
||||
|
||||
/**
|
||||
* @brief Datum for the Global Descriptor Table Register.
|
||||
*/
|
||||
struct gdtr_t {
|
||||
uint16_t size_minus_one;
|
||||
uint32_t offset;
|
||||
} __attribute__((packed));
|
||||
|
||||
/**
|
||||
* @brief One entry in the Global Descriptor Table.
|
||||
*/
|
||||
struct gdt_entry {
|
||||
uint16_t limit_0_15;
|
||||
uint16_t base_0_15;
|
||||
uint8_t base_16_23;
|
||||
uint8_t access_byte;
|
||||
uint8_t limit_16_19_and_flags;
|
||||
uint8_t base_24_31;
|
||||
} __attribute__((packed));
|
||||
|
||||
#define GDT_AB_AC (1u << 0) /**< Access byte (set to one by CPU when accessed) */
|
||||
#define GDT_AB_RW (1u << 1) /**< If CS: read access allowed. If DS: write access allowed. */
|
||||
#define GDT_AB_DC (1u << 2) /**< Direction bit */
|
||||
#define GDT_AB_EX (1u << 3) /**< 1 = segment is CS */
|
||||
#define GDT_AB_S (1u << 4) /**< 1 = segment is CS or DS; 0 = segment is gate or TSS */
|
||||
#define GDT_AB_RING_0 (0u << 5) /**< Privilege level: ring 0 */
|
||||
#define GDT_AB_RING_1 (1u << 5) /**< Privilege level: ring 1 */
|
||||
#define GDT_AB_RING_2 (2u << 5) /**< Privilege level: ring 2 */
|
||||
#define GDT_AB_RING_3 (3u << 5) /**< Privilege level: ring 3 */
|
||||
#define GDT_AB_PR (1u << 7) /**< Segment present */
|
||||
|
||||
#define GDT_FLAG_USR4 (1u << 4) /**< bit for free use */
|
||||
#define GDT_FLAG_L (1u << 5) /**< 1 = long mode */
|
||||
#define GDT_FLAG_SZ (1u << 6) /**< 0 = 16/64 bit selector; 1 = 32 bit selector */
|
||||
#define GDT_FLAG_GR (1u << 7) /**< 0 = limit given in 1 b blocks; 1 = limit given in 4k blocks */
|
||||
|
||||
typedef uint64_t pae_page_directory_pointer_table_t[512] __attribute__((aligned(0x1000)));
|
||||
typedef uint64_t pae_page_directory_t[512] __attribute__((aligned(0x1000)));
|
||||
typedef uint64_t pae_page_table_t[512] __attribute__((aligned(0x1000)));
|
||||
|
||||
#define PT_P (1ull << 0) /**< 1 = page present */
|
||||
#define PT_RW (1ull << 1) /**< 1 = page writable */
|
||||
#define PT_US (1ull << 2) /**< 1 = don't restrict to ring 0 */
|
||||
#define PT_PWT (1ull << 3) /**< 1 = use write-through; 0 = use write-back */
|
||||
#define PT_PCD (1ull << 4) /**< 1 = disable caching */
|
||||
#define PT_A (1ull << 5) /**< 1 = page was accessed (set by the CPU, used for swapping) */
|
||||
#define PT_D (1ull << 6) /**< 1 = dirty (set by the CPU when written to) */
|
||||
#define PT_S (1ull << 7) /**< 1 = 4MB pages */
|
||||
#define PT_PAT (1ull << 7) /**< ? */
|
||||
#define PT_G (1ull << 8) /**< 1 = global (ignored for page directory entries) */
|
||||
#define PT_USR9 (1ull << 9) /**< bit for free use */
|
||||
#define PT_USR10 (1ull << 10) /**< bit for free use */
|
||||
#define PT_USR11 (1ull << 11) /**< bit for free use */
|
||||
#define PT_XD (1ull << 63) /**< 1 = no execute */
|
||||
#define PT_ADDR_MASK (((1ull << 48) - 1) & ~((1ull << 12) - 1))
|
||||
|
||||
#define PF_EC_P (1u << 0) /**< 1 = page protection violation; 0 = page not present */
|
||||
#define PF_EC_W (1u << 1) /**< 1 = accessed writingly; 0 = readingly */
|
||||
#define PF_EC_U (1u << 2) /**< 1 = ring 3 */
|
||||
#define PF_EC_R (1u << 3) /**< ? */
|
||||
#define PF_EC_I (1u << 4) /**< 1 = exception in CS; 0 = exception in DS */
|
||||
|
||||
/**
|
||||
* @brief Error value for x86_get_pte().
|
||||
*
|
||||
* All bits are set but PT_P.
|
||||
* Most likely you are interested only if the page table is present.
|
||||
* Since the PTE bits 48 to 62 are restricted, this value cannot occur in an actual PTE.
|
||||
*/
|
||||
#define NO_PTE (~PT_P)
|
||||
/**
|
||||
* @brief Read page table entry for addr.
|
||||
* @param addr Does not have to be aligned.
|
||||
* @returns PTE iff addr has an entry in the PDPT and PD, otherwise NO_PTE.
|
||||
*/
|
||||
uint64_t x86_get_pte(uint32_t addr);
|
||||
|
||||
/**
|
||||
* @brief Maps a range of physical pages at a new virtual address.
|
||||
* @param physical_start Start address of the physical page. Must be aligned.
|
||||
* @param pages Number of physical pages.
|
||||
* @param bits Flags of the PTE for each page (do not forget PT_P).
|
||||
* @returns Start of the virtual address range, aligned of course.
|
||||
* @returns NULL if the memory was exhausted.
|
||||
*
|
||||
* This function is to be used by hardware drivers.
|
||||
*/
|
||||
void *x86_map_physical_pages(uint64_t physical_start, unsigned pages, uint64_t bits);
|
||||
|
||||
/**
|
||||
* @brief Allocate a range of pages.
|
||||
* @param pages Number of pages to allocate.
|
||||
* @param bits Flags of the PTE for each page (do not forget PT_P).
|
||||
* @returns Start of the virtual address range, aligned of course.
|
||||
* Use x86_get_pte() to determine the start of the physical address range.
|
||||
*/
|
||||
void *x86_get_virtual_pages(unsigned pages, uint64_t bits);
|
||||
|
||||
/**
|
||||
* @brief Deallocate a range of bytes that were allocated with x86_get_virtual_pages()
|
||||
* @param virtual_start The return value of x86_get_virtual_pages().
|
||||
* @param pages Must match the original call to x86_get_virtual_pages().
|
||||
*/
|
||||
void x86_release_virtual_pages(uint32_t virtual_start, unsigned pages);
|
||||
|
||||
/**
|
||||
* @brief Intializes the global descriptor table.
|
||||
* @details This should be the very first call in the startup routine.
|
||||
* Changing the segmentation confuses the JTAG.
|
||||
* @details 0x0000 is an invalid descriptor.
|
||||
* 0x0008 is the code segment, from 0x00000000 to 0xFFFFFFFF, executable, read-only.
|
||||
* 0x0010 is the data segment, from 0x00000000 to 0xFFFFFFFF, non-executable, writable.
|
||||
*/
|
||||
void x86_init_gdt(void);
|
||||
|
||||
#endif
|
||||
|
||||
/** @} */
|
415
cpu/x86/include/x86_pci.h
Normal file
415
cpu/x86/include/x86_pci.h
Normal file
@ -0,0 +1,415 @@
|
||||
/*
|
||||
* Copyright (C) 2014 René Kijewski <rene.kijewski@fu-berlin.de>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
/**
|
||||
* The PCI subsystem of x86 boards.
|
||||
*
|
||||
* @ingroup x86-irq
|
||||
* @{
|
||||
* @file
|
||||
* @author René Kijewski <rene.kijewski@fu-berlin.de>
|
||||
*/
|
||||
|
||||
#ifndef CPU__X86__PCI__H__
|
||||
#define CPU__X86__PCI__H__
|
||||
|
||||
#include "x86_pci_init.h"
|
||||
#include "x86_pic.h"
|
||||
|
||||
#include <machine/endian.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
/**
|
||||
* @brief Initialize the Peripheral Component Interconnect.
|
||||
*
|
||||
* This function is called during initialization by x86_startup().
|
||||
* You must not call this function on your own accord.
|
||||
*/
|
||||
void x86_init_pci(void);
|
||||
|
||||
#define PCI_CONFIG_DATA (0x0cfc)
|
||||
#define PCI_CONFIG_ADDRESS (0x0cf8)
|
||||
|
||||
#define PCI_IO_ENABLE (1u << 31)
|
||||
#define PCI_IO_SHIFT_BUS (16u)
|
||||
#define PCI_IO_SHIFT_DEV (11u)
|
||||
#define PCI_IO_SHIFT_FUN (8u)
|
||||
#define PCI_IO_SHIFT_REG (0u)
|
||||
|
||||
#define PCI_BUS_COUNT (1u << 8)
|
||||
#define PCI_DEV_COUNT (1u << 5)
|
||||
#define PCI_FUN_COUNT (1u << 3)
|
||||
|
||||
#if BYTE_ORDER == LITTLE_ENDIAN
|
||||
# define U8_PCItoH(x) (x)
|
||||
# define U16_PCItoH(x) (x)
|
||||
# define U32_PCItoH(x) (x)
|
||||
|
||||
# define U8_HtoPCI(x) (x)
|
||||
# define U16_HtoPCI(x) (x)
|
||||
# define U32_HtoPCI(x) (x)
|
||||
#else
|
||||
# error "Only BYTE_ORDER == LITTLE_ENDIAN is supported right now"
|
||||
#endif
|
||||
|
||||
// "PCI LOCAL BUS SPECIFICATION, REV. 3.0"; Appendix 6.1.
|
||||
|
||||
/**
|
||||
* @brief The registers 0x00 through 0x03 of a PCI device.
|
||||
*/
|
||||
typedef union pci_reg_0x00 {
|
||||
__extension__ struct {
|
||||
/**
|
||||
* @brief Vendor ID of the device
|
||||
*
|
||||
* The values PCI_HEADER_VENDOR_ID_INVALID, PCI_HEADER_VENDOR_ID_UNRESPONSIVE
|
||||
* and PCI_HEADER_VENDOR_ID_ABSENT denote a special status.
|
||||
*/
|
||||
uint16_t vendor_id;
|
||||
uint16_t device_id; /**< Vendor specific device ID */
|
||||
} __attribute__((packed));
|
||||
uint32_t packed;
|
||||
} __attribute__((packed)) pci_reg_0x00_t;
|
||||
|
||||
/**
|
||||
* @brief The registers 0x04 through 0x07 of a PCI device.
|
||||
*/
|
||||
typedef union pci_reg_0x04 {
|
||||
__extension__ struct {
|
||||
uint16_t command; /**< Port to tell the PCI device to do things. */
|
||||
uint16_t status; /**< Reading the current status of a PCI device. */
|
||||
} __attribute__((packed));
|
||||
uint32_t packed;
|
||||
} __attribute__((packed)) pci_reg_0x04_t;
|
||||
|
||||
/**
|
||||
* @brief The registers 0x08 through 0x0b of a PCI device.
|
||||
*/
|
||||
typedef union pci_reg_0x08 {
|
||||
__extension__ struct {
|
||||
uint8_t revision_id;
|
||||
uint8_t programming_interface;
|
||||
uint8_t subclass;
|
||||
uint8_t baseclass;
|
||||
} __attribute__((packed));
|
||||
uint32_t packed;
|
||||
} __attribute__((packed)) pci_reg_0x08_t;
|
||||
|
||||
/**
|
||||
* @brief The registers 0x0c through 0x0f of a PCI device.
|
||||
*/
|
||||
typedef union pci_reg_0x0c {
|
||||
__extension__ struct {
|
||||
uint8_t cache_line_size;
|
||||
uint8_t latency_timer;
|
||||
uint8_t header_type;
|
||||
uint8_t builtin_selftest;
|
||||
} __attribute__((packed));
|
||||
uint32_t packed;
|
||||
} __attribute__((packed)) pci_reg_0x0c_t;
|
||||
|
||||
/**
|
||||
* @brief Generic layout of the port space of a PCI device.
|
||||
*/
|
||||
typedef struct pci_config {
|
||||
union pci_reg_0x00 reg_0x00;
|
||||
union pci_reg_0x04 reg_0x04;
|
||||
union pci_reg_0x08 reg_0x08;
|
||||
union pci_reg_0x0c reg_0x0c;
|
||||
|
||||
/* reg = 0x10 */
|
||||
uint8_t header_type_specific1[0x34 - 0x10];
|
||||
|
||||
/* reg = 0x34 */
|
||||
uint8_t capatibilities_pointer;
|
||||
uint8_t header_type_specific2[0x3c - 0x35];
|
||||
|
||||
/* reg = 0x3C */
|
||||
uint8_t interrupt_line;
|
||||
uint8_t interrupt_pin;
|
||||
|
||||
uint8_t header_type_specific3[0x40 - 0x3e];
|
||||
} __attribute__((packed)) pci_config_t;
|
||||
|
||||
/**
|
||||
* @brief Layout of the port space for PCI devices with `header_type == PCI_HEADER_TYPE_GENERAL_DEVICE`.
|
||||
*/
|
||||
typedef struct pci_config_standard {
|
||||
union pci_reg_0x00 reg_0x00;
|
||||
union pci_reg_0x04 reg_0x04;
|
||||
union pci_reg_0x08 reg_0x08;
|
||||
union pci_reg_0x0c reg_0x0c;
|
||||
|
||||
/* reg = 0x10 */
|
||||
uint32_t bar[6];
|
||||
/* reg = 0x28 */
|
||||
uint32_t cardbus_cis_pointer;
|
||||
/* reg = 0x2c */
|
||||
uint16_t subsystem_vendor_id;
|
||||
uint16_t subsystem_id;
|
||||
// reg = 0x30 */
|
||||
uint32_t expansion_rom_address;
|
||||
|
||||
/* reg = 0x34 */
|
||||
uint8_t capatibilities_pointer;
|
||||
uint8_t header_type_specific2[0x3c - 0x35];
|
||||
|
||||
/* reg = 0x3C */
|
||||
uint8_t interrupt_line;
|
||||
uint8_t interrupt_pin;
|
||||
|
||||
uint8_t header_type_specific3[0x40 - 0x3e];
|
||||
} __attribute__((packed)) pci_config_standard_t;
|
||||
|
||||
/**
|
||||
* @brief Layout of the port space for PCI devices with `header_type == PCI_HEADER_TYPE_BRIDGE`.
|
||||
*/
|
||||
typedef struct pci_config_bridge {
|
||||
union pci_reg_0x00 reg_0x00;
|
||||
union pci_reg_0x04 reg_0x04;
|
||||
union pci_reg_0x08 reg_0x08;
|
||||
union pci_reg_0x0c reg_0x0c;
|
||||
|
||||
/* reg = 0x10 */
|
||||
uint32_t bar[2];
|
||||
|
||||
/* reg = 0x18 */
|
||||
uint8_t bus_primary;
|
||||
uint8_t bus_secondary;
|
||||
uint8_t bus_subordinary;
|
||||
uint8_t bus_secondary_latency_timer;
|
||||
|
||||
/* reg = 0x1c */
|
||||
uint8_t io_lower_base;
|
||||
uint8_t io_lower_limit;
|
||||
uint16_t status_secondary;
|
||||
|
||||
/* reg = 0x20 */
|
||||
uint16_t memory_base;
|
||||
uint16_t memory_limit;
|
||||
|
||||
/* reg = 0x24 */
|
||||
uint16_t prefetchable_lower_base;
|
||||
uint16_t prefetchable_lower_limit;
|
||||
|
||||
// reg = 0x28
|
||||
uint32_t prefetchable_upper_base;
|
||||
// reg = 0x2c
|
||||
uint32_t prefetchable_upper_limit;
|
||||
|
||||
// reg = 0x30
|
||||
uint16_t io_upper_base;
|
||||
uint16_t io_upper_limit;
|
||||
|
||||
// reg = 0x34
|
||||
uint8_t capatibilities_pointer;
|
||||
uint8_t header_type_specific2[0x38 - 0x35];
|
||||
|
||||
// reg = 0x38
|
||||
uint32_t extension_ron_address;
|
||||
|
||||
// reg = 0x3C
|
||||
uint8_t interrupt_line;
|
||||
uint8_t interrupt_pin;
|
||||
uint16_t bridge_control;
|
||||
} __attribute__((packed)) pci_config_bridge_t;
|
||||
|
||||
#define PCI_HEADER_VENDOR_ID_INVALID (0x0000u)
|
||||
#define PCI_HEADER_VENDOR_ID_UNRESPONSIVE (0x0001u)
|
||||
#define PCI_HEADER_VENDOR_ID_ABSENT (0xffffu)
|
||||
|
||||
#define PCI_HEADER_TYPE_GENERAL_DEVICE (0x00u)
|
||||
#define PCI_HEADER_TYPE_BRIDGE (0x01u)
|
||||
#define PCI_HEADER_TYPE_CARD_BUS (0x02u)
|
||||
|
||||
#define PCI_HEADER_TYPE_MULTI_FUNCTION (0x80u)
|
||||
#define PCI_HEADER_TYPE_MASK (0x7fu)
|
||||
|
||||
#define PCI_COMMAND_IO_SPACE (1u << 0)
|
||||
#define PCI_COMMAND_MEMORY_SPACE (1u << 1)
|
||||
#define PCI_COMMAND_BUS_MASTER (1u << 2)
|
||||
#define PCI_COMMAND_SPECIAL_CYCLES (1u << 3)
|
||||
#define PCI_COMMAND_MEM_WRITE_AND_INV (1u << 4)
|
||||
#define PCI_COMMAND_VGA_PALETTE_SNOOP (1u << 5)
|
||||
#define PCI_COMMAND_PARITY_ERR_RESPONSE (1u << 6)
|
||||
#define PCI_COMMAND_SERR (1u << 8)
|
||||
#define PCI_COMMAND_FAST_B2B (1u << 9)
|
||||
#define PCI_COMMAND_INTERRUPT_DISABLE (1u << 10)
|
||||
|
||||
#define PRI_STATUS_INTERRUPT (1u << 3)
|
||||
#define PRI_STATUS_CAPATIBILITIES (1u << 4)
|
||||
#define PRI_STATUS_66MHZ (1u << 5)
|
||||
#define PRI_STATUS_FAST_B2B (1u << 7)
|
||||
#define PRI_STATUS_MASTER_PARITY_ERROR (1u << 8)
|
||||
#define PRI_STATUS_DEVSEL_FAST (0u << 9)
|
||||
#define PRI_STATUS_DEVSEL_MEDIUM (1u << 9)
|
||||
#define PRI_STATUS_DEVSEL_SLOW (3u << 9)
|
||||
#define PRI_STATUS_DEVSEL_MASK (3u << 9)
|
||||
#define PRI_STATUS_SIG_TARGET_ABORT (1u << 11)
|
||||
#define PRI_STATUS_RECV_TARGET_ABORT (1u << 12)
|
||||
#define PRI_STATUS_RECV_MASTER_ABORT (1u << 13)
|
||||
#define PRI_STATUS_SIG_SYSTEM_ERROR (1u << 14)
|
||||
#define PRI_STATUS_PARITY_ERROR (1u << 15)
|
||||
|
||||
#define PCI_CAPS_PTR_MIN (0x40u)
|
||||
#define PCI_CAPS_PTR_MAX (0xf0u)
|
||||
|
||||
#define PCI_INTR_PIN_NONE (0x00u)
|
||||
#define PCI_INTR_PIN_A (0x01u)
|
||||
#define PCI_INTR_PIN_B (0x02u)
|
||||
#define PCI_INTR_PIN_C (0x03u)
|
||||
#define PCI_INTR_PIN_D (0x04u)
|
||||
|
||||
#define PCI_BAR_IO_SPACE (1u << 0)
|
||||
#define PCI_BAR_SPACE_32 (0u << 1)
|
||||
#define PCI_BAR_SPACE_1M (1u << 1)
|
||||
#define PCI_BAR_SPACE_64 (2u << 1)
|
||||
#define PCI_BAR_SPACE_MASK (3u << 1)
|
||||
#define PCI_BAR_PREFETCHABLE (1u << 3)
|
||||
#define PCI_BAR_ADDR_OFFS_MEM (4u)
|
||||
#define PCI_BAR_ADDR_OFFS_IO (2u)
|
||||
|
||||
/**
|
||||
* @brief Address type of a struct x86_pci_io.
|
||||
*/
|
||||
typedef enum {
|
||||
PCI_IO_INVALID = 0, /**< There was an error while configuring this IO port. */
|
||||
PCI_IO_MEM, /**< physical memory */
|
||||
PCI_IO_PORT, /**< port address */
|
||||
} x86_pci_io_type_t;
|
||||
|
||||
/**
|
||||
* @brief One IO port of a struct x86_known_pci_device.
|
||||
*/
|
||||
typedef struct x86_pci_io {
|
||||
x86_pci_io_type_t type; /**< Type of this IO port. */
|
||||
unsigned long length; /**< Length (in bytes or port) of this IO port. */
|
||||
union {
|
||||
void *ptr; /**< Base **virtual** memory address. */
|
||||
int port; /**< Base port number. */
|
||||
} addr; /**< Base address of the IO port. */
|
||||
uint8_t bar_num;
|
||||
} x86_pci_io_t;
|
||||
|
||||
struct x86_known_pci_device;
|
||||
|
||||
/**
|
||||
* @brief A callback handler if there was an IRQ on the IRQ line of this device.
|
||||
* @param[in] d Device that (might) need attention.
|
||||
*
|
||||
* Because PCI is multiplexer, there might not be an IRQ for this device.
|
||||
* This callback is called out of the interrupt handler (inISR() == true).
|
||||
* Lengthy operations should be handled in a dedicated thread; use msg_send_int().
|
||||
* You must no enable interrupt inside the handler.
|
||||
*/
|
||||
typedef void (*x86_pci_irq_handler_t)(struct x86_known_pci_device *d);
|
||||
|
||||
/**
|
||||
* @brief A PCI device.
|
||||
*/
|
||||
typedef struct x86_known_pci_device {
|
||||
uint8_t bus, dev, fun;
|
||||
pci_reg_0x00_t vendor;
|
||||
pci_reg_0x08_t class;
|
||||
|
||||
unsigned io_count; /**< Number of entries in io. */
|
||||
struct x86_pci_io **io; /**< Pointer to io_count pointers to configured IO ports. */
|
||||
|
||||
bool managed; /**< To be set by the hardware driver if it is going to manage this device. */
|
||||
uint8_t irq; /**< IRQ line of this device. Change through x86_pci_set_irq(). */
|
||||
x86_pci_irq_handler_t irq_handler; /**< Callback function if there was an interrupt on `irq`. */
|
||||
} x86_known_pci_device_t;
|
||||
|
||||
/**
|
||||
* @brief Enumerate unmanaged PCI devices.
|
||||
* @param index Opaque reentrant pointer for the next iteration. Must be zero at first call.
|
||||
* @returns Pointer to next unmanaged PCI device, or NULL.
|
||||
*
|
||||
* This function shall only be called by hardware drivers.
|
||||
* x86_init_pci() will call the hardware drivers once it is ready.
|
||||
* To not call earlier or later on your on accord.
|
||||
*
|
||||
* The result is a pointer to a pointer so that you can use realloc() to extend the struct as needed.
|
||||
* You must only do that if you are going to manage this device, set managed iff so.
|
||||
*/
|
||||
struct x86_known_pci_device **x86_enumerate_unmanaged_pci_devices(unsigned *index);
|
||||
|
||||
/**
|
||||
* @brief Enumerate all PCI devices.
|
||||
* @param index Opaque reentrant pointer for the next iteration. Must be zero at first call.
|
||||
* @returns Pointer to next PCI device, or NULL.
|
||||
*/
|
||||
const struct x86_known_pci_device *x86_enumerate_pci_devices(unsigned *index);
|
||||
|
||||
/**
|
||||
* @brief Read a long word from the PCI configuration space.
|
||||
* @param reg The register must be aligned on a 4 bytes boundary.
|
||||
*/
|
||||
uint32_t x86_pci_read(unsigned bus, unsigned dev, unsigned fun, unsigned reg);
|
||||
/**
|
||||
* @brief Read a byte from the PCI configuration space.
|
||||
* @param reg The register must be aligned on a 4 bytes boundary.
|
||||
*/
|
||||
uint8_t x86_pci_read8(unsigned bus, unsigned dev, unsigned fun, unsigned reg);
|
||||
/**
|
||||
* @brief Read a short word from the PCI configuration space.
|
||||
* @param reg The register must be aligned on a 4 bytes boundary.
|
||||
*/
|
||||
uint16_t x86_pci_read16(unsigned bus, unsigned dev, unsigned fun, unsigned reg);
|
||||
|
||||
/**
|
||||
* @brief Write a long word to the PCI configuration space.
|
||||
* @param reg The register must be aligned on a 4 bytes boundary.
|
||||
*/
|
||||
void x86_pci_write(unsigned bus, unsigned dev, unsigned fun, unsigned reg, uint32_t datum);
|
||||
/**
|
||||
* @brief Write a byte to the PCI configuration space.
|
||||
* @param reg The register must be aligned on a 4 bytes boundary.
|
||||
*/
|
||||
void x86_pci_write8(unsigned bus, unsigned dev, unsigned fun, unsigned reg, uint8_t datum);
|
||||
/**
|
||||
* @brief Write a short word to the PCI configuration space.
|
||||
* @param reg The register must be aligned on a 4 bytes boundary.
|
||||
*/
|
||||
void x86_pci_write16(unsigned bus, unsigned dev, unsigned fun, unsigned reg, uint16_t datum);
|
||||
|
||||
/**
|
||||
* @brief Read a long word from the PCI configuration space, and cast the datum accordingly.
|
||||
* @param REG Either `0x00`, `0x04`, `0x08`, or `0x0c`. Must be a constant token.
|
||||
* @returns A `union pci_reg_0x00`, `union pci_reg_0x04`, ...
|
||||
*/
|
||||
#define x86_pci_read_reg(REG, BUS, DEV, FUN) \
|
||||
((union pci_reg_##REG) { .packed = x86_pci_read((BUS), (DEV), (FUN), (REG)) })
|
||||
|
||||
#define PCI_IRQ_ACPI (PIC_NUM_LPT2) /**< IRQ line that may be used by the ACPI. */
|
||||
#define PCI_IRQ_NETWORKING (PIC_NUM_9) /**< IRQ line that may be used by networking devices. */
|
||||
#define PCI_IRQ_DEFAULT (PIC_NUM_ATA_4) /**< IRQ line that may be used by other devices. */
|
||||
#define PCI_IRQ_USB (PIC_NUM_ATA_3) /**< IRQ line that may be used by USB host controllers. */
|
||||
|
||||
/**
|
||||
* @brief Set the IRQ line if a PCI device.
|
||||
*
|
||||
* irq_num must be one of PCI_IRQ_ACPI, PCI_IRQ_NETWORKING, PCI_IRQ_DEFAULT, PCI_IRQ_USB.
|
||||
* Bad things will happen if you supply any other value.
|
||||
*/
|
||||
void x86_pci_set_irq(struct x86_known_pci_device *d, uint8_t irq_num);
|
||||
|
||||
#endif
|
||||
|
||||
/** @} */
|
8
cpu/x86/include/x86_pci_init.h
Normal file
8
cpu/x86/include/x86_pci_init.h
Normal file
@ -0,0 +1,8 @@
|
||||
#ifndef __X86__PCI_INIT__H
|
||||
#define __X86__PCI_INIT__H
|
||||
|
||||
/* no PCI devices are implemented, yet */
|
||||
|
||||
void x86_init_pci_devices(void);
|
||||
|
||||
#endif
|
53
cpu/x86/include/x86_pci_strings.h
Normal file
53
cpu/x86/include/x86_pci_strings.h
Normal file
@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright (C) 2014 René Kijewski <rene.kijewski@fu-berlin.de>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
/**
|
||||
* The string representation of PCI constants. For humans.
|
||||
*
|
||||
* @ingroup x86-irq
|
||||
* @{
|
||||
* @file
|
||||
* @author René Kijewski <rene.kijewski@fu-berlin.de>
|
||||
*/
|
||||
|
||||
#ifndef CPU__X86__PCI_NAMES__H__
|
||||
#define CPU__X86__PCI_NAMES__H__
|
||||
|
||||
/**
|
||||
* @brief Get a string representation of the device class.
|
||||
* @param[out] baseclass_name Name of the base class. Supply NULL if you do not care.
|
||||
* @return A string (lengthy text) that describes the device class.
|
||||
*
|
||||
* If the device class is unknown, then the hexadecimal value is returned.
|
||||
* This function is not thread safe.
|
||||
*/
|
||||
const char *x86_pci_subclass_to_string(unsigned baseclass, unsigned subclass, unsigned interface, const char **baseclass_name);
|
||||
|
||||
/**
|
||||
* @brief Get a string representation of the device name.
|
||||
* @param[out] vendor_name Name of the device vendor. Supply NULL if you do not care.
|
||||
* @return A string that tells the device name.
|
||||
*
|
||||
* If the device ID is unknown, then the hexadecimal value is returned.
|
||||
* This function is not thread safe.
|
||||
*/
|
||||
const char *x86_pci_device_id_to_string(unsigned vendor_id, unsigned device_id, const char **vendor_name);
|
||||
|
||||
#endif
|
||||
|
||||
/** @} */
|
168
cpu/x86/include/x86_pic.h
Normal file
168
cpu/x86/include/x86_pic.h
Normal file
@ -0,0 +1,168 @@
|
||||
/*
|
||||
* Copyright (C) 2014 René Kijewski <rene.kijewski@fu-berlin.de>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
/**
|
||||
* The IRQ handler of x86 boards.
|
||||
*
|
||||
* @ingroup x86-irq
|
||||
* @{
|
||||
* @file
|
||||
* @author René Kijewski <rene.kijewski@fu-berlin.de>
|
||||
*/
|
||||
|
||||
#ifndef CPU__X86__PIC__H__
|
||||
#define CPU__X86__PIC__H__
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/**
|
||||
* @brief Initialize the Programmable Interrupt Controller.
|
||||
*
|
||||
* This function is called during initialization by x86_startup().
|
||||
* You must not call this function on your own accord.
|
||||
*/
|
||||
void x86_init_pic(void);
|
||||
|
||||
#define X86_IRQ_COUNT (0x10) /**< Number of IRQ lines */
|
||||
|
||||
/**
|
||||
* @brief Offset of the IRQ master in the interrupt numbers.
|
||||
*
|
||||
* x86_interrupts.c expects this value to be `0x20`. Do not change.
|
||||
*/
|
||||
#define PIC_MASTER_INTERRUPT_BASE (0x20)
|
||||
/**
|
||||
* @brief Offset of the IRQ slave in the interrupt numbers.
|
||||
*
|
||||
* The whole IRQ subsystem expects the IRQ slave numbers to come immediately after the master.
|
||||
*/
|
||||
#define PIC_SLAVE_INTERRUPT_BASE (PIC_MASTER_INTERRUPT_BASE + 8)
|
||||
|
||||
#define PIC_MASTER (0x20)
|
||||
#define PIC_SLAVE (0xA0)
|
||||
|
||||
#define PIC_COMMAND (0x00)
|
||||
#define PIC_DATA (0x01)
|
||||
#define PIC_IMR (0x01)
|
||||
|
||||
#define PIC_EOI 0x20 /**< End-of-interrupt command code */
|
||||
|
||||
#define PIC_READ_IRR 0x0a /**< OCW3 irq ready next CMD read */
|
||||
#define PIC_READ_ISR 0x0b /**< OCW3 irq service next CMD read */
|
||||
|
||||
#define PIC_ICW1_ICW4 (0x01) /**< ICW4 (not) needed */
|
||||
#define PIC_ICW1_SINGLE (0x02) /**< Single (cascade) mode */
|
||||
#define PIC_ICW1_INTERVAL4 (0x04) /**< Call address interval 4 (8) */
|
||||
#define PIC_ICW1_LEVEL (0x08) /**< Level triggered (edge) mode */
|
||||
#define PIC_ICW1_INIT (0x10) /**< Initialization - required! */
|
||||
|
||||
#define PIC_ICW4_8086 (0x01) /**< 8086/88 (MCS-80/85) mode */
|
||||
#define PIC_ICW4_AUTO (0x02) /**< Auto (normal) EOI */
|
||||
#define PIC_ICW4_BUF_SLAVE (0x08) /**< Buffered mode/slave */
|
||||
#define PIC_ICW4_BUF_MASTER (0x0C) /**< Buffered mode/master */
|
||||
#define PIC_ICW4_SFNM (0x10) /**< Special fully nested (not) */
|
||||
|
||||
#define PIC_NUM_PIT (0x0) /**< IRQ line of the Programmable Interrupt Controller **/
|
||||
#define PIC_NUM_KEYBOARD_CONTROLLER_1 (0x1) /**< IRQ line of the first PS/2 port **/
|
||||
#define PIC_NUM_SLAVE (0x2) /**< not a valid IRQ line! */
|
||||
#define PIC_NUM_RS232_2_4 (0x3) /**< IRQ line of COM 2+4 **/
|
||||
#define PIC_NUM_RS232_1_3 (0x4) /**< IRQ line of COM 1+2 **/
|
||||
#define PIC_NUM_LPT2 (0x5) /**< IRQ line of the secondary printer or soundcard (available for PCI) **/
|
||||
#define PIC_NUM_FLOPPY (0x6) /**< IRQ line of the floppy drive. **/
|
||||
#define PIC_NUM_LPT1 (0x7) /**< IRQ line of the parallel port (available for PCI) **/
|
||||
|
||||
#define PIC_NUM_RTC (0x8) /**< IRQ line of the Real Time Clock **/
|
||||
#define PIC_NUM_9 (0x9) /**< Free to use IRQ line (available for PCI) **/
|
||||
#define PIC_NUM_ATA_4 (0xa) /**< Free to use IRQ line (available for PCI) **/
|
||||
#define PIC_NUM_ATA_3 (0xb) /**< Free to use IRQ line (available for PCI) **/
|
||||
#define PIC_NUM_KEYBOARD_CONTROLLER_2 (0xc) /**< IRQ line of the second PS/2 port **/
|
||||
#define PIC_NUM_FPU (0xd) /**< not a valid IRQ line! */
|
||||
#define PIC_NUM_ATA_1 (0xe) /**< IRQ line of the primary IDE controller **/
|
||||
#define PIC_NUM_ATA_2 (0xf) /**< IRQ line of the secondary IDQ controller **/
|
||||
|
||||
#define PIC_MASK_PIT (1 << PIC_NUM_PIT)
|
||||
#define PIC_MASK_KEYBOARD_CONTROLLER_1 (1 << PIC_NUM_KEYBOARD_CONTROLLER_1)
|
||||
#define PIC_MASK_SLAVE (1 << PIC_NUM_SLAVE)
|
||||
#define PIC_MASK_RS232_2_4 (1 << PIC_NUM_RS232_2_4)
|
||||
#define PIC_MASK_RS232_1_3 (1 << PIC_NUM_RS232_1_3)
|
||||
#define PIC_MASK_LPT2 (1 << PIC_NUM_LPT2)
|
||||
#define PIC_MASK_FLOPPY (1 << PIC_NUM_FLOPPY)
|
||||
#define PIC_MASK_LPT1 (1 << PIC_NUM_LPT1)
|
||||
|
||||
#define PIC_MASK_RTC (1 << PIC_NUM_RTC)
|
||||
#define PIC_MASK_9 (1 << PIC_NUM_9)
|
||||
#define PIC_MASK_ATA_4 (1 << PIC_NUM_ATA_4)
|
||||
#define PIC_MASK_ATA_3 (1 << PIC_NUM_ATA_3)
|
||||
#define PIC_MASK_KEYBOARD_CONTROLLER_2 (1 << PIC_NUM_KEYBOARD_CONTROLLER_2)
|
||||
#define PIC_MASK_FPU (1 << PIC_NUM_FPU)
|
||||
#define PIC_MASK_ATA_1 (1 << PIC_NUM_ATA_1)
|
||||
#define PIC_MASK_ATA_2 (1 << PIC_NUM_ATA_2)
|
||||
|
||||
/**
|
||||
* @brief Callback handler if there was an interrupt on this IRQ line.
|
||||
* @param irq_num IRQ line in question.
|
||||
*
|
||||
* This callback is called out of the interrupt handler (inISR() == true).
|
||||
* Lengthy operations should be handled in a dedicated thread; use msg_send_int().
|
||||
* You must no enable interrupt inside the handler.
|
||||
*/
|
||||
typedef void (*x86_irq_handler_t)(uint8_t irq_num);
|
||||
|
||||
/**
|
||||
* @brief Set callback function for an IRQ line.
|
||||
* @param handler The callback function, or NULL to do nothing.
|
||||
*
|
||||
* Setting a handler does not enable the interrupt.
|
||||
* Use x86_pic_enable_irq() or x86_pic_disable_irq() accordingly.
|
||||
*
|
||||
* This function must only be called with interrupts disabled.
|
||||
*
|
||||
* Beware on unsetting interrupt handlers:
|
||||
* The PIC default handler will still send an EOI.
|
||||
* Especially the keyboard controller does not like it,
|
||||
* if it is told that everything was done but it wasn't.
|
||||
* A raised #GP might be the least of your problems.
|
||||
*/
|
||||
void x86_pic_set_handler(unsigned irq, x86_irq_handler_t handler);
|
||||
|
||||
/**
|
||||
* @brief Set the enabled IRQs
|
||||
*
|
||||
* Beware: this is the exact opposite of masking IRQs.
|
||||
*
|
||||
* This function should only be called by other subsystems like the PCI subsystem.
|
||||
*/
|
||||
void x86_pic_set_enabled_irqs(uint16_t mask);
|
||||
|
||||
/**
|
||||
* @brief Enable (unmask) an IRQ
|
||||
*
|
||||
* This function should only be called by other subsystems like the PCI subsystem.
|
||||
*/
|
||||
void x86_pic_enable_irq(unsigned num);
|
||||
|
||||
/**
|
||||
* @brief Disable (mask) an IRQ
|
||||
*
|
||||
* This function should only be called by other subsystems like the PCI subsystem.
|
||||
*/
|
||||
void x86_pic_disable_irq(unsigned num);
|
||||
|
||||
#endif
|
||||
|
||||
/** @} */
|
114
cpu/x86/include/x86_pit.h
Normal file
114
cpu/x86/include/x86_pit.h
Normal file
@ -0,0 +1,114 @@
|
||||
/*
|
||||
* Copyright (C) 2014 René Kijewski <rene.kijewski@fu-berlin.de>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Interfacing with the PIT.
|
||||
*
|
||||
* The core of the x86 port only uses the RTC for the hwtimer.
|
||||
* Application developers are free to use this module.
|
||||
* Be aware of portability issues.
|
||||
*
|
||||
* You most likely want to use the vtimer interface instead.
|
||||
*
|
||||
* @ingroup x86-irq
|
||||
* @{
|
||||
* @file
|
||||
* @author René Kijewski <rene.kijewski@fu-berlin.de>
|
||||
*/
|
||||
|
||||
#ifndef CPU__X86__PIT__H__
|
||||
#define CPU__X86__PIT__H__
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#define PIT_CHANNEL_0_PORT (0x40) /**< Channel 0 */
|
||||
#define PIT_CHANNEL_1_PORT (0x41) /**< Channel 1, DO NOT USE */
|
||||
#define PIT_CHANNEL_2_PORT (0x42) /**< Channel 2, do not use if you can help it */
|
||||
#define PIT_COMMAND_PORT (0x43)
|
||||
|
||||
#define PIT_ACCESS_MODE_LATCH_COUNT (0 << 4)
|
||||
#define PIT_ACCESS_MODE_LO_BYTE (1 << 4)
|
||||
#define PIT_ACCESS_MODE_HI_BYTE (2 << 4)
|
||||
#define PIT_ACCESS_MODE_LO_HI (3 << 4)
|
||||
|
||||
#define PIT_INTERRUPT_ONTERMINAL_COUNT (0 << 1) /**< */
|
||||
#define PIT_ONE_SHOT (1 << 1) /**< */
|
||||
#define PIT_RATE_GENERATOR (2 << 1) /**< */
|
||||
#define PIT_SQUARE_WAVE (3 << 1) /**< */
|
||||
#define PIT_SOFWARE_TRIGGERED_STROBE (4 << 1) /**< */
|
||||
#define PIT_HARDWARE_TRIGGERED_STROBE (5 << 1) /**< */
|
||||
|
||||
#define PIT_MIN_HZ (19)
|
||||
#define PIT_MAX_HZ (1193181)
|
||||
|
||||
/**
|
||||
* @brief Initialize the Programmable Interval Timer.
|
||||
*
|
||||
* This function is called during initialization by x86_startup().
|
||||
* You must not call this function on your own accord.
|
||||
*/
|
||||
void x86_init_pit(void);
|
||||
|
||||
/**
|
||||
* @brief Reads the current value of the of the channel.
|
||||
* @param channel Channel (1, 2, or 3) to read from.
|
||||
* @returns Current value of the channel.
|
||||
*
|
||||
* Channel 1 is the only channel that you should access.
|
||||
*
|
||||
* Channel 2 might be absent on current systems.
|
||||
* It was never free to use for the OSes.
|
||||
* Never access this channel!
|
||||
*
|
||||
* Channel 3 was originally intended to be used by the sound card.
|
||||
* It's free to use, but you probably shouldn't.
|
||||
*/
|
||||
uint16_t x86_pit_read(unsigned channel);
|
||||
|
||||
/**
|
||||
* @brief Sets the speed and operation mode of a channel.
|
||||
* @param channel Channel (1, 2, or 3) to set.
|
||||
* @param mode Action on overflow.
|
||||
* @param max After how many ticks the timer should overflow.
|
||||
*
|
||||
* See x86_pit_read() for considerations about the channel.
|
||||
*
|
||||
* The timer has a speed of 1193181hz.
|
||||
* That value was convinient for hardware vendors,
|
||||
* but has only the quite useless prime factors 3, 11, 19, and 173.
|
||||
* That hurts the usefulness severly.
|
||||
*/
|
||||
void x86_pit_set2(unsigned channel, unsigned mode, uint16_t max);
|
||||
|
||||
/**
|
||||
* @brief Convenience wrapper for x86_pit_set2().
|
||||
* @param hz After how how long the timer should overflow.
|
||||
* @returns true iff the value of hz was sane.
|
||||
*
|
||||
* See x86_pit_set2() for a more detailed explanation.
|
||||
* max will be set to 1193181 / hz.
|
||||
* That means hz has to be a value between 19 and 1193181.
|
||||
*
|
||||
* Beware: the 1,193,163 different values for hz will only render 2,165 different values for max.
|
||||
*/
|
||||
bool x86_pit_set(unsigned channel, unsigned mode, unsigned hz);
|
||||
|
||||
#endif
|
||||
|
||||
/** @} */
|
186
cpu/x86/include/x86_ports.h
Normal file
186
cpu/x86/include/x86_ports.h
Normal file
@ -0,0 +1,186 @@
|
||||
/* Copyright (C) 2004, 2005, 2006 Board of Trustees, Leland Stanford
|
||||
* Jr. University. All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
// Copy of Pintos' threads/io.h, license header extracted from Pintos' LICENSE file.
|
||||
|
||||
#ifndef CPU__X86__PORTS_H__
|
||||
#define CPU__X86__PORTS_H__
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
|
||||
/**
|
||||
* @brief Reads and returns a byte from PORT.
|
||||
* @param[in] port Port to read from.
|
||||
* @returns Read value.
|
||||
*/
|
||||
static inline uint8_t __attribute__((always_inline)) inb(uint16_t port)
|
||||
{
|
||||
/* See [IA32-v2a] "IN". */
|
||||
uint8_t data;
|
||||
asm volatile("inb %w1, %b0" : "=a"(data) : "Nd"(port));
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Reads multiple bytes from a port.
|
||||
* @param[in] port Port to read from.
|
||||
* @param[out] addr Buffer to write the read values into.
|
||||
* @param[in] cnt Number of bytes to read.
|
||||
*/
|
||||
static inline void __attribute__((always_inline)) insb(uint16_t port, void *addr, size_t cnt)
|
||||
{
|
||||
/* See [IA32-v2a] "INS". */
|
||||
asm volatile("rep insb" : "+D"(addr), "+c"(cnt) : "d"(port) : "memory");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Reads and returns a word from PORT.
|
||||
* @param[in] port Port to read from.
|
||||
* @returns Read value.
|
||||
*/
|
||||
static inline uint16_t __attribute__((always_inline)) inw(uint16_t port)
|
||||
{
|
||||
uint16_t data;
|
||||
/* See [IA32-v2a] "IN". */
|
||||
asm volatile("inw %w1, %w0" : "=a"(data) : "Nd"(port));
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Reads multiple words from a port.
|
||||
* @param[in] port Port to read from.
|
||||
* @param[out] addr Buffer to write the read values into.
|
||||
* @param[in] cnt Number of words to read.
|
||||
*/
|
||||
static inline void __attribute__((always_inline)) insw(uint16_t port, void *addr, size_t cnt)
|
||||
{
|
||||
/* See [IA32-v2a] "INS". */
|
||||
asm volatile("rep insw" : "+D"(addr), "+c"(cnt) : "d"(port) : "memory");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Reads and returns a long from PORT.
|
||||
* @param[in] port Port to read from.
|
||||
* @returns Read value.
|
||||
*/
|
||||
static inline uint32_t __attribute__((always_inline)) inl(uint16_t port)
|
||||
{
|
||||
/* See [IA32-v2a] "IN". */
|
||||
uint32_t data;
|
||||
asm volatile("inl %w1, %0" : "=a"(data) : "Nd"(port));
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Reads multiple longs from a port.
|
||||
* @param[in] port Port to read from.
|
||||
* @param[out] addr Buffer to write the read values into.
|
||||
* @param[in] cnt Number of words to read.
|
||||
*/
|
||||
static inline void __attribute__((always_inline)) insl(uint16_t port, void *addr, size_t cnt)
|
||||
{
|
||||
/* See [IA32-v2a] "INS". */
|
||||
asm volatile("rep insl" : "+D"(addr), "+c"(cnt) : "d"(port) : "memory");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Writes a byte into a port.
|
||||
* @param[in] port Port to write into.
|
||||
* @param[in] data Byte to write.
|
||||
*/
|
||||
static inline void __attribute__((always_inline)) outb(uint16_t port, uint8_t data)
|
||||
{
|
||||
/* See [IA32-v2b] "OUT". */
|
||||
asm volatile("outb %b0, %w1" : : "a"(data), "Nd"(port));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Writes multiple bytes into a port.
|
||||
* @param[in] port Port to write into.
|
||||
* @param[in] addr Buffer to read from.
|
||||
* @param[in] cnt Number of bytes to write.
|
||||
*/
|
||||
static inline void __attribute__((always_inline)) outsb(uint16_t port, const void *addr, size_t cnt)
|
||||
{
|
||||
/* See [IA32-v2b] "OUTS". */
|
||||
asm volatile("rep outsb" : "+S"(addr), "+c"(cnt) : "d"(port));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Writes a word into a port.
|
||||
* @param[in] port Port to write into.
|
||||
* @param[in] data Word to write.
|
||||
*/
|
||||
static inline void __attribute__((always_inline)) outw(uint16_t port, uint16_t data)
|
||||
{
|
||||
/* See [IA32-v2b] "OUT". */
|
||||
asm volatile("outw %w0, %w1" : : "a"(data), "Nd"(port));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Writes multiple words into a port.
|
||||
* @param[in] port Port to write into.
|
||||
* @param[in] addr Buffer to read from.
|
||||
* @param[in] cnt Number of words to write.
|
||||
*/
|
||||
static inline void __attribute__((always_inline)) outsw(uint16_t port, const void *addr, size_t cnt)
|
||||
{
|
||||
/* See [IA32-v2b] "OUTS". */
|
||||
asm volatile("rep outsw" : "+S"(addr), "+c"(cnt) : "d"(port));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Writes a long into a port.
|
||||
* @param[in] port Port to write into.
|
||||
* @param[in] data Long to write.
|
||||
*/
|
||||
static inline void __attribute__((always_inline)) outl(uint16_t port, uint32_t data)
|
||||
{
|
||||
/* See [IA32-v2b] "OUT". */
|
||||
asm volatile("outl %0, %w1" : : "a"(data), "Nd"(port));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Writes multiple longs into a port.
|
||||
* @param[in] port Port to write into.
|
||||
* @param[in] addr Buffer to read from.
|
||||
* @param[in] cnt Number of longs to write.
|
||||
*/
|
||||
static inline void __attribute__((always_inline)) outsl(uint16_t port, const void *addr, size_t cnt)
|
||||
{
|
||||
/* See [IA32-v2b] "OUTS". */
|
||||
asm volatile("rep outsl" : "+S"(addr), "+c"(cnt) : "d"(port));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Make sure a write to a port was already acknowledged.
|
||||
*/
|
||||
static inline void __attribute__((always_inline)) io_wait(void)
|
||||
{
|
||||
asm volatile(" jmp 1f\n"
|
||||
"1: jmp 2f\n"
|
||||
"2:");
|
||||
}
|
||||
|
||||
#endif
|
89
cpu/x86/include/x86_reboot.h
Normal file
89
cpu/x86/include/x86_reboot.h
Normal file
@ -0,0 +1,89 @@
|
||||
/*
|
||||
* Copyright (C) 2014 René Kijewski <rene.kijewski@fu-berlin.de>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
/**
|
||||
* Rebooting x86 boards.
|
||||
*
|
||||
* @ingroup x86
|
||||
* @{
|
||||
* @file
|
||||
* @author René Kijewski <rene.kijewski@fu-berlin.de>
|
||||
*/
|
||||
|
||||
#ifndef CPU__X86__REBOOT__H__
|
||||
#define CPU__X86__REBOOT__H__
|
||||
|
||||
#include "kernel.h"
|
||||
|
||||
/**
|
||||
* @brief Prototype for a x86 reboot function.
|
||||
*
|
||||
* The function may return, then a naive reboot is tried.
|
||||
* It is safe to call x86_kbc_reboot() inside the handler,
|
||||
* then a naive reboot will be done immediately.
|
||||
*
|
||||
* The handler is called with interrupts disabled.
|
||||
* You should keep the interrupts disabled and use busy loops.
|
||||
*/
|
||||
typedef void (*x86_reboot_t)(void);
|
||||
|
||||
/**
|
||||
* @brief Reboots the system.
|
||||
*
|
||||
* First the function supplied if x86_set_reboot_fun() is called (iff applies).
|
||||
* Then a reboot using the keyboard controller is tried.
|
||||
* And if everything fails, a tripple fault is provoked.
|
||||
*/
|
||||
void NORETURN x86_kbc_reboot(void);
|
||||
|
||||
/**
|
||||
* @brief Set specialized reboot function.
|
||||
*
|
||||
* This function should be utilized by the ACPI subsystem.
|
||||
*/
|
||||
void x86_set_reboot_fun(x86_reboot_t fun);
|
||||
|
||||
/**
|
||||
* @brief Loads an empty interrupt descriptor table.
|
||||
*
|
||||
* Any interrupt that will occur causes a tripple fault, i.e. a reboot.
|
||||
* Must be called with interrupts disabled.
|
||||
*/
|
||||
void x86_load_empty_idt(void);
|
||||
|
||||
/**
|
||||
* @brief Prototype of the board specific x86 shutdown function.
|
||||
* @returns `false` if the shutdown could not be scheduled.
|
||||
*/
|
||||
typedef bool (*x86_shutdown_t)(void);
|
||||
|
||||
/**
|
||||
* @brief Shutdown and power off
|
||||
* @details May return. The shutdown only gets scheduled.
|
||||
* @returns `false` if shutting down is not possible at this moment.
|
||||
*/
|
||||
bool x86_shutdown(void);
|
||||
|
||||
/**
|
||||
* @brief Function to call on x86_shutdown()
|
||||
*/
|
||||
void x86_set_shutdown_fun(x86_shutdown_t fun);
|
||||
|
||||
#endif
|
||||
|
||||
/** @} */
|
252
cpu/x86/include/x86_registers.h
Normal file
252
cpu/x86/include/x86_registers.h
Normal file
@ -0,0 +1,252 @@
|
||||
/*
|
||||
* Copyright (C) 2014 René Kijewski <rene.kijewski@fu-berlin.de>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
/**
|
||||
* The various non-general purpose registers of x86 CPUs.
|
||||
*
|
||||
* @ingroup x86
|
||||
* @{
|
||||
* @file
|
||||
* @author René Kijewski <rene.kijewski@fu-berlin.de>
|
||||
*/
|
||||
|
||||
#ifndef CPU__X86__REGISTERS__H__
|
||||
#define CPU__X86__REGISTERS__H__
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/* see "Intel® Quark SoC X1000 Core 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
|
||||
|
||||
/** @} */
|
211
cpu/x86/include/x86_rtc.h
Normal file
211
cpu/x86/include/x86_rtc.h
Normal file
@ -0,0 +1,211 @@
|
||||
/*
|
||||
* Copyright (C) 2014 René Kijewski <rene.kijewski@fu-berlin.de>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Interfacing the realtime clock on x86 boards.
|
||||
*
|
||||
* Only use this module to read the current time.
|
||||
* Using the other functions in applications would break the hwtimer.
|
||||
*
|
||||
* @ingroup x86-irq
|
||||
* @{
|
||||
* @file
|
||||
* @author René Kijewski <rene.kijewski@fu-berlin.de>
|
||||
*/
|
||||
|
||||
#ifndef CPU__X86__RTC__H__
|
||||
#define CPU__X86__RTC__H__
|
||||
|
||||
#include "msg.h"
|
||||
#include "x86_cmos.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
/**
|
||||
* @brief A timestamp.
|
||||
*
|
||||
* The value of the century is unreliable.
|
||||
*/
|
||||
typedef union x86_rtc_data {
|
||||
__extension__ struct {
|
||||
uint8_t second, minute, hour;
|
||||
uint8_t day, month, year, century;
|
||||
};
|
||||
uint64_t timestamp;
|
||||
} x86_rtc_data_t;
|
||||
|
||||
#define RTC_REG_SECOND (0x00)
|
||||
#define RTC_REG_ALARM_SECOND (0x01)
|
||||
#define RTC_REG_MINUTE (0x02)
|
||||
#define RTC_REG_ALARM_MINUTE (0x03)
|
||||
#define RTC_REG_HOUR (0x04)
|
||||
#define RTC_REG_ALARM_HOUR (0x05)
|
||||
#define RTC_REG_DOW (0x06)
|
||||
#define RTC_REG_DAY (0x07)
|
||||
#define RTC_REG_MONTH (0x08)
|
||||
#define RTC_REG_YEAR (0x09)
|
||||
#define RTC_REG_A (0x0a)
|
||||
#define RTC_REG_B (0x0b)
|
||||
#define RTC_REG_C (0x0c)
|
||||
#define RTC_REG_D (0x0d)
|
||||
#define RTC_REG_POST (0x0e)
|
||||
#define RTC_REG_CENTURY (0x32)
|
||||
|
||||
#define RTC_REG_A_HZ_OFF ( 0 << 0)
|
||||
#define RTC_REG_A_HZ_8192 ( 3 << 0)
|
||||
#define RTC_REG_A_HZ_4096 ( 4 << 0)
|
||||
#define RTC_REG_A_HZ_2048 ( 5 << 0)
|
||||
#define RTC_REG_A_HZ_1024 ( 6 << 0)
|
||||
#define RTC_REG_A_HZ_512 ( 7 << 0)
|
||||
#define RTC_REG_A_HZ_256 ( 8 << 0)
|
||||
#define RTC_REG_A_HZ_128 ( 9 << 0)
|
||||
#define RTC_REG_A_HZ_64 (10 << 0)
|
||||
#define RTC_REG_A_HZ_32 (11 << 0)
|
||||
#define RTC_REG_A_HZ_16 (12 << 0)
|
||||
#define RTC_REG_A_HZ_8 (13 << 0)
|
||||
#define RTC_REG_A_HZ_4 (14 << 0)
|
||||
#define RTC_REG_A_HZ_2 (15 << 0)
|
||||
#define RTC_REG_A_HZ_MASK (15 << 0)
|
||||
#define RTC_REG_A_DIVIDER_MASK ( 3 << 4)
|
||||
#define RTC_REG_A_UPDATING ( 1 << 7)
|
||||
|
||||
#define RTC_REG_B_DST (1 << 0)
|
||||
#define RTC_REG_B_24H (1 << 1)
|
||||
#define RTC_REG_B_BIN (1 << 2)
|
||||
#define RTC_REG_B_WAVE (1 << 3)
|
||||
#define RTC_REG_B_INT_UPDATE (1 << 4)
|
||||
#define RTC_REG_B_INT_ALARM (1 << 5)
|
||||
#define RTC_REG_B_INT_PERIODIC (1 << 6)
|
||||
#define RTC_REG_B_INT_MASK (7 << 4)
|
||||
#define RTC_REG_B_UPDATE (1 << 7)
|
||||
|
||||
#define RTC_REG_C_IRQ_UPDATE (1 << 4)
|
||||
#define RTC_REG_C_IRQ_ALARM (1 << 5)
|
||||
#define RTC_REG_C_IRQ_PERIODIC (1 << 6)
|
||||
#define RTC_REG_C_IRQ (1 << 7)
|
||||
|
||||
#define RTC_REG_D_VALID (1 << 7)
|
||||
|
||||
#define RTC_REG_POST_TIME_INVALID (1 << 2)
|
||||
#define RTC_REG_POST_POWER_LOSS (1 << 7)
|
||||
|
||||
/**
|
||||
* @brief Initialize the Real Time Clock.
|
||||
*
|
||||
* This function is called during initialization by x86_startup().
|
||||
* You must not call this function on your own accord.
|
||||
*
|
||||
* The RTC subsystem will refuse to work if the CMOS says that
|
||||
* <li>there was a power loss,
|
||||
* <li>the stored time is invalid (i.e. February 30), or
|
||||
* <li>the CMOS had a checksum failure.
|
||||
*/
|
||||
void x86_init_rtc(void);
|
||||
|
||||
/**
|
||||
* @brief Read the current time.
|
||||
* @returns false iff the RTC is unreliable, the value of dest is random is this case.
|
||||
*
|
||||
* This reads the CMOS value
|
||||
* The standard does not tell the timezone of this value.
|
||||
*/
|
||||
bool x86_rtc_read(x86_rtc_data_t *dest);
|
||||
|
||||
/**
|
||||
* @brief A custom callback handler for RTC interrupts.
|
||||
* @param reg_c The value of CMOS register C.
|
||||
*/
|
||||
typedef void (*x86_rtc_callback_t)(uint8_t reg_c);
|
||||
|
||||
/**
|
||||
* @brief Set an RTC alarm.
|
||||
* @param[in] when Time when the RTC you raise an interrupt. The date part is ignored.
|
||||
* @param msg_content The value for msg_t::content.value.
|
||||
* @param target_pid The process which shall receive the message, `-1u` to disable.
|
||||
* @param allow_replace Whether it is allowed to overwrite an existing alarm.
|
||||
*
|
||||
* The value of msg_t::type will be `reg_c | (RTC_REG_B_INT_UPDATE << 8)`,
|
||||
* where `reg_c` is the value of CMOS register C.
|
||||
*
|
||||
* You should not call this function directly.
|
||||
* You should use hwtimer -- or better yet -- vtimer instead.
|
||||
*/
|
||||
bool x86_rtc_set_alarm(const x86_rtc_data_t *when, uint32_t msg_content, unsigned int target_pid, bool allow_replace);
|
||||
|
||||
/**
|
||||
* @brief Set up periodic interrupts
|
||||
* @param hz How often a second the interrupt should fire, e.g. RTC_REG_A_HZ_8192.
|
||||
* @param msg_content The value for msg_t::content.value.
|
||||
* @param target_pid The process which shall receive the message, `-1u` to disable.
|
||||
* @param allow_replace Whether it is allowed to overwrite an existing alarm.
|
||||
*
|
||||
* The value of msg_t::type will be `reg_c | (RTC_REG_B_INT_PERIODIC << 8)`,
|
||||
* where `reg_c` is the value of CMOS register C.
|
||||
*
|
||||
* You should not call this function directly.
|
||||
* You should use hwtimer -- or better yet -- vtimer instead.
|
||||
*/
|
||||
bool x86_rtc_set_periodic(uint8_t hz, uint32_t msg_content, unsigned int target_pid, bool allow_replace);
|
||||
|
||||
/**
|
||||
* @brief Set up secondly interrupts.
|
||||
* @param msg_content The value for msg_t::content.value.
|
||||
* @param target_pid The process which shall receive the message, `-1u` to disable.
|
||||
* @param allow_replace Whether it is allowed to overwrite an existing alarm.
|
||||
*
|
||||
* The value of msg_t::type will be `reg_c | (RTC_REG_B_INT_UPDATE << 8)`,
|
||||
* where `reg_c` is the value of CMOS register C.
|
||||
*
|
||||
* You should not call this function directly.
|
||||
* You should use hwtimer -- or better yet -- vtimer instead.
|
||||
*/
|
||||
bool x86_rtc_set_update(uint32_t msg_content, unsigned int target_pid, bool allow_replace);
|
||||
|
||||
/**
|
||||
* @brief Set custom alarm interrupt handler.
|
||||
* @param cb Your custom handler, or NULL to use default.
|
||||
*
|
||||
* You must never use this function.
|
||||
* It is only there for x86_hwtimer.c,
|
||||
* because the hwtimer subsystem gets set up before the message system works.
|
||||
*/
|
||||
void x86_rtc_set_alarm_callback(x86_rtc_callback_t cb);
|
||||
|
||||
/**
|
||||
* @brief Set custom periodic interrupt handler.
|
||||
* @param cb Your custom handler, or NULL to use default.
|
||||
*
|
||||
* You must never use this function.
|
||||
* It is only there for x86_hwtimer.c,
|
||||
* because the hwtimer subsystem gets set up before the message system works.
|
||||
*/
|
||||
void x86_rtc_set_periodic_callback(x86_rtc_callback_t cb);
|
||||
|
||||
/**
|
||||
* @brief Set custom update interrupt handler.
|
||||
* @param cb Your custom handler, or NULL to use default.
|
||||
*
|
||||
* You must never use this function.
|
||||
* It is only there for x86_hwtimer.c,
|
||||
* because the hwtimer subsystem gets set up before the message system works.
|
||||
*/
|
||||
void x86_rtc_set_update_callback(x86_rtc_callback_t cb);
|
||||
|
||||
#endif
|
||||
|
||||
/** @} */
|
49
cpu/x86/include/x86_threading.h
Normal file
49
cpu/x86/include/x86_threading.h
Normal file
@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright (C) 2014 René Kijewski <rene.kijewski@fu-berlin.de>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
/**
|
||||
* Using multiple threads on x86 boards.
|
||||
*
|
||||
* @ingroup x86
|
||||
* @defgroup x86-multithreading i586 multithreading support
|
||||
* @{
|
||||
* @file
|
||||
* @author René Kijewski <rene.kijewski@fu-berlin.de>
|
||||
*/
|
||||
|
||||
#ifndef CPU__X86__THREADING__H__
|
||||
#define CPU__X86__THREADING__H__
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
/**
|
||||
* @brief Initialize threading.
|
||||
*
|
||||
* This function is called during initialization by x86_startup().
|
||||
* You must not call this function on your own accord.
|
||||
*/
|
||||
void x86_init_threading(void);
|
||||
|
||||
/**
|
||||
* @brief The getter/setter for inISR() for the x86 port.
|
||||
*/
|
||||
extern bool x86_in_isr;
|
||||
|
||||
#endif
|
||||
|
||||
/** @} */
|
176
cpu/x86/include/x86_uart.h
Normal file
176
cpu/x86/include/x86_uart.h
Normal file
@ -0,0 +1,176 @@
|
||||
/*
|
||||
* Copyright (C) 2014 René Kijewski <rene.kijewski@fu-berlin.de>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
/**
|
||||
* Using Communations Port 0 (COM0) to interface with a human being on x86 boards.
|
||||
*
|
||||
* @ingroup x86-irq
|
||||
* @{
|
||||
* @file
|
||||
* @author René Kijewski <rene.kijewski@fu-berlin.de>
|
||||
*/
|
||||
|
||||
#ifndef CPU__X86__UART__H__
|
||||
#define CPU__X86__UART__H__
|
||||
|
||||
#include "x86_pic.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
/**
|
||||
* @brief Early initialization of the UART system, before there are interrupts.
|
||||
*
|
||||
* This function is called during initialization by x86_startup().
|
||||
* You must not call this function on your own accord.
|
||||
*/
|
||||
void x86_early_init_uart(void);
|
||||
|
||||
/**
|
||||
* @brief Full initialization of the Universal Asynchronous Receiver Transmitter.
|
||||
*
|
||||
* This function is called during initialization by x86_startup().
|
||||
* You must not call this function on your own accord.
|
||||
*/
|
||||
void x86_init_uart(void);
|
||||
|
||||
/**
|
||||
* @brief Write out characters to the UART.
|
||||
* @param[in] buf Buffer to write.
|
||||
* @param len Length of the buffer.
|
||||
* @returns Actual amount of bytes written, always the same as len.
|
||||
*
|
||||
* This function blocks with interrupts disabled once the output buffer is full.
|
||||
*/
|
||||
ssize_t x86_uart_write(const char *buf, size_t len);
|
||||
|
||||
/**
|
||||
* @brief Read in characters to the UART.
|
||||
* @param[out] buf Buffer to set.
|
||||
* @param len Length of the buffer.
|
||||
* @returns Actual amount of bytes read, always the same as len.
|
||||
*
|
||||
* This function blocks with interrupts disabled once the input buffer is full.
|
||||
*/
|
||||
ssize_t x86_uart_read(char *buf, size_t len);
|
||||
|
||||
#define COM1_PORT (0x03F8)
|
||||
#define COM2_PORT (0x02F8)
|
||||
#define COM3_PORT (0x03E8)
|
||||
#define COM4_PORT (0x02E8)
|
||||
|
||||
#define COM1_IRQ (PIC_NUM_RS232_1_3)
|
||||
#define COM2_IRQ (PIC_NUM_RS232_2_4)
|
||||
#define COM3_IRQ (PIC_NUM_RS232_1_3)
|
||||
#define COM4_IRQ (PIC_NUM_RS232_2_4)
|
||||
|
||||
/* 115200 baud */
|
||||
#define BAUD_LL (0x01)
|
||||
#define BAUD_HL (0x00)
|
||||
|
||||
enum uart_port_offs_t {
|
||||
/* DLAB RW */
|
||||
THR = 0, /* 0 W Transmitter Holding Buffer */
|
||||
RBR = 0, /* 0 R Receiver Buffer */
|
||||
DLL = 0, /* 1 RW Divisor Latch Low Byte */
|
||||
IER = 1, /* 0 RW Interrupt Enable Register */
|
||||
DLH = 1, /* 1 RW Divisor Latch High Byte */
|
||||
IIR = 2, /* - R Interrupt Identification Register */
|
||||
FCR = 2, /* - RW FIFO Control Register */
|
||||
LCR = 3, /* - RW Line Control Register */
|
||||
MCR = 4, /* - RW Modem Control Register */
|
||||
LSR = 5, /* - R Line Status Register */
|
||||
MSR = 6, /* - R Modem Status Register */
|
||||
SR = 7, /* - RW Scratch Register */
|
||||
};
|
||||
|
||||
enum ier_t {
|
||||
IER_RECV = 1 << 0, /* Enable Received Data Available Interrupt */
|
||||
IER_SEND = 1 << 1, /* Enable Transmitter Holding Register Empty Interrupt */
|
||||
IER_LS = 1 << 2, /* Enable Receiver Line Status Interrupt */
|
||||
IER_MS = 1 << 3, /* Enable Modem Status Interrupt */
|
||||
IER_SLEEP = 1 << 4, /* Enables Sleep Mode (16750) */
|
||||
IER_LPM = 1 << 5, /* Enables Low Power Mode (16750) */
|
||||
};
|
||||
|
||||
enum fcr_t {
|
||||
FCR_ENABLE = 1 << 0, /* Enable FIFOs */
|
||||
FCR_CLR_RECV = 1 << 1, /* Clear Receive FIFO */
|
||||
FCR_CLR_SEND = 1 << 2, /* Clear Transmit FIFO */
|
||||
FCR_MDA = 1 << 3, /* DMA Mode Select */
|
||||
FCR_64BYTE = 1 << 5, /* Enable 64 Byte FIFO (16750) */
|
||||
|
||||
FCR_TRIGGER_1 = 0 << 6, /* 1 byte */
|
||||
FCR_TRIGGER_16 = 1 << 6, /* 16 bytes */
|
||||
FCR_TRIGGER_32 = 2 << 6, /* 32 bytes */
|
||||
FCR_TRIGGER_56 = 3 << 6, /* 56 bytes */
|
||||
};
|
||||
|
||||
enum lcr_t {
|
||||
LCR_WORD_BITS_5 = (0) << 0, /* Word length: 5 bits */
|
||||
LCR_WORD_BITS_6 = (1) << 0, /* Word length: 6 bits */
|
||||
LCR_WORD_BITS_7 = (2) << 0, /* Word length: 7 bits */
|
||||
LCR_WORD_BITS_8 = (3) << 0, /* Word length: 8 bits */
|
||||
|
||||
LCR_STOP_BITS_1 = (0) << 2, /* Stop bits: 1 */
|
||||
LCR_STOP_BITS_2 = (1) << 2, /* Stop bits: 1.5 or 2 */
|
||||
|
||||
LCR_PAR_NONE = (0) << 3, /* no parity */
|
||||
LCR_PAR_ODD = (1) << 3, /* odd parity */
|
||||
LCR_PAR_EVEN = (3) << 3, /* even parity */
|
||||
LCR_PAR_MARK = (5) << 3, /* mark (?) */
|
||||
LCR_PAR_SPACE = (7) << 3, /* space (?) */
|
||||
|
||||
LCR_SET_BREAK = (1) << 6, /* set break enabled */
|
||||
|
||||
LCR_DLAB = (1) << 7, /* divisor latch access bit */
|
||||
};
|
||||
|
||||
enum mcr_t {
|
||||
MCR_DSR = 1 << 0, /* Data Terminal Ready */
|
||||
MCR_RTS = 1 << 1, /* Request To Send */
|
||||
MCR_AUX1 = 1 << 2, /* Auxiliary Output 1 */
|
||||
MCR_AUX2 = 1 << 3, /* Auxiliary Output 2 */
|
||||
MCR_LOOPBACK = 1 << 4, /* Loopback Mode */
|
||||
MCR_AUTOFLOW = 1 << 5, /* Autoflow Control Enabled (16750) */
|
||||
};
|
||||
|
||||
enum iir_t {
|
||||
IIR_IPF = 1 << 0, /* interrupt pending flag */
|
||||
|
||||
IIR_INT_MASK = 3 << 1,
|
||||
IIR_INT_MS = 0 << 1, /* modem status interrupt (reset: read MSR) */
|
||||
IIR_INT_TH = 1 << 1, /* THR empty interrupt (reset: read IIR or write to THR) */
|
||||
IIR_INT_BR = 2 << 1, /* recv data available interrupt (reset: read RBR) */
|
||||
IIR_INT_LS = 3 << 1, /* line status changed (reset: read LSR) */
|
||||
IIR_INT_TO = 6 << 1, /* timeout interrupt pending (reset: read RBR) */
|
||||
|
||||
IIR_FIFO64 = 1 << 5, /* 64 byte FIFO enabled */
|
||||
|
||||
IIR_FIFO_MASK = 3 << 6,
|
||||
IIR_FIFO_NONE = 0 << 6, /* no FIFO available */
|
||||
IIR_FIFO_RES = 1 << 6, /* reserved condition */
|
||||
IIR_FIFO_MAL = 2 << 6, /* FIFO enabled, not working */
|
||||
IIR_FIFO_ENABLED = 3 << 6, /* FIFO enabled */
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
/** @} */
|
62
cpu/x86/include/x86_videoram.h
Normal file
62
cpu/x86/include/x86_videoram.h
Normal file
@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright (C) 2014 René Kijewski <rene.kijewski@fu-berlin.de>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
/**
|
||||
* Writing to the videoram.
|
||||
*
|
||||
* @ingroup x86
|
||||
* @{
|
||||
* @file
|
||||
* @author René Kijewski <rene.kijewski@fu-berlin.de>
|
||||
*/
|
||||
|
||||
#ifndef CPU__X86__VIDEORAM_H__
|
||||
#define CPU__X86__VIDEORAM_H__
|
||||
|
||||
/**
|
||||
* @brief Print out a single character on the graphical device.
|
||||
*
|
||||
* This method can be used before the UART system was initialized.
|
||||
*
|
||||
* Special charactes that are understood: '\n' and '\r'.
|
||||
* Use DOS newlines.
|
||||
*/
|
||||
void videoram_putc(char c);
|
||||
|
||||
/**
|
||||
* @brief Print out multiple characters on the graphical device.
|
||||
*
|
||||
* This is the same as calling videoram_putc() repeatedly.
|
||||
*/
|
||||
void videoram_put(const char *s);
|
||||
|
||||
/**
|
||||
* @brief Print out multiple characters on the graphical device, then go to the next line.
|
||||
*
|
||||
* This is the same as calling `videoram_put(s); videoram_put("\r\n");`.
|
||||
*/
|
||||
void videoram_puts(const char *s);
|
||||
|
||||
/**
|
||||
* @brief Print out a hexadecimal number on the graphical device, including "0x" at the start.
|
||||
*/
|
||||
void videoram_put_hex(unsigned long v);
|
||||
|
||||
#endif
|
||||
|
||||
/** @} */
|
128
cpu/x86/nop_nop_nop.inc
Normal file
128
cpu/x86/nop_nop_nop.inc
Normal file
@ -0,0 +1,128 @@
|
||||
#if 0
|
||||
|
||||
from itertools import cycle
|
||||
|
||||
regs = [ ('eax', [1,0,0,0,0,0,0]),
|
||||
('ecx', [0,1,0,0,0,0,0]),
|
||||
('edx', [0,0,1,0,0,0,0]),
|
||||
#('ebx', [0,0,0,1,0,0,0]),
|
||||
#('ebp', [0,0,0,0,1,0,0]),
|
||||
('esi', [0,0,0,0,0,1,0]),
|
||||
('edi', [0,0,0,0,0,0,1]), ]
|
||||
REGS0 = list(regs)
|
||||
|
||||
for cur in cycle(range(len(regs))):
|
||||
nex = (cur + 1) % len(regs)
|
||||
print 'asm volatile ("xor %%%s, %%%s");' % (regs[cur][0], regs[nex][0])
|
||||
val = [a^b for a, b in zip(regs[cur][1], regs[nex][1])]
|
||||
regs[nex] = (regs[nex][0], val)
|
||||
if regs == REGS0:
|
||||
break
|
||||
|
||||
#endif
|
||||
|
||||
asm volatile ("xor %eax, %ecx");
|
||||
asm volatile ("xor %ecx, %edx");
|
||||
asm volatile ("xor %edx, %esi");
|
||||
asm volatile ("xor %esi, %edi");
|
||||
asm volatile ("xor %edi, %eax");
|
||||
asm volatile ("xor %eax, %ecx");
|
||||
asm volatile ("xor %ecx, %edx");
|
||||
asm volatile ("xor %edx, %esi");
|
||||
asm volatile ("xor %esi, %edi");
|
||||
asm volatile ("xor %edi, %eax");
|
||||
asm volatile ("xor %eax, %ecx");
|
||||
asm volatile ("xor %ecx, %edx");
|
||||
asm volatile ("xor %edx, %esi");
|
||||
asm volatile ("xor %esi, %edi");
|
||||
asm volatile ("xor %edi, %eax");
|
||||
asm volatile ("xor %eax, %ecx");
|
||||
asm volatile ("xor %ecx, %edx");
|
||||
asm volatile ("xor %edx, %esi");
|
||||
asm volatile ("xor %esi, %edi");
|
||||
asm volatile ("xor %edi, %eax");
|
||||
asm volatile ("xor %eax, %ecx");
|
||||
asm volatile ("xor %ecx, %edx");
|
||||
asm volatile ("xor %edx, %esi");
|
||||
asm volatile ("xor %esi, %edi");
|
||||
asm volatile ("xor %edi, %eax");
|
||||
asm volatile ("xor %eax, %ecx");
|
||||
asm volatile ("xor %ecx, %edx");
|
||||
asm volatile ("xor %edx, %esi");
|
||||
asm volatile ("xor %esi, %edi");
|
||||
asm volatile ("xor %edi, %eax");
|
||||
asm volatile ("xor %eax, %ecx");
|
||||
asm volatile ("xor %ecx, %edx");
|
||||
asm volatile ("xor %edx, %esi");
|
||||
asm volatile ("xor %esi, %edi");
|
||||
asm volatile ("xor %edi, %eax");
|
||||
asm volatile ("xor %eax, %ecx");
|
||||
asm volatile ("xor %ecx, %edx");
|
||||
asm volatile ("xor %edx, %esi");
|
||||
asm volatile ("xor %esi, %edi");
|
||||
asm volatile ("xor %edi, %eax");
|
||||
asm volatile ("xor %eax, %ecx");
|
||||
asm volatile ("xor %ecx, %edx");
|
||||
asm volatile ("xor %edx, %esi");
|
||||
asm volatile ("xor %esi, %edi");
|
||||
asm volatile ("xor %edi, %eax");
|
||||
asm volatile ("xor %eax, %ecx");
|
||||
asm volatile ("xor %ecx, %edx");
|
||||
asm volatile ("xor %edx, %esi");
|
||||
asm volatile ("xor %esi, %edi");
|
||||
asm volatile ("xor %edi, %eax");
|
||||
asm volatile ("xor %eax, %ecx");
|
||||
asm volatile ("xor %ecx, %edx");
|
||||
asm volatile ("xor %edx, %esi");
|
||||
asm volatile ("xor %esi, %edi");
|
||||
asm volatile ("xor %edi, %eax");
|
||||
asm volatile ("xor %eax, %ecx");
|
||||
asm volatile ("xor %ecx, %edx");
|
||||
asm volatile ("xor %edx, %esi");
|
||||
asm volatile ("xor %esi, %edi");
|
||||
asm volatile ("xor %edi, %eax");
|
||||
asm volatile ("xor %eax, %ecx");
|
||||
asm volatile ("xor %ecx, %edx");
|
||||
asm volatile ("xor %edx, %esi");
|
||||
asm volatile ("xor %esi, %edi");
|
||||
asm volatile ("xor %edi, %eax");
|
||||
asm volatile ("xor %eax, %ecx");
|
||||
asm volatile ("xor %ecx, %edx");
|
||||
asm volatile ("xor %edx, %esi");
|
||||
asm volatile ("xor %esi, %edi");
|
||||
asm volatile ("xor %edi, %eax");
|
||||
asm volatile ("xor %eax, %ecx");
|
||||
asm volatile ("xor %ecx, %edx");
|
||||
asm volatile ("xor %edx, %esi");
|
||||
asm volatile ("xor %esi, %edi");
|
||||
asm volatile ("xor %edi, %eax");
|
||||
asm volatile ("xor %eax, %ecx");
|
||||
asm volatile ("xor %ecx, %edx");
|
||||
asm volatile ("xor %edx, %esi");
|
||||
asm volatile ("xor %esi, %edi");
|
||||
asm volatile ("xor %edi, %eax");
|
||||
asm volatile ("xor %eax, %ecx");
|
||||
asm volatile ("xor %ecx, %edx");
|
||||
asm volatile ("xor %edx, %esi");
|
||||
asm volatile ("xor %esi, %edi");
|
||||
asm volatile ("xor %edi, %eax");
|
||||
asm volatile ("xor %eax, %ecx");
|
||||
asm volatile ("xor %ecx, %edx");
|
||||
asm volatile ("xor %edx, %esi");
|
||||
asm volatile ("xor %esi, %edi");
|
||||
asm volatile ("xor %edi, %eax");
|
||||
asm volatile ("xor %eax, %ecx");
|
||||
asm volatile ("xor %ecx, %edx");
|
||||
asm volatile ("xor %edx, %esi");
|
||||
asm volatile ("xor %esi, %edi");
|
||||
asm volatile ("xor %edi, %eax");
|
||||
asm volatile ("xor %eax, %ecx");
|
||||
asm volatile ("xor %ecx, %edx");
|
||||
asm volatile ("xor %edx, %esi");
|
||||
asm volatile ("xor %esi, %edi");
|
||||
asm volatile ("xor %edi, %eax");
|
||||
asm volatile ("xor %eax, %ecx");
|
||||
asm volatile ("xor %ecx, %edx");
|
||||
asm volatile ("xor %edx, %esi");
|
||||
asm volatile ("xor %esi, %edi");
|
||||
asm volatile ("xor %edi, %eax");
|
37
cpu/x86/x86_atomic.c
Normal file
37
cpu/x86/x86_atomic.c
Normal file
@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright (C) 2014 René Kijewski <rene.kijewski@fu-berlin.de>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ingroup x86-multithreading
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Atomic arithmetic for the x86 port.
|
||||
*
|
||||
* @author René Kijewski <rene.kijewski@fu-berlin.de>
|
||||
*
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include "atomic.h"
|
||||
|
||||
unsigned int atomic_set_return(unsigned int *val, unsigned int set)
|
||||
{
|
||||
asm volatile ("lock xchg %0, %1" : "+m"(*val), "+r"(set));
|
||||
return set;
|
||||
}
|
52
cpu/x86/x86_cmos.c
Normal file
52
cpu/x86/x86_cmos.c
Normal file
@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright (C) 2014 René Kijewski <rene.kijewski@fu-berlin.de>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ingroup x86
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Accessing the CMOS space of the BIOS.
|
||||
*
|
||||
* @author René Kijewski <rene.kijewski@fu-berlin.de>
|
||||
*
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include "x86_cmos.h"
|
||||
|
||||
uint8_t x86_cmos_read(int reg)
|
||||
{
|
||||
outb(CMOS_ADDRESS, reg);
|
||||
io_wait();
|
||||
return inb(CMOS_DATA);
|
||||
}
|
||||
|
||||
void x86_cmos_write(int reg, uint8_t value)
|
||||
{
|
||||
outb(CMOS_ADDRESS, reg);
|
||||
io_wait();
|
||||
outb(CMOS_DATA, value);
|
||||
}
|
||||
|
||||
void x86_cmos_serial(uint8_t (*serial)[CMOS_SERIAL_LEN])
|
||||
{
|
||||
for (unsigned i = 0; i < CMOS_SERIAL_LEN; ++i) {
|
||||
(*serial)[i] = x86_cmos_read(CMOS_SERIAL_OFFS + i);
|
||||
}
|
||||
}
|
137
cpu/x86/x86_glue.c
Normal file
137
cpu/x86/x86_glue.c
Normal file
@ -0,0 +1,137 @@
|
||||
/*
|
||||
* Copyright (C) 2014 René Kijewski <rene.kijewski@fu-berlin.de>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ingroup x86
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief The interface between newlib and kernel functions.
|
||||
*
|
||||
* @author René Kijewski <rene.kijewski@fu-berlin.de>
|
||||
*
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include "attributes.h"
|
||||
#include "cpu.h"
|
||||
#include "kernel_internal.h"
|
||||
#include "sched.h"
|
||||
#include "x86_uart.h"
|
||||
|
||||
#include <signal.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
void _exit(int status)
|
||||
{
|
||||
(void) status;
|
||||
sched_task_exit();
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
#ifndef MODULE_POSIX
|
||||
/* Already defined in /sys/posix/unistd.c */
|
||||
|
||||
int close(int fildes)
|
||||
{
|
||||
/* TODO */
|
||||
(void) fildes;
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
pid_t getpid(void)
|
||||
{
|
||||
return sched_active_pid;
|
||||
}
|
||||
|
||||
int fstat(int fildes, struct stat *buf)
|
||||
{
|
||||
/* TODO */
|
||||
(void) fildes;
|
||||
(void) buf;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int isatty(int fildes)
|
||||
{
|
||||
/* TODO */
|
||||
(void) fildes;
|
||||
return 0; /* sic */
|
||||
}
|
||||
|
||||
int kill(pid_t pid, int sig)
|
||||
{
|
||||
/* TODO */
|
||||
(void) pid;
|
||||
(void) sig;
|
||||
return -1;
|
||||
}
|
||||
|
||||
off_t lseek(int fildes, off_t offset, int whence)
|
||||
{
|
||||
/* TODO */
|
||||
(void) fildes;
|
||||
(void) offset;
|
||||
(void) whence;
|
||||
return (off_t) -1;
|
||||
}
|
||||
|
||||
ssize_t read(int fildes, void *buf, size_t nbyte)
|
||||
{
|
||||
if (nbyte == 0) {
|
||||
/* allow reading nothing from every FD ... */
|
||||
return 0;
|
||||
}
|
||||
else if (fildes == STDOUT_FILENO || fildes == STDERR_FILENO) {
|
||||
/* cannot read from STDOUT or STDERR */
|
||||
return -1;
|
||||
}
|
||||
else if (fildes == STDIN_FILENO) {
|
||||
return x86_uart_read(buf, nbyte);
|
||||
}
|
||||
|
||||
/* TODO: find appropriate FILE */
|
||||
(void) fildes;
|
||||
(void) buf;
|
||||
(void) nbyte;
|
||||
return -1;
|
||||
}
|
||||
|
||||
ssize_t write(int fildes, const void *buf, size_t nbyte)
|
||||
{
|
||||
if (nbyte == 0) {
|
||||
/* allow writing nothing to every FD ... */
|
||||
return 0;
|
||||
}
|
||||
else if (fildes == STDIN_FILENO) {
|
||||
/* cannot write to STDIN */
|
||||
return -1;
|
||||
}
|
||||
else if (fildes == STDOUT_FILENO || fildes == STDERR_FILENO) {
|
||||
return x86_uart_write(buf, nbyte);
|
||||
}
|
||||
|
||||
/* TODO: find appropriate FILE */
|
||||
(void) fildes;
|
||||
(void) buf;
|
||||
(void) nbyte;
|
||||
return -1;
|
||||
}
|
||||
|
540
cpu/x86/x86_hwtimer.c
Normal file
540
cpu/x86/x86_hwtimer.c
Normal file
@ -0,0 +1,540 @@
|
||||
/*
|
||||
* Copyright (C) 2014 René Kijewski <rene.kijewski@fu-berlin.de>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ingroup x86-irq
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Longterm and shortterm timeout handler using callbacks.
|
||||
*
|
||||
* @author René Kijewski <rene.kijewski@fu-berlin.de>
|
||||
*
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include "hwtimer_cpu.h"
|
||||
#include "x86_hwtimer.h"
|
||||
#include "x86_rtc.h"
|
||||
#include "x86_threading.h"
|
||||
#include "hwtimer_arch.h"
|
||||
#include "irq.h"
|
||||
#include "thread.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#define ENABLE_DEBUG (0)
|
||||
#include "debug.h"
|
||||
|
||||
#define US_PER_SECOND (1000l * 1000l)
|
||||
#define START_MAX_US_PREMATURELY (US_PER_SECOND / 2048)
|
||||
|
||||
#define NNN_TS_ITERATIONS (1024)
|
||||
#define NNN_TICK_ITERATIONS (16)
|
||||
|
||||
#define TICK_HZ_VAL (256)
|
||||
#define TICK_HZ_REG_A (RTC_REG_A_HZ_256)
|
||||
|
||||
#define ASM_FUN_ATTRIBUTES \
|
||||
__attribute__((noinline)) \
|
||||
__attribute__((no_instrument_function)) \
|
||||
__attribute__((optimize("Os", "omit-frame-pointer")))
|
||||
|
||||
static bool set_next_alarm(bool may_call);
|
||||
|
||||
static uint64_t ts_base;
|
||||
static x86_rtc_data_t rtc_base;
|
||||
|
||||
static uint64_t ts_per_nop_nop_nop;
|
||||
static uint64_t nop_nop_nops_per_tick;
|
||||
static uint64_t nop_nop_nops_per_second;
|
||||
static uint64_t instructions_per_second;
|
||||
|
||||
static void ASM_FUN_ATTRIBUTES nop_nop_nop(void)
|
||||
{
|
||||
# include "nop_nop_nop.inc"
|
||||
}
|
||||
|
||||
static uint64_t ASM_FUN_ATTRIBUTES rdtsc(void)
|
||||
{
|
||||
uint64_t result;
|
||||
asm volatile ("cpuid" :: "a"(0) : "ebx", "ecx", "edx");
|
||||
asm volatile ("rdtsc" : "=A"(result));
|
||||
return result;
|
||||
}
|
||||
|
||||
static volatile bool periodic_interrupt_called;
|
||||
|
||||
static void flip_periodic_interrupt_called(uint8_t reg_c)
|
||||
{
|
||||
(void) reg_c;
|
||||
|
||||
periodic_interrupt_called = !periodic_interrupt_called;
|
||||
}
|
||||
|
||||
static void measure_nop_nop_nops_per_tick(void)
|
||||
{
|
||||
x86_rtc_set_periodic_callback(flip_periodic_interrupt_called);
|
||||
x86_rtc_set_periodic(TICK_HZ_REG_A, 0, 0, true);
|
||||
for (unsigned i = 0; i < NNN_TICK_ITERATIONS; ++i) {
|
||||
periodic_interrupt_called = false;
|
||||
|
||||
eINT();
|
||||
while (!periodic_interrupt_called) {
|
||||
nop_nop_nop();
|
||||
}
|
||||
while (periodic_interrupt_called) {
|
||||
nop_nop_nop();
|
||||
}
|
||||
uint64_t counting_start = rdtsc();
|
||||
while (!periodic_interrupt_called) {
|
||||
++nop_nop_nops_per_tick;
|
||||
nop_nop_nop();
|
||||
}
|
||||
dINT();
|
||||
uint64_t counting_end = rdtsc();
|
||||
|
||||
ts_per_nop_nop_nop += counting_end - counting_start;
|
||||
}
|
||||
x86_rtc_set_periodic_callback(NULL);
|
||||
x86_rtc_set_periodic(RTC_REG_A_HZ_OFF, 0, -1, false);
|
||||
|
||||
/* instructions_per_second = nop_nop_nops_per_second * ts_per_nop_nop_nop: */
|
||||
instructions_per_second = nop_nop_nops_per_tick * TICK_HZ_VAL * ts_per_nop_nop_nop / nop_nop_nops_per_tick / NNN_TICK_ITERATIONS;
|
||||
/* nop_nop_nops_per_second = nop_nop_nops_per_tick / TICK_HZ_VAL: */
|
||||
nop_nop_nops_per_second = (nop_nop_nops_per_tick * TICK_HZ_VAL) / NNN_TICK_ITERATIONS;
|
||||
ts_per_nop_nop_nop /= nop_nop_nops_per_tick;
|
||||
nop_nop_nops_per_tick /= NNN_TICK_ITERATIONS;
|
||||
}
|
||||
|
||||
static void update_cb(uint8_t reg_c)
|
||||
{
|
||||
periodic_interrupt_called = reg_c & RTC_REG_C_IRQ_UPDATE;
|
||||
DEBUG("DEBUG update_cb(0x%02hhx): periodic_interrupt_called = %u\n", reg_c, periodic_interrupt_called);
|
||||
}
|
||||
|
||||
static void init_bases(void)
|
||||
{
|
||||
x86_rtc_set_periodic_callback(update_cb);
|
||||
x86_rtc_set_periodic(RTC_REG_A_HZ_2, 0, 0, true);
|
||||
|
||||
eINT();
|
||||
periodic_interrupt_called = false;
|
||||
while (!periodic_interrupt_called) {
|
||||
asm volatile ("hlt");
|
||||
}
|
||||
dINT();
|
||||
|
||||
x86_rtc_read(&rtc_base);
|
||||
ts_base = rdtsc();
|
||||
|
||||
printf(" %02hhu:%02hhu:%02hhu, %04u-%02hhu-%02hhu is %llu\n",
|
||||
rtc_base.hour, rtc_base.minute, rtc_base.second,
|
||||
rtc_base.century * 100 + rtc_base.year, rtc_base.month, rtc_base.day,
|
||||
ts_base);
|
||||
|
||||
x86_rtc_set_periodic_callback(NULL);
|
||||
x86_rtc_set_periodic(RTC_REG_A_HZ_OFF, 0, -1, false);
|
||||
}
|
||||
|
||||
void x86_init_hwtimer(void)
|
||||
{
|
||||
puts("Measuring CPU speed:");
|
||||
|
||||
measure_nop_nop_nops_per_tick();
|
||||
|
||||
printf(" CPU speed = %.3f MHz\n", instructions_per_second / (1024.f * 1024));
|
||||
|
||||
init_bases();
|
||||
}
|
||||
|
||||
static void (*hwtimer_callback)(int timer);
|
||||
|
||||
static char hwtimer_stack[KERNEL_CONF_STACKSIZE_DEFAULT];
|
||||
static int hwtimer_pid = -1;
|
||||
|
||||
struct alarm_time {
|
||||
uint64_t ts_absolute_alarm;
|
||||
bool enabled;
|
||||
struct alarm_time *next, *prev;
|
||||
};
|
||||
|
||||
static struct alarm_time timers[HWTIMER_MAXTIMERS];
|
||||
static struct alarm_time *timers_start;
|
||||
static bool hwtimer_ie;
|
||||
static unsigned rtc_alarm_ie, rtc_update_ie, rtc_periodic_ie;
|
||||
|
||||
static bool timer_unlink(unsigned i)
|
||||
{
|
||||
bool do_yield = false;
|
||||
|
||||
bool was_start = timers_start == &timers[i];
|
||||
|
||||
DEBUG("DEBUG timer_unlink(%u): was_start=%u\n", i, was_start);
|
||||
|
||||
if (timers[i].next) {
|
||||
timers[i].next->prev = timers[i].prev;
|
||||
}
|
||||
if (timers[i].prev) {
|
||||
timers[i].prev->next = timers[i].next;
|
||||
}
|
||||
if (was_start) {
|
||||
timers_start = timers[i].next;
|
||||
}
|
||||
timers[i].next = timers[i].prev = NULL;
|
||||
if (was_start && timers[i].enabled) {
|
||||
do_yield = set_next_alarm(false);
|
||||
}
|
||||
timers[i].enabled = false;
|
||||
|
||||
return do_yield;
|
||||
}
|
||||
|
||||
static void hwtimer_tick_handler(void)
|
||||
{
|
||||
msg_t msg_array[2];
|
||||
msg_init_queue(msg_array, sizeof (msg_array) / sizeof (*msg_array));
|
||||
while (1) {
|
||||
msg_t m;
|
||||
msg_receive(&m);
|
||||
|
||||
dINT();
|
||||
set_next_alarm(true);
|
||||
eINT();
|
||||
}
|
||||
}
|
||||
|
||||
static void stop_alarms(void)
|
||||
{
|
||||
DEBUG("DEBUG stop_alarms(): AIE=%u, UIE=%u, PIE=%u\n",
|
||||
rtc_alarm_ie, rtc_update_ie, rtc_periodic_ie);
|
||||
|
||||
if (rtc_alarm_ie) {
|
||||
rtc_alarm_ie = 0;
|
||||
x86_rtc_set_alarm(NULL, 0, -1u, false);
|
||||
}
|
||||
|
||||
if (rtc_update_ie) {
|
||||
rtc_update_ie = 0;
|
||||
x86_rtc_set_update(0, -1u, false);
|
||||
}
|
||||
|
||||
if (rtc_periodic_ie) {
|
||||
rtc_periodic_ie = 0;
|
||||
x86_rtc_set_periodic(RTC_REG_A_HZ_OFF, 0, -1u, false);
|
||||
}
|
||||
}
|
||||
|
||||
static bool set_next_alarm(bool may_call)
|
||||
{
|
||||
if (!hwtimer_ie) {
|
||||
return false;
|
||||
}
|
||||
|
||||
while (timers_start) {
|
||||
uint64_t ts_now = rdtsc();
|
||||
int64_t ts_future = (int64_t) timers_start->ts_absolute_alarm - (int64_t) ts_now;
|
||||
|
||||
/* prevent overflows */
|
||||
int64_t us_future = ts_future;
|
||||
us_future *= (int64_t) (US_PER_SECOND / 0x1000);
|
||||
us_future /= (int64_t) (instructions_per_second / 0x1000);
|
||||
|
||||
unsigned timer_i = timers_start - timers;
|
||||
|
||||
DEBUG("DEBUG set_next_alarm(): timers_start=%u, us_future=%lli, ts_future=%lli\n",
|
||||
timer_i, us_future, ts_future);
|
||||
|
||||
if (us_future <= START_MAX_US_PREMATURELY) {
|
||||
DEBUG(" callback(%u) (%lli µs prematurely), may_call=%u\n",
|
||||
timer_i, us_future, may_call);
|
||||
if (!may_call) {
|
||||
msg_t m;
|
||||
msg_send_int(&m, hwtimer_pid);
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
bool do_yield = timer_unlink(timer_i);
|
||||
|
||||
eINT();
|
||||
hwtimer_callback(timer_i);
|
||||
if (do_yield) {
|
||||
thread_yield();
|
||||
}
|
||||
dINT();
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
us_future -= START_MAX_US_PREMATURELY / 2;
|
||||
if (us_future > 5 * US_PER_SECOND) {
|
||||
us_future -= US_PER_SECOND;
|
||||
|
||||
int8_t seconds = (us_future / US_PER_SECOND) % 60;
|
||||
int8_t minutes = (us_future / US_PER_SECOND) / 60 % 60;
|
||||
int8_t hours = (us_future / US_PER_SECOND) / 60 / 60 % 24;
|
||||
|
||||
DEBUG(" setting AIE %02hhu:%02hhu:%02hhu\n", hours, minutes, seconds);
|
||||
|
||||
x86_rtc_data_t rtc_now;
|
||||
x86_rtc_read(&rtc_now);
|
||||
|
||||
rtc_now.second += seconds;
|
||||
if (rtc_now.second >= 60) {
|
||||
rtc_now.second -= 60;
|
||||
++minutes;
|
||||
}
|
||||
rtc_now.minute += minutes;
|
||||
if (rtc_now.minute >= 60) {
|
||||
rtc_now.minute -= 60;
|
||||
++hours;
|
||||
}
|
||||
rtc_now.hour += hours;
|
||||
if (rtc_now.hour > 24) {
|
||||
rtc_now.hour -= 24;
|
||||
}
|
||||
|
||||
rtc_alarm_ie = false;
|
||||
stop_alarms();
|
||||
x86_rtc_set_alarm(&rtc_now, 0, hwtimer_pid, true);
|
||||
rtc_alarm_ie = true;
|
||||
}
|
||||
else if (us_future > 1 * US_PER_SECOND) {
|
||||
DEBUG(" setting UIE\n");
|
||||
|
||||
rtc_update_ie = false;
|
||||
stop_alarms();
|
||||
x86_rtc_set_update(0, hwtimer_pid, true);
|
||||
rtc_update_ie = true;
|
||||
}
|
||||
else {
|
||||
/* TODO: this has to work without an epic if-else construct */
|
||||
unsigned hz;
|
||||
if ((unsigned long) us_future >= 1 * US_PER_SECOND / 2) {
|
||||
hz = RTC_REG_A_HZ_2;
|
||||
}
|
||||
else if ((unsigned long) us_future >= 1 * US_PER_SECOND / 4) {
|
||||
hz = RTC_REG_A_HZ_4;
|
||||
}
|
||||
else if ((unsigned long) us_future >= 1 * US_PER_SECOND / 8) {
|
||||
hz = RTC_REG_A_HZ_8;
|
||||
}
|
||||
else if ((unsigned long) us_future >= 1 * US_PER_SECOND / 16) {
|
||||
hz = RTC_REG_A_HZ_16;
|
||||
}
|
||||
else if ((unsigned long) us_future >= 1 * US_PER_SECOND / 32) {
|
||||
hz = RTC_REG_A_HZ_32;
|
||||
}
|
||||
else if ((unsigned long) us_future >= 1 * US_PER_SECOND / 64) {
|
||||
hz = RTC_REG_A_HZ_64;
|
||||
}
|
||||
else if ((unsigned long) us_future >= 1 * US_PER_SECOND / 128) {
|
||||
hz = RTC_REG_A_HZ_128;
|
||||
}
|
||||
else if ((unsigned long) us_future >= 1 * US_PER_SECOND / 256) {
|
||||
hz = RTC_REG_A_HZ_256;
|
||||
}
|
||||
else if ((unsigned long) us_future >= 1 * US_PER_SECOND / 512) {
|
||||
hz = RTC_REG_A_HZ_512;
|
||||
}
|
||||
else if ((unsigned long) us_future >= 1 * US_PER_SECOND / 1024) {
|
||||
hz = RTC_REG_A_HZ_1024;
|
||||
}
|
||||
else if ((unsigned long) us_future >= 1 * US_PER_SECOND / 2048) {
|
||||
hz = RTC_REG_A_HZ_2048;
|
||||
}
|
||||
else if ((unsigned long) us_future < 1 * US_PER_SECOND / 4096) {
|
||||
hz = RTC_REG_A_HZ_8192;
|
||||
}
|
||||
else {
|
||||
hz = RTC_REG_A_HZ_4096;
|
||||
}
|
||||
|
||||
if (hz != rtc_periodic_ie) {
|
||||
DEBUG(" setting PIE reg_c |= 0x%02x\n", hz);
|
||||
|
||||
rtc_periodic_ie = false;
|
||||
stop_alarms();
|
||||
x86_rtc_set_periodic(hz, 0, hwtimer_pid, true);
|
||||
rtc_periodic_ie = hz;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
stop_alarms();
|
||||
return false;
|
||||
}
|
||||
|
||||
void hwtimer_arch_init(void (*handler)(int), uint32_t fcpu)
|
||||
{
|
||||
(void) fcpu;
|
||||
|
||||
hwtimer_callback = handler;
|
||||
hwtimer_pid = thread_create(hwtimer_stack,
|
||||
sizeof (hwtimer_stack),
|
||||
1,
|
||||
CREATE_STACKTEST,
|
||||
hwtimer_tick_handler,
|
||||
"x86-hwtimer");
|
||||
hwtimer_ie = true;
|
||||
}
|
||||
|
||||
/* µs since x86_init_hwtimer() */
|
||||
unsigned long hwtimer_arch_now(void)
|
||||
{
|
||||
unsigned old_state = disableIRQ();
|
||||
uint64_t result = rdtsc();
|
||||
restoreIRQ(old_state);
|
||||
return (result - ts_base) * US_PER_SECOND / instructions_per_second;
|
||||
}
|
||||
|
||||
void hwtimer_arch_set(unsigned long offset_us, short timer)
|
||||
{
|
||||
unsigned old_state = disableIRQ();
|
||||
hwtimer_arch_set_absolute(offset_us + hwtimer_arch_now(), timer);
|
||||
restoreIRQ(old_state);
|
||||
}
|
||||
|
||||
void hwtimer_arch_enable_interrupt(void)
|
||||
{
|
||||
bool do_yield = false;
|
||||
|
||||
unsigned old_state = disableIRQ();
|
||||
if (!hwtimer_ie) {
|
||||
hwtimer_ie = true;
|
||||
do_yield = set_next_alarm(false);
|
||||
}
|
||||
restoreIRQ(old_state);
|
||||
|
||||
if (do_yield) {
|
||||
thread_yield();
|
||||
}
|
||||
}
|
||||
|
||||
void hwtimer_arch_disable_interrupt(void)
|
||||
{
|
||||
unsigned old_state = disableIRQ();
|
||||
if (hwtimer_ie) {
|
||||
stop_alarms();
|
||||
hwtimer_ie = false;
|
||||
}
|
||||
restoreIRQ(old_state);
|
||||
}
|
||||
|
||||
void hwtimer_arch_set_absolute(unsigned long value_us_, short timer)
|
||||
{
|
||||
unsigned old_state = disableIRQ();
|
||||
|
||||
DEBUG("DEBUG hwtimer_arch_set_absolute(%lu, %hu)\n", value_us_, timer);
|
||||
|
||||
bool new_top = false;
|
||||
|
||||
if (timers[timer].enabled) {
|
||||
DEBUG(" overwriting timers[%u]\n", timer);
|
||||
|
||||
timers[timer].enabled = false;
|
||||
timer_unlink(timer);
|
||||
|
||||
if (timers_start == &timers[timer]) {
|
||||
new_top = true;
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t now_ts = rdtsc();
|
||||
uint64_t now_us = ((now_ts - ts_base) * US_PER_SECOND) / instructions_per_second;
|
||||
|
||||
uint64_t future_us = value_us_;
|
||||
if (value_us_ < now_us) {
|
||||
future_us += 0x10000ull * 0x10000ull;
|
||||
}
|
||||
|
||||
future_us -= now_us;
|
||||
uint64_t future_ts = (future_us * instructions_per_second) / US_PER_SECOND;
|
||||
DEBUG(" future_us=%llu, future_ts=%llu, now_ts=%llu\n", future_us, future_ts, now_ts);
|
||||
|
||||
timers[timer].ts_absolute_alarm = future_ts + now_ts;
|
||||
timers[timer].enabled = true;
|
||||
|
||||
if (timers_start) {
|
||||
struct alarm_time *prev = timers_start;
|
||||
while (1) {
|
||||
if (prev->ts_absolute_alarm < timers[timer].ts_absolute_alarm) {
|
||||
if (prev->next) {
|
||||
prev = prev->next;
|
||||
}
|
||||
else {
|
||||
prev->next = &timers[timer];
|
||||
timers[timer].prev = prev;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
timers[timer].next = prev;
|
||||
timers[timer].prev = prev->prev;
|
||||
if (timers[timer].prev) {
|
||||
timers[timer].prev->next = &timers[timer];
|
||||
}
|
||||
else {
|
||||
timers_start = &timers[timer];
|
||||
new_top = true;
|
||||
}
|
||||
prev->prev = &timers[timer];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
timers_start = &timers[timer];
|
||||
new_top = true;
|
||||
}
|
||||
|
||||
bool do_yield = false;
|
||||
if (new_top) {
|
||||
do_yield = set_next_alarm(false);
|
||||
}
|
||||
|
||||
restoreIRQ(old_state);
|
||||
|
||||
if (do_yield) {
|
||||
thread_yield();
|
||||
}
|
||||
}
|
||||
|
||||
void hwtimer_arch_unset(short timer)
|
||||
{
|
||||
bool do_yield = false;
|
||||
|
||||
unsigned old_state = disableIRQ();
|
||||
if (timers[timer].enabled) {
|
||||
bool new_top = timers[timer].prev == NULL;
|
||||
timers[timer].enabled = false;
|
||||
timer_unlink(timer);
|
||||
if (new_top) {
|
||||
do_yield = set_next_alarm(false);
|
||||
}
|
||||
}
|
||||
restoreIRQ(old_state);
|
||||
|
||||
if (do_yield) {
|
||||
thread_yield();
|
||||
}
|
||||
}
|
431
cpu/x86/x86_interrupts.c
Normal file
431
cpu/x86/x86_interrupts.c
Normal file
@ -0,0 +1,431 @@
|
||||
/*
|
||||
* Copyright (C) 2014 René Kijewski <rene.kijewski@fu-berlin.de>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ingroup x86-irq
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Interrupt entry points for x86.
|
||||
*
|
||||
* @author René Kijewski <rene.kijewski@fu-berlin.de>
|
||||
*
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include "x86_interrupts.h"
|
||||
#include "x86_memory.h"
|
||||
#include "x86_ports.h"
|
||||
#include "x86_registers.h"
|
||||
#include "x86_threading.h"
|
||||
|
||||
#include <attributes.h>
|
||||
#include <cpu.h>
|
||||
#include <sched.h>
|
||||
#include <thread.h>
|
||||
#include <ucontext.h>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#define ASM_FUN_ATTRIBUTES \
|
||||
__attribute__((noinline)) \
|
||||
__attribute__((no_instrument_function)) \
|
||||
__attribute__((optimize("Os", "omit-frame-pointer")))
|
||||
|
||||
static const char *const exception_name[X86_MAX_INTERRUPTS] = {
|
||||
[0x00] = "Divide by zero",
|
||||
[0x01] = "Debug",
|
||||
[0x02] = "Non-maskable Interrupt",
|
||||
[0x03] = "Breakpoint",
|
||||
[0x04] = "Overflow",
|
||||
[0x05] = "Bound range",
|
||||
[0x06] = "Invalid Opcode",
|
||||
[0x07] = "Device not available",
|
||||
[0x08] = "Double Fault",
|
||||
[0x09] = NULL,
|
||||
[0x0a] = NULL,
|
||||
[0x0b] = NULL,
|
||||
[0x0c] = NULL,
|
||||
[0x0d] = "General Protection Fault",
|
||||
[0x0e] = "Page Fault",
|
||||
[0x0f] = NULL,
|
||||
[0x10] = "x87 Floating-Point Exception",
|
||||
[0x11] = "Alignment Check",
|
||||
[0x12] = "Machine Check",
|
||||
[0x13] = NULL,
|
||||
[0x14] = NULL,
|
||||
[0x15] = NULL,
|
||||
[0x16] = NULL,
|
||||
[0x17] = NULL,
|
||||
[0x18] = NULL,
|
||||
[0x19] = NULL,
|
||||
[0x1a] = NULL,
|
||||
[0x1b] = NULL,
|
||||
[0x1c] = NULL,
|
||||
[0x1d] = NULL,
|
||||
[0x1e] = NULL,
|
||||
[0x1f] = NULL,
|
||||
[0x20] = "PIC PIT",
|
||||
[0x21] = "PIC KBC1",
|
||||
[0x22] = NULL,
|
||||
[0x23] = "PIC RS232 2/4",
|
||||
[0x24] = "PIC RS232 1/3",
|
||||
[0x25] = "PIC LPT2",
|
||||
[0x26] = "PIC floppy",
|
||||
[0x27] = "PIC LPT1",
|
||||
[0x28] = "PIC RTC",
|
||||
[0x29] = "PIC #9",
|
||||
[0x2a] = "PIC ATA4",
|
||||
[0x2b] = "PIC ATA3",
|
||||
[0x2c] = "PIC KBC2",
|
||||
[0x2d] = NULL,
|
||||
[0x2e] = "PIC ATA1",
|
||||
[0x2f] = "PIC ATA2",
|
||||
};
|
||||
|
||||
uint8_t x86_current_interrupt;
|
||||
unsigned long x86_current_interrupt_error_code;
|
||||
struct x86_pushad x86_interrupted_ctx;
|
||||
char x86_interrupt_handler_stack[2048];
|
||||
|
||||
static x86_intr_handler_t x86_registered_interrupts[X86_MAX_INTERRUPTS];
|
||||
|
||||
static void print_stacktrace(unsigned long bp, unsigned long ip)
|
||||
{
|
||||
puts("<stack trace>");
|
||||
printf(" %08lx\n", ip);
|
||||
for (unsigned max_depth = 0; max_depth < 30; ++max_depth) {
|
||||
uint64_t pte = x86_get_pte(bp) & x86_get_pte(bp + 4);
|
||||
if (!(pte & PT_P) || !(pte & PT_PWT)) {
|
||||
puts(" ???");
|
||||
break;
|
||||
}
|
||||
|
||||
unsigned long *bp_ = (void *) bp;
|
||||
ip = bp_[1];
|
||||
if (ip == 0) {
|
||||
break;
|
||||
}
|
||||
printf(" %08lx\n", ip);
|
||||
bp = bp_[0];
|
||||
if (bp == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
puts("</stack trace>");
|
||||
}
|
||||
|
||||
void x86_print_registers(struct x86_pushad *orig_ctx, unsigned long error_code)
|
||||
{
|
||||
unsigned long *sp = (void *) orig_ctx->sp; /* ip, cs, flags */
|
||||
printf("EAX=%08lx ECX=%08lx EDX=%08lx EBX=%08lx\n", orig_ctx->ax, orig_ctx->cx, orig_ctx->dx, orig_ctx->bx);
|
||||
printf("ESP=%08lx EBP=%08lx ESI=%08lx EDI=%08lx\n", orig_ctx->sp, orig_ctx->bp, orig_ctx->si, orig_ctx->di);
|
||||
printf("Error code=%08lx\n", error_code);
|
||||
printf("CR0=%08x CR2=%08x CR3=%08x CR4=%08x\n", cr0_read(), cr2_read(), cr3_read(), cr4_read());
|
||||
printf("EIP=%04lx:%08lx EFLAGS=%08lx\n", sp[1], sp[0], sp[2]);
|
||||
|
||||
print_stacktrace(orig_ctx->bp, sp[0]);
|
||||
}
|
||||
|
||||
static void intr_handler_default(uint8_t intr_num, struct x86_pushad *orig_ctx, unsigned long error_code)
|
||||
{
|
||||
printf("Unhandled interrupt 0x%02x (%s)\n", intr_num, exception_name[intr_num]);
|
||||
x86_print_registers(orig_ctx, error_code);
|
||||
puts("Halting.");
|
||||
x86_hlt();
|
||||
}
|
||||
|
||||
void x86_int_exit(void) NORETURN;
|
||||
|
||||
static void continue_after_intr(void)
|
||||
{
|
||||
ucontext_t *ctx = (ucontext_t *) sched_active_thread->sp;
|
||||
x86_interrupted_ctx = ctx->uc_context.registers;
|
||||
asm volatile (
|
||||
"push %0\n" /* flags */
|
||||
"push $0x0008\n" /* cs */
|
||||
"push %1\n" /* ip */
|
||||
"jmp x86_int_exit"
|
||||
:: "g"(ctx->__intr.flags), "g"(ctx->__intr.ip)
|
||||
);
|
||||
__builtin_unreachable();
|
||||
}
|
||||
|
||||
static unsigned in_intr_handler = 0, old_intr;
|
||||
void x86_int_handler(void)
|
||||
{
|
||||
switch (in_intr_handler++) {
|
||||
case 0:
|
||||
break;
|
||||
case 1:
|
||||
printf("Interrupt 0x%02x (%s) while handling 0x%02x (%s)\n",
|
||||
x86_current_interrupt, exception_name[x86_current_interrupt],
|
||||
old_intr, exception_name[old_intr]);
|
||||
x86_print_registers(&x86_interrupted_ctx, x86_current_interrupt_error_code);
|
||||
puts("Halting.");
|
||||
x86_hlt();
|
||||
default:
|
||||
x86_hlt();
|
||||
}
|
||||
old_intr = x86_current_interrupt;
|
||||
|
||||
bool old_in_isr = x86_in_isr;
|
||||
x86_in_isr = true;
|
||||
|
||||
x86_intr_handler_t intr_handler = x86_registered_interrupts[x86_current_interrupt];
|
||||
(intr_handler ? intr_handler : intr_handler_default)(x86_current_interrupt, &x86_interrupted_ctx, x86_current_interrupt_error_code);
|
||||
|
||||
--in_intr_handler;
|
||||
|
||||
unsigned long *sp = (void *) x86_interrupted_ctx.sp; /* ip, cs, flags */
|
||||
if (!sched_context_switch_request || !(sp[2] & X86_IF)) {
|
||||
x86_in_isr = old_in_isr;
|
||||
return;
|
||||
}
|
||||
|
||||
ucontext_t *ctx = (ucontext_t *) sched_active_thread->sp;
|
||||
ctx->uc_context.registers = x86_interrupted_ctx;
|
||||
ctx->uc_stack.ss_sp = x86_interrupt_handler_stack;
|
||||
ctx->uc_stack.ss_size = sizeof x86_interrupt_handler_stack;
|
||||
asm volatile ("pushf; pop %0" : "=g"(ctx->uc_context.flags));
|
||||
ctx->uc_context.ip = (void *) (uintptr_t) &continue_after_intr;
|
||||
ctx->__intr.ip = sp[0];
|
||||
ctx->__intr.flags = sp[2];
|
||||
|
||||
thread_yield();
|
||||
__builtin_unreachable();
|
||||
}
|
||||
|
||||
void ASM_FUN_ATTRIBUTES NORETURN x86_int_entry(void)
|
||||
{
|
||||
asm volatile ("mov %eax, (4*0 + x86_interrupted_ctx)");
|
||||
asm volatile ("mov %ecx, (4*1 + x86_interrupted_ctx)");
|
||||
asm volatile ("mov %edx, (4*2 + x86_interrupted_ctx)");
|
||||
asm volatile ("mov %ebx, (4*3 + x86_interrupted_ctx)");
|
||||
asm volatile ("mov %ebp, (4*5 + x86_interrupted_ctx)");
|
||||
asm volatile ("mov %esi, (4*6 + x86_interrupted_ctx)");
|
||||
asm volatile ("mov %edi, (4*7 + x86_interrupted_ctx)");
|
||||
|
||||
asm volatile ("jnc 1f");
|
||||
asm volatile (" mov (%esp), %eax");
|
||||
asm volatile (" add $4, %esp");
|
||||
asm volatile (" jmp 2f");
|
||||
asm volatile ("1:");
|
||||
asm volatile (" xor %eax, %eax");
|
||||
asm volatile ("2:");
|
||||
asm volatile (" mov %eax, x86_current_interrupt_error_code");
|
||||
|
||||
asm volatile ("mov %esp, (4*4 + x86_interrupted_ctx)");
|
||||
asm volatile ("mov %0, %%esp" :: "g"(&x86_interrupt_handler_stack[sizeof x86_interrupt_handler_stack]));
|
||||
asm volatile ("call x86_int_handler");
|
||||
asm volatile ("jmp x86_int_exit");
|
||||
__builtin_unreachable();
|
||||
}
|
||||
|
||||
void ASM_FUN_ATTRIBUTES NORETURN x86_int_exit(void)
|
||||
{
|
||||
asm volatile ("mov (4*0 + x86_interrupted_ctx), %eax");
|
||||
asm volatile ("mov (4*1 + x86_interrupted_ctx), %ecx");
|
||||
asm volatile ("mov (4*2 + x86_interrupted_ctx), %edx");
|
||||
asm volatile ("mov (4*3 + x86_interrupted_ctx), %ebx");
|
||||
asm volatile ("mov (4*5 + x86_interrupted_ctx), %ebp");
|
||||
asm volatile ("mov (4*6 + x86_interrupted_ctx), %esi");
|
||||
asm volatile ("mov (4*7 + x86_interrupted_ctx), %edi");
|
||||
asm volatile ("mov (4*4 + x86_interrupted_ctx), %esp");
|
||||
|
||||
asm volatile ("iret");
|
||||
__builtin_unreachable();
|
||||
}
|
||||
|
||||
#define DECLARE_INT(NUM, HAS_ERROR_CODE, MNEMONIC) \
|
||||
static void ASM_FUN_ATTRIBUTES NORETURN x86_int_entry_##NUM##h(void) \
|
||||
{ \
|
||||
asm volatile ("movb %0, x86_current_interrupt" :: "n"(0x##NUM)); \
|
||||
if ((HAS_ERROR_CODE)) { \
|
||||
asm volatile ("stc"); \
|
||||
} \
|
||||
else { \
|
||||
asm volatile ("clc"); \
|
||||
}\
|
||||
asm volatile ("jmp x86_int_entry"); \
|
||||
__builtin_unreachable(); \
|
||||
}
|
||||
DECLARE_INT(00, 0, "#DE")
|
||||
DECLARE_INT(01, 0, "#DB")
|
||||
DECLARE_INT(02, 0, "NMI")
|
||||
DECLARE_INT(03, 0, "#BP")
|
||||
DECLARE_INT(04, 0, "#OF")
|
||||
DECLARE_INT(05, 0, "#BR")
|
||||
DECLARE_INT(06, 0, "#UD")
|
||||
DECLARE_INT(07, 0, "#NM")
|
||||
DECLARE_INT(08, 1, "#DF")
|
||||
DECLARE_INT(0d, 1, "#GP")
|
||||
DECLARE_INT(0e, 1, "#PF")
|
||||
DECLARE_INT(10, 0, "#MF")
|
||||
DECLARE_INT(11, 1, "#AC")
|
||||
DECLARE_INT(12, 0, "#MC")
|
||||
DECLARE_INT(20, 0, "PIC PIT")
|
||||
DECLARE_INT(21, 1, "PIC KBC1")
|
||||
DECLARE_INT(23, 0, "PIC RS232 2/4")
|
||||
DECLARE_INT(24, 0, "PIC RS232 1/3")
|
||||
DECLARE_INT(25, 0, "PIC LPT2")
|
||||
DECLARE_INT(26, 0, "PIC floppy")
|
||||
DECLARE_INT(27, 0, "PIC LPT1")
|
||||
DECLARE_INT(28, 0, "PIC RPC")
|
||||
DECLARE_INT(29, 0, "PIC #9")
|
||||
DECLARE_INT(2a, 0, "PIC ATA4")
|
||||
DECLARE_INT(2b, 0, "PIC ATA3")
|
||||
DECLARE_INT(2c, 0, "PIC KBC2")
|
||||
DECLARE_INT(2e, 0, "PIC ATA1")
|
||||
DECLARE_INT(2f, 0, "PIC ATA2")
|
||||
|
||||
static struct idt_desc X86_IDT_ENTRIES[X86_MAX_INTERRUPTS];
|
||||
static struct idtr_t idtr = {
|
||||
.limit = sizeof X86_IDT_ENTRIES,
|
||||
.base = &X86_IDT_ENTRIES[0],
|
||||
};
|
||||
|
||||
#define INTR_TEST_REG_AX (0xAA00AA00ul)
|
||||
#define INTR_TEST_REG_DX (0x00DD00DDul)
|
||||
#define INTR_TEST_REG_CX (0xC0C0C0C0ul)
|
||||
#define INTR_TEST_REG_BX (0x0B0B0B0Bul)
|
||||
#define INTR_TEST_REG_SI (0x00666600ul)
|
||||
#define INTR_TEST_REG_DI (0x33000033ul)
|
||||
|
||||
static void intr_handler_test_int_bp(uint8_t intr_num, struct x86_pushad *orig_ctx, unsigned long error_code)
|
||||
{
|
||||
(void) error_code;
|
||||
if (intr_num != X86_INT_BP ||
|
||||
orig_ctx->ax != INTR_TEST_REG_AX ||
|
||||
orig_ctx->dx != INTR_TEST_REG_DX ||
|
||||
orig_ctx->cx != INTR_TEST_REG_CX ||
|
||||
orig_ctx->bx != INTR_TEST_REG_BX ||
|
||||
orig_ctx->si != INTR_TEST_REG_SI ||
|
||||
orig_ctx->di != INTR_TEST_REG_DI
|
||||
) {
|
||||
puts("Interrupt handler test failed (int 3, capture registers).");
|
||||
x86_hlt();
|
||||
}
|
||||
orig_ctx->ax ^= -1;
|
||||
orig_ctx->dx ^= -2;
|
||||
orig_ctx->cx ^= -3;
|
||||
orig_ctx->bx ^= -4;
|
||||
orig_ctx->si ^= -5;
|
||||
orig_ctx->di ^= -6;
|
||||
}
|
||||
|
||||
static void test_int_bp(void)
|
||||
{
|
||||
x86_registered_interrupts[X86_INT_BP] = intr_handler_test_int_bp;
|
||||
|
||||
unsigned long ax = INTR_TEST_REG_AX;
|
||||
unsigned long dx = INTR_TEST_REG_DX;
|
||||
unsigned long cx = INTR_TEST_REG_CX;
|
||||
unsigned long bx = INTR_TEST_REG_BX;
|
||||
unsigned long si;
|
||||
unsigned long di;
|
||||
unsigned long eflags_before, eflags_after;
|
||||
asm volatile (
|
||||
"mov %8, %%esi\n"
|
||||
"mov %9, %%edi\n"
|
||||
"pushf; pop %6\n"
|
||||
"int3\n"
|
||||
"pushf; pop %7\n"
|
||||
"mov %%esi, %4\n"
|
||||
"mov %%edi, %5\n"
|
||||
: "+a"(ax), "+d"(dx), "+c"(cx), "+b"(bx), "=g"(si), "=g"(di), "=g"(eflags_before), "=g"(eflags_after)
|
||||
: "n"(INTR_TEST_REG_SI), "n"(INTR_TEST_REG_DI)
|
||||
: "esi", "edi"
|
||||
);
|
||||
if (ax != (INTR_TEST_REG_AX ^ -1) ||
|
||||
dx != (INTR_TEST_REG_DX ^ -2) ||
|
||||
cx != (INTR_TEST_REG_CX ^ -3) ||
|
||||
bx != (INTR_TEST_REG_BX ^ -4) ||
|
||||
si != (INTR_TEST_REG_SI ^ -5) ||
|
||||
di != (INTR_TEST_REG_DI ^ -6) ||
|
||||
/* ignore EFLAGS.TF, hurts debugging */
|
||||
((eflags_before != eflags_after) && ((eflags_before ^ eflags_after) != X86_TF))
|
||||
) {
|
||||
puts("Interrupt handler test failed (int 3, return code).");
|
||||
x86_hlt();
|
||||
}
|
||||
|
||||
x86_registered_interrupts[X86_INT_BP] = NULL;
|
||||
}
|
||||
|
||||
static inline void __attribute__((always_inline)) set_idt_desc(void (*fun_)(void), unsigned num, unsigned pl)
|
||||
{
|
||||
uintptr_t fun = (uintptr_t) fun_;
|
||||
X86_IDT_ENTRIES[num] = (struct idt_desc) {
|
||||
.offset_1 = fun & 0xffff,
|
||||
.selector = 8,
|
||||
.zero = 0,
|
||||
.type_attr = 14 | (0 << 4) | (pl << 5) | (1 << 7),
|
||||
.offset_2 = fun >> 16,
|
||||
};
|
||||
}
|
||||
|
||||
#define SET_IDT_DESC(NUM, HAS_ERROR_CODE, MNEMONIC, PL) \
|
||||
(set_idt_desc(x86_int_entry_##NUM##h, 0x##NUM, PL));
|
||||
|
||||
static void load_interrupt_descriptor_table(void)
|
||||
{
|
||||
SET_IDT_DESC(00, 0, "#DE", 0)
|
||||
SET_IDT_DESC(01, 0, "#DB", 0)
|
||||
SET_IDT_DESC(02, 0, "NMI", 0)
|
||||
SET_IDT_DESC(03, 0, "#BP", 3)
|
||||
SET_IDT_DESC(04, 0, "#OF", 0)
|
||||
SET_IDT_DESC(05, 0, "#BR", 0)
|
||||
SET_IDT_DESC(06, 0, "#UD", 0)
|
||||
SET_IDT_DESC(07, 0, "#NM", 0)
|
||||
SET_IDT_DESC(08, 1, "#DF", 0)
|
||||
SET_IDT_DESC(0d, 1, "#GP", 0)
|
||||
SET_IDT_DESC(0e, 1, "#PF", 0)
|
||||
SET_IDT_DESC(10, 0, "#MF", 0)
|
||||
SET_IDT_DESC(11, 1, "#AC", 0)
|
||||
SET_IDT_DESC(12, 0, "#MC", 0)
|
||||
SET_IDT_DESC(20, 0, "PIC PIT", 0)
|
||||
SET_IDT_DESC(21, 1, "PIC KBC1", 0)
|
||||
SET_IDT_DESC(23, 0, "PIC RS232 2/4", 0)
|
||||
SET_IDT_DESC(24, 0, "PIC RS232 1/3", 0)
|
||||
SET_IDT_DESC(25, 0, "PIC LPT2", 0)
|
||||
SET_IDT_DESC(26, 0, "PIC floppy", 0)
|
||||
SET_IDT_DESC(27, 0, "PIC LPT1", 0)
|
||||
SET_IDT_DESC(28, 0, "PIC RPC", 0)
|
||||
SET_IDT_DESC(29, 0, "PIC #9", 0)
|
||||
SET_IDT_DESC(2a, 0, "PIC ATA4", 0)
|
||||
SET_IDT_DESC(2b, 0, "PIC ATA3", 0)
|
||||
SET_IDT_DESC(2c, 0, "PIC KBC2", 0)
|
||||
SET_IDT_DESC(2e, 0, "PIC ATA1", 0)
|
||||
SET_IDT_DESC(2f, 0, "PIC ATA2", 0)
|
||||
|
||||
asm volatile ("lidt %0" :: "m"(idtr));
|
||||
}
|
||||
|
||||
void x86_init_interrupts(void)
|
||||
{
|
||||
load_interrupt_descriptor_table();
|
||||
test_int_bp();
|
||||
puts("Interrupt handling initialized");
|
||||
}
|
||||
|
||||
void x86_interrupt_handler_set(unsigned num, x86_intr_handler_t handler)
|
||||
{
|
||||
x86_registered_interrupts[num] = handler;
|
||||
}
|
457
cpu/x86/x86_memory.c
Normal file
457
cpu/x86/x86_memory.c
Normal file
@ -0,0 +1,457 @@
|
||||
/*
|
||||
* Copyright (C) 2014 René Kijewski <rene.kijewski@fu-berlin.de>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ingroup x86
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Virtual memory management.
|
||||
*
|
||||
* @author René Kijewski <rene.kijewski@fu-berlin.de>
|
||||
*
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include "x86_kernel_memory.h"
|
||||
#include "x86_interrupts.h"
|
||||
#include "x86_memory.h"
|
||||
#include "x86_registers.h"
|
||||
#include "cpu.h"
|
||||
#include "irq.h"
|
||||
#include "tlsf-malloc.h"
|
||||
|
||||
#include <malloc.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
/* Compare Figure 44 (p. 99) of "Intel® Quark SoC X1000 Core 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);
|
||||
}
|
331
cpu/x86/x86_pci.c
Normal file
331
cpu/x86/x86_pci.c
Normal file
@ -0,0 +1,331 @@
|
||||
/*
|
||||
* Copyright (C) 2014 René Kijewski <rene.kijewski@fu-berlin.de>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ingroup x86-irq
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief PCI configuration and accessing.
|
||||
*
|
||||
* @author René Kijewski <rene.kijewski@fu-berlin.de>
|
||||
*
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include "x86_memory.h"
|
||||
#include "x86_pci.h"
|
||||
#include "x86_pci_init.h"
|
||||
#include "x86_pci_strings.h"
|
||||
#include "x86_ports.h"
|
||||
|
||||
#include <malloc.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
static struct x86_known_pci_device **known_pci_devices = NULL;
|
||||
static unsigned num_known_pci_devices;
|
||||
|
||||
static void set_addr(unsigned bus, unsigned dev, unsigned fun, unsigned reg)
|
||||
{
|
||||
unsigned addr = PCI_IO_ENABLE
|
||||
| (bus << PCI_IO_SHIFT_BUS)
|
||||
| (dev << PCI_IO_SHIFT_DEV)
|
||||
| (fun << PCI_IO_SHIFT_FUN)
|
||||
| (reg << PCI_IO_SHIFT_REG);
|
||||
outl(PCI_CONFIG_ADDRESS, addr);
|
||||
}
|
||||
|
||||
uint32_t x86_pci_read(unsigned bus, unsigned dev, unsigned fun, unsigned reg)
|
||||
{
|
||||
set_addr(bus, dev, fun, reg);
|
||||
return U32_PCItoH(inl(PCI_CONFIG_DATA));
|
||||
}
|
||||
|
||||
uint8_t x86_pci_read8(unsigned bus, unsigned dev, unsigned fun, unsigned reg)
|
||||
{
|
||||
set_addr(bus, dev, fun, reg);
|
||||
return U8_PCItoH(inb(PCI_CONFIG_DATA));
|
||||
}
|
||||
|
||||
uint16_t x86_pci_read16(unsigned bus, unsigned dev, unsigned fun, unsigned reg)
|
||||
{
|
||||
set_addr(bus, dev, fun, reg);
|
||||
return U16_PCItoH(inw(PCI_CONFIG_DATA));
|
||||
}
|
||||
|
||||
static bool pci_vendor_id_valid(uint16_t vendor_id)
|
||||
{
|
||||
return vendor_id != PCI_HEADER_VENDOR_ID_INVALID &&
|
||||
vendor_id != PCI_HEADER_VENDOR_ID_UNRESPONSIVE &&
|
||||
vendor_id != PCI_HEADER_VENDOR_ID_ABSENT;
|
||||
}
|
||||
|
||||
void x86_pci_write(unsigned bus, unsigned dev, unsigned fun, unsigned reg, uint32_t datum)
|
||||
{
|
||||
set_addr(bus, dev, fun, reg);
|
||||
outl(PCI_CONFIG_DATA, U32_HtoPCI(datum));
|
||||
}
|
||||
|
||||
void x86_pci_write8(unsigned bus, unsigned dev, unsigned fun, unsigned reg, uint8_t datum)
|
||||
{
|
||||
set_addr(bus, dev, fun, reg);
|
||||
outb(PCI_CONFIG_DATA, U8_HtoPCI(datum));
|
||||
}
|
||||
|
||||
void x86_pci_write16(unsigned bus, unsigned dev, unsigned fun, unsigned reg, uint16_t datum)
|
||||
{
|
||||
set_addr(bus, dev, fun, reg);
|
||||
outw(PCI_CONFIG_DATA, U16_HtoPCI(datum));
|
||||
}
|
||||
|
||||
static unsigned pci_init_secondary_bus(unsigned bus, unsigned dev, unsigned fun)
|
||||
{
|
||||
known_pci_devices[num_known_pci_devices - 1]->managed = true;
|
||||
|
||||
/* TODO */
|
||||
printf(" TODO: pci_init_secondary_bus(0x%x, 0x%x, 0x%x)\n", bus, dev, fun);
|
||||
(void) bus;
|
||||
(void) dev;
|
||||
(void) fun;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void pci_setup_ios(struct x86_known_pci_device *dev)
|
||||
{
|
||||
/* § 6.2.5. (pp. 224) */
|
||||
|
||||
unsigned bar_count = 0;
|
||||
unsigned bar_base;
|
||||
|
||||
unsigned header_type = x86_pci_read_reg(0x0c, dev->bus, dev->dev, dev->fun).header_type & PCI_HEADER_TYPE_MASK;
|
||||
switch (header_type) {
|
||||
case PCI_HEADER_TYPE_GENERAL_DEVICE:
|
||||
bar_count = 6;
|
||||
bar_base = 0x10;
|
||||
break;
|
||||
case PCI_HEADER_TYPE_BRIDGE:
|
||||
bar_count = 2;
|
||||
bar_base = 0x10;
|
||||
break;
|
||||
default:
|
||||
printf(" Cannot configure header_type == 0x%02x, yet.\n", header_type);
|
||||
return;
|
||||
}
|
||||
|
||||
for (unsigned bar_num = 0; bar_num < bar_count; ++bar_num) {
|
||||
uint32_t old_bar = x86_pci_read(dev->bus, dev->dev, dev->fun, bar_base + 4 * bar_num);
|
||||
if (old_bar == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
x86_pci_write(dev->bus, dev->dev, dev->fun, bar_base + 4 * bar_num, -1ul);
|
||||
uint32_t tmp_bar = x86_pci_read(dev->bus, dev->dev, dev->fun, bar_base + 4 * bar_num);
|
||||
x86_pci_write(dev->bus, dev->dev, dev->fun, bar_base + 4 * bar_num, old_bar);
|
||||
if ((old_bar & PCI_BAR_IO_SPACE) != (tmp_bar & PCI_BAR_IO_SPACE)) {
|
||||
/* cannot happen (?) */
|
||||
continue;
|
||||
}
|
||||
|
||||
dev->io = realloc(dev->io, sizeof (*dev->io) * (dev->io_count + 1));
|
||||
struct x86_pci_io *io = calloc(1, sizeof *io);
|
||||
dev->io[dev->io_count] = io;
|
||||
io->bar_num = bar_num;
|
||||
++dev->io_count;
|
||||
|
||||
unsigned addr_offs = tmp_bar & PCI_BAR_IO_SPACE ? PCI_BAR_ADDR_OFFS_IO : PCI_BAR_ADDR_OFFS_MEM;
|
||||
uint32_t length_tmp = tmp_bar >> addr_offs;
|
||||
uint32_t length = 1 << addr_offs;
|
||||
while ((length_tmp & 1) == 0) {
|
||||
length <<= 1;
|
||||
length_tmp >>= 1;
|
||||
}
|
||||
io->length = length;
|
||||
|
||||
if (tmp_bar & PCI_BAR_IO_SPACE) {
|
||||
io->type = PCI_IO_PORT;
|
||||
io->addr.port = old_bar & ~((1 << PCI_BAR_ADDR_OFFS_IO) - 1);
|
||||
printf(" BAR %u: I/O space, ports 0x%04x-0x%04x\n",
|
||||
bar_num, io->addr.port, io->addr.port + length - 1);
|
||||
}
|
||||
else if ((old_bar & PCI_BAR_IO_SPACE) != PCI_BAR_SPACE_32 && (old_bar & PCI_BAR_IO_SPACE) != PCI_BAR_SPACE_64) {
|
||||
printf(" BAR %u: memory with unknown location 0x%x, ERROR!\n", bar_num, (old_bar >> 1) & 3);
|
||||
}
|
||||
else {
|
||||
uint32_t physical_start = old_bar & ~0xfff;
|
||||
void *ptr = x86_map_physical_pages(physical_start, (length + 0xfff) / 0x1000, PT_P | PT_G | PT_RW | PT_PWT | PT_PCD | PT_XD);
|
||||
if (!ptr) {
|
||||
io->type = PCI_IO_INVALID;
|
||||
printf(" BAR %u: memory, physical = 0x%08x-0x%08x, ERROR!\n",
|
||||
bar_num, (unsigned) physical_start, physical_start + length - 1);
|
||||
}
|
||||
else {
|
||||
io->type = PCI_IO_MEM;
|
||||
io->addr.ptr = (char *) ptr + (old_bar & ~0xfff & ~((1 << PCI_BAR_ADDR_OFFS_MEM) - 1));
|
||||
printf(" BAR %u: memory, physical = 0x%08x-0x%08x, virtual = 0x%08x-0x%08x\n",
|
||||
bar_num,
|
||||
physical_start, physical_start + length - 1,
|
||||
(unsigned) ptr, (uintptr_t) ptr + length - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void pci_find_on_bus(unsigned bus);
|
||||
|
||||
void x86_pci_set_irq(struct x86_known_pci_device *d, uint8_t irq_num)
|
||||
{
|
||||
if (d->irq == irq_num) {
|
||||
return;
|
||||
}
|
||||
|
||||
d->irq = irq_num;
|
||||
uint32_t old_3c = x86_pci_read(d->bus, d->dev, d->fun, 0x3c);
|
||||
x86_pci_write(d->bus, d->dev, d->fun, 0x3c, (old_3c & ~0xff) | d->irq);
|
||||
|
||||
printf(" IRQ: new = %u, old = %u\n", d->irq, old_3c & 0xff);
|
||||
}
|
||||
|
||||
static void pci_find_function(unsigned bus, unsigned dev, unsigned fun)
|
||||
{
|
||||
union pci_reg_0x00 vendor = x86_pci_read_reg(0x00, bus, dev, fun);
|
||||
if (!pci_vendor_id_valid(vendor.vendor_id)) {
|
||||
return;
|
||||
}
|
||||
|
||||
union pci_reg_0x08 class = x86_pci_read_reg(0x08, bus, dev, fun);
|
||||
const char *baseclass_name, *subclass_name = x86_pci_subclass_to_string(class.baseclass,
|
||||
class.subclass,
|
||||
class.programming_interface,
|
||||
&baseclass_name);
|
||||
const char *vendor_name, *device_name = x86_pci_device_id_to_string(vendor.vendor_id, vendor.device_id, &vendor_name);
|
||||
printf(" %02x:%02x.%x \"%s\": \"%s\" (%s: %s, rev: %02hhx)\n",
|
||||
bus, dev, fun, vendor_name, device_name, baseclass_name, subclass_name, class.revision_id);
|
||||
|
||||
known_pci_devices = realloc(known_pci_devices, sizeof (*known_pci_devices) * (num_known_pci_devices + 1));
|
||||
struct x86_known_pci_device *d = calloc(1, sizeof *d);
|
||||
known_pci_devices[num_known_pci_devices] = d;
|
||||
++num_known_pci_devices;
|
||||
|
||||
d->bus = bus;
|
||||
d->dev = dev;
|
||||
d->fun = fun;
|
||||
d->vendor = vendor;
|
||||
d->class = class;
|
||||
d->managed = false;
|
||||
|
||||
uint32_t old_3c = x86_pci_read(bus, dev, fun, 0x3c);
|
||||
if (old_3c & 0xff) {
|
||||
d->irq = PCI_IRQ_DEFAULT;
|
||||
x86_pci_write(bus, dev, fun, 0x3c, (old_3c & ~0xff) | d->irq);
|
||||
printf(" IRQ: new = %u, old = %u\n", d->irq, old_3c & 0xff);
|
||||
}
|
||||
|
||||
pci_setup_ios(d);
|
||||
|
||||
if (class.baseclass == 0x06 && class.subclass == 0x04) {
|
||||
unsigned secondary_bus = pci_init_secondary_bus(bus, dev, fun);
|
||||
if (secondary_bus != 0) {
|
||||
pci_find_on_bus(secondary_bus);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void pci_find_on_bus(unsigned bus)
|
||||
{
|
||||
for (unsigned dev = 0; dev < PCI_DEV_COUNT; ++dev) {
|
||||
if (!pci_vendor_id_valid(x86_pci_read_reg(0x00, bus, dev, 0).vendor_id)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (x86_pci_read_reg(0x0c, bus, dev, 0).header_type & PCI_HEADER_TYPE_MULTI_FUNCTION) {
|
||||
for (unsigned fun = 0; fun < PCI_FUN_COUNT; ++fun) {
|
||||
pci_find_function(bus, dev, fun);
|
||||
}
|
||||
}
|
||||
else {
|
||||
pci_find_function(bus, dev, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void pci_find(void)
|
||||
{
|
||||
if (x86_pci_read_reg(0x0c, 0, 0, 0).header_type & PCI_HEADER_TYPE_MULTI_FUNCTION) {
|
||||
for (unsigned fun = 0; fun < PCI_FUN_COUNT; ++fun) {
|
||||
if (pci_vendor_id_valid(x86_pci_read_reg(0x00, 0, 0, fun).vendor_id)) {
|
||||
pci_find_on_bus(fun);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
pci_find_on_bus(0);
|
||||
}
|
||||
}
|
||||
|
||||
static void irq_handler(uint8_t irq_num)
|
||||
{
|
||||
for (unsigned i = 0; i < num_known_pci_devices; ++i) {
|
||||
struct x86_known_pci_device *d = known_pci_devices[i];
|
||||
if (d->managed && d->irq_handler && d->irq == irq_num) {
|
||||
d->irq_handler(d);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void x86_init_pci(void)
|
||||
{
|
||||
x86_pic_set_handler(PCI_IRQ_ACPI, irq_handler);
|
||||
x86_pic_enable_irq(PCI_IRQ_ACPI);
|
||||
x86_pic_set_handler(PCI_IRQ_NETWORKING, irq_handler);
|
||||
x86_pic_enable_irq(PCI_IRQ_NETWORKING);
|
||||
x86_pic_set_handler(PCI_IRQ_DEFAULT, irq_handler);
|
||||
x86_pic_enable_irq(PCI_IRQ_DEFAULT);
|
||||
x86_pic_set_handler(PCI_IRQ_USB, irq_handler);
|
||||
x86_pic_enable_irq(PCI_IRQ_USB);
|
||||
|
||||
puts("Looking up PCI devices");
|
||||
pci_find();
|
||||
|
||||
x86_init_pci_devices();
|
||||
}
|
||||
|
||||
struct x86_known_pci_device **x86_enumerate_unmanaged_pci_devices(unsigned *index)
|
||||
{
|
||||
while (*index < num_known_pci_devices) {
|
||||
struct x86_known_pci_device **result = &known_pci_devices[*index];
|
||||
++*index;
|
||||
if (*result && !(**result).managed) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const struct x86_known_pci_device *x86_enumerate_pci_devices(unsigned *index)
|
||||
{
|
||||
while (*index < num_known_pci_devices) {
|
||||
struct x86_known_pci_device *result = known_pci_devices[*index];
|
||||
++*index;
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
37
cpu/x86/x86_pci_init.c
Normal file
37
cpu/x86/x86_pci_init.c
Normal file
@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright (C) 2014 René Kijewski <rene.kijewski@fu-berlin.de>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ingroup x86-irq
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief PCI device setup
|
||||
*
|
||||
* @author René Kijewski <rene.kijewski@fu-berlin.de>
|
||||
*
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include "x86_pci.h"
|
||||
#include "x86_pci_init.h"
|
||||
|
||||
void x86_init_pci_devices(void)
|
||||
{
|
||||
/* no PCI devices are implemented, yet */
|
||||
}
|
409
cpu/x86/x86_pci_strings.c
Normal file
409
cpu/x86/x86_pci_strings.c
Normal file
@ -0,0 +1,409 @@
|
||||
/*
|
||||
* Copyright (C) 2014 René Kijewski <rene.kijewski@fu-berlin.de>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ingroup x86-irq
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief String names of PCI classes, vendors, and devices.
|
||||
*
|
||||
* @author René Kijewski <rene.kijewski@fu-berlin.de>
|
||||
*
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include "x86_pci_strings.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
/* TODO: don't always include all strings */
|
||||
|
||||
const char *x86_pci_subclass_to_string(unsigned baseclass, unsigned subclass, unsigned interface, const char **baseclass_name)
|
||||
{
|
||||
static char temp_baseclass_number[7], temp_subclass_number[7];
|
||||
|
||||
const char *baseclass_name_null;
|
||||
if (!baseclass_name) {
|
||||
baseclass_name = &baseclass_name_null;
|
||||
}
|
||||
|
||||
// "PCI LOCAL BUS SPECIFICATION, REV. 3.0"; Appendix D.
|
||||
switch (baseclass) {
|
||||
case 0x00:
|
||||
*baseclass_name = "Device was built before Class Code definitions were finalized";
|
||||
switch (subclass) {
|
||||
case 0x00: return "Non-VGA";
|
||||
case 0x01: return "VGA";
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x01:
|
||||
*baseclass_name = "Mass storage controller";
|
||||
switch (subclass) {
|
||||
case 0x00: return "SCSI bus controller";
|
||||
case 0x01: return "IDE controller";
|
||||
case 0x02: return "Floppy disk controller";
|
||||
case 0x03: return "IPI bus controller";
|
||||
case 0x04: return "RAID controller";
|
||||
case 0x05: return "ATA controller";
|
||||
case 0x06: return "Serial ATA Direct Port Access";
|
||||
case 0x80: return "Other mass storage controller";
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x02:
|
||||
*baseclass_name = "Network controller";
|
||||
switch (subclass) {
|
||||
case 0x00: return "Ethernet controller";
|
||||
case 0x01: return "Token Ring controller";
|
||||
case 0x02: return "FDDI controller";
|
||||
case 0x03: return "ATM controller";
|
||||
case 0x04: return "ISDN controller";
|
||||
case 0x05: return "WorldFip controller";
|
||||
case 0x06: return "PICMG 2.14 Multi Computing";
|
||||
case 0x80: return "Other network controller";
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x03:
|
||||
*baseclass_name = "Display controller";
|
||||
switch (subclass) {
|
||||
case 0x00: switch (interface) {
|
||||
case 0x00: return "VGA-compatible controller";
|
||||
case 0x01: return "8514-compatible controller";
|
||||
default: return "[UNDEFINED IN ACPI 3.0]";
|
||||
}
|
||||
case 0x01: return "XGA coltroller";
|
||||
case 0x02: return "3D controller";
|
||||
case 0x80: return "Other display controller";
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x04:
|
||||
*baseclass_name = "Multimedia device";
|
||||
switch (subclass) {
|
||||
case 0x00: return "Video device";
|
||||
case 0x01: return "Audio device";
|
||||
case 0x02: return "Computer telephony device";
|
||||
case 0x80: return "Other multimedia device";
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x05:
|
||||
*baseclass_name = "Memory controller";
|
||||
switch (subclass) {
|
||||
case 0x00: return "RAM";
|
||||
case 0x01: return "Flash";
|
||||
case 0x80: return "Other memory controller";
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x06:
|
||||
*baseclass_name = "Bridge device";
|
||||
switch (subclass) {
|
||||
case 0x00: return "Host bridge";
|
||||
case 0x01: return "ISA bridge";
|
||||
case 0x02: return "EISA bridge";
|
||||
case 0x03: return "MCA bridge";
|
||||
case 0x04: return "PCI-to-PCI bridge";
|
||||
case 0x05: return "PCMCIA bridge";
|
||||
case 0x06: return "NuBus bridge";
|
||||
case 0x07: return "CardBus bridge";
|
||||
case 0x08: return "RACEway bridge";
|
||||
case 0x09: return "Semi-transparent PCI-to-PCI bridge";
|
||||
case 0x0a: return "InfiniBand-to-PCI host bridge";
|
||||
case 0x80: return "Other bridge device";
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x07:
|
||||
*baseclass_name = "Simple communication controller";
|
||||
switch (subclass) {
|
||||
case 0x00:
|
||||
switch (interface) {
|
||||
case 0x00: return "Generic XT-compatible serial controller";
|
||||
case 0x01: return "16450-compatible serial controller";
|
||||
case 0x02: return "16550-compatible serial controller";
|
||||
case 0x03: return "16650-compatible serial controller";
|
||||
case 0x04: return "16750-compatible serial controller";
|
||||
case 0x05: return "16850-compatible serial controller";
|
||||
case 0x06: return "16950-compatible serial controller";
|
||||
}
|
||||
break;
|
||||
case 0x01:
|
||||
switch (interface) {
|
||||
case 0x00: return "Parallel port";
|
||||
case 0x01: return "Bi-directional parallel port";
|
||||
case 0x02: return "ECP 1.X compliant parallel port";
|
||||
case 0x03: return "IEEE1284 controller";
|
||||
case 0xef: return "IEEE1284 target device";
|
||||
}
|
||||
break;
|
||||
case 0x02: return "Multiport serial controller";
|
||||
case 0x03:
|
||||
switch (interface) {
|
||||
case 0x00: return "Generic modem";
|
||||
case 0x01: return "Hayes compatible modem, 16450-compatible interface";
|
||||
case 0x02: return "Hayes compatible modem, 16550-compatible interface";
|
||||
case 0x03: return "Hayes compatible modem, 16650-compatible interface";
|
||||
case 0x04: return "Hayes compatible modem, 16750-compatible interface";
|
||||
}
|
||||
break;
|
||||
case 0x04: return "GPIB (IEEE 488.1/2) controller";
|
||||
case 0x05: return "Smart Card";
|
||||
case 0x80: return "Other communications device";
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x08:
|
||||
*baseclass_name = "Base system peripheral";
|
||||
switch (subclass) {
|
||||
case 0x00:
|
||||
switch (interface) {
|
||||
case 0x00: return "Generic 8259 PIC";
|
||||
case 0x01: return "ISA PIC";
|
||||
case 0x02: return "EISA PIC";
|
||||
case 0x10: return "I/O APIC interrupt controller (see below)";
|
||||
case 0x20: return "I/O(x) APIC interrupt controller";
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x01:
|
||||
switch (interface) {
|
||||
case 0x00: return "Generic 8237 DMA controller";
|
||||
case 0x01: return "ISA DMA controller";
|
||||
case 0x02: return "EISA DMA controller";
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x02:
|
||||
switch (interface) {
|
||||
case 0x00: return "Generic 8254 system timer";
|
||||
case 0x01: return "ISA system timer";
|
||||
case 0x02: return "EISA system timers (two timers)";
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x03:
|
||||
switch (interface) {
|
||||
case 0x00: return "Generic RTC controller";
|
||||
case 0x01: return "ISA RTC controller";
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x04: return "Generic PCI Hot-Plug controller";
|
||||
case 0x80: return "Other system peripheral";
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x09:
|
||||
*baseclass_name = "Input device";
|
||||
switch (subclass) {
|
||||
case 0x00: return "Keyboard controller";
|
||||
case 0x01: return "Digitizer (pen)";
|
||||
case 0x02: return "Mouse controller";
|
||||
case 0x03: return "Scanner controller";
|
||||
case 0x04: return "Gameport controller";
|
||||
case 0x80: return "Other input controller";
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x0a:
|
||||
*baseclass_name = "Docking station";
|
||||
switch (subclass) {
|
||||
case 0x00: return "Generic docking station";
|
||||
case 0x80: return "Other type of docking station";
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x0b:
|
||||
*baseclass_name = "Processor";
|
||||
switch (subclass) {
|
||||
case 0x00: return "386";
|
||||
case 0x01: return "486";
|
||||
case 0x02: return "Pentium";
|
||||
case 0x10: return "Alpha";
|
||||
case 0x20: return "PowerPC";
|
||||
case 0x30: return "MIPS";
|
||||
case 0x40: return "Co-processor";
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x0c:
|
||||
*baseclass_name = "Serial bus controller";
|
||||
switch (subclass) {
|
||||
case 0x00:
|
||||
switch (interface) {
|
||||
case 0x00: return "IEEE 1394 (FireWire)";
|
||||
case 0x01: return "IEEE 1394 following the 1394 OpenHCI specification";
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x01: return "ACCESS.bus";
|
||||
case 0x02: return "SSA";
|
||||
|
||||
case 0x03:
|
||||
switch (interface) {
|
||||
case 0x00: return "Universal Serial Bus (USB) following the Universal Host Controller Specification";
|
||||
case 0x10: return "Universal Serial Bus (USB) following the Open Host Controller Specification";
|
||||
case 0x20: return "USB2 host controller following the Intel Enhanced Host Controller Interface";
|
||||
case 0x80: return "Universal Serial Bus with no specific programming interface";
|
||||
case 0xfe: return "USB device";
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x04: return "Fibre Channel";
|
||||
case 0x05: return "SMBus";
|
||||
case 0x06: return "InfiniBand";
|
||||
|
||||
case 0x07:
|
||||
switch (interface) {
|
||||
case 0x00: return "IPMI SMIC Interface";
|
||||
case 0x01: return "IPMI Kybd Controller Style Interface";
|
||||
case 0x02: return "IPMI Block Transfer Interface";
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x08: return "SERCOS Interface Standard (IEC 61491)";
|
||||
case 0x09: return "CANbus";
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x0d:
|
||||
*baseclass_name = "Wireless controller";
|
||||
switch (subclass) {
|
||||
case 0x00: return "iRDA compatible controller";
|
||||
case 0x01: return "Consumer IR controller";
|
||||
case 0x10: return "RF controller";
|
||||
case 0x11: return "Bluetooth";
|
||||
case 0x12: return "Broadband";
|
||||
case 0x20: return "Ethernet (802.11a)";
|
||||
case 0x21: return "Ethernet (802.11b)";
|
||||
case 0x80: return "Other type of wireless controller";
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x0e:
|
||||
*baseclass_name = "Intelligent I/O controller";
|
||||
switch (subclass) {
|
||||
case 0x00:
|
||||
switch (interface) {
|
||||
case 0x00: return "Message FIFO at offset 040h";
|
||||
default: return "Intelligent I/O (I2O) Architecture Specification 1.0";
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x0f:
|
||||
*baseclass_name = "Satellite communication controller";
|
||||
switch (subclass) {
|
||||
case 0x01: return "TV";
|
||||
case 0x02: return "Audio";
|
||||
case 0x03: return "Voice";
|
||||
case 0x04: return "Data";
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x10:
|
||||
*baseclass_name = "Encryption/Decryption controller";
|
||||
switch (subclass) {
|
||||
case 0x00: return "Network and computing en/decryption";
|
||||
case 0x10: return "Entertainment en/decryption";
|
||||
case 0x80: return "Other en/decryption";
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x11:
|
||||
*baseclass_name = "Data acquisition and signal processing controller";
|
||||
switch (subclass) {
|
||||
case 0x00: return "DPIO modules";
|
||||
case 0x01: return "Performance counters";
|
||||
case 0x10: return "Communications synchronization plus time and frequency test/measurement";
|
||||
case 0x20: return "Management card";
|
||||
case 0x80: return "Other data acquisition/signal processing controllers";
|
||||
}
|
||||
break;
|
||||
|
||||
case 0xff:
|
||||
*baseclass_name = "Other device";
|
||||
break;
|
||||
|
||||
default:
|
||||
snprintf(temp_baseclass_number, sizeof temp_baseclass_number, "0x%04x", baseclass);
|
||||
*baseclass_name = temp_baseclass_number;
|
||||
}
|
||||
snprintf(temp_subclass_number, sizeof temp_subclass_number, "0x%04x", subclass);
|
||||
return temp_subclass_number;
|
||||
}
|
||||
|
||||
const char *x86_pci_device_id_to_string(unsigned vendor_id, unsigned device_id, const char **vendor_name)
|
||||
{
|
||||
static char temp_vendor_name[7], temp_device_name[7];
|
||||
|
||||
const char *vendor_name_null;
|
||||
if (!vendor_name) {
|
||||
vendor_name = &vendor_name_null;
|
||||
}
|
||||
|
||||
// http://pci-ids.ucw.cz/read/PC/
|
||||
// http://www.pcidatabase.com/
|
||||
switch (vendor_id) {
|
||||
case 0x1013:
|
||||
*vendor_name = "Cirrus Logic";
|
||||
switch (device_id) {
|
||||
case 0x00b8: return "GD 5446";
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x8086:
|
||||
*vendor_name = "Intel Corporation";
|
||||
switch (device_id) {
|
||||
case 0x100e: return "82540EM Gigabit Ethernet Controller";
|
||||
case 0x1237: return "440FX - 82441FX PMC [Natoma]";
|
||||
case 0x2415: return "82801AA AC'97 Audio Controller";
|
||||
case 0x2668: return "82801FB/FBM/FR/FW/FRW (ICH6 Family) HDA Controller";
|
||||
case 0x7000: return "82371SB PIIX3 ISA [Natoma/Triton II]";
|
||||
case 0x7010: return "82371SB PIIX3 IDE [Natoma/Triton II]";
|
||||
case 0x7020: return "82371SB PIIX3 USB [Natoma/Triton II]";
|
||||
case 0x7113: return "82371AB/EB/MB PIIX4 ACPI";
|
||||
|
||||
/* intel galileo */
|
||||
/* FIXME: find out the actual names ... */
|
||||
case 0x0958: return "Host Bridge";
|
||||
case 0x095E: return "Legacy Bridge";
|
||||
case 0x11C3: return "PCIe* Root Port 0";
|
||||
case 0x11C4: return "PCIe* Root Port 1";
|
||||
case 0x08A7: return "SDIO / eMMC Controller";
|
||||
case 0x0936: return "HS-UART";
|
||||
case 0x0939: return "USB 2.0 Device";
|
||||
case 0x093A: return "USB OHCI Host Controller";
|
||||
case 0x0937: return "10/100 Ethernet";
|
||||
case 0x0935: return "SPI Controller";
|
||||
case 0x0934: return "I2C* Controller and GPIO Controller";
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
snprintf(temp_vendor_name, sizeof temp_vendor_name, "0x%04x", vendor_id);
|
||||
*vendor_name = temp_vendor_name;
|
||||
}
|
||||
snprintf(temp_device_name, sizeof temp_device_name, "0x%04x", device_id);
|
||||
return temp_device_name;
|
||||
}
|
192
cpu/x86/x86_pic.c
Normal file
192
cpu/x86/x86_pic.c
Normal file
@ -0,0 +1,192 @@
|
||||
/*
|
||||
* Copyright (C) 2014 René Kijewski <rene.kijewski@fu-berlin.de>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ingroup x86-irq
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Configuration and interrupt handling for the Programmable Interrupt Controller (PIC).
|
||||
*
|
||||
* @author René Kijewski <rene.kijewski@fu-berlin.de>
|
||||
*
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include "x86_interrupts.h"
|
||||
#include "x86_pic.h"
|
||||
#include "x86_ports.h"
|
||||
#include "cpu.h"
|
||||
#include "irq.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
static x86_irq_handler_t x86_registered_irqs[X86_IRQ_COUNT];
|
||||
|
||||
static const char *const IRQ_NAMES[X86_IRQ_COUNT] __attribute__((unused)) = {
|
||||
"PIT",
|
||||
"KBC 1",
|
||||
NULL,
|
||||
"COM 2/4",
|
||||
"COM 1/3",
|
||||
"LPT 2",
|
||||
"FLOPPY",
|
||||
"LPT 1",
|
||||
"RTC",
|
||||
"#9",
|
||||
"ATA 4",
|
||||
"ATA 3",
|
||||
"KBC 2",
|
||||
NULL,
|
||||
"ATA 1",
|
||||
"ATA 2",
|
||||
};
|
||||
|
||||
static bool spurious_irq(uint8_t irq_num)
|
||||
{
|
||||
if (irq_num < 8) {
|
||||
outb(PIC_MASTER + PIC_COMMAND, PIC_READ_ISR);
|
||||
return (inb(PIC_MASTER + PIC_COMMAND) & (1 << irq_num)) == 0;
|
||||
}
|
||||
|
||||
return false; // TODO: does not work
|
||||
irq_num -= 8;
|
||||
outb(PIC_SLAVE + PIC_COMMAND, PIC_READ_ISR);
|
||||
if (inb(PIC_SLAVE + PIC_COMMAND) & (1 << irq_num)) {
|
||||
outb(PIC_MASTER + PIC_COMMAND, PIC_EOI);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void pic_interrupt_entry(uint8_t intr_num, struct x86_pushad *orig_ctx, unsigned long error_code)
|
||||
{
|
||||
(void) error_code;
|
||||
(void) orig_ctx;
|
||||
|
||||
uint8_t irq_num = intr_num - PIC_MASTER_INTERRUPT_BASE;
|
||||
if (spurious_irq(irq_num)) {
|
||||
return;
|
||||
}
|
||||
|
||||
x86_irq_handler_t handler = x86_registered_irqs[irq_num];
|
||||
if (handler) {
|
||||
handler(irq_num);
|
||||
}
|
||||
|
||||
outb(PIC_MASTER + PIC_COMMAND, PIC_EOI);
|
||||
if (irq_num > 7) {
|
||||
outb(PIC_SLAVE + PIC_COMMAND, PIC_EOI);
|
||||
}
|
||||
}
|
||||
|
||||
static void pic_remap(void)
|
||||
{
|
||||
/* tell both PICs we want to initialize them */
|
||||
outb(PIC_MASTER + PIC_COMMAND, PIC_ICW1_INIT + PIC_ICW1_ICW4);
|
||||
io_wait();
|
||||
outb(PIC_SLAVE + PIC_COMMAND, PIC_ICW1_INIT + PIC_ICW1_ICW4);
|
||||
io_wait();
|
||||
|
||||
/* which ISR should be called if an IRQ occurs */
|
||||
outb(PIC_MASTER + PIC_DATA, PIC_MASTER_INTERRUPT_BASE);
|
||||
io_wait();
|
||||
outb(PIC_SLAVE + PIC_DATA, PIC_SLAVE_INTERRUPT_BASE);
|
||||
io_wait();
|
||||
|
||||
/* IRQ of the master the slave talks to */
|
||||
outb(PIC_MASTER + PIC_DATA, 1 << PIC_NUM_SLAVE);
|
||||
io_wait();
|
||||
outb(PIC_SLAVE + PIC_DATA, PIC_NUM_SLAVE);
|
||||
io_wait();
|
||||
|
||||
/* use PC mode */
|
||||
outb(PIC_MASTER + PIC_DATA, PIC_ICW4_8086);
|
||||
io_wait();
|
||||
outb(PIC_SLAVE + PIC_DATA, PIC_ICW4_8086);
|
||||
}
|
||||
|
||||
static void pic_register_handler(void)
|
||||
{
|
||||
for (unsigned i = 0; i < X86_IRQ_COUNT; ++i) {
|
||||
x86_interrupt_handler_set(PIC_MASTER_INTERRUPT_BASE + i, &pic_interrupt_entry);
|
||||
}
|
||||
}
|
||||
|
||||
void x86_pic_set_enabled_irqs(uint16_t mask)
|
||||
{
|
||||
unsigned old_status = disableIRQ();
|
||||
|
||||
mask |= PIC_MASK_SLAVE;
|
||||
mask &= ~PIC_MASK_FPU;
|
||||
outb(PIC_MASTER + PIC_IMR, ~(uint8_t) mask);
|
||||
io_wait();
|
||||
outb(PIC_SLAVE + PIC_IMR, ~(uint8_t) (mask >> 8));
|
||||
|
||||
restoreIRQ(old_status);
|
||||
}
|
||||
|
||||
void x86_pic_enable_irq(unsigned num)
|
||||
{
|
||||
unsigned old_status = disableIRQ();
|
||||
|
||||
uint16_t port;
|
||||
if (num < 8) {
|
||||
port = PIC_MASTER;
|
||||
}
|
||||
else {
|
||||
num -= 8;
|
||||
port = PIC_SLAVE;
|
||||
}
|
||||
uint8_t cur = inb(port + PIC_IMR);
|
||||
outb(port + PIC_IMR, cur & ~(1 << num));
|
||||
|
||||
restoreIRQ(old_status);
|
||||
}
|
||||
|
||||
void x86_pic_disable_irq(unsigned num)
|
||||
{
|
||||
unsigned old_status = disableIRQ();
|
||||
|
||||
uint16_t port;
|
||||
if (num < 8) {
|
||||
port = PIC_MASTER;
|
||||
}
|
||||
else {
|
||||
num -= 8;
|
||||
port = PIC_SLAVE;
|
||||
}
|
||||
uint8_t cur = inb(port + PIC_IMR);
|
||||
outb(port + PIC_IMR, cur | (1 << num));
|
||||
|
||||
restoreIRQ(old_status);
|
||||
}
|
||||
|
||||
void x86_init_pic(void)
|
||||
{
|
||||
pic_register_handler();
|
||||
pic_remap();
|
||||
x86_pic_set_enabled_irqs(0);
|
||||
|
||||
puts("PIC initialized");
|
||||
}
|
||||
|
||||
void x86_pic_set_handler(unsigned irq, x86_irq_handler_t handler)
|
||||
{
|
||||
x86_registered_irqs[irq] = handler;
|
||||
}
|
72
cpu/x86/x86_pit.c
Normal file
72
cpu/x86/x86_pit.c
Normal file
@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright (C) 2014 René Kijewski <rene.kijewski@fu-berlin.de>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ingroup x86-irq
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Configuration and interrupt handling for the Programmable Interval Timer (PIT).
|
||||
*
|
||||
* @author René Kijewski <rene.kijewski@fu-berlin.de>
|
||||
*
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include "x86_interrupts.h"
|
||||
#include "x86_pit.h"
|
||||
#include "x86_ports.h"
|
||||
#include "cpu.h"
|
||||
#include "irq.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
void x86_init_pit(void)
|
||||
{
|
||||
puts("PIT initialized");
|
||||
}
|
||||
|
||||
uint16_t x86_pit_read(unsigned channel)
|
||||
{
|
||||
unsigned old_flags = disableIRQ();
|
||||
outb(PIT_COMMAND_PORT, (channel - 1) << 6 | PIT_ACCESS_MODE_LATCH_COUNT);
|
||||
uint16_t lohi = inb(PIT_CHANNEL_0_PORT + channel - 1);
|
||||
lohi += inb(PIT_CHANNEL_0_PORT + channel - 1) << 8;
|
||||
restoreIRQ(old_flags);
|
||||
return lohi;
|
||||
}
|
||||
|
||||
void x86_pit_set2(unsigned channel, unsigned mode, uint16_t max)
|
||||
{
|
||||
unsigned old_flags = disableIRQ();
|
||||
outb(PIT_COMMAND_PORT, ((channel - 1) << 6) | mode | PIT_ACCESS_MODE_LO_HI);
|
||||
outb(PIT_CHANNEL_0_PORT + channel - 1, max && 0xff);
|
||||
outb(PIT_CHANNEL_0_PORT + channel - 1, max >> 8);
|
||||
restoreIRQ(old_flags);
|
||||
}
|
||||
|
||||
bool x86_pit_set(unsigned channel, unsigned mode, unsigned hz)
|
||||
{
|
||||
if (PIT_MIN_HZ > hz || hz > PIT_MAX_HZ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint16_t max = PIT_MAX_HZ / hz;
|
||||
x86_pit_set2(channel, mode, max);
|
||||
return true;
|
||||
}
|
123
cpu/x86/x86_reboot.c
Normal file
123
cpu/x86/x86_reboot.c
Normal file
@ -0,0 +1,123 @@
|
||||
/*
|
||||
* Copyright (C) 2014 René Kijewski <rene.kijewski@fu-berlin.de>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ingroup x86
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Configurable reboot handler for x86.
|
||||
*
|
||||
* @author René Kijewski <rene.kijewski@fu-berlin.de>
|
||||
*
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include "x86_interrupts.h"
|
||||
#include "x86_ports.h"
|
||||
#include "x86_reboot.h"
|
||||
|
||||
#define KBC_DATA (0x60)
|
||||
#define KBC_STATUS (0x64)
|
||||
#define KBC_COMMAND (0x64)
|
||||
|
||||
#define KBC_OUTPUT_FULL (1u << 0)
|
||||
#define KBC_INPUT_FULL (1u << 1)
|
||||
|
||||
#define KBC_RESET ((uint8_t) ~(1u << 0))
|
||||
|
||||
static const struct idtr_t EMPTY_IDT = {
|
||||
.limit = 0,
|
||||
.base = NULL,
|
||||
};
|
||||
|
||||
void x86_load_empty_idt(void)
|
||||
{
|
||||
asm volatile ("lidt %0" :: "m"(EMPTY_IDT));
|
||||
}
|
||||
|
||||
static bool fail_violently;
|
||||
|
||||
void NORETURN x86_kbc_reboot(void)
|
||||
{
|
||||
/* First load an empty IDT. Any interrupt will cause a tripple fault. */
|
||||
x86_load_empty_idt();
|
||||
|
||||
while (1) {
|
||||
if (fail_violently) {
|
||||
asm volatile ("int3"); /* Cause a tripple fault. Won't return. */
|
||||
}
|
||||
fail_violently = true;
|
||||
|
||||
for (unsigned i = 0; i < 0x100; ++i) {
|
||||
for (unsigned j = 0; j < 0x10000; ++j) {
|
||||
uint8_t c = inb(KBC_STATUS);
|
||||
if (c & KBC_OUTPUT_FULL) {
|
||||
(void) inb(KBC_DATA);
|
||||
}
|
||||
else if (!(c & KBC_OUTPUT_FULL)) {
|
||||
outb(KBC_COMMAND, KBC_RESET);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
asm volatile ("int3"); /* Cause a tripple fault. Won't return. */
|
||||
}
|
||||
}
|
||||
|
||||
static x86_reboot_t reboot_fun;
|
||||
static bool reboot_twice;
|
||||
|
||||
int reboot_arch(int mode)
|
||||
{
|
||||
switch (mode) {
|
||||
case RB_AUTOBOOT:
|
||||
asm volatile ("cli");
|
||||
if (!reboot_twice) {
|
||||
reboot_twice = true;
|
||||
if (reboot_fun) {
|
||||
reboot_fun();
|
||||
}
|
||||
}
|
||||
x86_kbc_reboot();
|
||||
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
void x86_set_reboot_fun(x86_reboot_t fun)
|
||||
{
|
||||
reboot_fun = fun;
|
||||
}
|
||||
|
||||
static x86_shutdown_t shutdown_fun;
|
||||
|
||||
bool x86_shutdown(void)
|
||||
{
|
||||
if (!shutdown_fun) {
|
||||
return false;
|
||||
}
|
||||
return shutdown_fun();
|
||||
}
|
||||
|
||||
void x86_set_shutdown_fun(x86_shutdown_t fun)
|
||||
{
|
||||
shutdown_fun = fun;
|
||||
}
|
297
cpu/x86/x86_rtc.c
Normal file
297
cpu/x86/x86_rtc.c
Normal file
@ -0,0 +1,297 @@
|
||||
/*
|
||||
* Copyright (C) 2014 René Kijewski <rene.kijewski@fu-berlin.de>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ingroup x86-irq
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Reading and interrupt handling for the Real Time Clock (RTC).
|
||||
*
|
||||
* @author René Kijewski <rene.kijewski@fu-berlin.de>
|
||||
*
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include "x86_pic.h"
|
||||
#include "x86_rtc.h"
|
||||
#include "irq.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#define ENABLE_DEBUG (0)
|
||||
#include "debug.h"
|
||||
|
||||
static bool valid;
|
||||
|
||||
static int32_t alarm_msg_content, periodic_msg_content, update_msg_content;
|
||||
static unsigned alarm_pid = -1u, periodic_pid = -1u, update_pid = -1u;
|
||||
|
||||
static void alarm_callback_default(uint8_t reg_c)
|
||||
{
|
||||
if (alarm_pid != -1u) {
|
||||
msg_t m;
|
||||
m.type = reg_c | (RTC_REG_B_INT_ALARM << 8);
|
||||
m.content.value = alarm_msg_content;
|
||||
msg_send_int(&m, alarm_pid);
|
||||
}
|
||||
}
|
||||
|
||||
static void periodic_callback_default(uint8_t reg_c)
|
||||
{
|
||||
if (periodic_pid != -1u) {
|
||||
msg_t m;
|
||||
m.type = reg_c | (RTC_REG_B_INT_PERIODIC << 8);
|
||||
m.content.value = periodic_msg_content;
|
||||
msg_send_int(&m, periodic_pid);
|
||||
}
|
||||
}
|
||||
|
||||
static void update_callback_default(uint8_t reg_c)
|
||||
{
|
||||
if (update_pid != -1u) {
|
||||
msg_t m;
|
||||
m.type = reg_c | (RTC_REG_B_INT_UPDATE << 8);
|
||||
m.content.value = update_msg_content;
|
||||
msg_send_int(&m, update_pid);
|
||||
}
|
||||
}
|
||||
|
||||
static x86_rtc_callback_t alarm_callback = alarm_callback_default;
|
||||
static x86_rtc_callback_t periodic_callback = periodic_callback_default;
|
||||
static x86_rtc_callback_t update_callback = update_callback_default;
|
||||
|
||||
void x86_rtc_set_alarm_callback(x86_rtc_callback_t cb)
|
||||
{
|
||||
alarm_callback = cb ? cb : alarm_callback_default;
|
||||
}
|
||||
|
||||
void x86_rtc_set_periodic_callback(x86_rtc_callback_t cb)
|
||||
{
|
||||
periodic_callback = cb ? cb : periodic_callback_default;
|
||||
}
|
||||
|
||||
void x86_rtc_set_update_callback(x86_rtc_callback_t cb)
|
||||
{
|
||||
update_callback = cb ? cb : update_callback_default;
|
||||
}
|
||||
|
||||
static void rtc_irq_handler(uint8_t irq_num)
|
||||
{
|
||||
(void) irq_num; /* == PIC_NUM_RTC */
|
||||
|
||||
uint8_t c = x86_cmos_read(RTC_REG_C);
|
||||
DEBUG("RTC: c = 0x%02x, IRQ=%u, A=%u, P=%u, U=%u\n", c, c & RTC_REG_C_IRQ ? 1 : 0,
|
||||
c & RTC_REG_C_IRQ_ALARM ? 1 : 0,
|
||||
c & RTC_REG_C_IRQ_PERIODIC ? 1 : 0,
|
||||
c & RTC_REG_C_IRQ_UPDATE ? 1 : 0);
|
||||
if (!(c & RTC_REG_C_IRQ)) {
|
||||
return;
|
||||
}
|
||||
if (c & RTC_REG_C_IRQ_ALARM) {
|
||||
alarm_callback(c);
|
||||
}
|
||||
if (c & RTC_REG_C_IRQ_PERIODIC) {
|
||||
periodic_callback(c);
|
||||
}
|
||||
if (c & RTC_REG_C_IRQ_UPDATE) {
|
||||
update_callback(c);
|
||||
}
|
||||
}
|
||||
|
||||
void x86_init_rtc(void)
|
||||
{
|
||||
uint8_t d = x86_cmos_read(RTC_REG_D);
|
||||
valid = (d & RTC_REG_D_VALID) != 0;
|
||||
if (!valid) {
|
||||
puts("Warning: RTC does not work.");
|
||||
return;
|
||||
}
|
||||
|
||||
x86_cmos_write(RTC_REG_B, x86_cmos_read(RTC_REG_B) & ~RTC_REG_B_INT_MASK);
|
||||
rtc_irq_handler(0);
|
||||
x86_pic_set_handler(PIC_NUM_RTC, &rtc_irq_handler);
|
||||
x86_pic_enable_irq(PIC_NUM_RTC);
|
||||
|
||||
x86_rtc_data_t now;
|
||||
x86_rtc_read(&now);
|
||||
printf("RTC initialized [%02hhu:%02hhu:%02hhu, %04u-%02hhu-%02hhu]\n",
|
||||
now.hour, now.minute, now.second,
|
||||
now.century * 100 + now.year, now.month, now.day);
|
||||
|
||||
if (x86_cmos_read(RTC_REG_POST) & (RTC_REG_POST_POWER_LOSS | RTC_REG_POST_TIME_INVALID)) {
|
||||
puts("Warning: RTC time is invalid (power loss?)");
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool is_update_in_progress(void)
|
||||
{
|
||||
return (x86_cmos_read(RTC_REG_A) & RTC_REG_A_UPDATING) != 0;
|
||||
}
|
||||
|
||||
static uint8_t bcd2binary(uint8_t datum)
|
||||
{
|
||||
return (datum / 16) * 10 + (datum % 16);
|
||||
}
|
||||
|
||||
static uint8_t binary2bcd(uint8_t datum)
|
||||
{
|
||||
return (datum / 10) * 16 + (datum % 10);
|
||||
}
|
||||
|
||||
bool x86_rtc_read(x86_rtc_data_t *dest)
|
||||
{
|
||||
if (!valid) {
|
||||
return false;
|
||||
}
|
||||
|
||||
unsigned old_status = disableIRQ();
|
||||
|
||||
while (is_update_in_progress()) {
|
||||
asm volatile ("pause");
|
||||
}
|
||||
|
||||
uint8_t b = x86_cmos_read(RTC_REG_B);
|
||||
do {
|
||||
dest->second = x86_cmos_read(RTC_REG_SECOND);
|
||||
dest->minute = x86_cmos_read(RTC_REG_MINUTE);
|
||||
dest->hour = x86_cmos_read(RTC_REG_HOUR);
|
||||
dest->day = x86_cmos_read(RTC_REG_DAY);
|
||||
dest->month = x86_cmos_read(RTC_REG_MONTH);
|
||||
dest->year = x86_cmos_read(RTC_REG_YEAR);
|
||||
dest->century = bcd2binary(x86_cmos_read(RTC_REG_CENTURY));
|
||||
} while (dest->second != x86_cmos_read(RTC_REG_SECOND));
|
||||
if (dest->century == 0) {
|
||||
dest->century = 20; // safe guess
|
||||
}
|
||||
|
||||
if (!(b & RTC_REG_B_BIN)) {
|
||||
dest->second = bcd2binary(dest->second);
|
||||
dest->minute = bcd2binary(dest->minute);
|
||||
dest->hour = ((dest->hour & 0x0F) + (((dest->hour & 0x70) / 16) * 10)) | (dest->hour & 0x80);
|
||||
dest->day = bcd2binary(dest->day);
|
||||
dest->month = bcd2binary(dest->month);
|
||||
dest->year = bcd2binary(dest->year);
|
||||
}
|
||||
if (!(b & RTC_REG_B_24H) && (dest->hour & 0x80)) {
|
||||
dest->hour = ((dest->hour & 0x7F) + 12) % 24;
|
||||
}
|
||||
|
||||
restoreIRQ(old_status);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool x86_rtc_set_alarm(const x86_rtc_data_t *when, uint32_t msg_content, unsigned int target_pid, bool allow_replace)
|
||||
{
|
||||
if (!valid) {
|
||||
return false;
|
||||
}
|
||||
|
||||
unsigned old_status = disableIRQ();
|
||||
bool result;
|
||||
if (target_pid == -1u) {
|
||||
result = true;
|
||||
alarm_pid = -1u;
|
||||
|
||||
uint8_t b = x86_cmos_read(RTC_REG_B);
|
||||
x86_cmos_write(RTC_REG_B, b & ~RTC_REG_B_INT_ALARM);
|
||||
}
|
||||
else {
|
||||
result = allow_replace || alarm_pid == -1u;
|
||||
if (result) {
|
||||
alarm_msg_content = msg_content;
|
||||
alarm_pid = target_pid;
|
||||
|
||||
uint8_t b = x86_cmos_read(RTC_REG_B);
|
||||
if (b & RTC_REG_B_BIN) {
|
||||
x86_cmos_write(RTC_REG_ALARM_SECOND, when->second);
|
||||
x86_cmos_write(RTC_REG_ALARM_MINUTE, when->minute);
|
||||
x86_cmos_write(RTC_REG_ALARM_HOUR, when->hour);
|
||||
}
|
||||
else {
|
||||
x86_cmos_write(RTC_REG_ALARM_SECOND, binary2bcd(when->second));
|
||||
x86_cmos_write(RTC_REG_ALARM_MINUTE, binary2bcd(when->minute));
|
||||
x86_cmos_write(RTC_REG_ALARM_HOUR, binary2bcd(when->hour));
|
||||
}
|
||||
x86_cmos_write(RTC_REG_B, b | RTC_REG_B_INT_ALARM);
|
||||
}
|
||||
}
|
||||
rtc_irq_handler(0);
|
||||
restoreIRQ(old_status);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool x86_rtc_set_periodic(uint8_t hz, uint32_t msg_content, unsigned int target_pid, bool allow_replace)
|
||||
{
|
||||
if (!valid) {
|
||||
return false;
|
||||
}
|
||||
|
||||
unsigned old_status = disableIRQ();
|
||||
bool result;
|
||||
if (target_pid == -1u || hz == RTC_REG_A_HZ_OFF) {
|
||||
result = true;
|
||||
periodic_pid = -1u;
|
||||
|
||||
uint8_t old_divider = x86_cmos_read(RTC_REG_A) & ~RTC_REG_A_HZ_MASK;
|
||||
x86_cmos_write(RTC_REG_A, old_divider | RTC_REG_A_HZ_OFF);
|
||||
x86_cmos_write(RTC_REG_B, x86_cmos_read(RTC_REG_B) & ~RTC_REG_B_INT_PERIODIC);
|
||||
}
|
||||
else {
|
||||
result = allow_replace || periodic_pid == -1u;
|
||||
if (result) {
|
||||
periodic_msg_content = msg_content;
|
||||
periodic_pid = target_pid;
|
||||
|
||||
uint8_t old_divider = x86_cmos_read(RTC_REG_A) & ~RTC_REG_A_HZ_MASK;
|
||||
x86_cmos_write(RTC_REG_A, old_divider | hz);
|
||||
x86_cmos_write(RTC_REG_B, x86_cmos_read(RTC_REG_B) | RTC_REG_B_INT_PERIODIC);
|
||||
}
|
||||
}
|
||||
rtc_irq_handler(0);
|
||||
restoreIRQ(old_status);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool x86_rtc_set_update(uint32_t msg_content, unsigned int target_pid, bool allow_replace)
|
||||
{
|
||||
if (!valid) {
|
||||
return false;
|
||||
}
|
||||
|
||||
unsigned old_status = disableIRQ();
|
||||
bool result;
|
||||
if (target_pid == -1u) {
|
||||
result = true;
|
||||
update_pid = -1u;
|
||||
|
||||
x86_cmos_write(RTC_REG_B, x86_cmos_read(RTC_REG_B) & ~RTC_REG_B_INT_UPDATE);
|
||||
}
|
||||
else {
|
||||
result = allow_replace || update_pid == -1u;
|
||||
if (result) {
|
||||
update_msg_content = msg_content;
|
||||
update_pid = target_pid;
|
||||
|
||||
x86_cmos_write(RTC_REG_B, x86_cmos_read(RTC_REG_B) | RTC_REG_B_INT_UPDATE);
|
||||
}
|
||||
}
|
||||
rtc_irq_handler(0);
|
||||
restoreIRQ(old_status);
|
||||
return result;
|
||||
}
|
75
cpu/x86/x86_startup.c
Normal file
75
cpu/x86/x86_startup.c
Normal file
@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright (C) 2014 René Kijewski <rene.kijewski@fu-berlin.de>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ingroup x86
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief The common startup code for the x86 port.
|
||||
*
|
||||
* @author René Kijewski <rene.kijewski@fu-berlin.de>
|
||||
*
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include "x86_hwtimer.h"
|
||||
#include "x86_interrupts.h"
|
||||
#include "x86_memory.h"
|
||||
#include "x86_pci.h"
|
||||
#include "x86_pic.h"
|
||||
#include "x86_pit.h"
|
||||
#include "x86_rtc.h"
|
||||
#include "x86_threading.h"
|
||||
#include "x86_uart.h"
|
||||
|
||||
#include <cpu.h>
|
||||
#include <kernel_internal.h>
|
||||
#include <tlsf-malloc.h>
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
/* Must be <= 0x1000 because otherwise x86_map_physical_pages() might get a page out of this pool.
|
||||
* Because static memory has the PT_G flag, flushing the TLB would cause a stale PT
|
||||
* after calling add_pages_to_pool() in x86_map_physical_pages().
|
||||
*/
|
||||
static char early_malloc_pool[0x1000] __attribute__((aligned(4)));
|
||||
|
||||
void x86_startup(void)
|
||||
{
|
||||
tlsf_add_pool(early_malloc_pool, sizeof early_malloc_pool);
|
||||
|
||||
x86_early_init_uart();
|
||||
x86_init_threading();
|
||||
x86_init_interrupts();
|
||||
x86_init_pic();
|
||||
x86_init_uart();
|
||||
x86_init_memory();
|
||||
x86_init_rtc();
|
||||
x86_init_pit();
|
||||
x86_init_hwtimer();
|
||||
x86_init_pci();
|
||||
puts("RIOT x86 hardware initialization complete.");
|
||||
|
||||
x86_init_board();
|
||||
puts("RIOT board initialization complete.");
|
||||
|
||||
kernel_init(); /* should not return */
|
||||
puts("kernel_init returned");
|
||||
x86_hlt();
|
||||
}
|
223
cpu/x86/x86_threading.c
Normal file
223
cpu/x86/x86_threading.c
Normal file
@ -0,0 +1,223 @@
|
||||
/*
|
||||
* Copyright (C) 2014 René Kijewski <rene.kijewski@fu-berlin.de>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ingroup x86-multithreading
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Multi-thread management for x86.
|
||||
*
|
||||
* @author René Kijewski <rene.kijewski@fu-berlin.de>
|
||||
*
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include "x86_interrupts.h"
|
||||
#include "x86_reboot.h"
|
||||
#include "x86_registers.h"
|
||||
#include "x86_threading.h"
|
||||
#include "cpu.h"
|
||||
#include "irq.h"
|
||||
#include "kernel_internal.h"
|
||||
#include "ucontext.h"
|
||||
#include "sched.h"
|
||||
#include "stdbool.h"
|
||||
#include "thread.h"
|
||||
|
||||
#define ENABLE_DEBUG (0)
|
||||
#include "debug.h"
|
||||
|
||||
static char isr_stack[SIGSTKSZ];
|
||||
static ucontext_t isr_context;
|
||||
|
||||
static char end_stack[SIGSTKSZ];
|
||||
static ucontext_t end_context;
|
||||
|
||||
bool x86_in_isr = true;
|
||||
|
||||
static long fpu_owner = -1;
|
||||
|
||||
//static ucontext_t *cur_ctx, *isr_ctx;
|
||||
|
||||
int inISR(void)
|
||||
{
|
||||
return x86_in_isr;
|
||||
}
|
||||
|
||||
unsigned disableIRQ(void)
|
||||
{
|
||||
unsigned long eflags = x86_pushf_cli();
|
||||
return (eflags & X86_IF) != 0;
|
||||
}
|
||||
|
||||
unsigned enableIRQ(void)
|
||||
{
|
||||
unsigned long eflags;
|
||||
asm volatile ("pushf; pop %0; sti" : "=g"(eflags));
|
||||
return (eflags & X86_IF) != 0;
|
||||
}
|
||||
|
||||
void restoreIRQ(unsigned state)
|
||||
{
|
||||
if (state) {
|
||||
asm volatile ("sti");
|
||||
}
|
||||
else {
|
||||
asm volatile ("cli");
|
||||
}
|
||||
}
|
||||
|
||||
int inISR(void);
|
||||
|
||||
static void __attribute__((noreturn)) isr_thread_yield(void)
|
||||
{
|
||||
sched_run();
|
||||
ucontext_t *ctx = (ucontext_t *) sched_active_thread->sp;
|
||||
DEBUG("isr_thread_yield(): switching to (%s, %p)\n\n", sched_active_thread->name, ctx->uc_context.ip);
|
||||
|
||||
uint32_t cr0 = cr0_read();
|
||||
cr0 |= CR0_TS;
|
||||
cr0_write(cr0);
|
||||
|
||||
x86_in_isr = false;
|
||||
setcontext(ctx);
|
||||
}
|
||||
|
||||
void thread_yield(void)
|
||||
{
|
||||
if (x86_in_isr) {
|
||||
isr_thread_yield();
|
||||
}
|
||||
|
||||
unsigned old_intr = disableIRQ();
|
||||
|
||||
x86_in_isr = true;
|
||||
isr_context.uc_stack.ss_sp = isr_stack;
|
||||
isr_context.uc_stack.ss_size = sizeof isr_stack;
|
||||
makecontext(&isr_context, isr_thread_yield, 0);
|
||||
swapcontext((ucontext_t *) sched_active_thread->sp, &isr_context);
|
||||
|
||||
restoreIRQ(old_intr);
|
||||
}
|
||||
|
||||
void isr_cpu_switch_context_exit(void)
|
||||
{
|
||||
DEBUG("XXX: cpu_switch_context_exit(), num_tasks = %d\n", sched_num_threads);
|
||||
|
||||
if (sched_num_threads <= 2) {
|
||||
/* there is always "idle" and "x86-hwtimer" */
|
||||
DEBUG("cpu_switch_context_exit(): last task has ended. Shutting down.\n");
|
||||
x86_shutdown();
|
||||
}
|
||||
|
||||
if ((sched_context_switch_request == 1) || (sched_active_thread == NULL)) {
|
||||
sched_run();
|
||||
}
|
||||
|
||||
ucontext_t *ctx = (ucontext_t *)(sched_active_thread->sp);
|
||||
DEBUG("XXX: cpu_switch_context_exit(): calling setcontext(%s, %p)\n\n", sched_active_thread->name, ctx->uc_context.ip);
|
||||
|
||||
x86_in_isr = false;
|
||||
|
||||
setcontext(ctx);
|
||||
}
|
||||
|
||||
void cpu_switch_context_exit(void)
|
||||
{
|
||||
dINT();
|
||||
|
||||
if (!x86_in_isr) {
|
||||
x86_in_isr = true;
|
||||
isr_context.uc_stack.ss_sp = isr_stack;
|
||||
isr_context.uc_stack.ss_size = sizeof isr_stack;
|
||||
makecontext(&isr_context, isr_cpu_switch_context_exit, 0);
|
||||
setcontext(&isr_context);
|
||||
}
|
||||
else {
|
||||
isr_cpu_switch_context_exit();
|
||||
}
|
||||
__builtin_unreachable();
|
||||
}
|
||||
|
||||
char *thread_stack_init(void (*task_func)(void), void *stack_start, int stacksize)
|
||||
{
|
||||
DEBUG("thread_stack_init()\n");
|
||||
|
||||
unsigned int *stk = stack_start;
|
||||
|
||||
ucontext_t *p = (ucontext_t *)(stk + ((stacksize - sizeof(ucontext_t)) / sizeof(void *)));
|
||||
stacksize -= sizeof(ucontext_t);
|
||||
|
||||
getcontext(p);
|
||||
p->uc_stack.ss_sp = stk;
|
||||
p->uc_stack.ss_size = stacksize;
|
||||
p->uc_link = &end_context;
|
||||
p->uc_context.flags |= X86_IF;
|
||||
makecontext(p, task_func, 0);
|
||||
|
||||
return (char *) p;
|
||||
}
|
||||
|
||||
static void fpu_used_interrupt(uint8_t intr_num, struct x86_pushad *orig_ctx, unsigned long error_code)
|
||||
{
|
||||
static volatile struct x86_fxsave fpu_data;
|
||||
|
||||
(void) intr_num;
|
||||
(void) orig_ctx;
|
||||
(void) error_code;
|
||||
|
||||
asm volatile ("clts"); /* clear task switch flag */
|
||||
|
||||
if (fpu_owner == sched_active_pid) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (fpu_owner != -1) {
|
||||
ucontext_t *ctx_owner = (ucontext_t *) sched_threads[fpu_owner]->sp;
|
||||
asm volatile ("fxsave (%0)" :: "r"(&fpu_data));
|
||||
ctx_owner->__fxsave = fpu_data;
|
||||
}
|
||||
|
||||
ucontext_t *ctx_active = (ucontext_t *) sched_active_thread->sp;
|
||||
fpu_data = ctx_active->__fxsave;
|
||||
asm volatile ("fxrstor (%0)" :: "r"(&fpu_data));
|
||||
|
||||
fpu_owner = sched_active_pid;
|
||||
}
|
||||
|
||||
static void x86_thread_exit(void)
|
||||
{
|
||||
dINT();
|
||||
if (fpu_owner == sched_active_pid) {
|
||||
fpu_owner = -1;
|
||||
}
|
||||
sched_task_exit();
|
||||
}
|
||||
|
||||
void x86_init_threading(void)
|
||||
{
|
||||
getcontext(&end_context);
|
||||
end_context.uc_stack.ss_sp = end_stack;
|
||||
end_context.uc_stack.ss_size = sizeof end_stack;
|
||||
makecontext(&end_context, x86_thread_exit, 0);
|
||||
|
||||
x86_interrupt_handler_set(X86_INT_NM, fpu_used_interrupt);
|
||||
|
||||
DEBUG("Threading initialized\n");
|
||||
}
|
136
cpu/x86/x86_uart.c
Normal file
136
cpu/x86/x86_uart.c
Normal file
@ -0,0 +1,136 @@
|
||||
/*
|
||||
* Copyright (C) 2014 René Kijewski <rene.kijewski@fu-berlin.de>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ingroup x86-irq
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief UART reading and writing.
|
||||
*
|
||||
* @author René Kijewski <rene.kijewski@fu-berlin.de>
|
||||
*
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include "board.h"
|
||||
#include "x86_ports.h"
|
||||
#include "x86_uart.h"
|
||||
|
||||
#include <cpu.h>
|
||||
#include <board_uart0.h>
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
void x86_early_init_uart(void)
|
||||
{
|
||||
outb(UART_PORT + IER, 0x00); /* Disable all interrupts */
|
||||
|
||||
outb(UART_PORT + LCR, LCR_DLAB);
|
||||
outb(UART_PORT + DLL, BAUD_LL); /* Set divisor (lo byte) */
|
||||
outb(UART_PORT + DLH, BAUD_HL); /* (hi byte) */
|
||||
outb(UART_PORT + LCR, LCR_WORD_BITS_8 | LCR_PAR_NONE | LCR_STOP_BITS_1);
|
||||
outb(UART_PORT + FCR, FCR_ENABLE | FCR_CLR_RECV | FCR_CLR_SEND | FCR_TRIGGER_16);
|
||||
outb(UART_PORT + MCR, MCR_DSR | MCR_RTS | MCR_AUX2);
|
||||
}
|
||||
|
||||
static bool is_output_empty(void)
|
||||
{
|
||||
uint8_t data = inb(UART_PORT + LSR);
|
||||
return (data & 0x20) != 0;
|
||||
}
|
||||
|
||||
static bool is_input_empty(void)
|
||||
{
|
||||
uint8_t data = inb(UART_PORT + LSR);
|
||||
return (data & 0x01) == 0;
|
||||
}
|
||||
|
||||
ssize_t x86_uart_write(const char *buf, size_t len)
|
||||
{
|
||||
if (!UART_PORT) {
|
||||
return -1;
|
||||
}
|
||||
(void) buf;
|
||||
|
||||
size_t written = 0;
|
||||
while (written < len) {
|
||||
while (!is_output_empty()) {
|
||||
asm volatile ("pause");
|
||||
}
|
||||
outb(UART_PORT + THR, buf[written]);
|
||||
++written;
|
||||
}
|
||||
return written;
|
||||
}
|
||||
|
||||
ssize_t x86_uart_read(char *buf, size_t len)
|
||||
{
|
||||
if (!UART_PORT) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
size_t read = 0;
|
||||
while (read < len) {
|
||||
while (!is_input_empty()) {
|
||||
asm volatile ("pause");
|
||||
}
|
||||
buf[read] = inb(UART_PORT + RBR);
|
||||
++read;
|
||||
}
|
||||
return read;
|
||||
}
|
||||
|
||||
#ifdef MODULE_UART0
|
||||
static void com_handler(uint8_t irq_num)
|
||||
{
|
||||
(void) irq_num; /* == UART_IRQ */
|
||||
switch (inb(UART_PORT + IIR) & IIR_INT_MASK) {
|
||||
case IIR_INT_BR: {
|
||||
while (is_input_empty()) {
|
||||
asm volatile ("pause");
|
||||
}
|
||||
do {
|
||||
uint8_t c = inb(UART_PORT + RBR);
|
||||
uart0_handle_incoming(c);
|
||||
} while (!is_input_empty());
|
||||
uart0_notify_thread();
|
||||
break;
|
||||
}
|
||||
case IIR_INT_TH:
|
||||
case IIR_INT_LS:
|
||||
case IIR_INT_TO:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif /* ifdef MODULE_UART0 */
|
||||
|
||||
void x86_init_uart(void)
|
||||
{
|
||||
#ifdef MODULE_UART0
|
||||
x86_pic_set_handler(UART_IRQ, com_handler);
|
||||
x86_pic_enable_irq(UART_IRQ);
|
||||
|
||||
outb(UART_PORT + IER, IER_RECV); /* Enable receive interrupt */
|
||||
|
||||
puts("UART initialized");
|
||||
#endif
|
||||
}
|
150
cpu/x86/x86_ucontext.c
Normal file
150
cpu/x86/x86_ucontext.c
Normal file
@ -0,0 +1,150 @@
|
||||
/*
|
||||
* Copyright (C) 2014 René Kijewski <rene.kijewski@fu-berlin.de>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ingroup x86-multithreading
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Coroutine helper functions.
|
||||
*
|
||||
* @author René Kijewski <rene.kijewski@fu-berlin.de>
|
||||
*
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include <ucontext.h>
|
||||
|
||||
#include <stdarg.h>
|
||||
|
||||
int __attribute__((optimize("omit-frame-pointer"), no_instrument_function)) getcontext(ucontext_t *ucp)
|
||||
{
|
||||
asm volatile ("pushf\n" :: "a"(ucp));
|
||||
asm volatile ("pop 4*2(%eax)\n");
|
||||
|
||||
asm volatile ("mov %eax, 4*3(%eax)\n");
|
||||
asm volatile ("mov %ecx, 4*4(%eax)\n");
|
||||
asm volatile ("mov %edx, 4*5(%eax)\n");
|
||||
asm volatile ("mov %ebx, 4*6(%eax)\n");
|
||||
/* asm volatile ("mov %esp, 4*7(%eax)\n"); omitted */
|
||||
asm volatile ("mov %ebp, 4*8(%eax)\n");
|
||||
asm volatile ("mov %esi, 4*9(%eax)\n");
|
||||
asm volatile ("mov %edi, 4*10(%eax)\n");
|
||||
|
||||
asm volatile ("lea 4(%esp), %edx\n");
|
||||
asm volatile ("mov %edx, 4*0(%eax)\n");
|
||||
asm volatile ("xor %edx, %edx\n");
|
||||
asm volatile ("mov %edx, 4*1(%eax)\n");
|
||||
|
||||
asm volatile ("mov (%esp), %edx\n");
|
||||
asm volatile ("mov %edx, 4*11(%eax)\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int __attribute__((optimize("omit-frame-pointer"), no_instrument_function)) setcontext(const ucontext_t *ucp)
|
||||
{
|
||||
asm volatile ("1:\n" :: "a"(ucp));
|
||||
|
||||
/* asm volatile ("mov 4*3(%eax), %eax\n");, omitted */
|
||||
asm volatile ("mov 4*4(%eax), %ecx\n");
|
||||
/* asm volatile ("mov 4*5(%eax), %edx\n");, omitted */
|
||||
asm volatile ("mov 4*6(%eax), %ebx\n");
|
||||
/* asm volatile ("mov 4*7(%eax), %esp\n");, omitted */
|
||||
asm volatile ("mov 4*8(%eax), %ebp\n");
|
||||
asm volatile ("mov 4*9(%eax), %esi\n");
|
||||
asm volatile ("mov 4*10(%eax), %edi\n");
|
||||
|
||||
asm volatile ("mov 4*0(%eax), %esp\n");
|
||||
asm volatile ("add 4*1(%eax), %esp\n");
|
||||
|
||||
asm volatile ("mov 4*11(%eax), %edx\n");
|
||||
asm volatile ("mov %eax, %ebx\n");
|
||||
|
||||
asm volatile ("push 4*2(%eax)\n");
|
||||
asm volatile ("popf\n");
|
||||
|
||||
asm volatile ("call *%edx\n");
|
||||
|
||||
asm volatile ("mov 4*12(%ebx), %eax\n");
|
||||
asm volatile ("jmp 1b\n");
|
||||
__builtin_unreachable();
|
||||
}
|
||||
|
||||
static void __attribute__((optimize("omit-frame-pointer"), noreturn, no_instrument_function)) makecontext_entrypoint(void)
|
||||
{
|
||||
/* ebx = ucp, ecx = argc, ebp = arg[0], esi = arg[1], edi = arg[2] */
|
||||
asm volatile ("mov 4*3(%ebx), %eax\n"); /* eax = func */
|
||||
|
||||
asm volatile ("jecxz 0f\n");
|
||||
asm volatile ("cmpb $1, %cl; je 1f\n");
|
||||
asm volatile ("cmpb $2, %cl; je 2f\n");
|
||||
asm volatile ("cmpb $3, %cl; je 3f\n");
|
||||
asm volatile ("cmpb $4, %cl; je 4f\n");
|
||||
|
||||
asm volatile (" mov 4*7(%ebx), %edx; push %edx\n");
|
||||
asm volatile ("4: mov 4*5(%ebx), %edx; push %edx\n");
|
||||
asm volatile ("3: push %edi\n");
|
||||
asm volatile ("2: push %esi\n");
|
||||
asm volatile ("1: push %ebp\n");
|
||||
asm volatile ("0: call *%eax\n"); /* call func(...), preserves ebx */
|
||||
|
||||
asm volatile ("mov 4*12(%ebx), %eax\n");
|
||||
asm volatile ("push %eax\n");
|
||||
asm volatile ("call setcontext\n");
|
||||
__builtin_unreachable();
|
||||
}
|
||||
|
||||
void makecontext(ucontext_t *ucp, makecontext_fun_t func, int argc, ...)
|
||||
{
|
||||
ucp->uc_context.ip = (void *) (unsigned long) &makecontext_entrypoint;
|
||||
ucp->uc_context.registers.bx = (unsigned long) ucp;
|
||||
|
||||
ucp->uc_context.registers.ax = (unsigned long) func;
|
||||
ucp->uc_context.registers.cx = argc > 0 ? argc : 0;
|
||||
|
||||
if (argc <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
unsigned long *arg_registers[5] = {
|
||||
&ucp->uc_context.registers.bp,
|
||||
&ucp->uc_context.registers.si,
|
||||
&ucp->uc_context.registers.di,
|
||||
&ucp->uc_context.registers.dx,
|
||||
&ucp->uc_context.registers.sp,
|
||||
};
|
||||
va_list ap;
|
||||
va_start(ap, argc);
|
||||
for (int i = 0; i < argc; ++i) {
|
||||
*arg_registers[i] = va_arg(ap, unsigned long);
|
||||
}
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
int swapcontext(ucontext_t *oucp, const ucontext_t *ucp)
|
||||
{
|
||||
/* Valid sentinel: an address in .text section is not valid for a stack pointer. */
|
||||
oucp->uc_context.registers.sp = (unsigned long) &swapcontext;
|
||||
getcontext(oucp);
|
||||
if (oucp->uc_context.registers.sp == (unsigned long) &swapcontext) {
|
||||
oucp->uc_context.registers.sp = 0;
|
||||
setcontext(ucp);
|
||||
}
|
||||
return 0;
|
||||
}
|
82
cpu/x86/x86_videoram.c
Normal file
82
cpu/x86/x86_videoram.c
Normal file
@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright (C) 2014 René Kijewski <rene.kijewski@fu-berlin.de>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ingroup x86
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Trivial functions to write to the videoram.
|
||||
*
|
||||
* @author René Kijewski <rene.kijewski@fu-berlin.de>
|
||||
*
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include "x86_videoram.h"
|
||||
|
||||
#define VIDEORAM ((char *) 0xB8000)
|
||||
|
||||
void videoram_putc(char c)
|
||||
{
|
||||
static unsigned pos = 0;
|
||||
if (c == '\n') {
|
||||
pos += 80;
|
||||
}
|
||||
else if (c == '\r') {
|
||||
pos = (pos / 80) * 80;
|
||||
}
|
||||
else {
|
||||
VIDEORAM[2 * pos + 0] = c;
|
||||
VIDEORAM[2 * pos + 1] = (char) 0x9E; /* light blue bg, light yellow fg */
|
||||
++pos;
|
||||
}
|
||||
if (pos >= 25 * 80) {
|
||||
pos = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void videoram_put(const char *s)
|
||||
{
|
||||
for (; *s; ++s) {
|
||||
videoram_putc(*s);
|
||||
}
|
||||
}
|
||||
|
||||
void videoram_puts(const char *s)
|
||||
{
|
||||
videoram_put(s);
|
||||
videoram_put("\r\n");
|
||||
}
|
||||
|
||||
void videoram_put_hex(unsigned long v)
|
||||
{
|
||||
char s[2 + 2 * sizeof v + 1];
|
||||
char *p = &s[sizeof s];
|
||||
|
||||
*--p = 0;
|
||||
do {
|
||||
char c = v % 16;
|
||||
v /= 16;
|
||||
*--p = c + (c < 10 ? '0' : 'A' - 10);
|
||||
} while (v > 0);
|
||||
*--p = 'x';
|
||||
*--p = '0';
|
||||
|
||||
videoram_put(p);
|
||||
}
|
386
dist/tools/toolchains/build_x86.sh
vendored
Executable file
386
dist/tools/toolchains/build_x86.sh
vendored
Executable file
@ -0,0 +1,386 @@
|
||||
#!/bin/bash
|
||||
|
||||
# directory to download source files and store intermediates
|
||||
[[ -z $TARGET ]] && TARGET=i586-none-elf
|
||||
[[ -z $TMP_DIR ]] && TMP_DIR=/var/tmp/RIOT-toolchain-${USER}
|
||||
[[ -z $BUILDDIR ]] && BUILDDIR=${TMP_DIR}/${TARGET}
|
||||
[[ -z $PREFIX ]] && PREFIX=${PWD}/toolchain/${TARGET}
|
||||
[[ -z $MAKE_THREADS ]] && MAKE_THREADS=
|
||||
|
||||
NEWLIB_VER=2.1.0
|
||||
NEWLIB_MD5=c6559d83ecce4728a52f0ce7ec80de97
|
||||
NEWLIB_CONFIGURE_FLAGS=(
|
||||
--target=${TARGET}
|
||||
--prefix=${PREFIX}
|
||||
--with-gmp=${PREFIX}
|
||||
--with-mpfr=${PREFIX}
|
||||
--with-mpc=${PREFIX}
|
||||
--enable-interwork
|
||||
--enable-multilib
|
||||
|
||||
--enable-target-optspace
|
||||
--enable-lto
|
||||
--disable-newlib-supplied-syscalls
|
||||
--enable-newlib-reent-small
|
||||
--enable-newlib-io-long-long
|
||||
--enable-newlib-io-float
|
||||
--enable-newlib-io-pos-args
|
||||
--enable-newlib-io-c99-formats
|
||||
--enable-newlib-multithread
|
||||
)
|
||||
NEWLIB_PATCHES=(
|
||||
libgloss/i386/cygmon-gmon.c
|
||||
'64d
|
||||
59a
|
||||
#include <string.h>
|
||||
|
||||
.'
|
||||
|
||||
libgloss/arm/_exit.c
|
||||
'14a
|
||||
__builtin_unreachable ();
|
||||
.'
|
||||
|
||||
newlib/libc/stdlib/mallocr.c
|
||||
'3700d
|
||||
3434,3440d
|
||||
3422,3424d
|
||||
3400,3404d
|
||||
3357,3370d
|
||||
3278,3355d
|
||||
1764,1898d
|
||||
388d
|
||||
384,386d
|
||||
1,3d'
|
||||
|
||||
newlib/libc/stdlib/mlock.c
|
||||
'63,64d
|
||||
1d'
|
||||
)
|
||||
NEWLIB_TARGET_CFLAGS=(
|
||||
-DREENTRANT_SYSCALLS_PROVIDED
|
||||
-DMALLOC_PROVIDED
|
||||
-DSIGNAL_PROVIDED
|
||||
|
||||
-ggdb3
|
||||
-Os
|
||||
-flto
|
||||
-fomit-frame-pointer
|
||||
-ffunction-sections
|
||||
-fdata-sections
|
||||
)
|
||||
|
||||
GCC_VER=4.8.2
|
||||
GCC_MD5=a3d7d63b9cb6b6ea049469a0c4a43c9d
|
||||
GCC_CONFIGURE_FLAGS=(
|
||||
--target=${TARGET}
|
||||
--prefix=${PREFIX}
|
||||
--with-gmp=${PREFIX}
|
||||
--with-mpfr=${PREFIX}
|
||||
--with-mpc=${PREFIX}
|
||||
|
||||
--enable-interwork
|
||||
--enable-multilib
|
||||
--enable-languages="c,c++"
|
||||
--with-newlib
|
||||
#--enable-lto
|
||||
--disable-libssp
|
||||
--with-headers=${BUILDDIR}/newlib-${NEWLIB_VER}/newlib/libc/include
|
||||
--enable-obsolete
|
||||
--enable-target-optspace
|
||||
--disable-nls
|
||||
)
|
||||
|
||||
BINUTILS_VER=2.24
|
||||
BINUTILS_MD5=e0f71a7b2ddab0f8612336ac81d9636b
|
||||
BINUTILS_CONFIGURE_FLAGS=(
|
||||
--prefix=${PREFIX}
|
||||
|
||||
--enable-interwork
|
||||
--enable-multilib
|
||||
#--enable-lto
|
||||
--disable-nls
|
||||
)
|
||||
|
||||
GDB_VER=7.6
|
||||
GDB_MD5=fda57170e4d11cdde74259ca575412a8
|
||||
GDB_CONFIGURE_FLAGS=(
|
||||
--target=${TARGET}
|
||||
--prefix=${PREFIX}
|
||||
--with-gmp=${PREFIX}
|
||||
--with-mpfr=${PREFIX}
|
||||
--with-mpc=${PREFIX}
|
||||
|
||||
--enable-interwork
|
||||
--enable-multilib
|
||||
--disable-nls
|
||||
)
|
||||
|
||||
GMP_VER=5.1.3
|
||||
GMP_MD5=e5fe367801ff067b923d1e6a126448aa
|
||||
GMP_CONFIGURE_FLAGS=(
|
||||
--prefix=${PREFIX}
|
||||
|
||||
--enable-cxx
|
||||
--disable-nls
|
||||
)
|
||||
|
||||
MPFR_VER=3.1.2
|
||||
MPFR_MD5=e3d203d188b8fe60bb6578dd3152e05c
|
||||
MPFR_CONFIGURE_FLAGS=(
|
||||
--prefix=${PREFIX}
|
||||
--with-gmp=${PREFIX}
|
||||
|
||||
--disable-nls
|
||||
)
|
||||
|
||||
MPC_VER=1.0.2
|
||||
MPC_MD5=68fadff3358fb3e7976c7a398a0af4c3
|
||||
MPC_CONFIGURE_FLAGS=(
|
||||
--prefix=${PREFIX}
|
||||
--with-gmp=${PREFIX}
|
||||
--with-mpfr=${PREFIX}
|
||||
|
||||
--disable-nls
|
||||
)
|
||||
|
||||
DOWNLOADER=wget
|
||||
DOWNLOADER_OPTS="-nv -c"
|
||||
|
||||
SPACE_NEEDED=3264960
|
||||
|
||||
build_binutils() {
|
||||
cd ${BUILDDIR}
|
||||
echo
|
||||
[[ -e .binutils_built ]] && return
|
||||
|
||||
local TARGETS=(${TARGET} i386-efi-pe)
|
||||
for i in 0 1; do
|
||||
echo "Building binutils (${BINUTILS_VER}) for ${TARGETS[$i]} ..."
|
||||
|
||||
rm -rf binutils-${TARGETS[$i]}-build &&
|
||||
mkdir -p binutils-${TARGETS[$i]}-build &&
|
||||
cd binutils-${TARGETS[$i]}-build &&
|
||||
|
||||
../binutils-${BINUTILS_VER}/configure "${BINUTILS_CONFIGURE_FLAGS[@]}" --target=${TARGETS[$i]} --enable-targets=${TARGETS[$i]},${TARGETS[1-$i]} &&
|
||||
make ${MAKE_THREADS} all &&
|
||||
make install &&
|
||||
cd ${BUILDDIR} ||
|
||||
return $?
|
||||
done
|
||||
|
||||
date -uIns > .binutils_built
|
||||
}
|
||||
|
||||
build_gcc() {
|
||||
cd ${BUILDDIR}
|
||||
echo
|
||||
echo "Building gcc (${GCC_VER}) ..."
|
||||
[[ -e .gcc_built ]] && return
|
||||
|
||||
rm -rf gcc-build && mkdir -p gcc-build && cd gcc-build &&
|
||||
../gcc-${GCC_VER}/configure "${GCC_CONFIGURE_FLAGS[@]}" &&
|
||||
make ${MAKE_THREADS} all &&
|
||||
make install &&
|
||||
|
||||
cd ${BUILDDIR} &&
|
||||
date -uIns > .gcc_built
|
||||
}
|
||||
|
||||
build_newlib() {
|
||||
cd ${BUILDDIR}
|
||||
echo
|
||||
echo "Building newlib (${NEWLIB_VER}) ..."
|
||||
[[ -e .newlib_built ]] && return
|
||||
|
||||
rm -rf newlib-build && mkdir -p newlib-build && cd newlib-build &&
|
||||
CFLAGS="${CFLAGS} -DREENTRANT_SYSCALLS_PROVIDED -DMALLOC_PROVIDED" \
|
||||
../newlib-${NEWLIB_VER}/configure "${NEWLIB_CONFIGURE_FLAGS[@]}"
|
||||
make ${MAKE_THREADS} TARGET_CFLAGS="${NEWLIB_TARGET_CFLAGS[*]}" all &&
|
||||
make install &&
|
||||
|
||||
cd ${BUILDDIR} &&
|
||||
date -uIns > .newlib_built
|
||||
}
|
||||
|
||||
build_gdb() {
|
||||
cd ${BUILDDIR}
|
||||
echo
|
||||
echo "Building gdb (${GDB_VER}) ..."
|
||||
[[ -e .gdb_built ]] && return
|
||||
|
||||
rm -rf gdb-build && mkdir -p gdb-build && cd gdb-build &&
|
||||
../gdb-${GDB_VER}/configure "${GDB_CONFIGURE_FLAGS[@]}" &&
|
||||
make ${MAKE_THREADS} all CFLAGS="-U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=0" &&
|
||||
make install &&
|
||||
|
||||
cd ${BUILDDIR} &&
|
||||
date -uIns > .gdb_built
|
||||
}
|
||||
|
||||
build_gmp() {
|
||||
cd ${BUILDDIR}
|
||||
echo
|
||||
echo "Building gmp (${GMP_VER}) ..."
|
||||
[[ -e .gmp_built ]] && return
|
||||
|
||||
rm -rf gmp-build && mkdir -p gmp-build && cd gmp-build &&
|
||||
../gmp-${GMP_VER}/configure "${GMP_CONFIGURE_FLAGS[@]}" &&
|
||||
make ${MAKE_THREADS} all &&
|
||||
make install &&
|
||||
|
||||
cd ${BUILDDIR} &&
|
||||
date -uIns > .gmp_built
|
||||
}
|
||||
|
||||
build_mpfr() {
|
||||
cd ${BUILDDIR}
|
||||
echo
|
||||
echo "Building mpfr (${MPFR_VER}) ..."
|
||||
[[ -e .mpfr_built ]] && return
|
||||
|
||||
rm -rf mpfr-build && mkdir -p mpfr-build && cd mpfr-build &&
|
||||
../mpfr-${MPFR_VER}/configure "${MPFR_CONFIGURE_FLAGS[@]}" &&
|
||||
make ${MAKE_THREADS} all &&
|
||||
make install &&
|
||||
|
||||
cd ${BUILDDIR} &&
|
||||
date -uIns > .mpfr_built
|
||||
}
|
||||
|
||||
build_mpc() {
|
||||
cd ${BUILDDIR}
|
||||
echo
|
||||
echo "Building mpc (${MPC_VER}) ..."
|
||||
[[ -e .mpc_built ]] && return
|
||||
|
||||
rm -rf mpc-build && mkdir -p mpc-build && cd mpc-build &&
|
||||
../mpc-${MPC_VER}/configure "${MPC_CONFIGURE_FLAGS[@]}" &&
|
||||
make ${MAKE_THREADS} all &&
|
||||
make install &&
|
||||
|
||||
cd ${BUILDDIR} &&
|
||||
date -uIns > .mpc_built
|
||||
}
|
||||
|
||||
clean() {
|
||||
echo
|
||||
echo "Cleaning up..."
|
||||
for F in binutils gcc newlib gdb gmp mpfr mpc; do
|
||||
rm -rf ./.${F}_extracted ./.${F}_built ./${F}_build || return $?
|
||||
done
|
||||
}
|
||||
|
||||
export PATH=$PATH:${PREFIX}/bin
|
||||
|
||||
download() {
|
||||
echo
|
||||
echo "Downloading TAR files."
|
||||
|
||||
download_file ftp://ftp.cs.tu-berlin.de/pub/gnu/binutils binutils-${BINUTILS_VER}.tar.bz2 ${BINUTILS_MD5} &&
|
||||
download_file ftp://ftp.cs.tu-berlin.de/pub/gnu/gcc/gcc-${GCC_VER} gcc-${GCC_VER}.tar.bz2 ${GCC_MD5} &&
|
||||
download_file ftp://ftp.cs.tu-berlin.de/pub/gnu/gdb gdb-${GDB_VER}.tar.bz2 ${GDB_MD5} &&
|
||||
download_file ftp://ftp.cs.tu-berlin.de/pub/gnu/gmp gmp-${GMP_VER}.tar.xz ${GMP_MD5} &&
|
||||
download_file ftp://ftp.cs.tu-berlin.de/pub/gnu/mpfr mpfr-${MPFR_VER}.tar.xz ${MPFR_MD5} &&
|
||||
download_file ftp://ftp.cs.tu-berlin.de/pub/gnu/mpc mpc-${MPC_VER}.tar.gz ${MPC_MD5} &&
|
||||
download_file ftp://sources.redhat.com/pub/newlib newlib-${NEWLIB_VER}.tar.gz ${NEWLIB_MD5}
|
||||
}
|
||||
|
||||
extract() {
|
||||
echo
|
||||
echo "Extracting TAR files."
|
||||
|
||||
if [[ ! -x ./binutils-${BINUTILS_VER}/configure ]]; then
|
||||
tar xjf ${TMP_DIR}/binutils-${BINUTILS_VER}.tar.bz2 || return $?
|
||||
fi
|
||||
if [[ ! -x ./gcc-${GCC_VER}/configure ]]; then
|
||||
tar xjf ${TMP_DIR}/gcc-${GCC_VER}.tar.bz2 || return $?
|
||||
fi
|
||||
if [[ ! -x ./gdb-${GDB_VER}/configure ]]; then
|
||||
tar xjf ${TMP_DIR}/gdb-${GDB_VER}.tar.bz2 || return $?
|
||||
fi
|
||||
if [[ ! -x ./gmp-${GMP_VER}/configure ]]; then
|
||||
tar xJf ${TMP_DIR}/gmp-${GMP_VER}.tar.xz || return $?
|
||||
fi
|
||||
if [[ ! -x ./mpfr-${MPFR_VER}/configure ]]; then
|
||||
tar xJf ${TMP_DIR}/mpfr-${MPFR_VER}.tar.xz || return $?
|
||||
fi
|
||||
if [[ ! -x ./mpc-${MPC_VER}/configure ]]; then
|
||||
tar xzf ${TMP_DIR}/mpc-${MPC_VER}.tar.gz || return $?
|
||||
fi
|
||||
if [[ ! -x ./newlib-${NEWLIB_VER}/configure ]]; then
|
||||
tar xzf ${TMP_DIR}/newlib-${NEWLIB_VER}.tar.gz &&
|
||||
for (( I=0; I < ${#NEWLIB_PATCHES[@]}; I+=2 )); do
|
||||
echo "Applying Newlib patch $((${I} / 2 + 1))"
|
||||
echo "${NEWLIB_PATCHES[$I+1]}" | patch -e ./newlib-${NEWLIB_VER}/"${NEWLIB_PATCHES[$I]}" || return $?
|
||||
done
|
||||
fi
|
||||
}
|
||||
|
||||
download_file() {
|
||||
echo "Downloading ${1}/${2}..."
|
||||
cd ${TMP_DIR} &&
|
||||
${DOWNLOADER} ${DOWNLOADER_OPTS} $1/$2 &&
|
||||
echo -n "Checking MD5 of " &&
|
||||
echo "${3} ${2}" | md5sum -c - &&
|
||||
cd ${BUILDDIR}
|
||||
}
|
||||
|
||||
check_space() {
|
||||
echo
|
||||
echo "Checking disk space in ${TMP_DIR}"
|
||||
|
||||
FREETMP=$(df ${BUILDDIR} | awk '{ if (NR == 2) print $4}')
|
||||
if [[ $FREETMP -lt $SPACE_NEEDED ]]; then
|
||||
echo "Not enough available space in ${TMP_DIR}."
|
||||
echo "Minimum ${SPACE_NEEDED} free bytes required."
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
all() {
|
||||
echo
|
||||
echo "Starting in ${BUILDDIR}. Installing to ${PREFIX}."
|
||||
|
||||
check_space &&
|
||||
download &&
|
||||
extract &&
|
||||
build_binutils &&
|
||||
build_gmp &&
|
||||
build_mpfr &&
|
||||
build_mpc &&
|
||||
build_gcc &&
|
||||
build_newlib &&
|
||||
build_gdb &&
|
||||
|
||||
echo &&
|
||||
echo 'Success!' &&
|
||||
echo "Insert \"export PATH=${PREFIX}/bin:\$PATH\" to your .bashrc"
|
||||
}
|
||||
|
||||
usage() {
|
||||
echo "usage: ${0} [all | download | extract | clean | build_[binutils|gcc|newlib|gdb|gmp|mpfr|mpc]]"
|
||||
echo "example: ${0} all"
|
||||
echo ""
|
||||
echo "Builds a(n) \"${TARGET}\" toolchain,"
|
||||
echo "installs into \"${PREFIX}\", and"
|
||||
echo "uses \"${TMP_DIR}\" as temp."
|
||||
echo ""
|
||||
echo "Use PREFIX=... and TMP_DIR=... to change these directories."
|
||||
echo "Use TARGET=... to change the target (affects PREFIX)."
|
||||
echo ""
|
||||
echo "Run like \"MAKE_THREADS=-j$(nproc) ${0} all\" to speed up on multicore systems."
|
||||
}
|
||||
|
||||
if [[ -z $1 ]]; then
|
||||
usage
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mkdir -p ${PREFIX} &&
|
||||
mkdir -p ${TMP_DIR} &&
|
||||
mkdir -p ${BUILDDIR} &&
|
||||
cd ${BUILDDIR} &&
|
||||
for arg; do
|
||||
${arg} || exit $?
|
||||
done
|
167
dist/tools/toolchains/build_x86_newlib.sh
vendored
Executable file
167
dist/tools/toolchains/build_x86_newlib.sh
vendored
Executable file
@ -0,0 +1,167 @@
|
||||
#!/bin/bash
|
||||
|
||||
# directory to download source files and store intermediates
|
||||
[[ -z $TARGET ]] && TARGET=i586-none-elf
|
||||
[[ -z $TMP_DIR ]] && TMP_DIR=/var/tmp/RIOT-toolchain-${USER}
|
||||
[[ -z $BUILDDIR ]] && BUILDDIR=${TMP_DIR}/x86
|
||||
[[ -z $PREFIX ]] && PREFIX=${PWD}/toolchain/x86
|
||||
[[ -z $MAKE_THREADS ]] && MAKE_THREADS=
|
||||
|
||||
NEWLIB_VER=2.1.0
|
||||
NEWLIB_MD5=c6559d83ecce4728a52f0ce7ec80de97
|
||||
NEWLIB_CONFIGURE_FLAGS=(
|
||||
--target=${TARGET}
|
||||
--prefix=${PREFIX}
|
||||
--with-gmp=${PREFIX}
|
||||
--with-mpfr=${PREFIX}
|
||||
--with-mpc=${PREFIX}
|
||||
--enable-interwork
|
||||
--enable-multilib
|
||||
|
||||
--enable-target-optspace
|
||||
--enable-lto
|
||||
--disable-newlib-supplied-syscalls
|
||||
--enable-newlib-reent-small
|
||||
--enable-newlib-io-long-long
|
||||
--enable-newlib-io-float
|
||||
--enable-newlib-io-pos-args
|
||||
--enable-newlib-io-c99-formats
|
||||
--enable-newlib-multithread
|
||||
)
|
||||
NEWLIB_PATCHES=(
|
||||
libgloss/i386/cygmon-gmon.c
|
||||
'64d
|
||||
59a
|
||||
#include <string.h>
|
||||
|
||||
.'
|
||||
|
||||
libgloss/arm/_exit.c
|
||||
'14a
|
||||
__builtin_unreachable ();
|
||||
.'
|
||||
|
||||
newlib/libc/stdlib/mallocr.c
|
||||
'3700d
|
||||
3434,3440d
|
||||
3422,3424d
|
||||
3400,3404d
|
||||
3357,3370d
|
||||
3278,3355d
|
||||
1764,1898d
|
||||
388d
|
||||
384,386d
|
||||
1,3d'
|
||||
|
||||
newlib/libc/stdlib/mlock.c
|
||||
'63,64d
|
||||
1d'
|
||||
)
|
||||
NEWLIB_TARGET_CFLAGS=(
|
||||
-DREENTRANT_SYSCALLS_PROVIDED
|
||||
-DMALLOC_PROVIDED
|
||||
-DSIGNAL_PROVIDED
|
||||
|
||||
-ggdb3
|
||||
-Os
|
||||
-flto
|
||||
-fomit-frame-pointer
|
||||
-ffunction-sections
|
||||
-fdata-sections
|
||||
)
|
||||
|
||||
DOWNLOADER=wget
|
||||
DOWNLOADER_OPTS="-nv -c"
|
||||
|
||||
build_newlib() {
|
||||
cd ${BUILDDIR}
|
||||
echo
|
||||
echo "Building newlib (${NEWLIB_VER}) ..."
|
||||
[[ -e .newlib_built ]] && return
|
||||
|
||||
rm -rf newlib-build && mkdir -p newlib-build && cd newlib-build &&
|
||||
CFLAGS="${CFLAGS} -DREENTRANT_SYSCALLS_PROVIDED -DMALLOC_PROVIDED" \
|
||||
../newlib-${NEWLIB_VER}/configure "${NEWLIB_CONFIGURE_FLAGS[@]}"
|
||||
make ${MAKE_THREADS} TARGET_CFLAGS="${NEWLIB_TARGET_CFLAGS[*]}" all &&
|
||||
make install &&
|
||||
|
||||
cd ${BUILDDIR} &&
|
||||
date -uIns > .newlib_built
|
||||
}
|
||||
|
||||
clean() {
|
||||
echo
|
||||
echo "Cleaning up..."
|
||||
for F in newlib; do
|
||||
rm -rf ./.${F}_extracted ./.${F}_built ./${F}_build || return $?
|
||||
done
|
||||
}
|
||||
|
||||
download() {
|
||||
echo
|
||||
echo "Downloading TAR files."
|
||||
|
||||
download_file ftp://sources.redhat.com/pub/newlib newlib-${NEWLIB_VER}.tar.gz ${NEWLIB_MD5}
|
||||
}
|
||||
|
||||
extract() {
|
||||
echo
|
||||
echo "Extracting TAR files."
|
||||
|
||||
if [[ ! -x ./newlib-${NEWLIB_VER}/configure ]]; then
|
||||
tar xzf ${TMP_DIR}/newlib-${NEWLIB_VER}.tar.gz &&
|
||||
for (( I=0; I < ${#NEWLIB_PATCHES[@]}; I+=2 )); do
|
||||
echo "Applying Newlib patch $((${I} / 2 + 1))"
|
||||
echo "${NEWLIB_PATCHES[$I+1]}" | patch -e ./newlib-${NEWLIB_VER}/"${NEWLIB_PATCHES[$I]}" || return $?
|
||||
done
|
||||
fi
|
||||
}
|
||||
|
||||
download_file() {
|
||||
echo "Downloading ${1}/${2}..."
|
||||
cd ${TMP_DIR} &&
|
||||
${DOWNLOADER} ${DOWNLOADER_OPTS} $1/$2 &&
|
||||
echo -n "Checking MD5 of " &&
|
||||
echo "${3} ${2}" | md5sum -c - &&
|
||||
cd ${BUILDDIR}
|
||||
}
|
||||
|
||||
all() {
|
||||
echo
|
||||
echo "Starting in ${BUILDDIR}. Installing to ${PREFIX}."
|
||||
|
||||
download &&
|
||||
extract &&
|
||||
build_newlib &&
|
||||
|
||||
echo &&
|
||||
echo 'Success!' &&
|
||||
echo "Insert \"export PATH=${PREFIX}/bin:\$PATH\" to your .bashrc"
|
||||
}
|
||||
|
||||
usage() {
|
||||
echo "usage: ${0} [all | download | extract | clean | build_newlib]"
|
||||
echo "example: ${0} all"
|
||||
echo ""
|
||||
echo "Builds a(n) \"${TARGET}\" toolchain,"
|
||||
echo "installs into \"${PREFIX}\", and"
|
||||
echo "uses \"${TMP_DIR}\" as temp."
|
||||
echo ""
|
||||
echo "Use PREFIX=... and TMP_DIR=... to change these directories."
|
||||
echo "Use TARGET=... to change the target (affects PREFIX)."
|
||||
echo ""
|
||||
echo "Run like \"MAKE_THREADS=-j$(nproc) ${0} all\" to speed up on multicore systems."
|
||||
}
|
||||
|
||||
if [[ -z $1 ]]; then
|
||||
usage
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mkdir -p ${PREFIX} &&
|
||||
mkdir -p ${TMP_DIR} &&
|
||||
mkdir -p ${BUILDDIR} &&
|
||||
cd ${BUILDDIR} &&
|
||||
for arg; do
|
||||
${arg} || exit $?
|
||||
done
|
@ -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:
|
||||
|
||||
|
@ -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:
|
||||
|
||||
|
@ -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:
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
63
sys/shell/commands/sc_x86_lspci.c
Normal file
63
sys/shell/commands/sc_x86_lspci.c
Normal file
@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright (C) 2014 René Kijewski <rene.kijewski@fu-berlin.de>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ingroup shell_commands
|
||||
* @ingroup x86-irq
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief List PCI devices
|
||||
*
|
||||
* @author René Kijewski <rene.kijewski@fu-berlin.de>
|
||||
*
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include "x86_pci.h"
|
||||
#include "x86_pci_strings.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
extern void _x86_lspci(int argc, char **argv)
|
||||
{
|
||||
(void) argc;
|
||||
(void) argv;
|
||||
|
||||
unsigned index = 0;
|
||||
while (1) {
|
||||
const char *baseclass_name, *subclass_name, *vendor_name, *device_name;
|
||||
|
||||
const x86_known_pci_device_t *d = x86_enumerate_pci_devices(&index);
|
||||
if (!d) {
|
||||
break;
|
||||
}
|
||||
|
||||
subclass_name = x86_pci_subclass_to_string(d->class.baseclass,
|
||||
d->class.subclass,
|
||||
d->class.programming_interface,
|
||||
&baseclass_name);
|
||||
vendor_name = x86_pci_device_id_to_string(d->vendor.vendor_id,
|
||||
d->vendor.device_id,
|
||||
&device_name);
|
||||
printf("%02x:%02x.%x \"%s\": \"%s\" (%s: %s, rev: %02hhx)\n",
|
||||
d->bus, d->dev, d->fun,
|
||||
vendor_name, device_name,
|
||||
baseclass_name, subclass_name, d->class.revision_id);
|
||||
}
|
||||
}
|
@ -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}
|
||||
};
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user